-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Attached is the C source that is my first attempt at a mock launcher program. This program is built as the executable /usr/bin/mock with setuid:root, setgid:mock and will exec python with the input file /usr/bin/mock.py. The program successfully launches python, but I haven't worked through all the issues to complete a build (yet). I know that mock.py needs to be modified to remove the dont-run-as-root test and I've made a first cut at removing mock-helper from .cfg files, but I'm sure there are other things that need to be done. I have checked nothing into CVS at this point and even if/when I do, I suspect I'll be working off a branch for a while.
Note that the program makes use of Linux namespaces. This *should* make our handling of mount points within the chroot (/proc, /sys, etc.) a bit easier to clean up, since when the process dies the mounts should just go away. I haven't verified this though, so caveat emptor. Anyway, it's a starting point. Please look it over and provide feedback. Clark -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.3 (GNU/Linux) Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org iD8DBQFEkC9DHyuj/+TTEp0RAq+4AKCn4hj3bGInJBMWd1Bj6h1SMeCV+QCgt3NW hPHE9hW9DXL9ZkPLbaJrL6Y= =XtsC -----END PGP SIGNATURE-----
// // 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> #ifdef USE_SELINUX #include <selinux/selinux.h> #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 // void usage () { printf ("Usage: mock [command]\n"); exit (1); } // // 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 // void debug(const char *format, ...) { #ifdef DEBUG va_list ap; va_start (ap, format); fprintf (stderr, "DEBUG: "); vfprintf (stderr, format, ap); va_end (ap); #endif } /////////////////////////////////////////// // // 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; // catch the simple no arguments invocation if (argc < 2) usage(); // elevate our privileges if (setreuid (geteuid (), geteuid ())) error("setreuid failed: %s", strerror(errno)); debug("running with uid: %d, gid: %d\n", getuid(), getgid()); #ifdef USE_SELINUX // add LD_PRELOAD for our selinux lib if selinux is in use is set if ((is_selinux_enabled() > 0) && (use_selinux_preload == 1)) { ld_preload = strdup("LD_PRELOAD=libselinux-mock.so"); debug("adding ld_preload of %s\n", ld_preload); env[idx++] = ld_preload; } #endif // 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