Browse Source

Wrap the <prefix>/bin/openpkg command with a small SetUID program which executes the command under a particular user according to the following table: ----------------------------- --------------------- --------------- Caller is matched by entry in Command requires Command is then <prefix>/etc/openpkg/managers super-user privileges executed under ----------------------------- --------------------- --------------- no no caller no yes caller yes no management user yes yes super user ----------------------------- --------------------- --------------- By default the <prefix>/etc/openpkg/managers contains only entries corresponding to the OpenPKG instance management user (%{l_musr}, e.g. "openpkg") and the super user (%{l_susr}, e.g. "root"). As a result this especially means: 1. For regular users and their usage of <prefix>/bin/openpkg nothing changes. They are not affected by the feature at all. 2. The management user is able to now really manage the complete instance as it now is able to build and install packages, stop and start services, etc. For instance if it runs "<prefix>/etc/openpkg --rebuild ..." this is still done under his privileges, but the following "<prefix>/etc/openpkg -Uvh ..." is done with root privileges. This means no more "sudo" setups required for the management user and especially the OpenPKG handling is much simplified for the average user as he no longer has to think about whether he has to switch to or from root for the next command all the time. 3. The super user is now downgraded to the management user for commands which do not require super user privileges. This especially means that if the super user executes a "<prefix>/etc/openpkg --rebuild ..." it automatically internally is executed under the management user. This way no more packages _CAN_ be built under root privileges. This is a further improvement in security and also improves the OpenPKG "sane build environment" idea. The only less precise part of this feature is the determination whether a command really requires super user privileges. Without really parsing the complete "openpkg rpm" and "openpkg rc" command line we can only guess on this. Please review this part very carefully. We detected no problems until now, but there might be command lines where we might guess incorrectly. For this situation there is a "<prefix>/bin/openpkg --keep-privileges" option which disables the whole privilege upgrading/downgrading at all and this way allows one to easily circumvent a situation where we incorrectly guess the command line. For your security review of this patch please keep especially in mind that the OpenPKG management user from a security point of view always _was_ and still _is_ fully equal to the super user. In case you forgot this, the reason simply is that if you have the privileges of the management user you always can become the super user by just editing an arbitrary rc file of a daemon or something similar. So, we do NOT change the security paranoid aspects just by allowing the management user to automatically upgrade to the super user now.

Ralf S. Engelschall 19 years ago
parent
commit
a85e9054d3
5 changed files with 490 additions and 8 deletions
  1. 1 0
      openpkg/HISTORY
  2. 4 3
      openpkg/aux.wrapbin.sh
  3. 1 1
      openpkg/openpkg.boot
  4. 451 0
      openpkg/openpkg.c
  5. 33 4
      openpkg/openpkg.spec

+ 1 - 0
openpkg/HISTORY

@@ -2,6 +2,7 @@
 2006
 ====
 
+20060823 make <prefix>/bin/openpkg "set-uid" for allowing it to downgrade/upgrade privileges automatically
 20060823 upgrade to openpkg-tools 0.8.69 (no longer uses "curl -q" in "openpkg build")
 20060823 fix "openpkg release" with workaround for Solaris sed(1) which ignores lines without trailing newlines
 

+ 4 - 3
openpkg/aux.wrapbin.sh

@@ -173,13 +173,14 @@ echo "++ installing OpenPKG binary distribution"
 ) || exit 1
 
 #   fixate installation files
+#   (ATTENTION: order of chgrp/chown and chmod is important because of "set-UID" bits)
 echo "++ fixating OpenPKG instance filesystem hierarchy"
 ( echo 'fixate () {'
-  echo '    chmod "$1" "$4"'
-  echo '    chown "$2" "$4"'
   echo '    chgrp "$3" "$4"'
+  echo '    chown "$2" "$4"'
+  echo '    chmod "$1" "$4"'
   echo '}'
-  $l_prefix/bin/openpkg rpm -qa \
+  $l_prefix/bin/openpkg --keep-privileges rpm -q openpkg \
       --qf '[fixate %7.7{FILEMODES:octal} %{FILEUSERNAME:shescape} %{FILEGROUPNAME:shescape} ::%{FILENAMES:shescape}\n]' |\
       grep -v '(none)' | sed 's/^fixate .../fixate /' | sed -e "s; ::\\(.\\)@l_prefix@; \\1$l_prefix;"
 ) | sh 2>/dev/null || true

