Clean also '/var/lib/captive/tmp/*' besides '/var/lib/captive/*'.
[captive.git] / src / client / sandbox-server / main.c
1 /* $Id$
2  * filesystem sandbox server stub for libcaptive
3  * Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
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; exactly version 2 of June 1991 is required
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
20 #include "config.h"
21
22 #include <glib/gmessages.h>
23 #include <stdlib.h>
24 #include <glib/giochannel.h>
25 #include <glib/gerror.h>
26 #include <popt.h>
27 #include <string.h>
28 #include <stdio.h>
29 #include <locale.h>
30 #include "captive/options.h"
31 #include <glib-object.h>
32 #include "captive/macros.h"
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <unistd.h>
36 #include <dirent.h>
37 #include <errno.h>
38 #include <grp.h>
39 #include <pwd.h>
40 #include <fcntl.h>
41 #include <sys/file.h>
42 #include <sys/resource.h>
43 #include <orbit/orb-core/corba-defs.h>
44
45 #ifdef HAVE_ORBIT_LINK
46 char *link_get_tmpdir(void);
47 void link_set_tmpdir(const char *dir);
48 #else
49 #include <linc/linc-protocol.h> /* for linc_set_tmpdir() */
50 #endif
51
52
53 /* Do not: #include "../../libcaptive/sandbox/split.h"  * for captive_sandbox_fd_closeup(); FIXME *
54  * as it has libcaptive-dependent includes conditioned by ORBIT2.
55  * FIXME: Unify this declaration:
56  */
57 void captive_sandbox_fd_closeup(int fd_first_to_delete);
58 void captive_corba_sandbox_child(const gchar *chrooted_orbit_dir);
59
60
61 /* CONFIG: */
62
63 /* FIXME: We hit linc-1.0.1/src/linc-protocols.c/linc_protocol_get_sockaddr_unix()
64  * limit of socket pathname 64 characters.
65  * With CHROOT_PATH_HASHKEY_LENGTH 12 "linc-%x-%x-%x%x" still does not fit completely.
66  */
67 #define CHROOT_PATH_HASHKEY_LENGTH (12)
68
69
70 GQuark sandbox_server_main_error_quark(void)
71 {
72 GQuark r=0;
73
74         if (!r)
75                 r=g_quark_from_static_string("sandbox-server");
76
77         return r;
78 }
79
80
81 static gchar *optarg_setuid=CAPTIVE_SANDBOX_SETUID;
82 static gchar *optarg_setgid=CAPTIVE_SANDBOX_SETGID;
83 static gchar *optarg_chroot=CAPTIVE_SANDBOX_CHROOT;
84 static gint   optarg_no_rlimit=0;
85
86 static const struct poptOption popt_table[]={
87 #define SANDBOX_SERVER_POPT(longname,argInfoP,argP,descripP,argDescripP) \
88                 { \
89                         longName: (longname), \
90                         shortName: 0, \
91                         argInfo: (argInfoP), \
92                         arg: (void *)argP, \
93                         val: 0, \
94                         descrip: (descripP), \
95                         argDescrip: (argDescripP), \
96                 }
97
98                 SANDBOX_SERVER_POPT("setuid"   ,POPT_ARG_STRING,&optarg_setuid,
99                                 N_("Username or UID to become; \"-\" for disable"),N_("UID")),
100                 SANDBOX_SERVER_POPT("setgid"   ,POPT_ARG_STRING,&optarg_setgid,
101                                 N_("Groupname or GID to become; \"-\" for disable"),N_("GID")),
102                 SANDBOX_SERVER_POPT("chroot"   ,POPT_ARG_STRING,&optarg_chroot,
103                                 N_("Pathname to directory for chroot(2); \"-\" for disable"),N_("directory")),
104                 SANDBOX_SERVER_POPT("no-rlimit",POPT_ARG_NONE  ,&optarg_no_rlimit,
105                                 N_("Disable setrlimit(2) restrictions"),NULL),
106
107 #undef SANDBOX_SERVER_POPT
108                 POPT_AUTOHELP
109                 POPT_TABLEEND
110                 };
111
112
113 static gchar *fatal_argv0;
114
115 static void fatal(const char *fmt,...)
116 {
117 va_list ap;
118
119         fprintf(stderr,"%s: ",fatal_argv0);
120         va_start(ap,fmt);
121         vfprintf(stderr,fmt,ap);
122         va_end(ap);
123         fprintf(stderr,"!\nAborting!\n");
124         exit(EXIT_FAILURE);
125         /* NOTREACHED */
126         for (;;);
127 }
128
129 static void check_dir_safety(const gchar *dir)
130 {
131 gchar *local_dir;
132 const gchar *cs;
133 static gint depth=0;
134
135         if (++depth>=1000)
136                 fatal("Loop count >=%d during check_dir_safety(\"%s\")",depth,dir);
137
138         if (*dir!='/')
139                 fatal("chroot path \"%s\" not absolute",dir);
140         dir=captive_printf_alloca("%s/",dir);
141         local_dir=(gchar *)captive_strdup_alloca(dir);
142         for (cs=dir;cs;cs=strchr(cs+1,'/')) {
143 struct stat statbuf;
144
145                 g_assert(*cs=='/');
146                 /* Include the trailing '/' to resolve the root directory as "/". */
147                 memcpy(local_dir,dir,cs+1-dir);
148                 local_dir[cs+1-dir]=0;
149                 if (lstat(local_dir,&statbuf))
150                         fatal("lstat(\"%s\") of chroot path component: %m",local_dir);
151                 if (S_ISLNK(statbuf.st_mode)) {
152 char linkbuf[PATH_MAX];
153 int linkbuflen;
154
155                         if (0>(linkbuflen=readlink(local_dir,linkbuf,sizeof(linkbuf)-1)))
156                                 fatal("readlink(\"%s\") of chroot path component: %m",local_dir);
157                         linkbuf[linkbuflen]=0;
158                         check_dir_safety(linkbuf);
159                         if (stat(local_dir,&statbuf))   /* NOT lstat(2) */
160                                 fatal("stat(\"%s\") of chroot path component: %m",local_dir);
161                         }
162                 if (!S_ISDIR(statbuf.st_mode))
163                         fatal("lstat/stat(\"%s\") of chroot path component is !S_ISDIR",local_dir);
164                 if (statbuf.st_uid!=0)
165                         fatal("lstat/stat(\"%s\") of chroot path component has UID %d !=0",local_dir,(int)statbuf.st_uid);
166                 if (statbuf.st_gid!=0)
167                         fatal("lstat/stat(\"%s\") of chroot path component has GID %d !=0",local_dir,(int)statbuf.st_gid);
168                 if ((statbuf.st_mode&(S_IFDIR|S_ISVTX|0111)) != (S_IFDIR|0111))
169                         fatal("lstat/stat(\"%s\") of chroot path component has mode 0%o !=04[01]111",local_dir,(int)statbuf.st_mode);
170                 }
171
172         depth--;
173 }
174
175 static void chrooted_unlink_recursive(const gchar *pathname)
176 {
177 DIR *dir;
178 struct dirent *dirent;
179 static gint depth=0;
180 struct stat statbuf;
181
182         if (++depth>=1000)
183                 fatal("Loop count >=%d during chrooted_unlink_recursive(\"%s\")",depth,pathname);
184
185         /* Security: Do not allow anyone to escape the sandbox directory by symlinks. */
186         if (lstat(pathname,&statbuf))
187                 fatal("Cannot lstat(\"%s\") to delete leftover sandbox files: %m",pathname);
188         if (!S_ISDIR(statbuf.st_mode)) {
189                 if (unlink(pathname))
190                         fatal("Cannot unlink(\"%s\") to delete leftover sandbox files: %m",pathname);
191                 goto done;
192                 }
193         if (!(dir=opendir(pathname)))
194                 fatal("Cannot opendir(\"%s\") to delete leftover sandbox files: %m",pathname);
195         while (errno=0,(dirent=readdir(dir))) {
196 gchar *dirent_path;
197
198                 if (!strcmp(dirent->d_name,".") || !strcmp(dirent->d_name,".."))
199                         continue;
200                 dirent_path=g_strdup_printf("%s/%s",pathname,dirent->d_name);
201                 chrooted_unlink_recursive(dirent_path);
202                 g_free(dirent_path);
203                 }
204         if (errno)
205                 fatal("Cannot readdir(\"%s\") during delete of leftover sandbox files: %m",pathname);
206         if (closedir(dir))
207                 fatal("Cannot closedir(\"%s\") during delete of leftover sandbox files: %m",pathname);
208         if (rmdir(pathname))
209                 fatal("Cannot rmdir(\"%s\") during delete of leftover sandbox files: %m",pathname);
210
211 done:
212         depth--;
213 }
214
215 static void chrooted_cleanuplockeddirs(const gchar *pathname,const gchar *prefix)
216 {
217 DIR *dir;
218 struct dirent *dirent;
219
220         if (!(dir=opendir(pathname))) {
221                 if (errno!=ENOTDIR)
222                         fatal("Cannot opendir(\"%s\") to delete leftover sandbox files: %m",pathname);
223                 /* errno==ENOTDIR, a regular file */
224                 if (unlink(pathname))
225                         fatal("Cannot unlink(\"%s\") to delete leftover sandbox files: %m",pathname);
226                 return;
227                 }
228         while (errno=0,(dirent=readdir(dir))) {
229 gchar *dirent_path;
230 int direntfd;
231
232                 if (!strcmp(dirent->d_name,".") || !strcmp(dirent->d_name,".."))
233                         continue;
234                 if (strncmp(dirent->d_name,prefix,strlen(prefix)))
235                         continue;
236                 dirent_path=g_strdup_printf("%s/%s",pathname,dirent->d_name);
237                 if (-1==(direntfd=open(dirent_path,O_RDONLY))) {
238                         if (errno==ENOENT)      /* It could disappear in the meantime. */
239                                 goto next_dirent_free_dirent_path;
240                         fatal("Cannot open(\"%s\") as the child directory during delete of leftover sandbox files: %m",dirent_path);
241                         }
242                 if (flock(direntfd,LOCK_EX|LOCK_NB)) {
243                         if (errno==EWOULDBLOCK) /* Valid directory in use. */
244                                 goto next_dirent_close_direntfd;
245                         fatal("Cannot flock(\"%s\",LOCK_EX|LOCK_NB) child directory during delete of leftover sandbox files: %m",dirent_path);
246                         }
247                 chrooted_unlink_recursive(dirent_path);
248 next_dirent_close_direntfd:
249                 if (close(direntfd))
250                         fatal("Cannot close(\"%s\") child directory during delete of leftover sandbox files: %m",dirent_path);
251 next_dirent_free_dirent_path:
252                 g_free(dirent_path);
253                 }
254         if (errno)
255                 fatal("Cannot readdir(\"%s\") during delete of leftover sandbox files: %m",pathname);
256         if (closedir(dir))
257                 fatal("Cannot closedir(\"%s\") during delete of leftover sandbox files: %m",pathname);
258 }
259
260 static void chrooted_createdir(const gchar *dir,uid_t uid,gid_t gid,gboolean lock)
261 {
262 gint retries;
263
264         for (retries=0;retries<10;retries++) {
265 struct stat statbuf;
266 int dirfd;
267
268                 if (mkdir(dir,0711)) {
269                         if (errno!=EEXIST)
270                                 fatal("Failed to create chroot directory \"%s\": %m",dir);
271                         chrooted_unlink_recursive(dir);
272                         if (mkdir(dir,0711))
273                                 fatal("Failed to create chroot directory \"%s\" after attempted unlink: %m",dir);
274                         }
275                 if (!lock)
276                         break;
277                 dirfd=open(dir,O_RDONLY);
278                 if (dirfd==-1) {
279                         if (errno!=ENOENT)
280                                 fatal("Failed to open created chroot directory \"%s\" to lock it: %m",dir);
281                         continue;
282                         }
283                 /* Do not use 'LOCK_NB' here as the garbage collector should release it soon. */
284                 if (flock(dirfd,LOCK_EX))
285                         fatal("Failed to lock created chroot directory \"%s\": %m",dir);
286                 if (lstat(dir,&statbuf)) {
287                         if (errno!=ENOENT)
288                                 fatal("Failed to lstat(2) created chroot directory \"%s\": %m",dir);
289                         if (close(dirfd))
290                                 fatal("Failed to close created and locked chroot directory \"%s\": %m",dir);
291                         continue;
292                         }
293                 /* Leave 'dirfd' open to leave it LOCK_EX-ed. */
294                 break;
295                 }
296         if (chown(dir,uid,gid))
297                 fatal("Failed to chown(\"%s\",%d,%d): %m",dir,uid,gid);
298         if (chmod(dir,0711))    /* Just to be safe after chown(2); should be already done by mkdir(2). */
299                 fatal("Failed to chmod(\"%s\",0%o): %m",dir,0711);
300 }
301
302 static void sandbox_server_rlimit(int resource,const gchar *resource_string,rlim_t rlim_max)
303 {
304 struct rlimit rlim;
305
306         rlim.rlim_cur=rlim.rlim_max=rlim_max;
307         if (setrlimit(resource,&rlim))
308                 fatal("setrlimit(%s,%d): %m",resource_string,(int)rlim_max);
309         if (getrlimit(resource,&rlim))
310                 fatal("getrlimit(%s,%d): %m",resource_string,(int)rlim_max);
311         if (rlim.rlim_cur!=rlim_max || rlim.rlim_max!=rlim_max)
312                 fatal("Unsuccessful setrlimit(%s)",resource_string);
313 }
314
315 static void sandbox_server_mkdir_p(const gchar *dirpathname)
316 {
317 gchar *pathname=(/* de-const */ gchar *)captive_strdup_alloca(dirpathname);
318 gchar *gs,*gs2;
319
320         /* Missing mkdir(2) of the last component path is intentional: */
321         for (gs=pathname;(gs2=strchr(gs,'/'));gs=gs2) {
322                 *gs2='\0';
323                 if (*pathname && mkdir(pathname,S_ISVTX|0777)) {
324                         if (errno!=EEXIST)
325                                 fatal("Failed to mkdir(\"%s\"): %m",pathname);
326                         }
327                 *gs2++='/';
328                 }
329 }
330
331 static const gchar *chrooted_orbit_dir;
332
333 static void chroot_setup(gboolean fragile)
334 {
335 uid_t want_uid=0;
336 const gchar *want_uid_name=NULL;
337 gid_t want_gid=0;
338 char *endptr;
339 const gchar *chroot_pid_hashkey_dir=NULL;
340
341         if (fragile) {
342                 captive_sandbox_fd_closeup(2 /* STDERR */ +1);
343                 clearenv();
344                 }
345
346 #define CLEANEMPTY(var) G_STMT_START { \
347                 if ((var) && (!*(var) || *(var)=='-')) \
348                         (var)=NULL; \
349                 } G_STMT_END
350         CLEANEMPTY(optarg_setgid);
351         CLEANEMPTY(optarg_setuid);
352         CLEANEMPTY(optarg_chroot);
353 #undef CLEANEMPTY
354
355         if (optarg_setgid) {
356 long want_gidl;
357
358                 want_gidl=strtol(optarg_setgid,&endptr,10);
359                 if (!endptr || !*endptr) {
360                         want_gid=want_gidl;
361                         if (want_gidl<=0 || want_gid!=(gid_t)want_gidl)
362                                 fatal("Numeric setgid not parsable: %s",optarg_setgid);
363                         }
364                 else {
365 struct group *want_gid_group=NULL;
366                         if (!(want_gid_group=getgrnam(optarg_setgid)))
367                                 fatal("Unable to query setgid group name \"%s\"",optarg_setgid);
368                         want_gid=want_gid_group->gr_gid;
369                         }
370                 }
371
372         if (optarg_setuid) {
373 long want_uidl;
374 struct passwd *want_uid_passwd;
375                 want_uidl=strtol(optarg_setuid,&endptr,10);
376                 if (!endptr || !*endptr) {
377                         want_uid=want_uidl;
378                         if (want_uidl<=0 || want_uid!=(gid_t)want_uidl)
379                                 fatal("Numeric setuid not parsable: %s",optarg_setuid);
380                         }
381                 else {
382                         if (!(want_uid_passwd=getpwnam(optarg_setuid)))
383                                 fatal("Unable to query setuid user name \"%s\"",optarg_setuid);
384                         want_uid=want_uid_passwd->pw_uid;
385                         }
386                 if (!want_uid)
387                         fatal("Unable to detect setuid UID");
388                 if (!(want_uid_passwd=getpwuid(want_uid)))
389                         fatal("Unable to query name of UID %d",(int)want_uid);
390                 want_uid_name=captive_strdup_alloca(want_uid_passwd->pw_name);
391                 }
392
393         /* Prevent: GLib-WARNING **: getpwuid_r(): failed due to unknown user id (42)
394          * Try to invoke GLib g_get_any_init() before possible chroot(2) below.
395          */
396         g_get_user_name();
397         g_get_real_name();
398         g_get_home_dir();
399         g_get_tmp_dir();
400
401         /* Pre-resolve "link_get_tmpdir" symbol to prevent its later failed
402          * resolving in chroot(2) mode in Debian dynamic build.
403          */
404 #ifdef HAVE_ORBIT_LINK
405         g_free(link_get_tmpdir());      /* returns g_strdup()ed string */
406 #else
407         g_free(linc_get_tmpdir());      /* returns g_strdup()ed string */
408 #endif
409
410         if (fragile && !optarg_chroot)
411                 fatal("Fragile setuid/root environment but no --chroot set");
412         if (optarg_chroot) {
413 const gchar *chroot_pid_dir;
414 GRand *grand;
415 gchar chroot_hashkey[CHROOT_PATH_HASHKEY_LENGTH+1],*s;
416 gint gi;
417
418                 check_dir_safety(optarg_chroot);
419                 if (!(grand=g_rand_new()))      /* I hope g_rand_new() is security-safe. It looks so. */
420                         fatal("Cannot initialize random number generator g_rand_new()");
421                 for (s=chroot_hashkey;s<chroot_hashkey+CHROOT_PATH_HASHKEY_LENGTH;s++) {
422                         gi=g_rand_int_range(grand,0,10+26+26);
423                         /**/ if (gi>=0 && gi<10)
424                                 *s='0'+gi-(0);
425                         else if (gi>=10+0 && gi<10+26)
426                                 *s='a'+gi-(10);
427                         else if (gi>=10+26+0 && gi<10+26+26)
428                                 *s='A'+gi-(10+26);
429                         else g_assert_not_reached();
430                         }
431                 g_rand_free(grand);
432                 *s=0;
433                 if (geteuid()==0) {     /* Not 'fragile' as we can be native 'root'. */
434                         chrooted_cleanuplockeddirs(optarg_chroot,"s-");
435                         chrooted_cleanuplockeddirs(captive_printf_alloca("%s/tmp",optarg_chroot),"captive-orbit-");
436                         }
437                 chroot_pid_dir=captive_printf_alloca("%s/s-%d",optarg_chroot,(int)getpid());
438                 chrooted_createdir(chroot_pid_dir,(!optarg_setuid ? (uid_t)-1 : want_uid),(!optarg_setgid ? (gid_t)-1 : want_gid),
439                                 TRUE);  /* lock */
440                 chroot_pid_hashkey_dir=captive_printf_alloca("%s/%s",chroot_pid_dir,chroot_hashkey);
441                 chrooted_createdir(chroot_pid_hashkey_dir,(!optarg_setuid ? (uid_t)-1 : want_uid),(!optarg_setgid ? (gid_t)-1 : want_gid),
442                                 FALSE); /* lock */
443                 if (chroot(chroot_pid_hashkey_dir))
444                         fatal("Failed to chroot(\"%s\"): %m",chroot_pid_hashkey_dir);
445                 if (chdir("/"))
446                         fatal("Failed to chdir(\"%s\"): %m","/");
447                 /* Now it is safe to set umask(0000) as we are protected by 'chroot_hashkey'.
448                  * We need it to permit our spawning parent to hardlink its sockets to us.
449                  */
450                 umask(0000);
451                 if (umask(0000)!=0000)
452                         fatal("Failed to set umask(0%o): %m",0000);
453                 printf("chroot_pid_hashkey_dir=%s\n",chroot_pid_hashkey_dir);
454                 }
455
456         if (fragile && !optarg_setgid)
457                 fatal("Fragile setuid/root environment but no --setgid set");
458         if (optarg_setgid) {
459                 if (!want_gid || setgid(want_gid))
460                         fatal("Failed to setgid(%d)",(!want_gid ? -1 : (int)want_gid));
461                 if (setgroups(1 /* size */,&want_gid))
462                         fatal("Failed to setgroups(1,[%d])",(!want_gid ? -1 : (int)want_gid));
463                 }
464         if (fragile && !optarg_setuid)
465                 fatal("Fragile setuid/root environment but no --setuid set");
466         if (optarg_setuid) {
467                 if (!want_uid || setuid(want_uid))
468                         fatal("Failed to setuid(%d)",(!want_uid ? -1 : (int)want_uid));
469                 }
470
471         /* Prepare /t for /t/o-$PID directories for ORBit2
472          * and also for parent's hardlink to its /t/o-$pid directory. */
473         if (optarg_chroot) {
474 gchar *chrooted_orbit_dir_old;
475
476                 if (mkdir("/t",S_ISVTX|0777)) {
477                         if (errno!=EEXIST)
478                                 fatal("Failed to mkdir(\"%s\"): %m","/t");
479                         }
480                 if (mkdir("/etc",0700))
481                         fatal("Failed to mkdir(\"%s\"): %m","/etc");
482                 if (want_uid_name && want_uid && want_gid) {
483 FILE *f;
484                         if (!(f=fopen("/etc/passwd","w")))
485                                 fatal("Failed to fopen(\"%s\",\"w\"): %m","/etc/passwd");
486                         if (0>fprintf(f,"%s:*:%d:%d:%s:%s:/bin/false\n",want_uid_name,(int)want_uid,(int)want_gid,want_uid_name,optarg_chroot))
487                                 fatal("Failed to fprintf(\"%s\"): %m","/etc/passwd");
488                         if (fclose(f))
489                                 fatal("Failed to fclose(\"%s\"): %m","/etc/passwd");
490                         }
491                 g_assert(chroot_pid_hashkey_dir!=NULL);
492                 chrooted_orbit_dir=g_strdup_printf("%s/t/o-%d",chroot_pid_hashkey_dir,getpid());
493                 /* Last pathname component is not created: */
494                 sandbox_server_mkdir_p(chrooted_orbit_dir);
495                 /* Prepare '/tmp' for the initial CORBA_ORB_init() default path.
496                  * Workaround sandbox_server_mkdir_p() does not create last component.
497                  * Do not use '/tmp' directly as some distributions may set custom
498                  * tmpdir pathname by $ENV{"TMPDIR"} etc.
499                  */
500                 sandbox_server_mkdir_p(captive_printf_alloca("%s/",g_get_tmp_dir()));
501                 /* Set '0700' to prevent: Wrong permissions for ...
502                  * by linc-1.0.1-1/src/linc-protocols.c/make_local_tmpdir()
503                  */
504                 if (mkdir(chrooted_orbit_dir,0700)) {
505                         /* Do not: g_assert(errno==EEXIST);
506                          * as if 'optarg_chroot' the whole chroot(2)ed directory should be ours.
507                          */
508                         fatal("Cannot created chrooted_orbit_dir \"%s\": %m",chrooted_orbit_dir);
509                         }
510                 /* Init 'orb' to pass through its linc_set_tmpdir() to not to be overriden below. */
511                 {
512 CORBA_ORB orb;
513 CORBA_Environment ev;
514 int orb_argc=1;
515 gchar *orb_argv[]={
516                 (gchar *)captive_strdup_alloca("captive-sandbox-server"),
517                 NULL};
518
519                         CORBA_exception_init(&ev);
520                         /* libcaptive is single-threaded only, caller must lock it.
521                          * If thread A spawned the sandbox while currently doing its own work
522                          * and thread B calls the sandbox thread B waits on ORB_run()
523                          * while the sandbox waits for the response of thread A ORB. Deadlock.
524                          * "orbit-local-non-threaded-orb" requests thread unaware ORB.
525                          */
526                         orb=CORBA_ORB_init(&orb_argc,orb_argv,"orbit-local-non-threaded-orb",&ev);
527                         if (orb==CORBA_OBJECT_NIL)
528                                 fatal("Cannot initialize CORBA ORB (CORBA_OBJECT_NIL): %m");
529                         if (ev._major!=CORBA_NO_EXCEPTION)
530                                 fatal("Cannot initialize CORBA ORB (exception): %m");
531                         }
532 #ifdef HAVE_ORBIT_LINK
533                 chrooted_orbit_dir_old=link_get_tmpdir();       /* returns g_strdup()ed string */
534 #else
535                 chrooted_orbit_dir_old=linc_get_tmpdir();       /* returns g_strdup()ed string */
536 #endif
537                 g_assert(chrooted_orbit_dir_old!=NULL);
538 #ifdef HAVE_ORBIT_LINK
539                 link_set_tmpdir(chrooted_orbit_dir);
540 #else
541                 linc_set_tmpdir(chrooted_orbit_dir);
542 #endif
543                 if (!*chrooted_orbit_dir_old)
544                         fatal("Cannot detect chrooted_orbit_dir: --with-orbit-line incompatible with ORBit2 version");
545                 if (rmdir(chrooted_orbit_dir_old))
546                         fatal("Cannot remove old chrooted_orbit_dir \"%s\": %m",chrooted_orbit_dir_old);
547                 g_free(chrooted_orbit_dir_old);
548                 /* chmod(2) it to prevent mode limitation by
549                  * active ulimit(2) of being executed by mount(8).
550                  */
551                 /* Set '0777' as our parent does not have 'captive' user permissions. */
552                 if (chmod(chrooted_orbit_dir,S_ISVTX|0777))
553                         fatal("Cannot chmod 0%o chrooted_orbit_dir \"%s\": %m",S_ISVTX|0777,chrooted_orbit_dir);
554                 printf("chrooted_orbit_dir=%s\n",chrooted_orbit_dir);
555                 }
556
557         if (fragile || !optarg_no_rlimit) {
558 #define SANDBOX_SERVER_RLIMIT(what,how) sandbox_server_rlimit((what),G_STRINGIFY(what),(how))
559                 SANDBOX_SERVER_RLIMIT(RLIMIT_NPROC,0);
560                 SANDBOX_SERVER_RLIMIT(RLIMIT_MEMLOCK,0);
561                 SANDBOX_SERVER_RLIMIT(RLIMIT_CORE,0);
562                 SANDBOX_SERVER_RLIMIT(RLIMIT_FSIZE,0);
563                 SANDBOX_SERVER_RLIMIT(RLIMIT_NOFILE,16);        /* >=6; newer ORBit2/link require >6 */
564                 /* FIXME: Why flock(dirfd,...) in chrooted_createdir() succeeds?: */
565                 SANDBOX_SERVER_RLIMIT(RLIMIT_LOCKS,0);
566 #undef SANDBOX_SERVER_RLIMIT
567                 }
568
569         if (fragile) {
570 gid_t gid_list[2];
571 int gid_list_size,i;
572
573                 if (getuid()!=want_uid)
574                         fatal("getuid()=%d != want_uid=%d",(int)getuid(),(int)want_uid);
575                 if (geteuid()!=want_uid)
576                         fatal("geteuid()=%d != want_uid=%d",(int)geteuid(),(int)want_uid);
577                 if (getgid()!=want_gid)
578                         fatal("getgid()=%d != want_gid=%d",(int)getgid(),(int)want_gid);
579                 if (getegid()!=want_gid)
580                         fatal("getegid()=%d != want_gid=%d",(int)getegid(),(int)want_gid);
581                 gid_list_size=getgroups(G_N_ELEMENTS(gid_list),gid_list);
582                 for (i=0;i<gid_list_size;i++) {
583                         if (gid_list[i]!=want_gid)
584                                 fatal("getgroups() list member @%d %d != want_gid=%d",i,(int)gid_list[i],(int)want_gid);
585                         }
586                 }
587 }
588
589
590 int main(int argc,char **argv)
591 {
592 poptContext context;
593 int errint;
594 const char *cmd_arg;
595 struct captive_options options;
596 gboolean fragile;
597
598         g_log_set_always_fatal(~(0
599                         |G_LOG_LEVEL_MESSAGE
600                         |G_LOG_LEVEL_INFO
601                         |G_LOG_LEVEL_DEBUG
602                         ));
603
604         fatal_argv0=argv[0];
605         fragile=(getuid()!=geteuid() || getuid()==0 || geteuid()==0);
606
607 #ifndef MAINTAINER_MODE
608         if (fragile && (argc!=1 || argv[1]))
609                 fatal("Arguments invalid as running in fragile setuid/root environment");
610
611         if (fragile)
612                 chroot_setup(TRUE);
613 #endif /* MAINTAINER_MODE */
614
615         /* Initialize the i18n stuff */
616         setlocale(LC_ALL,"");
617         bindtextdomain(PACKAGE,LOCALEDIR);
618         textdomain(PACKAGE);
619
620         /* Initialize GObject subsystem of GLib. */
621         g_type_init();
622
623         captive_options_init(&options);
624         captive_options=&options;       /* for parsing by 'CAPTIVE_POPT_INCLUDE' */
625
626         context=poptGetContext(
627                         PACKAGE,        /* name */
628                         argc,(/*en-const*/const char **)argv,   /* argc,argv */
629                         popt_table,     /* options */
630                         POPT_CONTEXT_POSIXMEHARDER);    /* flags; && !POPT_CONTEXT_KEEP_FIRST */
631         if (context==NULL) {
632                 g_assert_not_reached(); /* argument recognization args_error */
633                 return EXIT_FAILURE;
634                 }
635         errint=poptReadDefaultConfig(context,
636                         TRUE);  /* useEnv */
637         if (errint!=0) {
638                 g_assert_not_reached(); /* argument recognization args_error */
639                 return EXIT_FAILURE;
640                 }
641         errint=poptGetNextOpt(context);
642         if (errint!=-1) {
643                 g_assert_not_reached(); /* some non-callbacked argument reached */
644                 return EXIT_FAILURE;
645                 }
646         cmd_arg=poptPeekArg(context);
647         if (cmd_arg) {
648                 g_assert_not_reached(); /* some non-option argument reached */
649                 return EXIT_FAILURE;
650                 }
651         /* 'cmd_arg'-style args gets cleared by 'poptFreeContext(context);' below */
652         poptFreeContext(context);
653
654 #ifdef MAINTAINER_MODE
655         chroot_setup(FALSE);
656 #endif /* MAINTAINER_MODE */
657
658         captive_options=NULL;   /* already parsed by 'CAPTIVE_POPT_INCLUDE' */
659
660         captive_corba_sandbox_child(chrooted_orbit_dir);
661
662         g_assert_not_reached();
663         return EXIT_SUCCESS;
664 }