X-Git-Url: http://git.linux-vserver.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=util-vserver%2Fsrc%2Frpm-fake.c;h=b3b641a9436c94f88e2bed380ace2d5c3c19a1cd;hb=9ccad609b7d342fd70d95720a98d1a2f073297e9;hp=1c2da075e8982da2d57b0f28de509f8c6573ede0;hpb=05aaebb391debe4dc4f657266727110462403ccc;p=util-vserver.git diff --git a/util-vserver/src/rpm-fake.c b/util-vserver/src/rpm-fake.c index 1c2da07..b3b641a 100644 --- a/util-vserver/src/rpm-fake.c +++ b/util-vserver/src/rpm-fake.c @@ -23,8 +23,11 @@ #include "pathconfig.h" #include "util.h" -#include +#include +#include +#include +#include #include #include #include @@ -42,12 +45,27 @@ #include #include #include -#include #include #include #include #include + + // from selinux.h + // FIXME: add configure autodetection and include directly +int rpm_execcon(unsigned int verified, + const char *filename, + char *const argv[], char *const envp[]); + + +#define ENSC_WRAPPERS_PREFIX "rpm-fake.so: " +#define ENSC_WRAPPERS_VSERVER 1 +#define ENSC_WRAPPERS_UNISTD 1 +#include + +#undef _POSIX_SOURCE +#include "capability-compat.h" + #ifndef CLONE_NEWNS # define CLONE_NEWNS 0x00020000 #endif @@ -56,19 +74,22 @@ #define PLATFORM_FILE "/etc/rpm/platform" #define INIT(FILE,FUNC) FUNC##_func = ((__typeof__(FUNC) *) (xdlsym(FILE, #FUNC))) -#define DECLARE(FUNC) static __typeof__(FUNC) * FUNC##_func = 0 +#define DECLARE(FUNC) static __typeof__(FUNC) * FUNC##_func = 0 +#define DEBUG 1 #define DBG_INIT 0x0001 #define DBG_VARIABLES 0x0002 #define DBG_RESOLVER 0x0004 #define DBG_EXECV 0x0008 +#define DBG_ENV 0x0010 #define DBG_VERBOSE0 0x8000 #define DBG_VERBOSE1 (0x4000 | DBG_VERBOSE0) #define DBG_VERBOSE2 (0x2000 | DBG_VERBOSE1) -static char const * ctx_s = 0; -static ctx_t ctx = VC_NOCTX; +int wrapper_exit_code = 255; + +static xid_t ctx = VC_NOCTX; static uint32_t caps = ~0; static int flags = 0; static char const * mnts = 0; @@ -79,7 +100,8 @@ static unsigned int debug_level = 0; static bool is_initialized = false; -DECLARE(execv); + //DECLARE(rpm_execcon); + //DECLARE(execv); DECLARE(getpwnam); DECLARE(getgrnam); DECLARE(endpwent); @@ -147,18 +169,125 @@ showVersion() exit(0); } +static void +unsetPreloadEnv() +{ + char *env = getenv("LD_PRELOAD"); + char *pos; + + // the const <-> non-const assignment is not an issue since the following + // modifying operations will not be executed in the const-case + env = env ? env : ""; + pos = strstr(env, LIBNAME); + + if (pos!=0) { + char *end_pos = pos + sizeof(LIBNAME); + bool is_end = (end_pos[-1]=='\0'); + char *start_pos; + + end_pos[-1] = '\0'; + start_pos = strrchr(env, ':'); + if (start_pos==0) start_pos = env; + else if (!is_end) ++start_pos; + + if (is_end) *start_pos = '\0'; + else memmove(start_pos, end_pos, strlen(end_pos)+1); + } + +#ifdef DEBUG + if (isDbgLevel(DBG_VERBOSE1|DBG_VARIABLES)) { + WRITE_MSG(2, "env='"); + WRITE_STR(2, env); + WRITE_MSG(2, "'\n"); + } +#endif + + if (*env=='\0') unsetenv("LD_PRELOAD"); +} + +static void +clearEnv() +{ + if (isDbgLevel(DBG_ENV)) WRITE_MSG(2, "clearEnv()\n"); + + unsetenv("RPM_FAKE_S_CONTEXT_REV"); + unsetenv("RPM_FAKE_S_CONTEXT_NR"); + unsetenv("RPM_FAKE_CTX"); + unsetenv("RPM_FAKE_FLAGS"); + unsetenv("RPM_FAKE_CHROOT"); + unsetenv("RPM_FAKE_NAMESPACE_MOUNTS"); + + unsetenv("RPM_FAKE_RESOLVER_GID"); + unsetenv("RPM_FAKE_RESOLVER_UID"); + unsetenv("RPM_FAKE_RESOLVER"); + unsetenv("RPM_FAKE_PWSOCKET"); + + unsetenv("RPM_FAKE_DEBUG"); + + unsetPreloadEnv(); +} + static int -getAndClearEnv(char const *key, int dflt) +getDefaultEnv(char const *key, int dflt) { char *env = getenv(key); int res; if (env==0 || env[0]=='\0') res = dflt; - else { - res = atoi(env); - unsetenv(key); + else res = atoi(env); + + return res; +} + + /// \returns true iff we are in ctx after leaving this function +static bool +setupContext(xid_t xid, char const **xid_str) +{ + bool res = false; + + if (vc_isSupported(vcFEATURE_MIGRATE)) { + xid_t rc=VC_NOCTX; + + if ((xid==VC_DYNAMIC_XID || !vc_is_dynamic_xid(xid)) && + (rc=vc_ctx_create(xid))==VC_NOCTX && + errno!=EEXIST) { + perror(ENSC_WRAPPERS_PREFIX "vc_create_context()"); + exit(255); + } + + if (rc!=VC_NOCTX) { + char buf[sizeof(xid_t)*3 + 128]; + size_t l; + struct vc_ctx_caps caps; + + strcpy(buf, "rpm-fake.so #"); + l = utilvserver_fmt_uint(buf+sizeof("rpm-fake.so #")-1, getppid()); + Evc_set_vhi_name(rc, vcVHI_CONTEXT, buf, sizeof("rpm-fake.so #")+l-1); + + caps.ccaps = 0ull; + caps.cmask = ~0ull; + caps.bcaps = ~vc_get_insecurebcaps(); + caps.bmask = ~0ull; + Evc_set_ccaps(rc, &caps); + + // context will be activated later... + + xid = rc; + res = true; + } } + if (xid==VC_DYNAMIC_XID) + *xid_str = 0; + else { + char buf[sizeof(xid_t)*3 + 2]; + size_t l; + + l = utilvserver_fmt_uint(buf, xid); buf[l] = '\0'; + *xid_str = strdup(buf); + } + + Ewrite(3, &xid, sizeof xid); return res; } @@ -180,11 +309,10 @@ initPwSocket() connect(pw_sock, (struct sockaddr *)(&addr), sizeof addr)==-1 || (flag=fcntl(pw_sock, F_GETFD))==-1 || fcntl(pw_sock, F_SETFD, flag | FD_CLOEXEC)==-1) { - perror("error while initializing pw-socket"); + perror(ENSC_WRAPPERS_PREFIX "error while initializing pw-socket"); exit(255); } } - unsetenv("RPM_FAKE_PWSOCKET"); } #else static void @@ -207,38 +335,55 @@ initPwSocket() pipe(sync_pipe)==-1 || fcntl(res_sock[0], F_SETFD, FD_CLOEXEC)==-1 || fcntl(sync_pipe[0], F_SETFD, FD_CLOEXEC)==-1) { - perror("rpm-fake.so: failed to create/initialize resolver-socket or pipe"); + perror(ENSC_WRAPPERS_PREFIX "failed to create/initialize resolver-socket or pipe"); exit(255); } pid = fork(); if (pid==-1) { - perror("rpm-fake.so: fork()"); + perror(ENSC_WRAPPERS_PREFIX "fork()"); exit(255); } if (pid==0) { - char const *args[10]; + char const *args[20]; char const **ptr = args; char const *env[] = { "HOME=/", "PATH=/bin:/usr/bin", 0 }; + char const *xid_str; + char flag_str[ sizeof(flags)*3 + 2]; + char caps_str[ sizeof(caps)*3 + 2]; + + clearEnv(); setsid(); dup2(res_sock[1], 0); dup2(res_sock[1], 1); - dup2(sync_pipe[1], 2); - close(sync_pipe[1]); + if (sync_pipe[1]!=3) { + close(3); + dup2(sync_pipe[1], 3); + close(sync_pipe[1]); + } close(res_sock[1]); /* ... *socket[0] are marked as close-on-exec ...*/ + flag_str[utilvserver_fmt_uint(flag_str, flags)] = '\0'; + caps_str[utilvserver_fmt_uint(caps_str, caps)] = '\0'; + *ptr++ = resolver; - if (root) { *ptr++ = "-r"; *ptr++ = root; } + *ptr++ = "-F"; *ptr++ = flag_str; + *ptr++ = "-C"; *ptr++ = caps_str; + if (root) { *ptr++ = "-r"; *ptr++ = "."; } if (uid) { *ptr++ = "-u"; *ptr++ = uid; } if (gid) { *ptr++ = "-g"; *ptr++ = gid; } - if (ctx_s) { *ptr++ = "-c"; *ptr++ = ctx_s; } - *ptr++ = 0; + + if (root) Echdir(root); + + if (setupContext(ctx, &xid_str)) { *ptr++ = "-s"; } + else if (xid_str) { *ptr++ = "-c"; *ptr++ = xid_str; } + *ptr++ = 0; execve(resolver, (char **)args, (char **)env); - perror("rpm-fake.so: failed to exec resolver"); + perror(ENSC_WRAPPERS_PREFIX "failed to exec resolver"); exit(255); } else { @@ -249,24 +394,49 @@ initPwSocket() pw_sock = res_sock[0]; sync_sock = sync_pipe[0]; - if (read(sync_sock, &c, 1)!=1 || + if (read(sync_sock, &ctx, sizeof ctx)!=sizeof(ctx) || + read(sync_sock, &c, 1)!=1 || write(pw_sock, ".", 1)!=1 || read(pw_sock, &c, 1)!=1 || c!='.') { - WRITE_MSG(2, "rpm-fake.so: failed to initialize communication with resolver"); + WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "failed to initialize communication with resolver\n"); exit(255); } - } - free((char *)ctx_s); - unsetenv("RPM_FAKE_RESOLVER_GID"); - unsetenv("RPM_FAKE_RESOLVER_UID"); - unsetenv("RPM_FAKE_RESOLVER"); + if (wait4(pid, 0, WNOHANG,0)==-1) { + WRITE_MSG(2, ENSC_WRAPPERS_PREFIX" unexpected initialization-error of resolver\n"); + exit(255); + } + } } } #endif static void +reduceCapabilities() +{ + struct __user_cap_header_struct header; + struct __user_cap_data_struct user; + + header.version = _LINUX_CAPABILITY_VERSION; + header.pid = 0; + + if (capget(&header, &user)==-1) { + perror("capget()"); + exit(wrapper_exit_code); + } + + user.effective &= ~(1< non-const assignment is not an issue since the following - // modifying operations will not be executed in the const-case - env = env ? env : ""; - pos = strstr(env, LIBNAME); - - if (pos!=0) { - char *end_pos = pos + sizeof(LIBNAME); - bool is_end = (end_pos[-1]=='\0'); - char *start_pos; - - end_pos[-1] = '\0'; - start_pos = strrchr(env, ':'); - if (start_pos==0) start_pos = env; - else if (!is_end) ++start_pos; - - if (is_end) *start_pos = '\0'; - else memmove(start_pos, end_pos, strlen(end_pos)+1); - } - -#ifdef DEBUG - if (isDbgLevel(DBG_VERBOSE1|DBG_VARIABLES)) { - WRITE_MSG(2, "env='"); - WRITE_STR(2, env); - WRITE_MSG(2, "'\n"); - } -#endif - - if (*env=='\0') unsetenv("LD_PRELOAD"); -} - void initRPMFake() { if (getenv("RPM_FAKE_VERSION")) showVersion(); if (getenv("RPM_FAKE_HELP")) showHelp(); - debug_level = getAndClearEnv("RPM_FAKE_DEBUG", 0); + debug_level = getDefaultEnv("RPM_FAKE_DEBUG", 0); if (isDbgLevel(DBG_INIT)) WRITE_MSG(2, ">>>>> initRPMFake <<<<<\n"); - fixPreloadEnv(); + reduceCapabilities(); initSymbols(); initEnvironment(); initPwSocket(); @@ -374,7 +509,7 @@ initRPMFake() void exitRPMFake() { - write(2, ">>>>> exitRPMFake <<<<<\n", 24); + if (isDbgLevel(DBG_INIT)) WRITE_MSG(2, ">>>>> exitRPMFake <<<<<\n"); if (pw_sock!=-1) { uint8_t c; read(sync_sock, &c, 1); @@ -456,12 +591,24 @@ endpwent() static int -execvWorker(char const *path, char * const argv[]) +execvWorker(char const *path, char * const argv[], char * const envp[]) { - int res; + int res = -1; + + if (vc_isSupported(vcFEATURE_MIGRATE)) + res = vc_ctx_migrate(ctx); + else { +#ifdef VC_ENABLE_API_COMPAT + res = vc_new_s_context(ctx,caps,flags); +#else + WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "can not change context: migrate kernel feature missing and 'compat' API disabled\n"); +#endif + } - if ((res=vc_new_s_context(ctx,caps,flags))!=-1 && - (res=execv_func(path, argv)!=-1) ) {} + clearEnv(); + + if (res!=-1) + res=execve(path, argv, envp); return res; } @@ -470,6 +617,7 @@ struct ExecvParams { char const * path; char * const * argv; + char * const * envp; char const * mnts; }; @@ -492,13 +640,14 @@ removeNamespaceMountsChild(struct ExecvParams const *params) ptr = strtok(0, ":"); } - return execvWorker(params->path, params->argv); + return execvWorker(params->path, params->argv, params->envp); } static int -removeNamespaceMounts(char const *path, char * const argv[]) +removeNamespaceMounts(char const *path, + char * const argv[], char * const envp[]) { - if (mnts==0) return execvWorker(path, argv); + if (mnts==0) return execvWorker(path, argv, envp); { int status; @@ -507,13 +656,18 @@ removeNamespaceMounts(char const *path, char * const argv[]) params.path = path; params.argv = argv; + params.envp = envp; params.mnts = mnts; // the rpmlib signal-handler is still active; use the default one to // make wait4() working... signal(SIGCHLD, SIG_DFL); - pid = syscall(__NR_clone, CLONE_NEWNS|SIGCHLD|CLONE_VFORK, 0); +#ifdef NDEBUG + pid = sys_clone(CLONE_NEWNS|SIGCHLD|CLONE_VFORK, 0); +#else + pid = sys_clone(CLONE_NEWNS|SIGCHLD, 0); +#endif switch (pid) { case -1 : return -1; @@ -535,7 +689,29 @@ removeNamespaceMounts(char const *path, char * const argv[]) int -execv(char const *path, char * const argv[]) __THROW +execv(char const *path, char * const argv[]) { - return removeNamespaceMounts(path, argv); + extern char **environ; + + if (isDbgLevel(DBG_EXECV)) { + WRITE_MSG(2, "execv('"); + WRITE_STR(2, path); + WRITE_MSG(2, "', ...)\n"); + } + + return removeNamespaceMounts(path, argv, environ); +} + +int +rpm_execcon(unsigned int UNUSED verified, + const char *filename, + char *const argv[], char *const envp[]) +{ + if (isDbgLevel(DBG_EXECV)) { + WRITE_MSG(2, "rpm_execcon(..., '"); + WRITE_STR(2, filename); + WRITE_MSG(2, "', ...)\n"); + } + + return removeNamespaceMounts(filename, argv, envp); }