+ 1 - 1
openpkg/openpkg.boot

@@ -490,7 +490,7 @@ echo "++ creating bootstrap binary shell script"
 files=`cat $spec |\
        sed -e '1,/%files/d' -e '/%clean/,$d' |\
        grep -v '^ *$' | grep -v '%defattr' |\
-       sed -e 's;%config(noreplace) *;;' -e 's;%config *;;' -e 's;%ghost *;;' \
+       sed -e 's;%config(noreplace) *;;' -e 's;%config *;;' -e 's;%ghost *;;' -e 's;%attr([^)]*) *;;' \
            -e 's;%dir *;;' -e 's;%{l_prefix}/;;' -e 's;^ *;;' -e "s;%{V_rpm};${V_rpm};"`
 db_files=""
 for db_file in \

+ 451 - 0
openpkg/openpkg.c

@@ -0,0 +1,451 @@
+/*
+**  openpkg -- OpenPKG Tool Chain
+**  Copyright (c) 2000-2006 OpenPKG Foundation e.V. <http://openpkg.net/>
+**  Copyright (c) 2000-2006 Ralf S. Engelschall <http://engelschall.com/>
+**
+**  Permission to use, copy, modify, and distribute this software for
+**  any purpose with or without fee is hereby granted, provided that
+**  the above copyright notice and this permission notice appear in all
+**  copies.
+**
+**  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+**  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+**  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+**  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
+**  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+**  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+**  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+**  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+**  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+**  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+**  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+**  SUCH DAMAGE.
+**
+**  openpkg.c: Execution Wrapper (Language: C)
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <grp.h>
+#include <unistd.h>
+#include <errno.h>
+
+/* sanity check compilation */
+#ifndef OPENPKG_PREFIX
+#error OpenPKG instance prefix not defined
+#endif
+#ifndef OPENPKG_SUSR
+#error OpenPKG super user not defined
+#endif
+#ifndef OPENPKG_MUSR
+#error OpenPKG management user not defined
+#endif
+
+/* platform specifics */
+#if defined(OPENPKG_PLATFORM_FREEBSD) || \
+    defined(OPENPKG_PLATFORM_NETBSD)  || \
+    defined(OPENPKG_PLATFORM_OPENBSD) || \
+    defined(OPENPKG_PLATFORM_SUNOS)   || \
+    defined(OPENPKG_PLATFORM_LINUX)   || \
+    defined(OPENPKG_PLATFORM_DARWIN)  || \
+    defined(OPENPKG_PLATFORM_AIX)     || \
+    defined(OPENPKG_PLATFORM_IRIX)    || \
+    defined(OPENPKG_PLATFORM_HPUX)
+#define HAVE_INITGROUPS
+#endif
+
+/* global debug enable flag */
+static int debug_enable = 0;
+
+/* helper function: emulate (still less portable) setenv(3) via (more portable) putenv(3) */
+static int my_setenv(const char *name, const char *value, int overwrite)
+{
+    char *pair;
+
+    if (overwrite == 0 && getenv(name) != NULL)
+        return 0;
+    if ((pair = malloc(strlen(name) + 1 + strlen(value) + 1)) == NULL)
+        return -1;
+    strcpy(pair, name);
+    strcat(pair, "=");
+    strcat(pair, value);
+    putenv(pair);
+    return 0;
+}
+
+/* helper function for printing a warning message */
+static void warn(const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    fprintf(stderr, "openpkg:WARNING: ");
+    vfprintf(stderr, fmt, ap);
+    fprintf(stderr, "\n");
+    va_end(ap);
+    return;
+}
+
+/* helper function for printing a debug message */
+static void debug(const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    if (debug_enable) {
+        fprintf(stderr, "openpkg:DEBUG: ");
+        vfprintf(stderr, fmt, ap);
+        fprintf(stderr, "\n");
+    }
+    va_end(ap);
+    return;
+}
+
+/* helper function for printing a fatal message and exit */
+static void fatal(const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    fprintf(stderr, "openpkg:ERROR: ");
+    vfprintf(stderr, fmt, ap);
+    fprintf(stderr, "\n");
+    va_end(ap);
+    exit(1);
+    return;
+}
+
+/* adjust process privileges */
+static void adjust_privileges(uid_t uid, gid_t gid, int login)
+{
+    struct passwd *pw;
+
+    /* optionally emulate a more complete login */
+    if (login) {
+        /* determine information about user id */
+        if ((pw = getpwuid(uid)) == NULL)
+            fatal("unable to resolve user id \"%d\": %s\n", uid, strerror(errno));
+
+        /* reset some essential environment variables */
+        my_setenv("LOGNAME", pw->pw_name,   1);
+        my_setenv("USER",    pw->pw_name,   1);
+        my_setenv("SHELL",   pw->pw_shell,  1);
+        my_setenv("HOME",    pw->pw_dir,    1);
+
+#ifdef HAVE_INITGROUPS
+        /* initialize complete group access list */
+        if (initgroups(pw->pw_name, pw->pw_gid) == -1)
+            fatal("failed to initialize access group list via initgroups(3): %s", strerror(errno));
+#endif
+    }
+
+    /* switch to group id (first) */
+    if (setgid(gid) == -1)
+        fatal("failed to set group id via setgid(2): %s", strerror(errno));
+
+    /* switch to user id (second) */
+    if (setuid(uid) == -1)
+        fatal("failed to set user id via setuid(2): %s", strerror(errno));
+
+    return;
+}
+
+/* check whether caller is an explictly configured management user */
+static int check_whether_is_manager(uid_t my_uid, gid_t my_gid, uid_t m_uid, gid_t m_gid)
+{
+    char buf[1024];
+    char *username;
+    char *groupname;
+    char *filename;
+    struct stat sb;
+    char *cp;
+    FILE *fp;
+    struct passwd *pw;
+    struct group *gr;
+    int i;
+    int ok_uid;
+    int ok_gid;
+    int is_manager;
+
+    is_manager = 0;
+
+    /* path to the managers configuration file */
+    filename = OPENPKG_PREFIX "/etc/openpkg/managers";
+
+    /* check permissions of file */
+    if (stat(filename, &sb) == -1) {
+        warn("unable to determine information about configuration"
+             " file \"%s\": %s -- ignoring file", filename, strerror(errno));
+        return 0;
+    }
+    if (sb.st_uid != m_uid) {
+        warn("invalid owner user id %d (expected %d) on configuration"
+             " file \"%s\" -- ignoring file", sb.st_uid, m_uid, filename);
+        return 0;
+    }
+    if (sb.st_gid != m_gid) {
+        warn("invalid owner group id %d (expected %d) on configuration"
+             " file \"%s\" -- ignoring file", sb.st_gid, m_gid, filename);
+        return 0;
+    }
+    if (sb.st_mode != (S_IFREG|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH)) {
+        warn("invalid permissions on configuration"
+             " file \"%s\" -- ignoring file", filename);
+        return 0;
+    }
+
+    /* parse configuration file */
+    if ((fp = fopen(filename, "r")) == NULL) {
+        warn("unable to open configuration file \"%s\": %s -- ignoring file", filename, strerror(errno));
+        return 0;
+    }
+    while ((cp = fgets(buf, sizeof(buf), fp)) != NULL) {
+        /* parse entry as "<username>[:<groupname>]" where both
+           <username> and <groupname> can be set and default to "*"
+           to indicate an arbitrary user or group */
+        if ((i = strlen(buf)) == 0) {
+            warn("unexpected empty buffer during parsing of configuration file \"%\"", filename);
+            break;
+        }
+        if (i >= sizeof(buf)) {
+            warn("unexpected buffer overflow during parsing of configuration file \"%\"", filename);
+            break;
+        }
+        if (buf[i-1] != '\r' && buf[i-1] != '\n') {
+            warn("unexpected non-newline-terminated line found during parsing of configuration file \"%\"", filename);
+            break;
+        }
+        username = buf + strspn(buf, " \t");
+        cp = username + strcspn(username, " \t#\r\n");
+        *cp = '\0';
+        if (username[0] == '#' || username[0] == '\r' || username[0] == '\n' || username[0] == '\0')
+            continue;
+        groupname = "*";
+        if ((cp = strchr(username, ':')) != NULL) {
+            *cp++ = '\0';
+            groupname = cp;
+        }
+        debug("parsing result: username=\"%s\" groupname=\"%s\"", username, groupname);
+
+        /* check whether UID is ok */
+        ok_uid = 0;
+        if (strcmp(username, "*") == 0)
+            ok_uid = 1;
+        else {
+            if ((pw = getpwnam(username)) == NULL) {
+                warn("invalid username \"%s\" in \"%s\"\n", username, filename);
+                continue;
+            }
+            if (pw->pw_uid == my_uid)
+                ok_uid = 1;
+        }
+
+        /* check whether GID is ok */
+        ok_gid = 0;
+        if (strcmp(groupname, "*") == 0)
+            ok_gid = 1;
+        else {
+            if ((gr = getgrnam(groupname)) == NULL) {
+                warn("invalid groupname \"%s\" in \"%s\"\n", groupname, filename);
+                continue;
+            }
+            if (gr->gr_gid == my_gid)
+                ok_gid = 1;
+        }
+
+        /* if both UID and GID are ok, user is manager */
+        debug("matching: username ok = %s, groupname ok = %s", ok_uid ? "yes" : "no", ok_gid ? "yes" : "no");
+        if (ok_uid && ok_gid) {
+            is_manager = 1;
+            break;
+        }
+    }
+    fclose(fp);
+    return is_manager;
+}
+
+/* check whether command requires super-user privileges */
+static int check_whether_require_superuser(int argc, char *argv[])
+{
+    int require_superuser;
+    int i, j;
+
+    require_superuser = 0;
+    if (argc > 1 && strcmp(argv[1], "rpm") == 0) {
+        for (i = 2; i < argc; i++) {
+            if (strcmp(argv[i], "--") == 0)
+                break;
+            else if (   strcmp(argv[i], "--erase")      == 0
+                     || strcmp(argv[i], "--freshen")    == 0
+                     || strcmp(argv[i], "--install")    == 0
+                     || strcmp(argv[i], "--upgrade")    == 0
+                     || strcmp(argv[i], "--import")     == 0
+                     || strcmp(argv[i], "--initdb")     == 0
+                     || strcmp(argv[i], "--rebuilddb")  == 0
+                     || strcmp(argv[i], "--db-build")   == 0
+                     || strcmp(argv[i], "--db-rebuild") == 0
+                     || strcmp(argv[i], "--db-cleanup") == 0
+                     || strcmp(argv[i], "--db-fixate")  == 0
+                     || strcmp(argv[i], "--setperms")   == 0
+                     || strcmp(argv[i], "--setugids")   == 0) {
+                require_superuser = 1;
+                break;
+            }
+            else if (argv[i][0] == '-' && argv[i][1] != '-') {
+                for (j = 1; argv[i][j] != '\0'; j++) {
+                    if (    (   argv[i][j] == 'q'
+                             || argv[i][j] == 'V'
+                             || argv[i][j] == 'K')
+                         && argv[i][j+1] != '\0') {
+                        j++;
+                        continue;
+                    }
+                    else if (   argv[i][j] == 'i'
+                        || argv[i][j] == 'U'
+                        || argv[i][j] == 'F'
+                        || argv[i][j] == 'e') {
+                        require_superuser = 1;
+                        break;
+                    }
+                }
+                if (require_superuser)
+                    break;
+            }
+        }
+    }
+    else if (argc > 1 && strcmp(argv[1], "rc") == 0) {
+        require_superuser = 1;
+        for (i = 2; i < argc; i++) {
+            if (strcmp(argv[i], "--") == 0)
+                break;
+            else if (   strcmp(argv[i], "-q")       == 0
+                     || strcmp(argv[i], "--query")  == 0
+                     || strcmp(argv[i], "-c")       == 0
+                     || strcmp(argv[i], "--config") == 0) {
+                require_superuser = 0;
+                break;
+            }
+        }
+    }
+    return require_superuser;
+}
+
+/* main program */
+int main(int argc, char **argv, char **envp)
+{
+    int keep_original_privileges;
+    int is_manager;
+    int require_superuser;
+    uid_t my_uid, my_euid;
+    gid_t my_gid, my_egid;
+    uid_t m_uid;
+    gid_t m_gid;
+    uid_t s_uid;
+    gid_t s_gid;
+    struct passwd *pw;
+    struct group *gr;
+    int dry_run;
+
+    /* parse command line options */
+    dry_run = 0;
+    keep_original_privileges = 0;
+    if (argc <= 0)
+        abort();
+    argv++; argc--;
+    while (argc > 0) {
+        if (argv[0][0] != '-')
+            break;
+        else if (strcmp(argv[0], "--") == 0) {
+            argv++; argc--;
+            break;
+        }
+        else if (strcmp(argv[0], "--debug") == 0) {
+            debug_enable = 1;
+            argv++; argc--;
+            continue;
+        }
+        else if (strcmp(argv[0], "--dry-run") == 0) {
+            dry_run = 1;
+            argv++; argc--;
+            continue;
+        }
+        else if (strcmp(argv[0], "--keep-privileges") == 0) {
+            keep_original_privileges = 1;
+            argv++; argc--;
+            continue;
+        }
+        break;
+    }
+    argv--; argc++;
+
+    /* determine our current real and effective user/group ids */
+    my_uid  = getuid();
+    my_gid  = getgid();
+    my_euid = geteuid();
+    my_egid = getegid();
+    if ((pw = getpwuid(my_uid)) == NULL)
+        fatal("unable to resolve current user id %d: %s\n", my_uid, strerror(errno));
+    if ((gr = getgrgid(my_gid)) == NULL)
+        fatal("unable to resolve current user id %d: %s\n", my_uid, strerror(errno));
+    debug("current-user: usr=%s uid=%d euid=%d grp=%s gid=%d egid=%d",
+          pw->pw_name, my_uid, my_euid, gr->gr_name, my_gid, my_egid);
+
+    /* determine super user/group id */
+    if ((pw = getpwnam(OPENPKG_SUSR)) == NULL)
+        fatal("unable to resolve OpenPKG superuser username \"%s\": %s\n", OPENPKG_SUSR, strerror(errno));
+    s_uid = pw->pw_uid;
+    s_gid = pw->pw_gid;
+    debug("super-user: s_usr=%s s_uid=%d s_gid=%d", OPENPKG_SUSR, s_uid, s_gid);
+
+    /* determine management user/group id */
+    if ((pw = getpwnam(OPENPKG_MUSR)) == NULL)
+        fatal("unable to resolve OpenPKG management username \"%s\": %s\n", OPENPKG_MUSR, strerror(errno));
+    m_uid = pw->pw_uid;
+    m_gid = pw->pw_gid;
+    debug("management-user: m_grp=%s m_uid=%d m_gid=%d", OPENPKG_MUSR, m_uid, m_gid);
+
+    /* determine whether caller is explicitly configured as a management user */
+    is_manager = 0;
+    if (!keep_original_privileges)
+        is_manager = check_whether_is_manager(my_uid, my_gid, m_uid, m_gid);
+    debug("current user is manager: %s", is_manager ? "yes" : "no");
+
+    /* determine whether command requires super-user privileges */
+    require_superuser = check_whether_require_superuser(argc, argv);
+    debug("current command requires super user privileges: %s", require_superuser ? "yes" : "no");
+
+    /* adjust privileges according to determined information */
+    if (!keep_original_privileges && require_superuser && is_manager && my_euid == 0) {
+        /* increase privileges to super user */
+        debug("increase privileges to super user");
+        adjust_privileges(s_uid, s_gid, 1);
+    }
+    else if (!keep_original_privileges && !require_superuser && is_manager && my_euid == 0) {
+        /* decrease privileges to management user */
+        debug("decrease privileges to management user");
+        adjust_privileges(m_uid, m_gid, 1);
+    }
+    else /* keep_original_privileges || !is_manager */ {
+        /* drop effective privileges for current user*/
+        debug("drop effective privileges for current user");
+        adjust_privileges(my_uid, my_gid, 0);
+    }
+
+    /* pass-through control to real Execution Frontend (shell script) */
+    argv[0] = OPENPKG_PREFIX "/lib/openpkg/openpkg";
+    debug("execute \"%s\"", argv[0]);
+    if (!dry_run) {
+        if (execve(argv[0], argv, envp) == -1)
+            fatal("failed to execute \"%s\": %s", argv[0], strerror(errno));
+        /* NOT REACHED */
+        fatal("INTERNAL ERROR");
+    }
+    return 0;
+}
+

