3 // Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; version 2 of the License.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 #include "pathconfig.h"
26 #include <lib/vserver.h>
27 #include <lib/internal.h>
28 #include <lib_internal/sys_clone.h>
30 #include <sys/socket.h>
36 #include <asm/unistd.h>
42 #include <sys/mount.h>
46 #include <sys/types.h>
55 // FIXME: add configure autodetection and include <selinux.h> directly
56 int rpm_execcon(unsigned int verified,
58 char *const argv[], char *const envp[]);
61 #define ENSC_WRAPPERS_PREFIX "rpm-fake.so: "
62 #define ENSC_WRAPPERS_VSERVER 1
63 #define ENSC_WRAPPERS_UNISTD 1
67 #include "capability-compat.h"
69 #define LIBNAME "rpm-fake.so"
70 #define PLATFORM_FILE "/etc/rpm/platform"
72 #define INIT(FILE,FUNC) FUNC##_func = ((__typeof__(FUNC) *) (xdlsym(FILE, #FUNC)))
73 #define DECLARE(FUNC) static __typeof__(FUNC) * FUNC##_func = 0
77 #define DBG_INIT 0x0001
78 #define DBG_VARIABLES 0x0002
79 #define DBG_RESOLVER 0x0004
80 #define DBG_EXECV 0x0008
81 #define DBG_ENV 0x0010
82 #define DBG_VERBOSE0 0x8000
83 #define DBG_VERBOSE1 (0x4000 | DBG_VERBOSE0)
84 #define DBG_VERBOSE2 (0x2000 | DBG_VERBOSE1)
86 int wrapper_exit_code = 255;
88 static xid_t ctx = VC_NOCTX;
89 static uint32_t caps = ~0;
91 static char const * mnts = 0;
92 static char const * root = 0;
93 static int pw_sock = -1;
94 static int sync_sock = -1;
95 static unsigned int debug_level = 0;
97 static bool is_initialized = false;
99 static bool ctx_created = false;
101 //DECLARE(rpm_execcon);
108 static void initRPMFake() __attribute__((__constructor__));
109 static void exitRPMFake() __attribute__((__destructor__));
112 isDbgLevel(unsigned int level)
114 return ((debug_level&level)==level);
118 xdlsym(void *handle, const char *symbol)
120 void *res = dlsym(handle, symbol);
122 char const *error = dlerror();
123 Vwrite(2, symbol, strlen(symbol));
125 Vwrite(2, error, strlen(error));
138 "Usage: LD_PRELOAD=" LIBNAME " <executable> <args>*\n\n"
139 LIBNAME " unterstands the following environment variables:\n"
140 " $RPM_FAKE_RESOLVER ... program which does the NSS resolving (defaults\n"
141 " to " RESOLVER_PROG ")\n"
142 " $RPM_FAKE_RESOLVER_UID ... uid of the resolver program\n"
143 " $RPM_FAKE_RESOLVER_GID ... gid of the resolver program\n"
144 " $RPM_FAKE_CTX ... vserver context which shall be used for resolver\n"
146 " $RPM_FAKE_CAP ... linux capability remove-mask for the context\n"
147 " $RPM_FAKE_FLAGS ... vserver flags of the context\n"
148 " $RPM_FAKE_CHROOT ... directory of the chroot environment\n"
149 " $RPM_FAKE_NAMESPACE_MOUNTS\n"
150 " ... colon separated list of directories which will\n"
151 " umounted before scriptlet execution\n\n"
152 " $RPM_FAKE_HELP ... shows this message\n"
153 " $RPM_FAKE_VERSION ... shows the version of this program\n\n"
154 " $RPM_FAKE_DEBUG ... sets the debuglevel bitmask\n\n"
155 "Please report bugs to " PACKAGE_BUGREPORT "\n");
163 LIBNAME " " VERSION " -- wrapper around rpm\n"
164 "This program is part of " PACKAGE_STRING "\n\n"
165 "Copyright (C) 2003 Enrico Scholz\n"
166 VERSION_COPYRIGHT_DISCLAIMER);
173 char *env = getenv("LD_PRELOAD");
176 // the const <-> non-const assignment is not an issue since the following
177 // modifying operations will not be executed in the const-case
178 env = env ? env : "";
179 pos = strstr(env, LIBNAME);
182 char *end_pos = pos + sizeof(LIBNAME);
183 bool is_end = (end_pos[-1]=='\0');
187 start_pos = strrchr(env, ':');
188 if (start_pos==0) start_pos = env;
189 else if (!is_end) ++start_pos;
191 if (is_end) *start_pos = '\0';
192 else memmove(start_pos, end_pos, strlen(end_pos)+1);
196 if (isDbgLevel(DBG_VERBOSE1|DBG_VARIABLES)) {
197 WRITE_MSG(2, "env='");
203 if (*env=='\0') unsetenv("LD_PRELOAD");
209 if (isDbgLevel(DBG_ENV)) WRITE_MSG(2, "clearEnv()\n");
211 unsetenv("RPM_FAKE_S_CONTEXT_REV");
212 unsetenv("RPM_FAKE_S_CONTEXT_NR");
213 unsetenv("RPM_FAKE_CTX");
214 unsetenv("RPM_FAKE_FLAGS");
215 unsetenv("RPM_FAKE_CHROOT");
216 unsetenv("RPM_FAKE_NAMESPACE_MOUNTS");
218 unsetenv("RPM_FAKE_RESOLVER_GID");
219 unsetenv("RPM_FAKE_RESOLVER_UID");
220 unsetenv("RPM_FAKE_RESOLVER");
221 unsetenv("RPM_FAKE_PWSOCKET");
223 unsetenv("RPM_FAKE_DEBUG");
229 getDefaultEnv(char const *key, int dflt)
231 char *env = getenv(key);
234 if (env==0 || env[0]=='\0') res = dflt;
235 else res = atoi(env);
240 /// \returns true iff we are in ctx after leaving this function
242 setupContext(xid_t xid, char const **xid_str)
246 if (vc_isSupported(vcFEATURE_MIGRATE)) {
249 if ((xid==VC_DYNAMIC_XID || !vc_is_dynamic_xid(xid)) &&
250 (rc=vc_ctx_create(xid, NULL))==VC_NOCTX &&
252 perror(ENSC_WRAPPERS_PREFIX "vc_ctx_create()");
257 char buf[sizeof(xid_t)*3 + 128];
259 struct vc_ctx_caps caps;
260 struct vc_ctx_flags flags;
262 strcpy(buf, "rpm-fake.so #");
263 l = utilvserver_fmt_uint(buf+sizeof("rpm-fake.so #")-1, getppid());
264 Evc_set_vhi_name(rc, vcVHI_CONTEXT, buf, sizeof("rpm-fake.so #")+l-1);
268 caps.bcaps = ~vc_get_insecurebcaps();
270 Evc_set_ccaps(rc, &caps);
273 flags.mask = VC_VXF_SC_HELPER;
274 Evc_set_cflags(rc, &flags);
276 // context will be activated later...
284 if (xid==VC_DYNAMIC_XID)
287 char buf[sizeof(xid_t)*3 + 2];
290 l = utilvserver_fmt_uint(buf, xid); buf[l] = '\0';
291 *xid_str = strdup(buf);
294 Ewrite(3, &xid, sizeof xid);
302 char const * sock_name = getenv("RPM_FAKE_PWSOCKET");
305 struct sockaddr_un addr = {
306 .sun_family = AF_UNIX,
309 strncpy(addr.sun_path, sock_name, sizeof(addr.sun_path)-1);
310 addr.sun_path[sizeof(addr.sun_path)-1]='\0';
312 if ((pw_sock=socket(AF_UNIX, SOCK_STREAM, 0))==-1 ||
313 connect(pw_sock, (struct sockaddr *)(&addr), sizeof addr)==-1 ||
314 (flag=fcntl(pw_sock, F_GETFD))==-1 ||
315 fcntl(pw_sock, F_SETFD, flag | FD_CLOEXEC)==-1) {
316 perror(ENSC_WRAPPERS_PREFIX "error while initializing pw-socket");
325 char const * resolver = getenv("RPM_FAKE_RESOLVER");
326 if (resolver==0) resolver=RESOLVER_PROG;
328 if (resolver!=0 && *resolver!='\0') {
335 uid=getenv("RPM_FAKE_RESOLVER_UID");
336 gid=getenv("RPM_FAKE_RESOLVER_GID");
338 if (socketpair(AF_UNIX, SOCK_STREAM, 0, res_sock)==-1 ||
339 pipe(sync_pipe)==-1 ||
340 fcntl(res_sock[0], F_SETFD, FD_CLOEXEC)==-1 ||
341 fcntl(sync_pipe[0], F_SETFD, FD_CLOEXEC)==-1) {
342 perror(ENSC_WRAPPERS_PREFIX "failed to create/initialize resolver-socket or pipe");
348 perror(ENSC_WRAPPERS_PREFIX "fork()");
353 char const *args[20];
354 char const **ptr = args;
355 char const *env[] = { "HOME=/", "PATH=/bin:/usr/bin", 0 };
357 char flag_str[ sizeof(flags)*3 + 2];
358 char caps_str[ sizeof(caps)*3 + 2];
363 dup2(res_sock[1], 0);
364 dup2(res_sock[1], 1);
365 if (sync_pipe[1]!=3) {
367 dup2(sync_pipe[1], 3);
371 /* ... *socket[0] are marked as close-on-exec ...*/
373 flag_str[utilvserver_fmt_uint(flag_str, flags)] = '\0';
374 caps_str[utilvserver_fmt_uint(caps_str, caps)] = '\0';
377 *ptr++ = "-F"; *ptr++ = flag_str;
378 *ptr++ = "-C"; *ptr++ = caps_str;
379 if (root) { *ptr++ = "-r"; *ptr++ = "."; }
380 if (uid) { *ptr++ = "-u"; *ptr++ = uid; }
381 if (gid) { *ptr++ = "-g"; *ptr++ = gid; }
383 if (root) Echdir(root);
385 if (setupContext(ctx, &xid_str)) { *ptr++ = "-s"; }
386 else if (xid_str) { *ptr++ = "-c"; *ptr++ = xid_str; }
389 execve(resolver, (char **)args, (char **)env);
390 perror(ENSC_WRAPPERS_PREFIX "failed to exec resolver");
398 pw_sock = res_sock[0];
399 sync_sock = sync_pipe[0];
401 if (read(sync_sock, &ctx, sizeof ctx)!=sizeof(ctx) ||
402 read(sync_sock, &c, 1)!=1 ||
403 write(pw_sock, ".", 1)!=1 ||
404 read(pw_sock, &c, 1)!=1 ||
406 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "failed to initialize communication with resolver\n");
410 if (wait4(pid, 0, WNOHANG,0)==-1) {
411 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX" unexpected initialization-error of resolver\n");
423 struct __user_cap_header_struct header;
424 struct __user_cap_data_struct user[2];
426 header.version = _LINUX_CAPABILITY_VERSION_2;
430 if (capget(&header, user)==-1) {
432 header.version != _LINUX_CAPABILITY_VERSION_2) {
433 header.version = _LINUX_CAPABILITY_VERSION_1;
438 exit(wrapper_exit_code);
441 user[0].effective &= ~(1<<CAP_MKNOD);
442 user[0].permitted &= ~(1<<CAP_MKNOD);
443 user[0].inheritable &= ~(1<<CAP_MKNOD);
445 if (capset(&header, user)==-1) {
447 exit(wrapper_exit_code);
457 if (is_initialized) return;
459 syscall_rev = getDefaultEnv("RPM_FAKE_S_CONTEXT_REV", 0);
460 syscall_nr = getDefaultEnv("RPM_FAKE_S_CONTEXT_NR", 273);
462 #ifdef VC_ENABLE_API_LEGACY
464 extern void vc_init_internal_legacy(int ctx_rev, int ctx_number,
465 int ipv4_rev, int ipv4_number);
467 vc_init_internal_legacy(syscall_rev, syscall_nr, 3, 274);
471 ctx = getDefaultEnv("RPM_FAKE_CTX", VC_DYNAMIC_XID);
472 caps = getDefaultEnv("RPM_FAKE_CAP", ~0x3404040f);
473 flags = getDefaultEnv("RPM_FAKE_FLAGS", 0);
474 root = getenv("RPM_FAKE_CHROOT");
475 mnts = getenv("RPM_FAKE_NAMESPACE_MOUNTS");
476 if (mnts && *mnts) mnts = strdup(mnts);
480 if (isDbgLevel(DBG_VERBOSE1))
481 dprintf(2, "ctx=%u, caps=%016x, flags=%016x,\nroot='%s',\nmnts='%s'\n",
482 ctx, caps, flags, root, mnts);
485 is_initialized = true;
491 //INIT(RTLD_NEXT, rpm_execcon);
492 //INIT(RTLD_NEXT, execv);
493 INIT(RTLD_NEXT, getgrnam);
494 INIT(RTLD_NEXT, getpwnam);
495 INIT(RTLD_NEXT, endpwent);
496 INIT(RTLD_NEXT, endgrent);
502 if (getenv("RPM_FAKE_VERSION")) showVersion();
503 if (getenv("RPM_FAKE_HELP")) showHelp();
505 debug_level = getDefaultEnv("RPM_FAKE_DEBUG", 0);
507 if (isDbgLevel(DBG_INIT)) WRITE_MSG(2, ">>>>> initRPMFake <<<<<\n");
509 reduceCapabilities();
515 if (isDbgLevel(DBG_VARIABLES|DBG_VERBOSE2)) {
524 if (isDbgLevel(DBG_INIT)) WRITE_MSG(2, ">>>>> exitRPMFake <<<<<\n");
527 if (read(sync_sock, &c, 1)!=1) { /*...*/ }
528 if (write(pw_sock, "Q", 1)!=1) { /*...*/ }
530 if (vc_isSupported(vcFEATURE_VWAIT)) {
531 if (vc_wait_exit(ctx)==-1) { /*...*/ }
535 if (read(sync_sock, &c, 1)!=0) { /*...*/}
542 //============ the worker part ===========
546 doPwStringRequest(uint32_t *result, char style, char const *name)
548 uint32_t len = strlen(name);
552 return (TEMP_FAILURE_RETRY(read (sync_sock, &c, 1))==1 &&
553 TEMP_FAILURE_RETRY(write(pw_sock, &style, 1))==1 &&
554 TEMP_FAILURE_RETRY(write(pw_sock, &len, sizeof len))==sizeof(len) &&
555 TEMP_FAILURE_RETRY(write(pw_sock, name, len))==(ssize_t)(len) &&
556 TEMP_FAILURE_RETRY(read (pw_sock, &code, sizeof code))==sizeof(code) &&
557 TEMP_FAILURE_RETRY(read (pw_sock, result, sizeof *result))==sizeof(*result) &&
562 getpwnam(const char * name)
564 if (pw_sock==-1) return getpwnam_func(name);
567 static struct passwd res = {
572 .pw_shell = "/bin/false"
575 res.pw_name = (char *)(name);
576 if (!doPwStringRequest(&id, 'P', name)) return 0;
584 getgrnam(const char * name)
586 if (pw_sock==-1) return getgrnam_func(name);
589 static struct group res = {
594 res.gr_name = (char *)(name);
595 if (!doPwStringRequest(&id, 'G', name)) return 0;
605 if (pw_sock==-1) endgrent_func();
606 TEMP_FAILURE_RETRY(write(pw_sock, "Cg", 2));
612 if (pw_sock==-1) endpwent_func();
613 TEMP_FAILURE_RETRY(write(pw_sock, "Cp", 2));
618 execvWorker(char const *path, char * const argv[], char * const envp[])
622 if (vc_isSupported(vcFEATURE_MIGRATE))
623 res = vc_ctx_migrate(ctx, 0);
625 #ifdef VC_ENABLE_API_COMPAT
626 res = vc_new_s_context(ctx,caps,flags);
628 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "can not change context: migrate kernel feature missing and 'compat' API disabled\n");
635 res=execve(path, argv, envp);
649 removeNamespaceMountsChild(struct ExecvParams const *params)
651 char buf[strlen(params->mnts)+1], *ptr;
653 strcpy(buf, params->mnts);
654 ptr = strtok(buf, ":");
656 if (umount2(ptr, 0)==-1) {
657 // FIXME: What is the semantic for CLONE_NEWNS? Is it ok that mounts in
658 // chroots are visible only, when chroot is on /dev/root?
660 // For now, ignore any errors, but future versions should handle them.
664 ptr = strtok(0, ":");
667 return execvWorker(params->path, params->argv, params->envp);
671 removeNamespaceMounts(char const *path,
672 char * const argv[], char * const envp[])
674 if (mnts==0) return execvWorker(path, argv, envp);
679 struct ExecvParams params;
686 // the rpmlib signal-handler is still active; use the default one to
687 // make wait4() working...
688 signal(SIGCHLD, SIG_DFL);
691 pid = sys_clone(CLONE_NEWNS|SIGCHLD|CLONE_VFORK, 0);
693 pid = sys_clone(CLONE_NEWNS|SIGCHLD, 0);
698 case 0 : _exit(removeNamespaceMountsChild(¶ms));
702 while ((p=wait4(pid, &status, 0,0))==-1 &&
703 (errno==EINTR || errno==EAGAIN)) ;
705 if (p==-1) return -1;
707 if (WIFEXITED(status)) _exit(WEXITSTATUS(status));
708 if (WIFSIGNALED(status)) kill(getpid(), WTERMSIG(status));
716 execv(char const *path, char * const argv[])
718 extern char **environ;
720 if (isDbgLevel(DBG_EXECV)) {
721 WRITE_MSG(2, "execv('");
723 WRITE_MSG(2, "', ...)\n");
726 return removeNamespaceMounts(path, argv, environ);
730 rpm_execcon(unsigned int UNUSED verified,
731 const char *filename,
732 char *const argv[], char *const envp[])
734 if (isDbgLevel(DBG_EXECV)) {
735 WRITE_MSG(2, "rpm_execcon(..., '");
736 WRITE_STR(2, filename);
737 WRITE_MSG(2, "', ...)\n");
740 return removeNamespaceMounts(filename, argv, envp);