On Sat, Aug 19, 2006, Ralf S. Engelschall wrote:

> Unix Hackers in our OpenPKG community, please review the following
> security-sensitive patch to the OpenPKG bootstrap package.
> [...]

After the first feedbacks arrived I've further improved the patch. The
latest version is appended. Please review this one.

                                       Ralf S. Engelschall
                                       [EMAIL PROTECTED]
                                       www.engelschall.com

? openpkg
? x
Index: openpkg.boot
===================================================================
RCS file: /v/openpkg/cvs/openpkg-src/openpkg/openpkg.boot,v
retrieving revision 1.59
diff -u -d -u -d -u -d -r1.59 openpkg.boot
--- openpkg.boot        22 Jun 2006 08:01:37 -0000      1.59
+++ openpkg.boot        19 Aug 2006 15:39:43 -0000
@@ -490,7 +490,7 @@
 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 \
Index: openpkg.c
===================================================================
RCS file: openpkg.c
diff -N openpkg.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ openpkg.c   19 Aug 2006 15:39:43 -0000
@@ -0,0 +1,333 @@
+/*
+**  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)
+#define HAVE_INITGROUPS
+#endif
+
+/* 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 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 */
+        setenv("LOGNAME", pw->pw_name,   1);
+        setenv("USER",    pw->pw_name,   1);
+        setenv("SHELL",   pw->pw_shell,  1);
+        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;
+}
+
+/* main program */
+int main(int argc, char **argv, char **envp)
+{
+    int keep_original_privileges;
+    int is_manager;
+    int require_superuser;
+    char buf[1024];
+    char *username;
+    char *groupname;
+    char *filename;
+    struct stat sb;
+    char *cp;
+    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;
+    FILE *fp;
+    struct passwd *pw;
+    struct group *gr;
+    int i, j;
+    int ok_uid;
+    int ok_gid;
+
+    /* determine our current real and effective user/group ids */
+    my_uid  = getuid();
+    my_gid  = getgid();
+    my_euid = geteuid();
+    my_egid = getegid();
+
+    /* determine superuser/management 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;
+    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;
+
+    /* check whether we are forced to keep original privileges
+       (mainly in order to circumvent problems in case our
+       "require_superuser" guessing below is too weak!) */
+    keep_original_privileges = 0;
+    if (argc > 1 && strcmp(argv[1], "--keep-privileges") == 0) {
+        keep_original_privileges = 1;
+        argv++;
+        argc--;
+    }
+
+    /* read list of explicitly configured management users */
+    is_manager = 0;
+    if (!keep_original_privileges) {
+        filename = OPENPKG_PREFIX "/etc/openpkg/managers";
+        /* check permissions of file */
+        if (stat(filename, &sb) == 0) {
+            if (sb.st_uid != m_uid)
+                fatal("invaid owner user id %d (expected %d) on configuration 
file \"%s\"", sb.st_uid, m_uid, filename);
+            if (sb.st_gid != m_gid)
+                fatal("invaid owner group id %d (expected %d) on configuration 
file \"%s\"", sb.st_gid, m_gid, filename);
+            if (sb.st_mode != (S_IFREG|S_IRUSR|S_IWUSR))
+                fatal("invaid permissions on configuration file \"%s\"", 
filename);
+            if ((fp = fopen(filename, "r")) == NULL)
+                fatal("unable to open configuration file \"%s\": %s", 
filename, strerror(errno));
+            else {
+                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;
+                    }
+
+                    /* 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 */
+                    if (ok_uid && ok_gid) {
+                        is_manager = 1;
+                        break;
+                    }
+                }
+                fclose(fp);
+            }
+        }
+    }
+
+    /* determine whether command requires super-user privileges */
+    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;
+            }
+        }
+    }
+
+    /* adjust privileges according to determined information */
+    if (!keep_original_privileges && require_superuser && is_manager && 
my_euid == 0) {
+        /* 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 */
+        adjust_privileges(m_uid, m_gid, 1);
+    }
+    else /* keep_original_privileges || !is_manager */ {
+        /* drop privileges */
+        adjust_privileges(my_uid, my_gid, 0);
+    }
+
+    /* pass-through control to real Execution Frontend (shell script) */
+    argv[0] = OPENPKG_PREFIX "/lib/openpkg/openpkg";
+    if (execve(argv[0], argv, envp) == -1)
+        fatal("failed to execute \"%s\": %s", argv[0], strerror(errno));
+
+    /* NOT REACHED */
+    fatal("INTERNAL ERROR");
+    return 0;
+}
+
Index: openpkg.spec
===================================================================
RCS file: /v/openpkg/cvs/openpkg-src/openpkg/openpkg.spec,v
retrieving revision 1.511
diff -u -d -u -d -u -d -r1.511 openpkg.spec
--- openpkg.spec        18 Aug 2006 13:17:10 -0000      1.511
+++ openpkg.spec        19 Aug 2006 15:39:43 -0000
@@ -144,6 +144,7 @@
 Source69:     release.sh
 Source70:     release.pod
 Source71:     release.8
+Source72:     openpkg.c
 
 #   build information
 Prefix:       %{l_prefix}
@@ -942,6 +943,19 @@
       ${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 @@
         <`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 @@
         $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 @@
     %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 @@
     %ghost %{l_prefix}/etc/openpkg/register.prep
     %ghost %{l_prefix}/etc/openpkg/register.tran
     %ghost %{l_prefix}/etc/openpkg/register.util
+    %config %attr(600,%{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 @@
     %{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

Reply via email to