+ 33 - 4
openpkg/openpkg.spec

@@ -144,6 +144,7 @@ Source68:     openssl.patch
 Source69:     release.sh
 Source70:     release.pod
 Source71:     release.8
+Source72:     openpkg.c
 
 #   build information
 Prefix:       %{l_prefix}
@@ -942,6 +943,19 @@ Provides:     OpenPKG
       ${l_make}
     ) || exit $?
 
+    #   build frontend wrapper
+    ( os_name=`(uname -s) 2>/dev/null` || os_name='Unknown'
+      os_name=`echo "${os_name}" |\
+               sed -e 's;[^a-zA-Z0-9];;g' |\
+               tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'`
+      ${l_cc} \
+          -DOPENPKG_PLATFORM_${os_name} \
+          -DOPENPKG_PREFIX=\"%{l_prefix}\" \
+          -DOPENPKG_SUSR=\"%{l_susr}\" \
+          -DOPENPKG_MUSR=\"%{l_musr}\" \
+          -o openpkg `SOURCE openpkg.c`
+    ) || exit $?
+
 %install
     #   skip in bootstrap phase 2 (see openpkg.boot)
     [ ".$OPENPKG_BOOT" = .1 ] && exit 0
@@ -1198,10 +1212,13 @@ Provides:     OpenPKG
         <`SOURCE rpmtool` >$RPM_BUILD_ROOT%{l_prefix}/lib/openpkg/rpmtool
     chmod a+x $RPM_BUILD_ROOT%{l_prefix}/lib/openpkg/rpmtool
 
