-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Oliver Falk wrote:
> Hi Clark!
>
>

Hi back Oliver!

>
> Thx for letting us know about your progress, Clark!
>
> Best would be you send a patch/diff to the list - first of all, so
> all can investigate... At least I would appreciate!


Good point. Attached is a 'cvs diff" of my tree, plus the new source
file src/mock.c

Comments welcome.

Clark

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.3 (GNU/Linux)
Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org

iD8DBQFEmaB/Hyuj/+TTEp0RApGrAJ0YRlbn+ksIrZ9nKeDtun0GlEsrWQCgvrkm
nQoGUnXgJ9XCxONZNluFVHU=
=zP36
-----END PGP SIGNATURE-----

Index: Makefile
===================================================================
RCS file: /cvs/fedora/mock/Makefile,v
retrieving revision 1.8
diff -u -r1.8 Makefile
--- Makefile	12 Apr 2006 14:23:17 -0000	1.8
+++ Makefile	21 Jun 2006 19:35:35 -0000
@@ -21,7 +21,7 @@
 install:
 	mkdir -p $(DESTDIR)/usr/bin/
 	mkdir -p $(DESTDIR)/usr/libexec
-	install -m 755 mock.py $(DESTDIR)/usr/bin/mock
+	install -m 755 mock.py $(DESTDIR)/usr/bin
 	install -m 755 mock-yum $(DESTDIR)/usr/libexec/mock-yum
 	mkdir -p $(DESTDIR)/var/lib/mock
 	for d in $(SUBDIRS); do make  DESTDIR=`cd $(DESTDIR); pwd` -C $$d install; [ $$? = 0 ] || exit 1; done
Index: mock.py
===================================================================
RCS file: /cvs/fedora/mock/mock.py,v
retrieving revision 1.53
diff -u -r1.53 mock.py
--- mock.py	8 Jun 2006 21:28:46 -0000	1.53
+++ mock.py	21 Jun 2006 19:35:35 -0000
@@ -106,6 +106,17 @@
         self._state = 'unstarted'
         self.tmplog = LogBuffer()
         self.config = config
+
+        # save our uids
+        self.highuid = os.geteuid()
+        self.lowuid = os.getuid()
+
+        # set gid to mock group
+        os.setgid(os.getegid())
+            
+        # insure we're *not* running privileged
+        self.drop()
+        
         root = config['root']
         if config.has_key('unique-ext'):
             root = "%s-%s" % (root, config['unique-ext'])
@@ -151,6 +162,14 @@
         cfgout.flush()
         cfgout.close()
     
+    def elevate(self):
+        self.debug("elevate: setting uid to %d" % self.highuid)
+        os.setreuid(self.highuid, self.lowuid)
+
+    def drop(self):
+        self.debug("drop: setting uid to %d" % self.lowuid)
+        os.setreuid(self.lowuid, self.highuid)
+        
     def log(self, msg):
         if self.config['quiet']: return
         print msg
@@ -184,8 +203,7 @@
             
         if os.path.exists(self.basedir):
             cmd = '%s -rf %s' % (self.config['rm'], self.basedir)
-            (retval, output) = self.do(cmd)
-
+            (retval, output) = self.do_elevated(cmd)
             if retval != 0:
                 error("Errors cleaning out chroot: %s" % output)
                 if os.path.exists(self.rootdir):
@@ -209,14 +227,28 @@
 
     def unpack(self):
         self.state('unpack cache')
-        cmd = '%s %s %s' % (self.config['unpack_cmd'], self.basedir, self.cache_file)
-        self.do(cmd)
+        if self.cache_file.find(".bz2") != -1:
+            opts = "-jxpf"
+        elif self.cache_file.find(".gz") != -1:
+            opts = "-zxpf"
+        else:
+            opts = "-xpf"
+        cmd = '%s %s %s %s' % (self.config['unpack_cmd'], opts, self.basedir, self.cache_file)
+        (retval, output) = self.do_elevated(cmd)
+        return retval
 
     def pack(self):
         self.state('create cache')
         self._ensure_dir(os.path.join(self.config['basedir'], self.config['cache_topdir']))
