-----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