-    #   install OpenPKG tool chain execution frontend
+    #   install OpenPKG tool chain execution frontend and execution wrapper
+    cp openpkg $RPM_BUILD_ROOT%{l_prefix}/bin/openpkg
+    ${l_strip} $RPM_BUILD_ROOT%{l_prefix}/bin/openpkg
+    chmod 4775 $RPM_BUILD_ROOT%{l_prefix}/bin/openpkg
     sed -e "s;@l_prefix@;%{l_prefix};g" \
-        <`SOURCE openpkg.sh` >$RPM_BUILD_ROOT%{l_prefix}/bin/openpkg
-    chmod 755 $RPM_BUILD_ROOT%{l_prefix}/bin/openpkg
+        <`SOURCE openpkg.sh` >$RPM_BUILD_ROOT%{l_prefix}/lib/openpkg/openpkg
+    chmod 755 $RPM_BUILD_ROOT%{l_prefix}/lib/openpkg/openpkg
     sed -e "s:@l_prefix@:%{l_prefix}:g" \
         <`SOURCE openpkg.1` \
         >$RPM_BUILD_ROOT%{l_prefix}/man/man1/openpkg.1
@@ -1290,6 +1307,16 @@ Provides:     OpenPKG
         $RPM_BUILD_ROOT%{l_prefix}/etc/openpkg/register.tran \
         $RPM_BUILD_ROOT%{l_prefix}/etc/openpkg/register.util
 