-        cmd = '%s %s %s root' % (self.config['pack_cmd'], self.basedir, self.cache_file)
-        self.do(cmd)
+        if self.cache_file.find(".bz2") != -1:
+            opts = "-jlcf"
+        elif self.cache_file.find(".gz") != -1:
+            opts = "-zlcf"
+        else:
+            opts = "-clf"
+        cmd = '%s %s %s root' % (self.config['pack_cmd'], opts, self.cache_file)
+        (retval, output) = self.do_elevated(cmd)
+        return retval
     
     def prep(self):
         self.state("prep")
@@ -260,7 +292,7 @@
         command = '%s %s' % (basecmd, cmd)
         self.debug("yum: command %s" % command)
 
-        (retval, output) = self.do(command)
+        (retval, output) = self.do_elevated(command)
 
         if retval != 0:
             raise YumError, "Error performing yum command: %s" % command
@@ -412,10 +444,10 @@
         self._ensure_dir(procdir)
 
         self.debug("mounting proc in %s" % procdir)
-        command = '%s -t proc proc %s/proc' % (self.config['mount'], 
+        command = '%s -n -t proc proc %s/proc' % (self.config['mount'], 
                                                self.rootdir)
         track.write('proc\n')
-        (retval, output) = self.do(command)
+        (retval, output) = self.do_elevated(command)
         track.flush()
         
         if retval != 0:
@@ -427,9 +459,9 @@
         devptsdir = os.path.join(self.rootdir, 'dev/pts')
         self._ensure_dir(devptsdir)
         self.debug("mounting devpts in %s" % devptsdir)
-        command = '%s -t devpts devpts %s' % (self.config['mount'], devptsdir)
+        command = '%s -n -t devpts devpts %s' % (self.config['mount'], devptsdir)
         track.write('dev/pts\n')
-        (retval, output) = self.do(command)
+        (retval, output) = self.do_elevated(command)
         track.flush()
         track.close()
 
@@ -442,7 +474,7 @@
     
         item = '%s/%s' % (self.rootdir, path)
         command = '%s %s' % (self.config['umount'], item)
-        (retval, output) = self.do(command)
+        (retval, output) = self.do_elevated(command)
     
         if retval != 0:
             if output.find('not mounted') == -1: # this probably won't work in other LOCALES
@@ -464,7 +496,9 @@
             if len(item.strip()) < 1:
                 continue
             
+            self.elevate()
             self._umount(item)
+            self.drop()
             
         # poof, no more file
         if os.path.exists(mf):
@@ -504,6 +538,30 @@
 
         return (retval, output)
 
+    def do_elevated(self, cmd):
+        if os.getuid() != self.highuid:
+            need_drop = 1
+        else:
+            need_drop = 0
+        try:
+            self.elevate()
+            (retval, output) = self.do(cmd)
+        finally:
+            if need_drop: self.drop()
+        return (retval, output)
+
+    def do_asuser(self, cmd):
+        if os.getuid() != self.lowuid:
+            need_elevate = 1
+        else:
+            need_elevate = 0
+        try:
+            self.drop()
+            (retval, output) = self.do(cmd)
+        finally:
+            if need_elevate: self.elevate()
+        return (retval, output)
+
     def do_chroot(self, command, fatal = False, exitcode=None):
         """execute given command in root"""
         cmd = ""
@@ -518,7 +576,7 @@
                                                  self.rootdir,
                                                  self.config['runuser'],
                                                  command)
-        (ret, output) = self.do(cmd)
+        (ret, output) = self.do_elevated(cmd)
         if (ret != 0) and fatal:
             self.close()
             if exitcode:
@@ -544,13 +602,17 @@
             if n.startswith('rpmlib'):
                 continue
 
+            self.elevate()
             req = rpmUtils.miscutils.formatRequire(n, v, f)
+            self.drop()
             reqlist.append(req)
         
         # Extract SRPM name components - still not nice, shouldn't this
         # be somewhere in the "hdr" parameter?
         fname = os.path.split(str(srpm))[1]
