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