+    #   install default managers configuration file
+    ( echo "##"
+      echo "##  managers -- OpenPKG Instance Managers"
+      echo "##"
+      echo ""
+      echo "%{l_susr}"
+      echo "%{l_musr}"
+      echo ""
+    ) >$RPM_BUILD_ROOT%{l_prefix}/etc/openpkg/managers
+
     #   install overriding RPM configuration files
     sed -e "s:@l_prefix@:%{l_prefix}:g" \
         <`SOURCE rpmpopt` \
@@ -1331,7 +1358,7 @@ Provides:     OpenPKG
     %dir %{l_prefix}/RPM/TMP
     %dir %{l_prefix}/cgi
     %dir %{l_prefix}/bin
-    %{l_prefix}/bin/openpkg
+    %attr(4755,%{l_susr},%{l_mgrp}) %{l_prefix}/bin/openpkg
     %dir %{l_prefix}/etc
     %{l_prefix}/etc/rc
     %config(noreplace) %{l_prefix}/etc/rc.conf
@@ -1346,6 +1373,7 @@ Provides:     OpenPKG
     %ghost %{l_prefix}/etc/openpkg/register.prep
     %ghost %{l_prefix}/etc/openpkg/register.tran
     %ghost %{l_prefix}/etc/openpkg/register.util
+    %config %attr(664,%{l_musr},%{l_mgrp}) %{l_prefix}/etc/openpkg/managers
     %{l_prefix}/etc/openpkg/openpkg.pgp
     %dir %{l_prefix}/include
     %dir %{l_prefix}/include/openpkg
@@ -1429,6 +1457,7 @@ Provides:     OpenPKG
     %{l_prefix}/lib/openpkg/librpmio.a
     %{l_prefix}/lib/openpkg/librpmpopt.a
     %{l_prefix}/lib/openpkg/librpmz.a
+    %{l_prefix}/lib/openpkg/openpkg
     %dir %{l_prefix}/libexec
     %dir %{l_prefix}/libexec/openpkg
     %{l_prefix}/libexec/openpkg/uuid