+        self.elevate()
         name, ver, rel, epoch, arch = rpmUtils.miscutils.splitFilename(fname)
+        self.drop()
 
         # Add the 'more_buildreqs' for this SRPM (if defined)
         for this_srpm in ['-'.join([name,ver,rel]),
@@ -564,7 +626,10 @@
                     reqlist.append(req)
                 break
         
-        return rpmUtils.miscutils.unique(reqlist)
+        self.elevate()
+        ret = rpmUtils.miscutils.unique(reqlist)
+        self.drop()
+        return ret
     
     def _prep_install(self):
         """prep chroot for installation"""
@@ -597,7 +662,7 @@
             cmd = '%s %s -m %s %s %s %s' % (self.config['mknod'], 
                       devpath, perm, devtype, major, minor)
             if not os.path.exists(devpath):
-                (retval, output) = self.do(cmd)
+                (retval, output) = self.do_elevated(cmd)
                 if retval != 0:
                     raise RootError, "could not mknod error was: %s" % output
 
@@ -760,12 +825,12 @@
 
 def setup_default_config_opts(config_opts):
     config_opts['basedir'] = '/var/lib/mock/' # root name is automatically added to this
-    config_opts['chroot'] = '/usr/sbin/mock-helper chroot'
-    config_opts['mount'] = '/usr/sbin/mock-helper mount'
-    config_opts['umount'] = '/usr/sbin/mock-helper umount'
-    config_opts['rm'] = '/usr/sbin/mock-helper rm'
-    config_opts['mknod'] = '/usr/sbin/mock-helper mknod'
-    config_opts['yum'] = '/usr/sbin/mock-helper yum'
+    config_opts['chroot'] = 'chroot'
+    config_opts['mount'] = 'mount'
+    config_opts['umount'] = 'umount'
+    config_opts['rm'] = 'rm'
+    config_opts['mknod'] = 'mknod'
+    config_opts['yum'] = 'yum'
     config_opts['runuser'] = '/sbin/runuser'
     config_opts['chroot_setup_cmd'] = 'install buildsys-build'
     config_opts['chrootuser'] = 'mockbuild'
@@ -792,8 +857,8 @@
     # caching-related config options
     config_opts['rebuild_cache'] = False
     config_opts['use_cache'] = False
-    config_opts['pack_cmd'] = "/usr/sbin/mock-helper pack"
-    config_opts['unpack_cmd'] = "/usr/sbin/mock-helper unpack"
+    config_opts['pack_cmd'] = "tar"
+    config_opts['unpack_cmd'] = "tar"
     config_opts['cache_ext'] = ".tar.gz"
     config_opts['cache_topdir'] = "root-cache"
     config_opts['max_cache_age_days'] = 15
@@ -902,11 +967,6 @@
         print "You need to be a member of the mock group for this to work"
         sys.exit(1)
 
-    # and make sure they're not root
-    if os.geteuid() == 0:
-        error("Don't try to run mock as root!")
-        sys.exit(1)
-        
     # defaults
     config_opts = {}
     setup_default_config_opts(config_opts)
Index: mock.spec
===================================================================
RCS file: /cvs/fedora/mock/mock.spec,v
retrieving revision 1.16
diff -u -r1.16 mock.spec
--- mock.spec	7 Jun 2006 12:29:15 -0000	1.16
+++ mock.spec	21 Jun 2006 19:35:35 -0000
@@ -1,6 +1,6 @@
 Summary: Builds packages inside chroots
 Name: mock
-Version: 0.6
+Version: 0.7
 Release: 1
 License: GPL
 Group: Development/Tools
@@ -28,7 +28,7 @@
 # make the default.cfg link
 cd $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}
 ln -s fedora-development-i386-core.cfg default.cfg
-
+cd $RPM_BUILD_ROOT/%{_bindir}
 
 %clean
 rm -rf $RPM_BUILD_ROOT
