// vim:expandtab:autoindent:tabstop=4:shiftwidth=4:filetype=c:textwidth=0: // // mock.c - setuid program for launching mock.py // // Copyright (c) 2006 - Clark Williams // portions lifted from mock-helper.c by Seth Vidal // namespace idea courtesy Enrico Scholz // // 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. // #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "version.h" #define PYTHON_PATH "/usr/bin/python" #define MOCK_PATH LIBEXECDIR "/mock.py" //#define PYTHON_PATH "/usr/bin/python" //#define MOCK_PATH "/tmp/foo.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 **newargv; int newargc, newargvsz; int i; int status; pid_t pid; char * env[MAX_ENV_SIZE+1] = { [0] = SAFE_PATH, [1] = SAFE_HOME, [2] = NSFLAG, }; int idx = 3; //keep in sync with env, above if (getenv("MOCKDEBUG")) debugging = 1; // copy in allowed environment variables to our environment debug("copying envionment\n"); for (i = 0; i < ALLOWED_ENV_SIZE; ++i) { debug("try to get %s\n", ALLOWED_ENV[i]); char *ptr = getenv (ALLOWED_ENV[i]); if (!ptr) continue; debug("copying var: %s\n", ptr); ptr -= strlen (ALLOWED_ENV[i]) + 1; env[idx++] = ptr; } assert(idx <= MAX_ENV_SIZE); env[idx] = NULL; // set up a new argv/argc // new argv[0] will be "/usr/bin/python" // new argv[1] will be "/usr/libexec/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 = alloca(newargvsz); newargv[0] = PYTHON_PATH; newargv[1] = MOCK_PATH; debug("argv[%d] = %s\n", 0, newargv[0]); debug("argv[%d] = %s\n", 1, newargv[1]); for (i = 1; i < argc; i++) { newargv[i + 1] = argv[i]; debug("argv[%d] = %s\n", i+2, newargv[i + 1]); } newargv[newargc] = NULL; debug("INFO: mock suid wrapper version " MOCK_RELEASE_VERSION " \n"); // 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? }