added Vector_zeroEnd() function
[util-vserver.git] / util-vserver / src / rpm-fake.c
1 // $Id$    --*- c++ -*--
2
3 // Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 //  
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.
8 //  
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.
13 //  
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.
17 //  
18
19 #ifdef HAVE_CONFIG_H
20 #  include <config.h>
21 #endif
22
23 #include "pathconfig.h"
24 #include "sys_clone.h"
25 #include "util.h"
26
27 #include "lib/vserver.h"
28 #include "lib/internal.h"
29
30 #include <sys/socket.h>
31 #include <dlfcn.h>
32 #include <stdlib.h>
33 #include <stdint.h>
34 #include <stdio.h>
35 #include <unistd.h>
36 #include <asm/unistd.h>
37 #include <string.h>
38 #include <stdbool.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <stdarg.h>
42 #include <sys/mount.h>
43 #include <linux/fs.h>
44 #include <sched.h>
45 #include <signal.h>
46 #include <sys/types.h>
47 #include <sys/wait.h>
48 #include <sys/un.h>
49 #include <fcntl.h>
50 #include <pwd.h>
51 #include <grp.h>
52
53 #define ENSC_WRAPPERS_PREFIX    "rpm-fake.so: "
54 #define ENSC_WRAPPERS_VSERVER   1
55 #define ENSC_WRAPPERS_UNISTD    1
56 #include <wrappers.h>
57
58 #ifndef CLONE_NEWNS
59 #  define CLONE_NEWNS   0x00020000
60 #endif
61
62 #define LIBNAME         "rpm-fake.so"
63 #define PLATFORM_FILE   "/etc/rpm/platform"
64
65 #define INIT(FILE,FUNC) FUNC##_func = ((__typeof__(FUNC) *) (xdlsym(FILE, #FUNC)))
66 #define DECLARE(FUNC)   static __typeof__(FUNC) *       FUNC##_func = 0
67
68
69 #define DBG_INIT        0x0001
70 #define DBG_VARIABLES   0x0002
71 #define DBG_RESOLVER    0x0004
72 #define DBG_EXECV       0x0008
73 #define DBG_VERBOSE0    0x8000
74 #define DBG_VERBOSE1    (0x4000 | DBG_VERBOSE0)
75 #define DBG_VERBOSE2    (0x2000 | DBG_VERBOSE1)
76
77 int                     wrapper_exit_code = 255;
78
79 static xid_t            ctx   = VC_NOCTX;
80 static uint32_t         caps  = ~0;
81 static int              flags = 0;
82 static char const *     mnts  = 0;
83 static char const *     root  = 0;
84 static int              pw_sock   = -1;
85 static int              sync_sock = -1;
86 static unsigned int     debug_level = 0;
87
88 static bool             is_initialized = false;
89
90 DECLARE(execv);
91 DECLARE(getpwnam);
92 DECLARE(getgrnam);
93 DECLARE(endpwent);
94 DECLARE(endgrent);
95
96 static void             initRPMFake() __attribute__((__constructor__));
97 static void             exitRPMFake() __attribute__((__destructor__));
98
99 static inline bool
100 isDbgLevel(unsigned int level)
101 {
102   return ((debug_level&level)==level);
103 }
104
105 static void *
106 xdlsym(void *handle, const char *symbol)
107 {
108   void  *res = dlsym(handle, symbol);
109   if (res==0) {
110     char const  *error = dlerror();
111     write(2, symbol, strlen(symbol));
112     write(2, ": ", 2);
113     write(2, error, strlen(error));
114     write(2, "\n", 2);
115
116     _exit(255);
117   }
118
119   return res;
120 }
121
122 static void
123 showHelp()
124 {
125   WRITE_MSG(1,
126             "Usage: LD_PRELOAD=" LIBNAME " <executable> <args>*\n\n"
127             LIBNAME " unterstands the following environment variables:\n"
128             "  $RPM_FAKE_RESOLVER     ...  program which does the NSS resolving (defaults\n"
129             "                              to " RESOLVER_PROG ")\n"
130             "  $RPM_FAKE_RESOLVER_UID ...  uid of the resolver program\n"
131             "  $RPM_FAKE_RESOLVER_GID ...  gid of the resolver program\n"
132             "  $RPM_FAKE_CTX          ...  vserver context which shall be used for resolver\n"
133             "                              and scriptlets\n"
134             "  $RPM_FAKE_CAP          ...  linux capability remove-mask for the context\n"
135             "  $RPM_FAKE_FLAGS        ...  vserver flags of the context\n"
136             "  $RPM_FAKE_CHROOT       ...  directory of the chroot environment\n"
137             "  $RPM_FAKE_NAMESPACE_MOUNTS\n"
138             "                          ... colon separated list of directories which will\n"
139             "                              umounted before scriptlet execution\n\n"
140             "  $RPM_FAKE_HELP          ... shows this message\n"
141             "  $RPM_FAKE_VERSION       ... shows the version of this program\n\n"
142             "  $RPM_FAKE_DEBUG         ... sets the debuglevel bitmask\n\n"
143             "Please report bugs to " PACKAGE_BUGREPORT "\n");
144   exit(0);
145 }
146
147 static void
148 showVersion()
149 {
150   WRITE_MSG(1,
151             LIBNAME " " VERSION " -- wrapper around rpm\n"
152             "This program is part of " PACKAGE_STRING "\n\n"
153             "Copyright (C) 2003 Enrico Scholz\n"
154             VERSION_COPYRIGHT_DISCLAIMER);
155   exit(0);
156 }
157
158 static int
159 getAndClearEnv(char const *key, int dflt)
160 {
161   char          *env = getenv(key);
162   int           res;
163
164   if (env==0 || env[0]=='\0') res = dflt;
165   else {
166     res = atoi(env);
167     unsetenv(key);
168   }
169
170   return res;
171 }
172
173   /// \returns true iff we are in ctx after leaving this function
174 static bool
175 setupContext(xid_t xid, char const **xid_str)
176 {
177   bool          res = false;
178   
179   if (vc_isSupported(vcFEATURE_MIGRATE)) {
180     xid_t       rc=VC_NOCTX;
181
182     if ((xid==VC_DYNAMIC_XID || !vc_is_dynamic_xid(xid)) &&
183         (rc=vc_ctx_create(xid))==VC_NOCTX &&
184         errno!=EEXIST) {
185       perror(ENSC_WRAPPERS_PREFIX "vc_create_context()");
186       exit(255);
187     }
188
189     if (rc!=VC_NOCTX) {
190       char                      buf[sizeof(xid_t)*3 + 128];
191       size_t                    l;
192       struct vc_ctx_caps        caps;
193       
194       strcpy(buf, "rpm-fake.so #");
195       l = utilvserver_fmt_uint(buf+sizeof("rpm-fake.so #")-1, getppid());
196       Evc_set_vhi_name(rc, vcVHI_CONTEXT, buf, sizeof("rpm-fake.so #")+l-1);
197
198       caps.ccaps =  0ull;
199       caps.cmask = ~0ull;
200       caps.bcaps = ~vc_get_insecurebcaps();
201       caps.bmask = ~0ull;
202       Evc_set_ccaps(rc, &caps);
203       
204         // context will be activated later...
205
206       xid = rc;
207       res = true;
208     }
209   }
210
211   if (xid==VC_DYNAMIC_XID)
212     *xid_str = 0;
213   else {
214     char                buf[sizeof(xid_t)*3 + 2];
215     size_t              l;
216     
217     l        = utilvserver_fmt_uint(buf, xid); buf[l] = '\0';
218     *xid_str = strdup(buf);
219   }
220  
221   Ewrite(3, &xid, sizeof xid);
222   return res;
223 }
224
225 #if 0
226 static void
227 initPwSocket()
228 {
229   char const *  sock_name = getenv("RPM_FAKE_PWSOCKET");
230   if (sock_name!=0) {
231     int flag;
232     struct sockaddr_un  addr = {
233       .sun_family = AF_UNIX,
234     };
235
236     strncpy(addr.sun_path, sock_name, sizeof(addr.sun_path)-1);
237     addr.sun_path[sizeof(addr.sun_path)-1]='\0';
238     
239     if ((pw_sock=socket(AF_UNIX, SOCK_STREAM, 0))==-1 ||
240         connect(pw_sock, (struct sockaddr *)(&addr), sizeof addr)==-1 ||
241         (flag=fcntl(pw_sock, F_GETFD))==-1 ||
242         fcntl(pw_sock, F_SETFD, flag | FD_CLOEXEC)==-1) {
243       perror(ENSC_WRAPPERS_PREFIX "error while initializing pw-socket");
244       exit(255);
245     }
246   }
247   unsetenv("RPM_FAKE_PWSOCKET");
248 }
249 #else
250 static void
251 initPwSocket()
252 {
253   char const *  resolver = getenv("RPM_FAKE_RESOLVER");
254   if (resolver==0) resolver=RESOLVER_PROG;
255   
256   if (resolver!=0 && *resolver!='\0') {
257     int                 res_sock[2];
258     int                 sync_pipe[2];
259     pid_t               pid;
260     char const *        uid=0;
261     char const *        gid=0;
262
263     uid=getenv("RPM_FAKE_RESOLVER_UID");
264     gid=getenv("RPM_FAKE_RESOLVER_GID");
265
266     if (socketpair(AF_UNIX, SOCK_STREAM, 0, res_sock)==-1 ||
267         pipe(sync_pipe)==-1 ||
268         fcntl(res_sock[0],  F_SETFD, FD_CLOEXEC)==-1 ||
269         fcntl(sync_pipe[0], F_SETFD, FD_CLOEXEC)==-1) {
270       perror(ENSC_WRAPPERS_PREFIX "failed to create/initialize resolver-socket or pipe");
271       exit(255);
272     }
273
274     pid = fork();
275     if (pid==-1) {
276       perror(ENSC_WRAPPERS_PREFIX "fork()");
277       exit(255);
278     }
279
280     if (pid==0) {
281       char const        *args[20];
282       char const        **ptr  = args;
283       char const        *env[] = { "HOME=/", "PATH=/bin:/usr/bin", 0 };
284       char const        *xid_str;
285       char              flag_str[ sizeof(flags)*3 + 2];
286       char              caps_str[ sizeof(caps)*3  + 2];
287       
288       setsid();
289       dup2(res_sock[1],  0);
290       dup2(res_sock[1],  1);
291       if (sync_pipe[1]!=3) {
292         close(3);
293         dup2(sync_pipe[1], 3);
294         close(sync_pipe[1]);
295       }
296       close(res_sock[1]);
297         /* ... *socket[0] are marked as close-on-exec ...*/
298
299       flag_str[utilvserver_fmt_uint(flag_str, flags)] = '\0';
300       caps_str[utilvserver_fmt_uint(caps_str, caps)]  = '\0';
301
302       *ptr++ = resolver;
303       *ptr++ = "-F"; *ptr++ = flag_str;
304       *ptr++ = "-C"; *ptr++ = caps_str;
305       if (root)  { *ptr++ = "-r"; *ptr++ = root;  }
306       if (uid)   { *ptr++ = "-u"; *ptr++ = uid;   }
307       if (gid)   { *ptr++ = "-g"; *ptr++ = gid;   }
308
309       if (setupContext(ctx, &xid_str)) { *ptr++ = "-s"; }
310       else if (xid_str)                { *ptr++ = "-c"; *ptr++ = xid_str; }
311       
312       *ptr++ = 0;
313       execve(resolver, (char **)args, (char **)env);
314       perror(ENSC_WRAPPERS_PREFIX "failed to exec resolver");
315       exit(255);
316     }
317     else {
318       uint8_t           c;
319
320       close(res_sock[1]);
321       close(sync_pipe[1]);
322       pw_sock   = res_sock[0];
323       sync_sock = sync_pipe[0];
324
325       if (read(sync_sock, &ctx, sizeof ctx)!=sizeof(ctx) ||
326           read(sync_sock, &c, 1)!=1 ||
327           write(pw_sock, ".", 1)!=1 ||
328           read(pw_sock, &c,   1)!=1 ||
329           c!='.') {
330         WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "failed to initialize communication with resolver\n");
331         exit(255);
332       }
333
334       if (wait4(pid, 0, WNOHANG,0)==-1) {
335         WRITE_MSG(2, ENSC_WRAPPERS_PREFIX" unexpected initialization-error of resolver\n");
336         exit(255);
337       }
338     }
339
340     unsetenv("RPM_FAKE_RESOLVER_GID");
341     unsetenv("RPM_FAKE_RESOLVER_UID");
342     unsetenv("RPM_FAKE_RESOLVER");    
343   }
344 }
345 #endif
346
347 static void
348 initEnvironment()
349 {
350   int           syscall_rev;
351   int           syscall_nr;
352   
353   if (is_initialized) return;
354
355   syscall_rev = getAndClearEnv("RPM_FAKE_S_CONTEXT_REV", 0);
356   syscall_nr  = getAndClearEnv("RPM_FAKE_S_CONTEXT_NR",  273);
357   
358 #ifdef VC_ENABLE_API_LEGACY
359   {
360     extern void vc_init_internal_legacy(int ctx_rev, int ctx_number,
361                                         int ipv4_rev, int ipv4_number);
362   
363     vc_init_internal_legacy(syscall_rev, syscall_nr, 3, 274);
364   }
365 #endif
366
367   ctx       = getAndClearEnv("RPM_FAKE_CTX",  VC_DYNAMIC_XID);
368   caps      = getAndClearEnv("RPM_FAKE_CAP",  ~0x3404040f);
369   flags     = getAndClearEnv("RPM_FAKE_FLAGS", 0);
370   root      = getenv("RPM_FAKE_CHROOT");
371   mnts      = getenv("RPM_FAKE_NAMESPACE_MOUNTS");
372   if (mnts && *mnts) mnts = strdup(mnts);
373   else               mnts = 0;
374
375   unsetenv("RPM_FAKE_CHROOT");
376   unsetenv("RPM_FAKE_NAMESPACE_MOUNTS");
377
378   
379   is_initialized = true;
380 }
381
382 static void
383 initSymbols()
384 {
385   INIT(RTLD_NEXT, execv);
386   INIT(RTLD_NEXT, getgrnam);
387   INIT(RTLD_NEXT, getpwnam);
388   INIT(RTLD_NEXT, endpwent);
389   INIT(RTLD_NEXT, endgrent);
390 }
391
392 static void
393 fixPreloadEnv()
394 {
395   char                  *env = getenv("LD_PRELOAD");
396   char                  *pos;
397
398     // the const <-> non-const assignment is not an issue since the following
399     // modifying operations will not be executed in the const-case
400   env = env ? env : "";
401   pos = strstr(env, LIBNAME);
402
403   if (pos!=0) {
404     char        *end_pos = pos + sizeof(LIBNAME);
405     bool        is_end = (end_pos[-1]=='\0');
406     char        *start_pos;
407
408     end_pos[-1] = '\0';
409     start_pos   = strrchr(env, ':');
410     if (start_pos==0) start_pos = env;
411     else if (!is_end) ++start_pos;
412
413     if (is_end) *start_pos = '\0';
414     else        memmove(start_pos, end_pos, strlen(end_pos)+1);
415   }
416
417 #ifdef DEBUG
418   if (isDbgLevel(DBG_VERBOSE1|DBG_VARIABLES)) {
419     WRITE_MSG(2, "env='");
420     WRITE_STR(2, env);
421     WRITE_MSG(2, "'\n");
422   }
423 #endif
424
425   if (*env=='\0') unsetenv("LD_PRELOAD");
426 }
427
428 void
429 initRPMFake()
430 {
431   if (getenv("RPM_FAKE_VERSION")) showVersion();
432   if (getenv("RPM_FAKE_HELP"))    showHelp();
433   
434   debug_level = getAndClearEnv("RPM_FAKE_DEBUG", 0);
435
436   if (isDbgLevel(DBG_INIT)) WRITE_MSG(2, ">>>>> initRPMFake <<<<<\n");
437   
438   fixPreloadEnv();
439   initSymbols();
440   initEnvironment();
441   initPwSocket();
442
443 #if 0
444   if (isDbgLevel(DBG_VARIABLES|DBG_VERBOSE2)) {
445     
446   }
447 #endif
448 }
449
450 void
451 exitRPMFake()
452
453   if (isDbgLevel(DBG_INIT)) WRITE_MSG(2, ">>>>> exitRPMFake <<<<<\n");
454   if (pw_sock!=-1) {
455     uint8_t     c;
456     read(sync_sock, &c, 1);
457     write(pw_sock, "Q", 1);
458   }
459 }
460
461
462   //============   the worker part   ===========
463
464
465 static bool
466 doPwStringRequest(uint32_t *result, char style, char const *name)
467 {
468   uint32_t      len = strlen(name);
469   uint8_t       code;
470   uint8_t       c;
471
472     // read the token...
473   read(sync_sock, &c, 1);
474
475   write(pw_sock, &style, 1);
476   write(pw_sock, &len,   sizeof len);
477   write(pw_sock, name,   len);
478   read (pw_sock, &code,  sizeof code);
479   read (pw_sock, result, sizeof *result);
480
481   return code!=0;
482 }
483
484 struct passwd *
485 getpwnam(const char * name)
486 {
487   if (pw_sock==-1) return getpwnam_func(name);
488   else {
489     static struct passwd        res = {
490       .pw_passwd = "*",
491       .pw_gid    = -1,
492       .pw_gecos  = "",
493       .pw_dir    = "/",
494       .pw_shell  = "/bin/false"
495     };
496
497     res.pw_name = (char *)(name);
498     if (!doPwStringRequest(&res.pw_uid, 'P', name)) return 0;
499     
500     return &res;
501   }
502 }
503
504 struct group *
505 getgrnam(const char * name)
506 {
507   if (pw_sock==-1) return getgrnam_func(name);
508   else {
509     static struct group         res = {
510       .gr_passwd = "*",
511       .gr_mem    = 0
512     };
513
514     res.gr_name = (char *)(name);
515     if (!doPwStringRequest(&res.gr_gid, 'G', name)) return 0;
516
517     return &res;
518   }
519 }
520
521 void
522 endgrent()
523 {
524   if (pw_sock==-1) endgrent_func();
525 }
526
527 void
528 endpwent()
529 {
530   if (pw_sock==-1) endpwent_func();
531 }
532
533
534 static int
535 execvWorker(char const *path, char * const argv[])
536 {
537   int           res = -1;
538
539   if (vc_isSupported(vcFEATURE_MIGRATE))
540     res = vc_ctx_migrate(ctx);
541   else
542 #ifdef VC_ENABLE_API_COMPAT  
543     res = vc_new_s_context(ctx,caps,flags);
544 #else
545     WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "can not change context: migrate kernel feature missing and 'compat' API disabled\n");
546 #endif
547     
548   if (res!=-1)
549     res=execv_func(path, argv);
550
551   return res;
552 }
553
554 struct ExecvParams
555 {
556     char const *        path;
557     char * const *      argv;
558     char const *        mnts;
559 };
560
561 static int
562 removeNamespaceMountsChild(struct ExecvParams const *params)
563 {
564   char                  buf[strlen(params->mnts)+1], *ptr;
565
566   strcpy(buf, params->mnts);
567   ptr = strtok(buf, ":");
568   while (ptr) {
569     if (umount2(ptr, 0)==-1) {
570         // FIXME: What is the semantic for CLONE_NEWNS? Is it ok that mounts in
571         // chroots are visible only, when chroot is on /dev/root?
572         //
573         // For now, ignore any errors, but future versions should handle them.
574
575         //return -1;
576     }
577     ptr = strtok(0, ":");
578   }
579
580   return execvWorker(params->path, params->argv);
581 }
582
583 static int
584 removeNamespaceMounts(char const *path, char * const argv[])
585 {
586   if (mnts==0) return execvWorker(path, argv);
587
588   {
589     int                         status;
590     pid_t                       p, pid;
591     struct ExecvParams          params;
592
593     params.path = path;
594     params.argv = argv;
595     params.mnts = mnts;
596
597       // the rpmlib signal-handler is still active; use the default one to
598       // make wait4() working...
599     signal(SIGCHLD, SIG_DFL);
600
601 #ifdef NDEBUG
602     pid = sys_clone(CLONE_NEWNS|SIGCHLD|CLONE_VFORK, 0);
603 #else
604     pid = sys_clone(CLONE_NEWNS|SIGCHLD, 0);
605 #endif
606
607     switch (pid) {
608       case -1   :  return -1;
609       case 0    :  _exit(removeNamespaceMountsChild(&params));
610       default   :  break;
611     }
612         
613     while ((p=wait4(pid, &status, 0,0))==-1 &&
614            (errno==EINTR || errno==EAGAIN)) ;
615
616     if (p==-1)   return -1;
617
618     if (WIFEXITED(status))   _exit(WEXITSTATUS(status));
619     if (WIFSIGNALED(status)) kill(getpid(), WTERMSIG(status));
620
621     return -1;
622   }
623 }
624
625
626 int
627 execv(char const *path, char * const argv[]) __THROW
628 {
629   return removeNamespaceMounts(path, argv);
630 }