/[path-sandbox]/trunk/src/sandbox.c
Gentoo

Contents of /trunk/src/sandbox.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 268 - (show annotations) (download) (as text)
Wed Jul 5 13:50:48 2006 UTC (7 years, 9 months ago) by azarah
File MIME type: text/x-csrc
File size: 17018 byte(s)
Just killing the child with SANDBOX_ABORT do not actually abort the make process
in many cases.  So also kill the offending child, and pray that make will also
abort.  This is really hackish, and we should rather kill the whole process tree,
but currently its too much work (considering that we are in signal context which
probably will make things difficult - not even talking about the bsd's ...), so it
will have to do.

1 /*
2 ** Path sandbox for the gentoo linux portage package system, initially
3 ** based on the ROCK Linux Wrapper for getting a list of created files
4 **
5 ** to integrate with bash, bash should have been built like this
6 **
7 ** ./configure --prefix=<prefix> --host=<host> --without-gnu-malloc
8 **
9 ** it's very important that the --enable-static-link option is NOT specified
10 **
11 ** Copyright (C) 2001 Geert Bevin, Uwyn, http://www.uwyn.com
12 ** Distributed under the terms of the GNU General Public License, v2 or later
13 ** Author : Geert Bevin <gbevin@uwyn.com>
14 ** $Header$
15 */
16
17 /* #define _GNU_SOURCE */
18
19 #include <errno.h>
20 #include <signal.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <limits.h>
24 #include <string.h>
25 #include <sys/wait.h>
26 #include <signal.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29
30 #include "sandbox.h"
31
32 struct sandbox_info_t {
33 char sandbox_log[SB_PATH_MAX];
34 char sandbox_debug_log[SB_PATH_MAX];
35 char sandbox_lib[SB_PATH_MAX];
36 char sandbox_rc[SB_PATH_MAX];
37 char work_dir[SB_PATH_MAX];
38 char var_tmp_dir[SB_PATH_MAX];
39 char tmp_dir[SB_PATH_MAX];
40 char *home_dir;
41 } sandbox_info_t;
42
43 static int print_debug = 0;
44
45 volatile static int stop_called = 0;
46 volatile static pid_t child_pid = 0;
47
48 extern char **environ;
49
50 int sandbox_setup(struct sandbox_info_t *sandbox_info)
51 {
52 if (NULL != getenv(ENV_PORTAGE_TMPDIR)) {
53 /* Portage handle setting SANDBOX_WRITE itself. */
54 sandbox_info->work_dir[0] = '\0';
55 } else {
56 if (NULL == getcwd(sandbox_info->work_dir, SB_PATH_MAX)) {
57 perror("sandbox: Failed to get current directory");
58 return -1;
59 }
60 }
61
62 /* Do not resolve symlinks, etc .. libsandbox will handle that. */
63 if (1 != is_dir(VAR_TMPDIR, 1)) {
64 perror("sandbox: Failed to get var_tmp_dir");
65 return -1;
66 }
67 snprintf(sandbox_info->var_tmp_dir, SB_PATH_MAX, "%s", VAR_TMPDIR);
68
69 if (-1 == get_tmp_dir(sandbox_info->tmp_dir)) {
70 perror("sandbox: Failed to get tmp_dir");
71 return -1;
72 }
73 setenv(ENV_TMPDIR, sandbox_info->tmp_dir, 1);
74
75 sandbox_info->home_dir = getenv("HOME");
76 if (!sandbox_info->home_dir) {
77 sandbox_info->home_dir = sandbox_info->tmp_dir;
78 setenv("HOME", sandbox_info->home_dir, 1);
79 }
80
81 /* Generate sandbox lib path */
82 get_sandbox_lib(sandbox_info->sandbox_lib);
83
84 /* Generate sandbox bashrc path */
85 get_sandbox_rc(sandbox_info->sandbox_rc);
86
87 /* Generate sandbox log full path */
88 get_sandbox_log(sandbox_info->sandbox_log);
89 if (1 == exists(sandbox_info->sandbox_log)) {
90 if (-1 == unlink(sandbox_info->sandbox_log)) {
91 perror("sandbox: Could not unlink old log file");
92 return -1;
93 }
94 }
95
96 /* Generate sandbox debug log full path */
97 get_sandbox_debug_log(sandbox_info->sandbox_debug_log);
98 if (1 == exists(sandbox_info->sandbox_debug_log)) {
99 if (-1 == unlink(sandbox_info->sandbox_debug_log)) {
100 perror("sandbox: Could not unlink old debug log file");
101 return -1;
102 }
103 }
104
105 return 0;
106 }
107
108 int print_sandbox_log(char *sandbox_log)
109 {
110 int sandbox_log_file = -1;
111 char *beep_count_env = NULL;
112 int i, color, beep_count = 0;
113 long len = 0;
114 char *buffer = NULL;
115
116 if (1 != is_file(sandbox_log)) {
117 perror("sandbox: Log file is not a regular file");
118 return 0;
119 }
120
121 sandbox_log_file = open(sandbox_log, O_RDONLY);
122 if (-1 == sandbox_log_file) {
123 perror("sandbox: Could not open Log file");
124 return 0;
125 }
126
127 len = file_length(sandbox_log_file);
128 buffer = (char *)malloc((len + 1) * sizeof(char));
129 memset(buffer, 0, len + 1);
130 read(sandbox_log_file, buffer, len);
131 close(sandbox_log_file);
132
133 color = ((is_env_on(ENV_NOCOLOR)) ? 0 : 1);
134
135 EERROR(color,
136 "--------------------------- ACCESS VIOLATION SUMMARY ---------------------------",
137 "\n");
138 EERROR(color, "LOG FILE = \"%s\"", "\n\n", sandbox_log);
139 fprintf(stderr, "%s", buffer);
140 if (NULL != buffer)
141 free(buffer);
142 EERROR(color,
143 "--------------------------------------------------------------------------------",
144 "\n");
145
146 beep_count_env = getenv(ENV_SANDBOX_BEEP);
147 if (beep_count_env)
148 beep_count = atoi(beep_count_env);
149 else
150 beep_count = DEFAULT_BEEP_COUNT;
151
152 for (i = 0; i < beep_count; i++) {
153 fputc('\a', stderr);
154 if (i < beep_count - 1)
155 sleep(1);
156 }
157
158 return 1;
159 }
160
161 void stop(int signum)
162 {
163 if (0 == stop_called) {
164 stop_called = 1;
165 printf("sandbox: Caught signal %d in pid %d\n",
166 signum, getpid());
167 } else {
168 fprintf(stderr,
169 "sandbox: Signal already caught and busy still cleaning up!\n");
170 }
171 }
172
173 void usr1_handler(int signum, siginfo_t *siginfo, void *ucontext)
174 {
175 if (0 == stop_called) {
176 stop_called = 1;
177 printf("sandbox: Caught signal %d in pid %d\n",
178 signum, getpid());
179
180 /* FIXME: This is really bad form, as we should kill the whole process
181 * tree, but currently that is too much work and not worth the
182 * effort. Thus we only kill the calling process and our child
183 * for now.
184 */
185 if (siginfo->si_pid > 0)
186 kill(siginfo->si_pid, SIGKILL);
187 kill(child_pid, SIGKILL);
188 } else {
189 fprintf(stderr,
190 "sandbox: Signal already caught and busy still cleaning up!\n");
191 }
192 }
193
194 int get_sandbox_write_envvar(char *buf, struct sandbox_info_t *sandbox_info)
195 {
196 int retval = 0;
197
198 /* bzero out entire buffer then append trailing 0 */
199 memset(buf, 0, SB_BUF_LEN);
200
201 /* these could go into make.globals later on */
202 retval = snprintf(buf, SB_BUF_LEN,
203 "%s:%s/.gconfd/lock:%s/.bash_history:%s:%s:%s:%s",
204 "/dev/zero:/dev/null:/dev/full:/dev/fd:/proc/self/fd:/dev/pts/:"
205 "/dev/vc/:/dev/pty:/dev/tty:/dev/tts:/dev/console:"
206 "/dev/shm:/dev/shm/ngpt:/var/log/scrollkeeper.log:"
207 "/usr/tmp/conftest:/usr/lib/conftest:"
208 "/usr/lib32/conftest:/usr/lib64/conftest:"
209 "/usr/tmp/cf:/usr/lib/cf:/usr/lib32/cf:/usr/lib64/cf",
210 sandbox_info->home_dir, sandbox_info->home_dir,
211 sandbox_info->work_dir[0] != '\0' ? sandbox_info->work_dir
212 : "",
213 sandbox_info->tmp_dir, sandbox_info->var_tmp_dir,
214 "/tmp/:/var/tmp/");
215 if (SB_BUF_LEN <= retval) {
216 errno = EMSGSIZE;
217 perror("sandbox: Failed to generate SANDBOX_WRITE");
218 return -1;
219 }
220
221 return 0;
222 }
223
224 int get_sandbox_predict_envvar(char *buf, struct sandbox_info_t *sandbox_info)
225 {
226 int retval = 0;
227 /* bzero out entire buffer then append trailing 0 */
228 memset(buf, 0, SB_BUF_LEN);
229
230 /* these should go into make.globals later on */
231 retval = snprintf(buf, SB_BUF_LEN, "%s/.:"
232 "/usr/lib/python2.0/:"
233 "/usr/lib/python2.1/:"
234 "/usr/lib/python2.2/:"
235 "/usr/lib/python2.3/:"
236 "/usr/lib/python2.4/:"
237 "/usr/lib/python2.5/:"
238 "/usr/lib/python3.0/:"
239 "/var/db/aliases.db:"
240 "/var/db/netgroup.db:"
241 "/var/db/netmasks.db:"
242 "/var/db/ethers.db:"
243 "/var/db/rpc.db:"
244 "/var/db/protocols.db:"
245 "/var/db/services.db:"
246 "/var/db/networks.db:"
247 "/var/db/hosts.db:"
248 "/var/db/group.db:"
249 "/var/db/passwd.db",
250 sandbox_info->home_dir);
251 if (SB_BUF_LEN <= retval) {
252 errno = EMSGSIZE;
253 perror("sandbox: Failed to generate SANDBOX_PREDICT");
254 return -1;
255 }
256
257 return 0;
258 }
259
260 int sandbox_setenv(char **env, const char *name, const char *val) {
261 char **tmp_env = env;
262 char *tmp_string = NULL;
263 int retval = 0;
264
265 /* XXX: We add the new variable to the end (no replacing). If this
266 * is changed, we need to fix sandbox_setup_environ() below */
267 while (NULL != *tmp_env)
268 tmp_env++;
269
270 /* strlen(name) + strlen(val) + '=' + '\0' */
271 /* FIXME: Should probably free this at some stage - more neatness than
272 * a real leak that will cause issues. */
273 tmp_string = calloc(strlen(name) + strlen(val) + 2, sizeof(char *));
274 if (NULL == tmp_string) {
275 perror("sandbox: Out of memory (sandbox_setenv)");
276 exit(EXIT_FAILURE);
277 }
278
279 retval = snprintf(tmp_string, strlen(name) + strlen(val) + 2, "%s=%s",
280 name, val);
281 *tmp_env = tmp_string;
282
283 return 0;
284 }
285
286 /* We setup the environment child side only to prevent issues with
287 * setting LD_PRELOAD parent side */
288 char **sandbox_setup_environ(struct sandbox_info_t *sandbox_info, bool interactive)
289 {
290 int env_size = 0;
291 int have_ld_preload = 0;
292
293 char **new_environ;
294 char **env_ptr = environ;
295 char sandbox_write_envvar[SB_BUF_LEN];
296 char sandbox_predict_envvar[SB_BUF_LEN];
297 char *ld_preload_envvar = NULL;
298 char *orig_ld_preload_envvar = NULL;
299 char sb_pid[64];
300
301 /* Unset these, as its easier than replacing when setting up our
302 * new environment below */
303 unsetenv(ENV_SANDBOX_ON);
304 unsetenv(ENV_SANDBOX_PID);
305 unsetenv(ENV_SANDBOX_LIB);
306 unsetenv(ENV_SANDBOX_BASHRC);
307 unsetenv(ENV_SANDBOX_LOG);
308 unsetenv(ENV_SANDBOX_DEBUG_LOG);
309 unsetenv(ENV_SANDBOX_ACTIVE);
310
311 if (NULL != getenv(ENV_LD_PRELOAD)) {
312 have_ld_preload = 1;
313 orig_ld_preload_envvar = getenv(ENV_LD_PRELOAD);
314
315 /* FIXME: Should probably free this at some stage - more neatness
316 * than a real leak that will cause issues. */
317 ld_preload_envvar = calloc(strlen(orig_ld_preload_envvar) +
318 strlen(sandbox_info->sandbox_lib) + 2,
319 sizeof(char *));
320 if (NULL == ld_preload_envvar)
321 return NULL;
322 snprintf(ld_preload_envvar, strlen(orig_ld_preload_envvar) +
323 strlen(sandbox_info->sandbox_lib) + 2, "%s %s",
324 sandbox_info->sandbox_lib, orig_ld_preload_envvar);
325 } else {
326 /* FIXME: Should probably free this at some stage - more neatness
327 * than a real leak that will cause issues. */
328 ld_preload_envvar = gstrndup(sandbox_info->sandbox_lib,
329 strlen(sandbox_info->sandbox_lib));
330 if (NULL == ld_preload_envvar)
331 return NULL;
332 }
333 /* Do not unset this, as strange things might happen */
334 /* unsetenv(ENV_LD_PRELOAD); */
335
336 while (NULL != *env_ptr) {
337 env_size++;
338 env_ptr++;
339 }
340
341 /* FIXME: Should probably free this at some stage - more neatness than
342 * a real leak that will cause issues. */
343 new_environ = calloc((env_size + 15 + 1) * sizeof(char *), sizeof(char *));
344 if (NULL == new_environ)
345 return NULL;
346
347 snprintf(sb_pid, sizeof(sb_pid), "%i", getpid());
348
349 /* First add our new variables to the beginning - this is due to some
350 * weirdness that I cannot remember */
351 sandbox_setenv(new_environ, ENV_SANDBOX_ON, "1");
352 sandbox_setenv(new_environ, ENV_SANDBOX_PID, sb_pid);
353 sandbox_setenv(new_environ, ENV_SANDBOX_LIB, sandbox_info->sandbox_lib);
354 sandbox_setenv(new_environ, ENV_SANDBOX_BASHRC, sandbox_info->sandbox_rc);
355 sandbox_setenv(new_environ, ENV_SANDBOX_LOG, sandbox_info->sandbox_log);
356 sandbox_setenv(new_environ, ENV_SANDBOX_DEBUG_LOG,
357 sandbox_info->sandbox_debug_log);
358 /* Is this an interactive session? */
359 if (interactive)
360 sandbox_setenv(new_environ, ENV_SANDBOX_INTRACTV, "1");
361 /* Just set the these if not already set so that is_env_on() work */
362 if (!getenv(ENV_SANDBOX_VERBOSE))
363 sandbox_setenv(new_environ, ENV_SANDBOX_VERBOSE, "1");
364 if (!getenv(ENV_SANDBOX_DEBUG))
365 sandbox_setenv(new_environ, ENV_SANDBOX_DEBUG, "0");
366 if (!getenv(ENV_NOCOLOR))
367 sandbox_setenv(new_environ, ENV_NOCOLOR, "0");
368 /* If LD_PRELOAD was not set, set it here, else do it below */
369 if (1 != have_ld_preload)
370 sandbox_setenv(new_environ, ENV_LD_PRELOAD, ld_preload_envvar);
371
372 if (!getenv(ENV_SANDBOX_DENY))
373 sandbox_setenv(new_environ, ENV_SANDBOX_DENY, LD_PRELOAD_FILE);
374
375 if (!getenv(ENV_SANDBOX_READ))
376 sandbox_setenv(new_environ, ENV_SANDBOX_READ, "/");
377
378 if (-1 == get_sandbox_write_envvar(sandbox_write_envvar, sandbox_info))
379 return NULL;
380 if (!getenv(ENV_SANDBOX_WRITE))
381 sandbox_setenv(new_environ, ENV_SANDBOX_WRITE, sandbox_write_envvar);
382
383 if (-1 == get_sandbox_predict_envvar(sandbox_predict_envvar, sandbox_info))
384 return NULL;
385 if (!getenv(ENV_SANDBOX_PREDICT))
386 sandbox_setenv(new_environ, ENV_SANDBOX_PREDICT, sandbox_predict_envvar);
387
388 /* Make sure our bashrc gets preference */
389 sandbox_setenv(new_environ, ENV_BASH_ENV, sandbox_info->sandbox_rc);
390
391 /* This one should NEVER be set in ebuilds, as it is the one
392 * private thing libsandbox.so use to test if the sandbox
393 * should be active for this pid, or not.
394 *
395 * azarah (3 Aug 2002)
396 */
397
398 sandbox_setenv(new_environ, ENV_SANDBOX_ACTIVE, SANDBOX_ACTIVE);
399
400 env_size = 0;
401 while (NULL != new_environ[env_size])
402 env_size++;
403
404 /* Now add the rest */
405 env_ptr = environ;
406 while (NULL != *env_ptr) {
407 if ((1 == have_ld_preload) &&
408 (strstr(*env_ptr, LD_PRELOAD_EQ) == *env_ptr))
409 /* If LD_PRELOAD was set, and this is it in the original
410 * environment, replace it with our new copy */
411 /* XXX: The following works as it just add whatever as
412 * the last variable to nev_environ */
413 sandbox_setenv(new_environ, ENV_LD_PRELOAD,
414 ld_preload_envvar);
415 else
416 new_environ[env_size + (env_ptr - environ)] = *env_ptr;
417 env_ptr++;
418 }
419
420 return new_environ;
421 }
422
423 int spawn_shell(char *argv_bash[], char *env[], int debug)
424 {
425 int status = 0;
426 int ret = 0;
427
428 child_pid = fork();
429
430 /* Child's process */
431 if (0 == child_pid) {
432 execve(argv_bash[0], argv_bash, env);
433 return 0;
434 } else if (child_pid < 0) {
435 if (debug)
436 fprintf(stderr, "Process failed to spawn!\n");
437 return 0;
438 }
439 ret = waitpid(child_pid, &status, 0);
440 if ((-1 == ret) || (status > 0)) {
441 if (debug)
442 fprintf(stderr, "Process returned with failed exit status!\n");
443 return 0;
444 }
445
446 return 1;
447 }
448
449 int main(int argc, char **argv)
450 {
451 struct sigaction act_new;
452
453 int i = 0, success = 1;
454 int sandbox_log_presence = 0;
455 long len;
456
457 struct sandbox_info_t sandbox_info;
458
459 char **sandbox_environ;
460 char **argv_bash = NULL;
461
462 char *run_str = "-c";
463
464 /* Only print info if called with no arguments .... */
465 if (argc < 2)
466 print_debug = 1;
467
468 if (print_debug)
469 printf("========================== Gentoo linux path sandbox ===========================\n");
470
471 /* check if a sandbox is already running */
472 if (NULL != getenv(ENV_SANDBOX_ACTIVE)) {
473 fprintf(stderr, "Not launching a new sandbox instance\n");
474 fprintf(stderr, "Another one is already running in this process hierarchy.\n");
475 exit(EXIT_FAILURE);
476 }
477
478 /* determine the location of all the sandbox support files */
479 if (print_debug)
480 printf("Detection of the support files.\n");
481
482 if (-1 == sandbox_setup(&sandbox_info)) {
483 fprintf(stderr, "sandbox: Failed to setup sandbox.");
484 exit(EXIT_FAILURE);
485 }
486
487 /* verify the existance of required files */
488 if (print_debug)
489 printf("Verification of the required files.\n");
490
491 #ifndef SB_HAVE_MULTILIB
492 if (0 >= exists(sandbox_info.sandbox_lib)) {
493 perror("sandbox: Could not open the sandbox library");
494 exit(EXIT_FAILURE);
495 }
496 #endif
497 if (0 >= exists(sandbox_info.sandbox_rc)) {
498 perror("sandbox: Could not open the sandbox rc file");
499 exit(EXIT_FAILURE);
500 }
501
502 /* set up the required environment variables */
503 if (print_debug)
504 printf("Setting up the required environment variables.\n");
505
506 /* Setup the child environment stuff */
507 sandbox_environ = sandbox_setup_environ(&sandbox_info, print_debug);
508 if (NULL == sandbox_environ) {
509 perror("sandbox: Out of memory (environ)");
510 exit(EXIT_FAILURE);
511 }
512
513 /* If not in portage, cd into it work directory */
514 if ('\0' != sandbox_info.work_dir[0])
515 chdir(sandbox_info.work_dir);
516
517 argv_bash = (char **)malloc(6 * sizeof(char *));
518 argv_bash[0] = strdup("/bin/bash");
519 argv_bash[1] = strdup("-rcfile");
520 argv_bash[2] = strdup(sandbox_info.sandbox_rc);
521
522 if (argc < 2)
523 argv_bash[3] = NULL;
524 else
525 argv_bash[3] = strdup(run_str); /* "-c" */
526
527 argv_bash[4] = NULL; /* strdup(run_arg); */
528 argv_bash[5] = NULL;
529
530 if (argc >= 2) {
531 for (i = 1; i < argc; i++) {
532 if (NULL == argv_bash[4])
533 len = 0;
534 else
535 len = strlen(argv_bash[4]);
536
537 argv_bash[4] = (char *)realloc(argv_bash[4],
538 (len + strlen(argv[i]) + 2) * sizeof(char));
539
540 if (0 == len)
541 argv_bash[4][0] = 0;
542 if (1 != i)
543 strcat(argv_bash[4], " ");
544
545 strcat(argv_bash[4], argv[i]);
546 }
547 }
548
549 /* set up the required signal handlers */
550 signal(SIGHUP, &stop);
551 signal(SIGINT, &stop);
552 signal(SIGQUIT, &stop);
553 signal(SIGTERM, &stop);
554 act_new.sa_sigaction = usr1_handler;
555 sigemptyset (&act_new.sa_mask);
556 act_new.sa_flags = SA_SIGINFO | SA_RESTART;
557 sigaction (SIGUSR1, &act_new, NULL);
558
559 /* STARTING PROTECTED ENVIRONMENT */
560 if (print_debug) {
561 printf("The protected environment has been started.\n");
562 printf("--------------------------------------------------------------------------------\n");
563 }
564
565 if (print_debug)
566 printf("Process being started in forked instance.\n");
567
568 /* Start Bash */
569 if (!spawn_shell(argv_bash, sandbox_environ, print_debug))
570 success = 0;
571
572 /* Free bash stuff */
573 for (i = 0; i < 6; i++) {
574 if (argv_bash[i])
575 free(argv_bash[i]);
576 argv_bash[i] = NULL;
577 }
578 if (argv_bash)
579 free(argv_bash);
580 argv_bash = NULL;
581
582 if (print_debug)
583 printf("Cleaning up sandbox process\n");
584
585 if (print_debug) {
586 printf("========================== Gentoo linux path sandbox ===========================\n");
587 printf("The protected environment has been shut down.\n");
588 }
589
590 if (1 == exists(sandbox_info.sandbox_log)) {
591 sandbox_log_presence = 1;
592 print_sandbox_log(sandbox_info.sandbox_log);
593 } else if (print_debug) {
594 printf("--------------------------------------------------------------------------------\n");
595 }
596
597 if ((sandbox_log_presence) || (!success))
598 return 1;
599 else
600 return 0;
601 }
602
603 // vim:noexpandtab noai:cindent ai

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

  ViewVC Help
Powered by ViewVC 1.1.20