#include "pathconfig.h"
#include "util.h"
-#include <vserver.h>
+#include <lib/vserver.h>
+#include <lib/internal.h>
+#include <lib_internal/sys_clone.h>
+#include <sys/socket.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <stdint.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
-#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <pwd.h>
#include <grp.h>
+
+ // from selinux.h
+ // FIXME: add configure autodetection and include <selinux.h> 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 <wrappers.h>
+
+#undef _POSIX_SOURCE
+#include "capability-compat.h"
+
#ifndef CLONE_NEWNS
# define CLONE_NEWNS 0x00020000
#endif
#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;
+int wrapper_exit_code = 255;
+
static xid_t ctx = VC_NOCTX;
static uint32_t caps = ~0;
static int flags = 0;
static bool is_initialized = false;
-DECLARE(execv);
+ //DECLARE(rpm_execcon);
+ //DECLARE(execv);
DECLARE(getpwnam);
DECLARE(getgrnam);
DECLARE(endpwent);
void *res = dlsym(handle, symbol);
if (res==0) {
char const *error = dlerror();
- write(2, symbol, strlen(symbol));
- write(2, ": ", 2);
- write(2, error, strlen(error));
- write(2, "\n", 2);
+ Vwrite(2, symbol, strlen(symbol));
+ Vwrite(2, ": ", 2);
+ Vwrite(2, error, strlen(error));
+ Vwrite(2, "\n", 2);
_exit(255);
}
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;
}
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
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 {
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<<CAP_MKNOD);
+ user.permitted &= ~(1<<CAP_MKNOD);
+ user.inheritable &= ~(1<<CAP_MKNOD);
+
+ if (capset(&header, &user)==-1) {
+ perror("capset()");
+ exit(wrapper_exit_code);
+ }
+}
+
+static void
initEnvironment()
{
int syscall_rev;
if (is_initialized) return;
- syscall_rev = getAndClearEnv("RPM_FAKE_S_CONTEXT_REV", 0);
- syscall_nr = getAndClearEnv("RPM_FAKE_S_CONTEXT_NR", 273);
+ syscall_rev = getDefaultEnv("RPM_FAKE_S_CONTEXT_REV", 0);
+ syscall_nr = getDefaultEnv("RPM_FAKE_S_CONTEXT_NR", 273);
#ifdef VC_ENABLE_API_LEGACY
{
}
#endif
- ctx_s = getenv("RPM_FAKE_CTX");
- if (ctx_s) ctx_s = strdup(ctx_s);
-
- ctx = getAndClearEnv("RPM_FAKE_CTX", VC_RANDCTX);
- caps = getAndClearEnv("RPM_FAKE_CAP", ~0x3404040f);
- flags = getAndClearEnv("RPM_FAKE_FLAGS", 0);
+ ctx = getDefaultEnv("RPM_FAKE_CTX", VC_DYNAMIC_XID);
+ caps = getDefaultEnv("RPM_FAKE_CAP", ~0x3404040f);
+ flags = getDefaultEnv("RPM_FAKE_FLAGS", 0);
root = getenv("RPM_FAKE_CHROOT");
mnts = getenv("RPM_FAKE_NAMESPACE_MOUNTS");
- if (mnts!=0) mnts = strdup(mnts);
-
- unsetenv("RPM_FAKE_CHROOT");
- unsetenv("RPM_FAKE_NAMESPACE_MOUNTS");
+ if (mnts && *mnts) mnts = strdup(mnts);
+ else mnts = 0;
+#if DEBUG
+ if (isDbgLevel(DBG_VERBOSE1))
+ dprintf(2, "ctx=%u, caps=%016x, flags=%016x,\nroot='%s',\nmnts='%s'\n",
+ ctx, caps, flags, root, mnts);
+#endif
is_initialized = true;
}
static void
initSymbols()
{
- INIT(RTLD_NEXT, execv);
+ //INIT(RTLD_NEXT, rpm_execcon);
+ //INIT(RTLD_NEXT, execv);
INIT(RTLD_NEXT, getgrnam);
INIT(RTLD_NEXT, getpwnam);
INIT(RTLD_NEXT, endpwent);
INIT(RTLD_NEXT, endgrent);
}
-static void
-fixPreloadEnv()
-{
- 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");
-}
-
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();
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);
- write(pw_sock, "Q", 1);
+ if (read(sync_sock, &c, 1)!=1) { /*...*/ }
+ if (write(pw_sock, "Q", 1)!=1) { /*...*/ }
}
}
static bool
doPwStringRequest(uint32_t *result, char style, char const *name)
{
- uint32_t len = strlen(name);
+ size_t len = strlen(name);
uint8_t code;
uint8_t c;
- // read the token...
- read(sync_sock, &c, 1);
-
- write(pw_sock, &style, 1);
- write(pw_sock, &len, sizeof len);
- write(pw_sock, name, len);
- read (pw_sock, &code, sizeof code);
- read (pw_sock, result, sizeof *result);
-
- return code!=0;
+ return (TEMP_FAILURE_RETRY(read (sync_sock, &c, 1))==1 &&
+
+ TEMP_FAILURE_RETRY(write(pw_sock, &style, 1))==1 &&
+ TEMP_FAILURE_RETRY(write(pw_sock, &len, sizeof len))==sizeof(len) &&
+ TEMP_FAILURE_RETRY(write(pw_sock, name, len))==(ssize_t)(len) &&
+ TEMP_FAILURE_RETRY(read (pw_sock, &code, sizeof code))==sizeof(code) &&
+ TEMP_FAILURE_RETRY(read (pw_sock, result, sizeof *result))==sizeof(*result) &&
+ code!=0);
}
struct passwd *
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;
}
{
char const * path;
char * const * argv;
+ char * const * envp;
char const * mnts;
};
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;
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;
int
-execv(char const *path, char * const argv[]) __THROW
+execv(char const *path, char * const 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);
+}
+
+int
+is_selinux_enabled()
{
- return removeNamespaceMounts(path, argv);
+ return 0;
}