@@ -45,10 +45,10 @@
 %doc README ChangeLog
 %dir  %{_sysconfdir}/%{name}
 %config(noreplace) %{_sysconfdir}/%{name}/*.cfg
-%{_bindir}/%{name}
+%attr(04750, root, mock) %{_bindir}/%{name}
+%{_bindir}/%{name}.py*
 %{_libexecdir}/mock-yum
 %{_mandir}/man1/mock.1*
-%attr(04750, root, mock) %{_sbindir}/mock-helper
 %attr(02775, root, mock) %dir /var/lib/mock
 %{_libdir}/libselinux-mock.so
 
Index: etc/defaults.cfg
===================================================================
RCS file: /cvs/fedora/mock/etc/defaults.cfg,v
retrieving revision 1.5
diff -u -r1.5 defaults.cfg
--- etc/defaults.cfg	13 Jun 2006 05:31:30 -0000	1.5
+++ etc/defaults.cfg	21 Jun 2006 19:35:35 -0000
@@ -7,29 +7,29 @@
 #
 # config_opts['foo'] = bar
 config_opts['basedir'] = '/var/lib/mock/'
-config_opts['chroot'] = '/usr/sbin/mock-helper chroot'
-config_opts['mount'] = '/usr/sbin/mock-helper mount'
-config_opts['umount'] = '/usr/sbin/mock-helper umount'
-config_opts['rm'] = '/usr/sbin/mock-helper rm'
-config_opts['mknod'] = '/usr/sbin/mock-helper mknod'
-config_opts['yum'] = '/usr/sbin/mock-helper yum'
+config_opts['chroot'] = 'chroot'
+config_opts['mount'] = 'mount'
+config_opts['umount'] = 'umount'
+config_opts['rm'] = 'rm'
+config_opts['mknod'] = 'mknod'
+config_opts['yum'] = 'yum'
 config_opts['runuser'] = '/sbin/runuser'
 config_opts['chrootuser'] = 'mockbuild'
 config_opts['chrootgroup'] = 'mockbuild'
-config_opts['chrootuid'] = os.geteuid()
-config_opts['chrootgid'] = os.getegid()
+#config_opts['chrootuid'] = os.geteuid()
+#config_opts['chrootgid'] = os.getegid()
 config_opts['chroothome'] = '/builddir'
 config_opts['clean'] = True
 
 # caching related options
-config_opts['rebuild_cache'] = False
-config_opts['use_cache'] = False
-config_opts['pack_cmd'] = "/usr/sbin/mock-helper pack"
-config_opts['unpack_cmd'] = "/usr/sbin/mock-helper unpack"
+config_opts['rebuild_cache'] = True
+config_opts['use_cache'] = True
+config_opts['pack_cmd'] = "tar"
+config_opts['unpack_cmd'] = "tar"
 config_opts['cache_ext'] = ".tar.gz"
 config_opts['cache_topdir'] = "root-cache"
 config_opts['max_cache_age_days'] = 15
 
 
 config_opts['chroot_setup_cmd'] = 'install buildsys-build'
-#config_opts['chroot_setup_cmd'] = 'groupinstall build'
\ No newline at end of file
+#config_opts['chroot_setup_cmd'] = 'groupinstall build'
Index: src/Makefile
===================================================================
RCS file: /cvs/fedora/mock/src/Makefile,v
retrieving revision 1.2
diff -u -r1.2 Makefile
--- src/Makefile	17 Jun 2005 20:53:31 -0000	1.2
+++ src/Makefile	21 Jun 2006 19:35:35 -0000
@@ -1,32 +1,43 @@
-CC=gcc
-EXECUTABLE=mock-helper
-SBINDIR=/usr/sbin
-MOCKGROUP=mock
-DESTDIR=''
-INSTALL=/usr/bin/install
-MKDIR=/bin/mkdir
+CC	:=	gcc
+CFLAGS 	:= 	-O2 -g -Wall -Werror
+#DEBUG	:= 	-DDEBUG=1
+EXECUTABLE:=	mock
+LIBRARY	:=	libselinux-mock.so
+BINDIR	:=	/usr/bin
+MOCKGROUP:=	mock
+#DESTDIR	:=	''
+INSTALL	:=	/usr/bin/install
+MKDIR	:=	/bin/mkdir
 
 ifndef NOSELINUX
 SELINUXFLAGS=-DUSE_SELINUX=1 -lselinux
 endif
 
 ifneq (,$(filter ppc64 x86_64 s390x,$(shell uname -m)))
-LIBDIR = /usr/lib64
+LIBDIR 	:=	/usr/lib64
 else
-LIBDIR = /usr/lib
+LIBDIR 	:=	/usr/lib
 endif
 
-all: 
-	$(CC) $(CFLAGS) -o $(EXECUTABLE) mock-helper.c $(SELINUXFLAGS)
+all: $(EXECUTABLE) $(LIBRARY)
+
+$(LIBRARY): selinux-mock.o
+	$(LD) -shared -o $(LIBRARY) selinux-mock.o
+
+selinux-mock.o:  selinux-mock.c
 	$(CC) $(CFLAGS) -fPIC -c selinux-mock.c
-	$(LD) -shared -o libselinux-mock.so selinux-mock.o
+
+$(EXECUTABLE):	mock.c
+	$(CC) $(CFLAGS) $(DEBUG) -o $@ $<
 
 clean:
-	rm -f $(EXECUTABLE)
+	rm -f $(EXECUTABLE) $(LIBRARY)
 	rm -f *~ *.bak *.o *.so
 
-install:
-	$(MKDIR) -p $(DESTDIR)/$(SBINDIR) $(DESTDIR)/$(LIBDIR)
-	$(INSTALL) -m 4750 $(EXECUTABLE) $(DESTDIR)/$(SBINDIR)/$(EXECUTABLE)
-	$(INSTALL) -m 755 libselinux-mock.so $(DESTDIR)/$(LIBDIR)
-
+install:   $(EXECUTABLE) $(LIBRARY)
+	[ -d $(DESTDIR)/$(BINDIR) ] || \
+		$(MKDIR) -p $(DESTDIR)/$(BINDIR)
+	[ -d $(DESTDIR)/$(LIBDIR) ] || \
+		$(MKDIR) -p $(DESTDIR)/$(LIBDIR)
+	$(INSTALL) -o root -g $(MOCKGROUP) -m 4750 $(EXECUTABLE) $(DESTDIR)/$(BINDIR)
+	$(INSTALL) -m 755 $(LIBRARY) $(DESTDIR)/$(LIBDIR)
//
// mock.c - setuid program for launching mock.py
//
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// Copyright (c) 2006 - Clark Williams <[EMAIL PROTECTED]>
//   portions lifted from mock-helper.c by Seth Vidal
//   namespace idea courtesy Enrico Scholz <[EMAIL PROTECTED]>

//#define _GNU_SOURCE

#include "config.h"

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sched.h>
#include <assert.h>
#include <asm/unistd.h>
#include <signal.h>

#if 0
#ifdef USE_SELINUX
#include <selinux/selinux.h>
#endif
#endif

#define PYTHON_PATH	"/usr/bin/python"
#define MOCK_PATH	"/usr/bin/mock.py"

static char const * const ALLOWED_ENV[] =
{
	"dist",
	"ftp_proxy", 
	"http_proxy", 
	"https_proxy", 
	"no_proxy", 
	"PS1",
};

#define ALLOWED_ENV_SIZE (sizeof (ALLOWED_ENV) / sizeof (ALLOWED_ENV[0]))
#define SAFE_PATH	"PATH=/bin:/usr/bin:/usr/sbin"
#define SAFE_HOME	"HOME=/root"
#define NSFLAG		"NAMESPACE=1"

// Note that MAX_ENV_SIZE is allowed size, plus the ones we add plus a null entry
// so, if you add more variables, increase the constant below!
#define MAX_ENV_SIZE	4 + ALLOWED_ENV_SIZE

//
// helper functions
//

//
// print formatted string to stderr and terminate
//
void error (const char *format, ...)
{
	va_list ap;
	
	va_start (ap, format);
	fprintf (stderr, "mock: error: ");
	vfprintf (stderr, format, ap);
	va_end (ap);
	fprintf (stderr, "\n");
	exit (1);
}

//
// debug print
//
static int debugging = 0;

void debug(const char *format, ...)
{

	if (debugging) {
		va_list ap;
		
		va_start (ap, format);
		fprintf (stderr, "DEBUG: ");
		vfprintf (stderr, format, ap);
		va_end (ap);
	}
}

///////////////////////////////////////////
//
// main logic
//
//////////////////////////////////////////


int main (int argc, char **argv)
{
	char *env[MAX_ENV_SIZE];
	char **newargv;
	int newargc, newargvsz;
	int i, idx = 2;
	int status;
	pid_t pid;

	if (getenv("MOCKDEBUG"))
		debugging = 1;

	// copy in allowed environment variables to our environment
	debug("copying envionment\n");
	memset(env, '\0', sizeof(char *) * (3 + ALLOWED_ENV_SIZE));
	env[0] = strdup(SAFE_PATH);
	env[1] = strdup(SAFE_HOME);
	env[2] = strdup(NSFLAG);
	for (i = 0; i < ALLOWED_ENV_SIZE; ++i)
	{
		char *ptr = getenv (ALLOWED_ENV[i]);
		if (ptr==0) continue;
		ptr -= strlen (ALLOWED_ENV[i]) + 1;
		env[idx++] = ptr;
	}
	assert(idx <= MAX_ENV_SIZE);

	// set up a new argv/argc
	//     new argv[0] will be "/usr/bin/python"
	//     new argv[1] will be "/usr/bin/mock.py"
	//     remainder of new argv will be old argv[1:n]
	//     allocate one extra for null at end
	newargc = argc + 1;
	newargvsz = sizeof(char *) * (newargc + 1);
	newargv = malloc(newargvsz);
	if (newargv == NULL)
		error("malloc of argument vector (%d bytes) failed!\n", newargvsz);
	memset(newargv, '\0', newargvsz);
	newargv[0] = strdup(PYTHON_PATH);
	debug("argv[0] = %s\n", newargv[0]);
	newargv[1] = strdup(MOCK_PATH);
	debug("argv[1] = %s\n", newargv[1]);
	for (i = 1; i < argc; i++) {
		newargv[i+1] = argv[i];
		debug("argv[%d] = %s\n", i+1, newargv[i+1]);
	}

	// clone a new process with a separate namespace
	// Note: we have to use syscall here, since we want the
	//       raw system call 'clone', not the glibc library wrapper
	// Also note: The SIGCHLD or'ed into the flags argument. If you
	// don't specify an exit signal, the child is detached and waitpid
	// won't work (how the heck Enrico figured it out is beyond me, since
	// there are only two mentions of CSIGNAL in fork.c...)
	debug("cloning new namespace\n");
	pid = syscall(__NR_clone, CLONE_VFORK|CLONE_NEWNS|SIGCHLD, 0);

	// urk! no clone?
	if (pid == -1)
		error("clone failed: %s\n", strerror(errno));

	// exec python
	if (pid == 0) {
		debug("exec'ing python\n");
		execve(PYTHON_PATH, newargv, env);
		error("execve failed: %s\n", strerror(errno));
	}
	
	// wait for the child to finish and exit appropriately
	debug("waiting for child to finish\n");
	if (waitpid(pid, &status, 0) != pid)
		error("waitpid failed: %s\n", strerror(errno));
	if (WIFEXITED(status)) {
		debug("Exiting with status 0x%x (0x%x)\n", WEXITSTATUS(status), status);
		exit(WEXITSTATUS(status));
	}
	if (WIFSIGNALED(status))
		error("errored out with signal %d\n", WTERMSIG(status));

	exit(-1);	// WTF? how did we get here?
}
--
Fedora-buildsys-list mailing list
Fedora-buildsys-list@redhat.com
https://www.redhat.com/mailman/listinfo/fedora-buildsys-list

Reply via email to