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

Contents of /trunk/libsandbox.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 144 - (show annotations) (download) (as text)
Thu Jul 7 22:58:16 2005 UTC (13 years, 5 months ago) by azarah
File MIME type: text/x-csrc
File size: 35751 byte(s)
Cleanup access/log printing.  Make access printing honour NOCOLOR.  Fix log
printing's last line not honouring NOCOLOR.

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 *
15 * Post Bevin leaving Gentoo ranks:
16 * --------------------------------
17 * Ripped out all the wrappers, and implemented those of InstallWatch.
18 * Losts of cleanups and bugfixes. Implement a execve that forces $LIBSANDBOX
19 * in $LD_PRELOAD. Reformat the whole thing to look somewhat like the reworked
20 * sandbox.c from Brad House <brad@mainstreetsoftworks.com>.
21 *
22 * Martin Schlemmer <azarah@gentoo.org> (18 Aug 2002)
23 *
24 * Partly Copyright (C) 1998-9 Pancrazio `Ezio' de Mauro <p@demauro.net>,
25 * as some of the InstallWatch code was used.
26 *
27 *
28 * $Header$
29 *
30 */
31
32 /* Uncomment below to enable wrapping of mknod().
33 * This is broken currently. */
34 /* #define WRAP_MKNOD 1 */
35
36 /* Uncomment below to enable the use of strtok_r(). */
37 #define REENTRANT_STRTOK 1
38
39 /* Uncomment below to enable memory debugging. */
40 /* #define SB_MEM_DEBUG 1 */
41
42 #define open xxx_open
43 #define open64 xxx_open64
44
45 /* Wrapping mknod, do not have any effect, and
46 * wrapping __xmknod causes calls to it to segfault
47 */
48 #ifdef WRAP_MKNOD
49 # define __xmknod xxx___xmknod
50 #endif
51
52 #include <dirent.h>
53 #include <dlfcn.h>
54 #include <errno.h>
55 #include <fcntl.h>
56 #include <libgen.h>
57 #include <stdarg.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <sys/file.h>
62 #include <sys/stat.h>
63 #include <sys/types.h>
64 #include <sys/param.h>
65 #include <unistd.h>
66 #include <utime.h>
67 #include "config.h"
68 #include "localdecls.h"
69 #include "symbols.h"
70
71 #ifdef SB_MEM_DEBUG
72 # include <mcheck.h>
73 #endif
74
75 #ifdef WRAP_MKNOD
76 # undef __xmknod
77 #endif
78
79 #undef open
80 #undef open64
81
82 //#include "localdecls.h"
83 #include "sandbox.h"
84
85 /* Macros to check if a function should be executed */
86 #define FUNCTION_SANDBOX_SAFE(func, path) \
87 ((0 == is_sandbox_on()) || (1 == before_syscall(func, path)))
88
89 #define FUNCTION_SANDBOX_SAFE_ACCESS(func, path, flags) \
90 ((0 == is_sandbox_on()) || (1 == before_syscall_access(func, path, flags)))
91
92 #define FUNCTION_SANDBOX_SAFE_OPEN_INT(func, path, flags) \
93 ((0 == is_sandbox_on()) || (1 == before_syscall_open_int(func, path, flags)))
94
95 #define FUNCTION_SANDBOX_SAFE_OPEN_CHAR(func, path, mode) \
96 ((0 == is_sandbox_on()) || (1 == before_syscall_open_char(func, path, mode)))
97
98 /* Macro to check if a wrapper is defined, if not
99 * then try to resolve it again. */
100 #define check_dlsym(name) \
101 { \
102 int old_errno=errno; \
103 if (!true_ ## name) \
104 true_ ## name = get_dlsym(symname_ ## name, symver_ ## name); \
105 errno=old_errno; \
106 }
107
108 static char sandbox_lib[SB_PATH_MAX];
109
110 typedef struct {
111 int show_access_violation;
112 char **deny_prefixes;
113 int num_deny_prefixes;
114 char **read_prefixes;
115 int num_read_prefixes;
116 char **write_prefixes;
117 int num_write_prefixes;
118 char **predict_prefixes;
119 int num_predict_prefixes;
120 char **write_denied_prefixes;
121 int num_write_denied_prefixes;
122 } sbcontext_t;
123
124 static sbcontext_t sbcontext;
125 static char **cached_env_vars;
126 static int sb_init = 0;
127 static int sb_path_size_warning = 0;
128
129 void __attribute__ ((constructor)) libsb_init(void);
130 void __attribute__ ((destructor)) libsb_fini(void);
131
132 /* glibc modified realpath() functions */
133 static char *erealpath(const char *, char *);
134 /* glibc modified getcwd() functions */
135 static char *egetcwd(char *, size_t);
136
137 static void init_wrappers(void);
138 static void *get_dlsym(const char *, const char *);
139 static int canonicalize(const char *, char *);
140 static char *filter_path(const char *, int);
141 static int check_prefixes(char **, int, const char *);
142 static int check_access(sbcontext_t *, const char *, const char *, const char *);
143 static int check_syscall(sbcontext_t *, const char *, const char *);
144 static int before_syscall(const char *, const char *);
145 static int before_syscall_access(const char *, const char *, int);
146 static int before_syscall_open_int(const char *, const char *, int);
147 static int before_syscall_open_char(const char *, const char *, const char *);
148 static void clean_env_entries(char ***, int *);
149 static void init_context(sbcontext_t *);
150 static void init_env_entries(char ***, int *, const char *, const char *, int);
151 static int is_sandbox_on();
152
153 /* Wrapped functions */
154
155 extern int chmod(const char *, mode_t);
156 static int (*true_chmod) (const char *, mode_t) = NULL;
157 extern int chown(const char *, uid_t, gid_t);
158 static int (*true_chown) (const char *, uid_t, gid_t) = NULL;
159 extern int creat(const char *, mode_t);
160 static int (*true_creat) (const char *, mode_t) = NULL;
161 extern FILE *fopen(const char *, const char *);
162 static FILE *(*true_fopen) (const char *, const char *) = NULL;
163 extern int lchown(const char *, uid_t, gid_t);
164 static int (*true_lchown) (const char *, uid_t, gid_t) = NULL;
165 extern int link(const char *, const char *);
166 static int (*true_link) (const char *, const char *) = NULL;
167 extern int mkdir(const char *, mode_t);
168 static int (*true_mkdir) (const char *, mode_t) = NULL;
169 extern DIR *opendir(const char *);
170 static DIR *(*true_opendir) (const char *) = NULL;
171 #ifdef WRAP_MKNOD
172 extern int __xmknod(const char *, mode_t, dev_t);
173 static int (*true___xmknod) (const char *, mode_t, dev_t) = NULL;
174 #endif
175 extern int access(const char *, int);
176 static int (*true_access) (const char *, int) = NULL;
177 extern int open(const char *, int, ...);
178 static int (*true_open) (const char *, int, ...) = NULL;
179 extern int rename(const char *, const char *);
180 static int (*true_rename) (const char *, const char *) = NULL;
181 extern int rmdir(const char *);
182 static int (*true_rmdir) (const char *) = NULL;
183 extern int symlink(const char *, const char *);
184 static int (*true_symlink) (const char *, const char *) = NULL;
185 extern int truncate(const char *, TRUNCATE_T);
186 static int (*true_truncate) (const char *, TRUNCATE_T) = NULL;
187 extern int unlink(const char *);
188 static int (*true_unlink) (const char *) = NULL;
189
190 #if (GLIBC_MINOR >= 1)
191
192 extern int creat64(const char *, __mode_t);
193 static int (*true_creat64) (const char *, __mode_t) = NULL;
194 extern FILE *fopen64(const char *, const char *);
195 static FILE *(*true_fopen64) (const char *, const char *) = NULL;
196 extern int open64(const char *, int, ...);
197 static int (*true_open64) (const char *, int, ...) = NULL;
198 extern int truncate64(const char *, __off64_t);
199 static int (*true_truncate64) (const char *, __off64_t) = NULL;
200
201 #endif
202
203 extern int execve(const char *filename, char *const argv[], char *const envp[]);
204 static int (*true_execve) (const char *, char *const[], char *const[]);
205
206 /*
207 * Initialize the shabang
208 */
209
210 static void init_wrappers(void)
211 {
212 check_dlsym(chmod);
213 check_dlsym(chown);
214 check_dlsym(creat);
215 check_dlsym(fopen);
216 check_dlsym(lchown);
217 check_dlsym(link);
218 check_dlsym(mkdir);
219 check_dlsym(opendir);
220 #ifdef WRAP_MKNOD
221 check_dlsym(__xmknod);
222 #endif
223 check_dlsym(access);
224 check_dlsym(open);
225 check_dlsym(rename);
226 check_dlsym(rmdir);
227 check_dlsym(symlink);
228 check_dlsym(truncate);
229 check_dlsym(unlink);
230
231 #if (GLIBC_MINOR >= 1)
232 check_dlsym(creat64);
233 check_dlsym(fopen64);
234 check_dlsym(open64);
235 check_dlsym(truncate64);
236 #endif
237
238 check_dlsym(execve);
239 }
240
241 static void *libc_handle = NULL;
242
243 static void *get_dlsym(const char *symname, const char *symver)
244 {
245 void *symaddr = NULL;
246
247 if (NULL == libc_handle) {
248 #ifdef BROKEN_RTLD_NEXT
249 libc_handle = dlopen(LIBC_VERSION, RTLD_LAZY);
250 if (!libc_handle) {
251 printf("libsandbox.so: Can't dlopen libc: %s\n", dlerror());
252 abort();
253 }
254 #else
255 libc_handle = RTLD_NEXT;
256 #endif
257 }
258
259 if (NULL == symver)
260 symaddr = dlsym(libc_handle, symname);
261 else
262 symaddr = dlvsym(libc_handle, symname, symver);
263 if (!symaddr) {
264 printf("libsandbox.so: Can't resolve %s: %s\n", symname, dlerror());
265 abort();
266 }
267
268 return symaddr;
269 }
270
271
272 void __attribute__ ((destructor)) libsb_fini(void)
273 {
274 int x;
275 if(NULL != cached_env_vars) {
276 for(x=0; x < 4; x++) {
277 if(NULL != cached_env_vars[x])
278 free(cached_env_vars[x]);
279 }
280 free(cached_env_vars);
281 }
282 clean_env_entries(&(sbcontext.deny_prefixes),
283 &(sbcontext.num_deny_prefixes));
284 clean_env_entries(&(sbcontext.read_prefixes),
285 &(sbcontext.num_read_prefixes));
286 clean_env_entries(&(sbcontext.write_prefixes),
287 &(sbcontext.num_write_prefixes));
288 clean_env_entries(&(sbcontext.predict_prefixes),
289 &(sbcontext.num_predict_prefixes));
290 }
291
292 void __attribute__ ((constructor)) libsb_init(void)
293 {
294 int old_errno = errno;
295
296 #ifdef SB_MEM_DEBUG
297 mtrace();
298 #endif
299
300 init_wrappers();
301
302 /* Get the path and name to this library */
303 get_sandbox_lib(sandbox_lib);
304
305 // sb_init = 1;
306
307 errno = old_errno;
308 }
309
310 static int canonicalize(const char *path, char *resolved_path)
311 {
312 int old_errno = errno;
313 char *retval;
314
315 *resolved_path = '\0';
316
317 /* If path == NULL, return or we get a segfault */
318 if (NULL == path) {
319 errno = EINVAL;
320 return -1;
321 }
322
323 /* Do not try to resolve an empty path */
324 if ('\0' == path[0]) {
325 errno = old_errno;
326 return 0;
327 }
328
329 retval = erealpath(path, resolved_path);
330
331 if ((NULL == retval) && (path[0] != '/')) {
332 /* The path could not be canonicalized, append it
333 * to the current working directory if it was not
334 * an absolute path
335 */
336
337 if (ENAMETOOLONG == errno)
338 return -1;
339
340 if (NULL == egetcwd(resolved_path, SB_PATH_MAX - 2))
341 return -1;
342 snprintf((char *)(resolved_path + strlen(resolved_path)),
343 SB_PATH_MAX - strlen(resolved_path), "/%s", path);
344
345 if (NULL == erealpath(resolved_path, resolved_path)) {
346 if (errno == ENAMETOOLONG) {
347 /* The resolved path is too long for the buffer to hold */
348 return -1;
349 } else {
350 /* Whatever it resolved, is not a valid path */
351 errno = ENOENT;
352 return -1;
353 }
354 }
355
356 } else if ((NULL == retval) && (path[0] == '/')) {
357 /* Whatever it resolved, is not a valid path */
358 errno = ENOENT;
359 return -1;
360 }
361
362 errno = old_errno;
363 return 0;
364 }
365
366 static char *filter_path(const char *path, int follow_link)
367 {
368 int old_errno = errno;
369 char tmp_str1[SB_PATH_MAX], tmp_str2[SB_PATH_MAX];
370 char *dname, *bname;
371 char *filtered_path;
372
373 if (NULL == path)
374 return NULL;
375
376 filtered_path = malloc(SB_PATH_MAX * sizeof(char));
377 if (NULL == filtered_path)
378 return NULL;
379
380 if (0 == follow_link) {
381 if (-1 == canonicalize(path, filtered_path))
382 return NULL;
383 } else {
384 /* Basically we get the realpath which should resolve symlinks,
385 * etc. If that fails (might not exist), we try to get the
386 * realpath of the parent directory, as that should hopefully
387 * exist. If all else fails, just go with canonicalize */
388 if (NULL == realpath(path, filtered_path)) {
389 snprintf(tmp_str1, SB_PATH_MAX, "%s", path);
390
391 dname = dirname(tmp_str1);
392
393 /* If not, then check if we can resolve the
394 * parent directory */
395 if (NULL == realpath(dname, filtered_path)) {
396 /* Fall back to canonicalize */
397 if (-1 == canonicalize(path, filtered_path))
398 return NULL;
399 } else {
400 /* OK, now add the basename to keep our access
401 * checking happy (don't want '/usr/lib' if we
402 * tried to do something with non-existing
403 * file '/usr/lib/cf*' ...) */
404 snprintf(tmp_str2, SB_PATH_MAX, "%s", path);
405
406 bname = basename(tmp_str2);
407 snprintf((char *)(filtered_path + strlen(filtered_path)),
408 SB_PATH_MAX - strlen(filtered_path), "%s%s",
409 (filtered_path[strlen(filtered_path) - 1] != '/') ? "/" : "",
410 bname);
411 }
412 }
413 }
414
415 errno = old_errno;
416
417 return filtered_path;
418 }
419
420 /*
421 * Wrapper Functions
422 */
423
424 int chmod(const char *path, mode_t mode)
425 {
426 int result = -1;
427
428 if FUNCTION_SANDBOX_SAFE("chmod", path) {
429 check_dlsym(chmod);
430 result = true_chmod(path, mode);
431 }
432
433 return result;
434 }
435
436 int chown(const char *path, uid_t owner, gid_t group)
437 {
438 int result = -1;
439
440 if FUNCTION_SANDBOX_SAFE("chown", path) {
441 check_dlsym(chown);
442 result = true_chown(path, owner, group);
443 }
444
445 return result;
446 }
447
448 int creat(const char *pathname, mode_t mode)
449 {
450 /* Is it a system call? */
451 int result = -1;
452
453 if FUNCTION_SANDBOX_SAFE("creat", pathname) {
454 check_dlsym(open);
455 result = true_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);
456 }
457
458 return result;
459 }
460
461 FILE *fopen(const char *pathname, const char *mode)
462 {
463 FILE *result = NULL;
464
465 if FUNCTION_SANDBOX_SAFE_OPEN_CHAR("fopen", pathname, mode) {
466 check_dlsym(fopen);
467 result = true_fopen(pathname, mode);
468 }
469
470 return result;
471 }
472
473 int lchown(const char *path, uid_t owner, gid_t group)
474 {
475 int result = -1;
476
477 if FUNCTION_SANDBOX_SAFE("lchown", path) {
478 check_dlsym(lchown);
479 result = true_lchown(path, owner, group);
480 }
481
482 return result;
483 }
484
485 int link(const char *oldpath, const char *newpath)
486 {
487 int result = -1;
488
489 if FUNCTION_SANDBOX_SAFE("link", newpath) {
490 check_dlsym(link);
491 result = true_link(oldpath, newpath);
492 }
493
494 return result;
495 }
496
497 int mkdir(const char *pathname, mode_t mode)
498 {
499 struct stat st;
500 int result = -1, my_errno = errno;
501 char canonic[SB_PATH_MAX];
502
503 if (-1 == canonicalize(pathname, canonic))
504 /* Path is too long to canonicalize, do not fail, but just let
505 * the real function handle it (see bug #94630 and #21766). */
506 if (ENAMETOOLONG != errno)
507 return -1;
508
509 /* XXX: Hack to prevent errors if the directory exist,
510 * and are not writable - we rather return EEXIST rather
511 * than failing */
512 if (0 == lstat(canonic, &st)) {
513 errno = EEXIST;
514 return -1;
515 }
516 errno = my_errno;
517
518 if FUNCTION_SANDBOX_SAFE("mkdir", pathname) {
519 check_dlsym(mkdir);
520 result = true_mkdir(pathname, mode);
521 }
522
523 return result;
524 }
525
526 DIR *opendir(const char *name)
527 {
528 DIR *result = NULL;
529
530 if FUNCTION_SANDBOX_SAFE("opendir", name) {
531 check_dlsym(opendir);
532 result = true_opendir(name);
533 }
534
535 return result;
536 }
537
538 #ifdef WRAP_MKNOD
539
540 int __xmknod(const char *pathname, mode_t mode, dev_t dev)
541 {
542 int result = -1;
543
544 if FUNCTION_SANDBOX_SAFE("__xmknod", pathname) {
545 check_dlsym(__xmknod);
546 result = true___xmknod(pathname, mode, dev);
547 }
548
549 return result;
550 }
551
552 #endif
553
554 int access(const char *pathname, int mode)
555 {
556 int result = -1;
557
558 if FUNCTION_SANDBOX_SAFE_ACCESS("access", pathname, mode) {
559 check_dlsym(access);
560 result = true_access(pathname, mode);
561 }
562
563 return result;
564 }
565
566 /* Eventually, there is a third parameter: it's mode_t mode */
567 int open(const char *pathname, int flags, ...)
568 {
569 va_list ap;
570 mode_t mode = 0;
571 int result = -1;
572
573 if (flags & O_CREAT) {
574 va_start(ap, flags);
575 mode = va_arg(ap, mode_t);
576 va_end(ap);
577 }
578
579 if FUNCTION_SANDBOX_SAFE_OPEN_INT("open", pathname, flags) {
580 check_dlsym(open);
581 result = true_open(pathname, flags, mode);
582 }
583
584 return result;
585 }
586
587 int rename(const char *oldpath, const char *newpath)
588 {
589 int result = -1;
590
591 if (FUNCTION_SANDBOX_SAFE("rename", oldpath) &&
592 FUNCTION_SANDBOX_SAFE("rename", newpath)) {
593 check_dlsym(rename);
594 result = true_rename(oldpath, newpath);
595 }
596
597 return result;
598 }
599
600 int rmdir(const char *pathname)
601 {
602 int result = -1;
603
604 if FUNCTION_SANDBOX_SAFE("rmdir", pathname) {
605 check_dlsym(rmdir);
606 result = true_rmdir(pathname);
607 }
608
609 return result;
610 }
611
612 int symlink(const char *oldpath, const char *newpath)
613 {
614 int result = -1;
615
616 if FUNCTION_SANDBOX_SAFE("symlink", newpath) {
617 check_dlsym(symlink);
618 result = true_symlink(oldpath, newpath);
619 }
620
621 return result;
622 }
623
624 int truncate(const char *path, TRUNCATE_T length)
625 {
626 int result = -1;
627
628 if FUNCTION_SANDBOX_SAFE("truncate", path) {
629 check_dlsym(truncate);
630 result = true_truncate(path, length);
631 }
632
633 return result;
634 }
635
636 int unlink(const char *pathname)
637 {
638 int result = -1;
639 char canonic[SB_PATH_MAX];
640
641 if (-1 == canonicalize(pathname, canonic))
642 /* Path is too long to canonicalize, do not fail, but just let
643 * the real function handle it (see bug #94630 and #21766). */
644 if (ENAMETOOLONG != errno)
645 return -1;
646
647 /* XXX: Hack to make sure sandboxed process cannot remove
648 * a device node, bug #79836. */
649 if ((0 == strncmp(canonic, "/dev/null", 9)) ||
650 (0 == strncmp(canonic, "/dev/zero", 9))) {
651 errno = EACCES;
652 return result;
653 }
654
655 if FUNCTION_SANDBOX_SAFE("unlink", pathname) {
656 check_dlsym(unlink);
657 result = true_unlink(pathname);
658 }
659
660 return result;
661 }
662
663 #if (GLIBC_MINOR >= 1)
664
665 int creat64(const char *pathname, __mode_t mode)
666 {
667 /* Is it a system call? */
668 int result = -1;
669
670 if FUNCTION_SANDBOX_SAFE("creat64", pathname) {
671 check_dlsym(open64);
672 result = true_open64(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);
673 }
674
675 return result;
676 }
677
678 FILE *fopen64(const char *pathname, const char *mode)
679 {
680 FILE *result = NULL;
681
682 if FUNCTION_SANDBOX_SAFE_OPEN_CHAR("fopen64", pathname, mode) {
683 check_dlsym(fopen64);
684 result = true_fopen64(pathname, mode);
685 }
686
687 return result;
688 }
689
690 /* Eventually, there is a third parameter: it's mode_t mode */
691 int open64(const char *pathname, int flags, ...)
692 {
693 va_list ap;
694 mode_t mode = 0;
695 int result = -1;
696
697 if (flags & O_CREAT) {
698 va_start(ap, flags);
699 mode = va_arg(ap, mode_t);
700 va_end(ap);
701 }
702
703 if FUNCTION_SANDBOX_SAFE_OPEN_INT("open64", pathname, flags) {
704 check_dlsym(open64);
705 result = true_open64(pathname, flags, mode);
706 }
707
708 return result;
709 }
710
711 int truncate64(const char *path, __off64_t length)
712 {
713 int result = -1;
714
715 if FUNCTION_SANDBOX_SAFE("truncate64", path) {
716 check_dlsym(truncate64);
717 result = true_truncate64(path, length);
718 }
719
720 return result;
721 }
722
723 #endif /* GLIBC_MINOR >= 1 */
724
725 /*
726 * Exec Wrappers
727 */
728
729 int execve(const char *filename, char *const argv[], char *const envp[])
730 {
731 int old_errno = errno;
732 int result = -1;
733 int count = 0;
734 int env_len = 0;
735 char **my_env = NULL;
736 int kill_env = 1;
737 /* We limit the size LD_PRELOAD can be here, but it should be enough */
738 char tmp_str[SB_BUF_LEN];
739
740 if FUNCTION_SANDBOX_SAFE("execve", filename) {
741 while (envp[count] != NULL) {
742 /* Check if we do not have to do anything */
743 if (strstr(envp[count], LD_PRELOAD_EQ) == envp[count]) {
744 if (NULL != strstr(envp[count], sandbox_lib)) {
745 my_env = (char **)envp;
746 kill_env = 0;
747 goto end_loop;
748 }
749 }
750
751 /* If LD_PRELOAD is set and sandbox_lib not in it */
752 if (((strstr(envp[count], LD_PRELOAD_EQ) == envp[count]) &&
753 (NULL == strstr(envp[count], sandbox_lib))) ||
754 /* Or LD_PRELOAD is not set, and this is the last loop */
755 ((strstr(envp[count], LD_PRELOAD_EQ) != envp[count]) &&
756 (NULL == envp[count + 1]))) {
757 int i = 0;
758 int add_ldpreload = 0;
759 const int max_envp_len = strlen(envp[count]) + strlen(sandbox_lib) + 1;
760
761 /* Fail safe ... */
762 if (max_envp_len > SB_BUF_LEN) {
763 fprintf(stderr, "sandbox: max_envp_len too big!\n");
764 errno = ENOMEM;
765 return result;
766 }
767
768 /* Calculate envp size */
769 my_env = (char **)envp;
770 do
771 env_len++;
772 while (NULL != *my_env++);
773
774 /* Should we add LD_PRELOAD ? */
775 if (strstr(envp[count], LD_PRELOAD_EQ) != envp[count])
776 add_ldpreload = 1;
777
778 my_env = (char **)calloc(env_len + add_ldpreload, sizeof(char *));
779 if (NULL == my_env) {
780 errno = ENOMEM;
781 return result;
782 }
783 /* Copy envp to my_env */
784 do
785 /* Leave a space for LD_PRELOAD if needed */
786 my_env[i + add_ldpreload] = envp[i];
787 while (NULL != envp[i++]);
788
789 /* Add 'LD_PRELOAD=' to the beginning of our new string */
790 snprintf(tmp_str, max_envp_len, "%s%s", LD_PRELOAD_EQ, sandbox_lib);
791
792 /* LD_PRELOAD already have variables other than sandbox_lib,
793 * thus we have to add sandbox_lib seperated via a whitespace. */
794 if (0 == add_ldpreload) {
795 snprintf((char *)(tmp_str + strlen(tmp_str)),
796 max_envp_len - strlen(tmp_str) + 1, " %s",
797 (char *)(envp[count] + strlen(LD_PRELOAD_EQ)));
798 }
799
800 /* Valid string? */
801 tmp_str[max_envp_len] = '\0';
802
803 /* Ok, replace my_env[count] with our version that contains
804 * sandbox_lib ... */
805 if (1 == add_ldpreload)
806 /* We reserved a space for LD_PRELOAD above */
807 my_env[0] = tmp_str;
808 else
809 my_env[count] = tmp_str;
810
811 goto end_loop;
812 }
813 count++;
814 }
815
816 end_loop:
817 errno = old_errno;
818 check_dlsym(execve);
819 result = true_execve(filename, argv, my_env);
820 old_errno = errno;
821
822 if (my_env && kill_env)
823 free(my_env);
824 }
825
826 errno = old_errno;
827
828 return result;
829 }
830
831 /*
832 * Internal Functions
833 */
834
835 #if (GLIBC_MINOR == 1)
836
837 /* This hack is needed for glibc 2.1.1 (and others?)
838 * (not really needed, but good example) */
839 extern int fclose(FILE *);
840 static int (*true_fclose) (FILE *) = NULL;
841 int fclose(FILE * file)
842 {
843 int result = -1;
844
845 check_dlsym(fclose);
846 result = true_fclose(file);
847
848 return result;
849 }
850
851 #endif /* GLIBC_MINOR == 1 */
852
853 static void init_context(sbcontext_t * context)
854 {
855 context->show_access_violation = 1;
856 context->deny_prefixes = NULL;
857 context->num_deny_prefixes = 0;
858 context->read_prefixes = NULL;
859 context->num_read_prefixes = 0;
860 context->write_prefixes = NULL;
861 context->num_write_prefixes = 0;
862 context->predict_prefixes = NULL;
863 context->num_predict_prefixes = 0;
864 context->write_denied_prefixes = NULL;
865 context->num_write_denied_prefixes = 0;
866 }
867
868 static void clean_env_entries(char ***prefixes_array, int *prefixes_num)
869 {
870 int old_errno = errno;
871 int i = 0;
872
873 if (NULL != *prefixes_array) {
874 for (i = 0; i < *prefixes_num; i++) {
875 if (NULL != (*prefixes_array)[i]) {
876 free((*prefixes_array)[i]);
877 (*prefixes_array)[i] = NULL;
878 }
879 }
880 if (NULL != *prefixes_array)
881 free(*prefixes_array);
882 *prefixes_array = NULL;
883 *prefixes_num = 0;
884 }
885
886 errno = old_errno;
887 }
888
889 #define pfx_num (*prefixes_num)
890 #define pfx_array (*prefixes_array)
891 #define pfx_item ((*prefixes_array)[(*prefixes_num)])
892
893 static void init_env_entries(char ***prefixes_array, int *prefixes_num, const char *env, const char *prefixes_env, int warn)
894 {
895 char *token = NULL;
896 char *rpath = NULL;
897 char *buffer = NULL;
898 char *buffer_ptr = NULL;
899 int prefixes_env_length = strlen(prefixes_env);
900 int num_delimiters = 0;
901 int i = 0;
902 int old_errno = errno;
903
904 if (NULL == prefixes_env) {
905 /* Do not warn if this is in init stage, as we might get
906 * issues due to LD_PRELOAD already set (bug #91431). */
907 if (1 == sb_init)
908 fprintf(stderr, "Sandbox error : the %s environmental variable should be defined.\n", env);
909 if(pfx_array) {
910 for(i = 0; i < pfx_num; i++)
911 free(pfx_item);
912 free(pfx_array);
913 }
914 pfx_num = 0;
915
916 goto done;
917 }
918
919 for (i = 0; i < prefixes_env_length; i++) {
920 if (':' == prefixes_env[i])
921 num_delimiters++;
922 }
923
924 /* num_delimiters might be 0, and we need 2 entries at least */
925 pfx_array = malloc(((num_delimiters * 2) + 2) * sizeof(char *));
926 if (NULL == pfx_array)
927 goto error;
928 buffer = strndup(prefixes_env, prefixes_env_length);
929 if (NULL == buffer)
930 goto error;
931 buffer_ptr = buffer;
932
933 #ifdef REENTRANT_STRTOK
934 token = strtok_r(buffer_ptr, ":", &buffer_ptr);
935 #else
936 token = strtok(buffer_ptr, ":");
937 #endif
938
939 while ((NULL != token) && (strlen(token) > 0)) {
940 pfx_item = filter_path(token, 0);
941 if (NULL != pfx_item) {
942 pfx_num++;
943
944 /* Now add the realpath if it exists and
945 * are not a duplicate */
946 rpath = malloc(SB_PATH_MAX * sizeof(char));
947 if (NULL != rpath) {
948 pfx_item = realpath(*(&(pfx_item) - 1), rpath);
949 if ((NULL != pfx_item) &&
950 (0 != strcmp(*(&(pfx_item) - 1), pfx_item))) {
951 pfx_num++;
952 } else {
953 free(rpath);
954 pfx_item = NULL;
955 }
956 } else {
957 goto error;
958 }
959 }
960
961 #ifdef REENTRANT_STRTOK
962 token = strtok_r(NULL, ":", &buffer_ptr);
963 #else
964 token = strtok(NULL, ":");
965 #endif
966 }
967
968 free(buffer);
969
970 done:
971 errno = old_errno;
972 return;
973
974 error:
975 fprintf(stderr, "\e[31;01mERROR\033[0m Could not initialize!\n");
976 exit(EXIT_FAILURE);
977 }
978
979 static int check_prefixes(char **prefixes, int num_prefixes, const char *path)
980 {
981 int i = 0;
982
983 if (NULL == prefixes)
984 return 0;
985
986 for (i = 0; i < num_prefixes; i++) {
987 if (NULL != prefixes[i]) {
988 if (0 == strncmp(path, prefixes[i], strlen(prefixes[i])))
989 return 1;
990 }
991 }
992
993 return 0;
994 }
995
996 static int check_access(sbcontext_t * sbcontext, const char *func, const char *path, const char *fpath)
997 {
998 int old_errno = errno;
999 int result = 0;
1000 int retval;
1001
1002 retval = check_prefixes(sbcontext->deny_prefixes,
1003 sbcontext->num_deny_prefixes, fpath);
1004 if (1 == retval)
1005 /* Fall in a read/write denied path, Deny Access */
1006 goto out;
1007
1008 /* Hardcode denying write to log dir */
1009 if (0 == strncmp(fpath, SANDBOX_LOG_LOCATION,
1010 strlen(SANDBOX_LOG_LOCATION)))
1011 goto out;
1012
1013 if ((NULL != sbcontext->read_prefixes) &&
1014 ((0 == strncmp(func, "access_rd", 9)) ||
1015 (0 == strncmp(func, "open_rd", 7)) ||
1016 (0 == strncmp(func, "popen", 5)) ||
1017 (0 == strncmp(func, "opendir", 7)) ||
1018 (0 == strncmp(func, "system", 6)) ||
1019 (0 == strncmp(func, "execl", 5)) ||
1020 (0 == strncmp(func, "execlp", 6)) ||
1021 (0 == strncmp(func, "execle", 6)) ||
1022 (0 == strncmp(func, "execv", 5)) ||
1023 (0 == strncmp(func, "execvp", 6)) ||
1024 (0 == strncmp(func, "execve", 6)))) {
1025 retval = check_prefixes(sbcontext->read_prefixes,
1026 sbcontext->num_read_prefixes, fpath);
1027 if (1 == retval) {
1028 /* Fall in a readable path, Grant Access */
1029 result = 1;
1030 goto out;
1031 }
1032 }
1033
1034 if ((0 == strncmp(func, "access_wr", 7)) ||
1035 (0 == strncmp(func, "open_wr", 7)) ||
1036 (0 == strncmp(func, "creat", 5)) ||
1037 (0 == strncmp(func, "creat64", 7)) ||
1038 (0 == strncmp(func, "mkdir", 5)) ||
1039 (0 == strncmp(func, "mknod", 5)) ||
1040 (0 == strncmp(func, "mkfifo", 6)) ||
1041 (0 == strncmp(func, "link", 4)) ||
1042 (0 == strncmp(func, "symlink", 7)) ||
1043 (0 == strncmp(func, "rename", 6)) ||
1044 (0 == strncmp(func, "utime", 5)) ||
1045 (0 == strncmp(func, "utimes", 6)) ||
1046 (0 == strncmp(func, "unlink", 6)) ||
1047 (0 == strncmp(func, "rmdir", 5)) ||
1048 (0 == strncmp(func, "chown", 5)) ||
1049 (0 == strncmp(func, "lchown", 6)) ||
1050 (0 == strncmp(func, "chmod", 5)) ||
1051 (0 == strncmp(func, "truncate", 8)) ||
1052 (0 == strncmp(func, "ftruncate", 9)) ||
1053 (0 == strncmp(func, "truncate64", 10)) ||
1054 (0 == strncmp(func, "ftruncate64", 11))) {
1055 struct stat st;
1056 char proc_self_fd[SB_PATH_MAX];
1057
1058 retval = check_prefixes(sbcontext->write_denied_prefixes,
1059 sbcontext->num_write_denied_prefixes,
1060 fpath);
1061 if (1 == retval)
1062 /* Falls in a write denied path, Deny Access */
1063 goto out;
1064
1065 retval = check_prefixes(sbcontext->write_prefixes,
1066 sbcontext->num_write_prefixes, fpath);
1067 if (1 == retval) {
1068 /* Falls in a writable path, Grant Access */
1069 result = 1;
1070 goto out;
1071 }
1072
1073 /* XXX: Hack to enable us to remove symlinks pointing
1074 * to protected stuff. First we make sure that the
1075 * passed path is writable, and if so, check if its a
1076 * symlink, and give access only if the resolved path
1077 * of the symlink's parent also have write access. */
1078 if (((0 == strncmp(func, "unlink", 6)) ||
1079 (0 == strncmp(func, "lchown", 6)) ||
1080 (0 == strncmp(func, "rename", 6))) &&
1081 ((-1 != lstat(path, &st)) && (S_ISLNK(st.st_mode)))) {
1082 /* Check if the symlink unresolved path have access */
1083 retval = check_prefixes(sbcontext->write_prefixes,
1084 sbcontext->num_write_prefixes, path);
1085 if (1 == retval) { /* Does have write access on path */
1086 char tmp_buf[SB_PATH_MAX];
1087 char *dname, *rpath;
1088
1089 snprintf(tmp_buf, SB_PATH_MAX, "%s", path);
1090
1091 dname = dirname(tmp_buf);
1092 /* Get symlink resolved path */
1093 rpath = filter_path(dname, 1);
1094 if (NULL == rpath)
1095 /* Don't really worry here about
1096 * memory issues */
1097 goto unlink_hack_end;
1098
1099 /* Now check if the symlink resolved path have access */
1100 retval = check_prefixes(sbcontext->write_prefixes,
1101 sbcontext->num_write_prefixes,
1102 rpath);
1103 free(rpath);
1104 if (1 == retval) {
1105 /* Does have write access on path, so
1106 * enable the hack as it is a symlink */
1107 result = 1;
1108 goto out;
1109 }
1110 }
1111 }
1112 unlink_hack_end:
1113
1114 /* XXX: Hack to allow writing to '/proc/self/fd' (bug #91516)
1115 * It needs to be here, as for each process '/proc/self'
1116 * will differ ... */
1117 if ((0 == strncmp(fpath, "/proc", strlen("/proc"))) &&
1118 (NULL != realpath("/proc/self/fd", proc_self_fd))) {
1119 if (0 == strncmp(fpath, proc_self_fd,
1120 strlen(proc_self_fd))) {
1121 result = 1;
1122 goto out;
1123 }
1124 }
1125
1126 retval = check_prefixes(sbcontext->predict_prefixes,
1127 sbcontext->num_predict_prefixes, fpath);
1128 if (1 == retval) {
1129 /* Is a known access violation, so deny access,
1130 * and do not log it */
1131 sbcontext->show_access_violation = 0;
1132 goto out;
1133 }
1134 }
1135
1136 out:
1137 errno = old_errno;
1138
1139 return result;
1140 }
1141
1142 static int check_syscall(sbcontext_t * sbcontext, const char *func, const char *file)
1143 {
1144 struct stat log_stat;
1145 char buffer[512];
1146 char *absolute_path = NULL;
1147 char *resolved_path = NULL;
1148 char *log_path = NULL;
1149 int old_errno = errno;
1150 int result = 1;
1151 int log_file = 0;
1152 int debug = 0;
1153 int color = ((getenv("NOCOLOR") != NULL) ? 0 : 1);
1154
1155 init_wrappers();
1156
1157 absolute_path = filter_path(file, 0);
1158 if (NULL == absolute_path)
1159 goto fp_error;
1160 resolved_path = filter_path(file, 1);
1161 if (NULL == resolved_path)
1162 goto fp_error;
1163
1164 if (NULL == getenv("SANDBOX_DEBUG")) {
1165 log_path = getenv("SANDBOX_LOG");
1166 } else {
1167 log_path = getenv("SANDBOX_DEBUG_LOG");
1168 debug = 1;
1169 }
1170
1171 result = check_access(sbcontext, func, absolute_path, resolved_path);
1172
1173 if ((0 == result) && (1 == sbcontext->show_access_violation)) {
1174 EERROR(color, "ACCESS DENIED", " %s:%*s%s\n",
1175 func, (int)(10 - strlen(func)), "", absolute_path);
1176 } else if ((1 == debug) && (1 == sbcontext->show_access_violation)) {
1177 EINFO(color, "ACCESS ALLOWED", " %s:%*s%s\n",
1178 func, (int)(10 - strlen(func)), "", absolute_path);
1179 } else if ((1 == debug) && (0 == sbcontext->show_access_violation)) {
1180 EWARN(color, "ACCESS PREDICTED", " %s:%*s%s\n",
1181 func, (int)(10 - strlen(func)), "", absolute_path);
1182 }
1183
1184 if ((NULL != log_path) &&
1185 (((0 == debug) && (0 == result) && (1 == sbcontext->show_access_violation)) ||
1186 (1 == debug))) {
1187 if (0 != strncmp(absolute_path, resolved_path, strlen(absolute_path))) {
1188 sprintf(buffer, "%s:%*s%s (symlink to %s)\n", func,
1189 (int)(10 - strlen(func)), "",
1190 absolute_path, resolved_path);
1191 } else {
1192 sprintf(buffer, "%s:%*s%s\n", func,
1193 (int)(10 - strlen(func)), "",
1194 absolute_path);
1195 }
1196 if ((0 == lstat(log_path, &log_stat)) &&
1197 (0 == S_ISREG(log_stat.st_mode))) {
1198 EERROR(color, "SECURITY BREACH", " '%s' %s\n", log_path,
1199 "already exists and is not a regular file!");
1200 } else {
1201 check_dlsym(open);
1202 log_file = true_open(log_path, O_APPEND | O_WRONLY |
1203 O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP |
1204 S_IROTH);
1205 if (log_file >= 0) {
1206 write(log_file, buffer, strlen(buffer));
1207 close(log_file);
1208 }
1209 }
1210 }
1211
1212 if (NULL != absolute_path)
1213 free(absolute_path);
1214 if (NULL != resolved_path)
1215 free(resolved_path);
1216
1217 errno = old_errno;
1218
1219 return result;
1220
1221 fp_error:
1222 if (NULL != absolute_path)
1223 free(absolute_path);
1224 if (NULL != resolved_path)
1225 free(resolved_path);
1226
1227 /* The path is too long to be canonicalized, so just warn and let the
1228 * function handle it (see bug #94630 and #21766 for more info) */
1229 if (ENAMETOOLONG == errno) {
1230 if (0 == sb_path_size_warning) {
1231 fprintf(stderr, "\e[31;01mPATH LENGTH\033[0m %s:%*s%s\n",
1232 func, (int)(10 - strlen(func)), "", file);
1233 sb_path_size_warning = 1;
1234 }
1235
1236 return 1;
1237 }
1238
1239 return 0;
1240 }
1241
1242 static int is_sandbox_on()
1243 {
1244 int old_errno = errno;
1245
1246 /* $SANDBOX_ACTIVE is an env variable that should ONLY
1247 * be used internal by sandbox.c and libsanbox.c. External
1248 * sources should NEVER set it, else the sandbox is enabled
1249 * in some cases when run in parallel with another sandbox,
1250 * but not even in the sandbox shell.
1251 *
1252 * Azarah (3 Aug 2002)
1253 */
1254 if ((NULL != getenv("SANDBOX_ON")) &&
1255 (0 == strncmp(getenv("SANDBOX_ON"), "1", 1)) &&
1256 (NULL != getenv("SANDBOX_ACTIVE")) &&
1257 (0 == strncmp(getenv("SANDBOX_ACTIVE"), "armedandready", 13))) {
1258 errno = old_errno;
1259 return 1;
1260 } else {
1261 errno = old_errno;
1262 return 0;
1263 }
1264 }
1265
1266 static int before_syscall(const char *func, const char *file)
1267 {
1268 int old_errno = errno;
1269 int result = 1;
1270 // static sbcontext_t sbcontext;
1271 char *deny = getenv("SANDBOX_DENY");
1272 char *read = getenv("SANDBOX_READ");
1273 char *write = getenv("SANDBOX_WRITE");
1274 char *predict = getenv("SANDBOX_PREDICT");
1275
1276 if (NULL == file || 0 == strlen(file)) {
1277 /* The file/directory does not exist */
1278 errno = ENOENT;
1279 return 0;
1280 }
1281
1282 if(sb_init == 0) {
1283 init_context(&sbcontext);
1284 cached_env_vars = malloc(sizeof(char *)*4);
1285 cached_env_vars[0] = cached_env_vars[1] = cached_env_vars[2] = cached_env_vars[3] = NULL;
1286 sb_init=1;
1287 }
1288
1289 if((deny == NULL && cached_env_vars[0] != deny) || cached_env_vars[0] == NULL ||
1290 strcmp(cached_env_vars[0], deny) != 0) {
1291
1292 clean_env_entries(&(sbcontext.deny_prefixes),
1293 &(sbcontext.num_deny_prefixes));
1294
1295 if(NULL != cached_env_vars[0])
1296 free(cached_env_vars[0]);
1297
1298 if(NULL != deny) {
1299 init_env_entries(&(sbcontext.deny_prefixes),
1300 &(sbcontext.num_deny_prefixes), "SANDBOX_DENY", deny, 1);
1301 cached_env_vars[0] = strdup(deny);
1302 } else {
1303 cached_env_vars[0] = NULL;
1304 }
1305 }
1306
1307 if((read == NULL && cached_env_vars[1] != read) || cached_env_vars[1] == NULL ||
1308 strcmp(cached_env_vars[1], read) != 0) {
1309
1310 clean_env_entries(&(sbcontext.read_prefixes),
1311 &(sbcontext.num_read_prefixes));
1312
1313 if(NULL != cached_env_vars[1])
1314 free(cached_env_vars[1]);
1315
1316 if(NULL != read) {
1317 init_env_entries(&(sbcontext.read_prefixes),
1318 &(sbcontext.num_read_prefixes), "SANDBOX_READ", read, 1);
1319 cached_env_vars[1] = strdup(read);
1320 } else {
1321 cached_env_vars[1] = NULL;
1322 }
1323 }
1324
1325 if((write == NULL && cached_env_vars[2] != write) || cached_env_vars[2] == NULL ||
1326 strcmp(cached_env_vars[2], write) != 0) {
1327
1328 clean_env_entries(&(sbcontext.write_prefixes),
1329 &(sbcontext.num_write_prefixes));
1330
1331 if(NULL != cached_env_vars[2])
1332 free(cached_env_vars[2]);
1333
1334 if(NULL != write) {
1335 init_env_entries(&(sbcontext.write_prefixes),
1336 &(sbcontext.num_write_prefixes), "SANDBOX_WRITE", write, 1);
1337 cached_env_vars[2] = strdup(write);
1338 } else {
1339 cached_env_vars[2] = NULL;
1340 }
1341 }
1342
1343 if((predict == NULL && cached_env_vars[3] != predict) || cached_env_vars[3] == NULL ||
1344 strcmp(cached_env_vars[3], predict) != 0) {
1345
1346 clean_env_entries(&(sbcontext.predict_prefixes),
1347 &(sbcontext.num_predict_prefixes));
1348
1349 if(NULL != cached_env_vars[3])
1350 free(cached_env_vars[3]);
1351
1352 if(NULL != predict) {
1353 init_env_entries(&(sbcontext.predict_prefixes),
1354 &(sbcontext.num_predict_prefixes), "SANDBOX_PREDICT", predict, 1);
1355 cached_env_vars[3] = strdup(predict);
1356 } else {
1357 cached_env_vars[3] = NULL;
1358 }
1359
1360 }
1361
1362 /* Might have been reset in check_access() */
1363 sbcontext.show_access_violation = 1;
1364
1365 result = check_syscall(&sbcontext, func, file);
1366
1367 errno = old_errno;
1368
1369 if (0 == result) {
1370 errno = EACCES;
1371 }
1372
1373 return result;
1374 }
1375
1376 static int before_syscall_access(const char *func, const char *file, int flags)
1377 {
1378 if (flags & W_OK) {
1379 return before_syscall("access_wr", file);
1380 } else {
1381 return before_syscall("access_rd", file);
1382 }
1383 }
1384
1385 static int before_syscall_open_int(const char *func, const char *file, int flags)
1386 {
1387 if ((flags & O_WRONLY) || (flags & O_RDWR)) {
1388 return before_syscall("open_wr", file);
1389 } else {
1390 return before_syscall("open_rd", file);
1391 }
1392 }
1393
1394 static int before_syscall_open_char(const char *func, const char *file, const char *mode)
1395 {
1396 if (*mode == 'r' && ((strcmp(mode, "r") == 0) ||
1397 /* The strspn accept args are known non-writable modifiers */
1398 (strlen(++mode) == strspn(mode, "xbtmc")))) {
1399 return before_syscall("open_rd", file);
1400 } else {
1401 return before_syscall("open_wr", file);
1402 }
1403 }
1404
1405 #include "getcwd.c"
1406 #include "canonicalize.c"
1407 #include "sandbox_futils.c"
1408
1409 // 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