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