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

Contents of /trunk/src/libsandbox.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 263 - (show annotations) (download) (as text)
Tue Jul 4 14:58:16 2006 UTC (8 years, 1 month ago) by azarah
File MIME type: text/x-csrc
File size: 44596 byte(s)
Add is_env_off().  Also check for true/false. Use is_env_*.

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 memory debugging. */
33 /* #define SB_MEM_DEBUG 1 */
34
35 #define open xxx_open
36 #define open64 xxx_open64
37
38 #include "config.h"
39
40 /* Better way would be to only define _GNU_SOURCE when __GLIBC__ is defined,
41 * but including features.h and then defining _GNU_SOURCE do not work */
42 #if defined(HAVE_RTLD_NEXT)
43 # define _GNU_SOURCE
44 #endif
45 #include <dirent.h>
46 #include <dlfcn.h>
47 #include <errno.h>
48 #include <fcntl.h>
49 #include <libgen.h>
50 #include <stdarg.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <sys/file.h>
55 #include <sys/stat.h>
56 #include <sys/types.h>
57 #include <sys/param.h>
58 #include <signal.h>
59 #include <unistd.h>
60 #include <utime.h>
61
62 #include "localdecls.h"
63
64 #if !defined(BROKEN_RTLD_NEXT) && defined(HAVE_RTLD_NEXT)
65 # define USE_RTLD_NEXT
66 #endif
67
68 #ifdef SB_MEM_DEBUG
69 # include <mcheck.h>
70 #endif
71
72 #undef open
73 #undef open64
74
75 #include "sandbox.h"
76
77 #define LOG_VERSION "1.0"
78 #define LOG_STRING "VERSION " LOG_VERSION "\n"
79 #define LOG_FMT_FUNC "FORMAT: F - Function called\n"
80 #define LOG_FMT_ACCESS "FORMAT: S - Access Status\n"
81 #define LOG_FMT_PATH "FORMAT: P - Path as passed to function\n"
82 #define LOG_FMT_APATH "FORMAT: A - Absolute Path (not canonical)\n"
83 #define LOG_FMT_RPATH "FORMAT: R - Canonical Path\n"
84 #define LOG_FMT_CMDLINE "FORMAT: C - Command Line\n"
85
86 /* Macros to check if a function should be executed */
87 #define FUNCTION_SANDBOX_SAFE(_func, _path) \
88 ((0 == is_sandbox_on()) || (1 == before_syscall(_func, _path)))
89
90 #define FUNCTION_SANDBOX_SAFE_ACCESS(_func, _path, _flags) \
91 ((0 == is_sandbox_on()) || (1 == before_syscall_access(_func, _path, _flags)))
92
93 #define FUNCTION_SANDBOX_SAFE_OPEN_INT(_func, _path, _flags) \
94 ((0 == is_sandbox_on()) || (1 == before_syscall_open_int(_func, _path, _flags)))
95
96 #define FUNCTION_SANDBOX_SAFE_OPEN_CHAR(_func, _path, _mode) \
97 ((0 == is_sandbox_on()) || (1 == before_syscall_open_char(_func, _path, _mode)))
98
99 /* Macro to check if a wrapper is defined, if not
100 * then try to resolve it again. */
101 #define check_dlsym(_name) \
102 { \
103 int old_errno = errno; \
104 if (!true_ ## _name) \
105 true_ ## _name = get_dlsym(symname_ ## _name, symver_ ## _name); \
106 errno = old_errno; \
107 }
108
109 static char sandbox_lib[SB_PATH_MAX];
110
111 typedef struct {
112 int show_access_violation;
113 char **deny_prefixes;
114 int num_deny_prefixes;
115 char **read_prefixes;
116 int num_read_prefixes;
117 char **write_prefixes;
118 int num_write_prefixes;
119 char **predict_prefixes;
120 int num_predict_prefixes;
121 char **write_denied_prefixes;
122 int num_write_denied_prefixes;
123 } sbcontext_t;
124
125 static sbcontext_t sbcontext;
126 static char **cached_env_vars;
127 int sandbox_on = 1;
128 static int sb_init = 0;
129 static int sb_path_size_warning = 0;
130
131 void __attribute__ ((constructor)) libsb_init(void);
132 void __attribute__ ((destructor)) libsb_fini(void);
133
134 static void *get_dlsym(const char *, const char *);
135 static int canonicalize(const char *, char *);
136 static char *resolve_path(const char *, int);
137 /* From procps */
138 static char** file2strvec(const char *, const char *);
139 static int write_logfile(const char *, const char *, const char *,
140 const char *, const char *, bool, bool);
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 /*
154 * Initialize the shabang
155 */
156
157 static void *libc_handle = NULL;
158
159 void __attribute__ ((destructor)) libsb_fini(void)
160 {
161 int x;
162
163 sb_init = 0;
164
165 if(NULL != cached_env_vars) {
166 for(x=0; x < 4; x++) {
167 if(NULL != cached_env_vars[x]) {
168 free(cached_env_vars[x]);
169 cached_env_vars[x] = NULL;
170 }
171 }
172 free(cached_env_vars);
173 cached_env_vars = NULL;
174 }
175
176 clean_env_entries(&(sbcontext.deny_prefixes),
177 &(sbcontext.num_deny_prefixes));
178 clean_env_entries(&(sbcontext.read_prefixes),
179 &(sbcontext.num_read_prefixes));
180 clean_env_entries(&(sbcontext.write_prefixes),
181 &(sbcontext.num_write_prefixes));
182 clean_env_entries(&(sbcontext.predict_prefixes),
183 &(sbcontext.num_predict_prefixes));
184 }
185
186 void __attribute__ ((constructor)) libsb_init(void)
187 {
188 int old_errno = errno;
189
190 #ifdef SB_MEM_DEBUG
191 mtrace();
192 #endif
193
194 /* Get the path and name to this library */
195 get_sandbox_lib(sandbox_lib);
196
197 // sb_init = 1;
198
199 errno = old_errno;
200 }
201
202 static void *get_dlsym(const char *symname, const char *symver)
203 {
204 void *symaddr = NULL;
205
206 if (NULL == libc_handle) {
207 #if !defined(USE_RTLD_NEXT)
208 libc_handle = dlopen(LIBC_VERSION, RTLD_LAZY);
209 if (!libc_handle) {
210 fprintf(stderr, "libsandbox: Can't dlopen libc: %s\n",
211 dlerror());
212 exit(EXIT_FAILURE);
213 }
214 #else
215 libc_handle = RTLD_NEXT;
216 #endif
217 }
218
219 if (NULL == symver)
220 symaddr = dlsym(libc_handle, symname);
221 else
222 symaddr = dlvsym(libc_handle, symname, symver);
223 if (!symaddr) {
224 fprintf(stderr, "libsandbox: Can't resolve %s: %s\n",
225 symname, dlerror());
226 exit(EXIT_FAILURE);
227 }
228
229 return symaddr;
230 }
231
232 static int canonicalize(const char *path, char *resolved_path)
233 {
234 int old_errno = errno;
235 char *retval;
236
237 *resolved_path = '\0';
238
239 /* If path == NULL, return or we get a segfault */
240 if (NULL == path) {
241 errno = EINVAL;
242 return -1;
243 }
244
245 /* Do not try to resolve an empty path */
246 if ('\0' == path[0]) {
247 errno = old_errno;
248 return 0;
249 }
250
251 retval = erealpath(path, resolved_path);
252
253 if ((NULL == retval) && (path[0] != '/')) {
254 /* The path could not be canonicalized, append it
255 * to the current working directory if it was not
256 * an absolute path
257 */
258
259 if (ENAMETOOLONG == errno)
260 return -1;
261
262 if (NULL == egetcwd(resolved_path, SB_PATH_MAX - 2))
263 return -1;
264 snprintf((char *)(resolved_path + strlen(resolved_path)),
265 SB_PATH_MAX - strlen(resolved_path), "/%s", path);
266
267 if (NULL == erealpath(resolved_path, resolved_path)) {
268 if (errno == ENAMETOOLONG) {
269 /* The resolved path is too long for the buffer to hold */
270 return -1;
271 } else {
272 /* Whatever it resolved, is not a valid path */
273 errno = ENOENT;
274 return -1;
275 }
276 }
277
278 } else if ((NULL == retval) && (path[0] == '/')) {
279 /* Whatever it resolved, is not a valid path */
280 errno = ENOENT;
281 return -1;
282 }
283
284 errno = old_errno;
285 return 0;
286 }
287
288 static char *resolve_path(const char *path, int follow_link)
289 {
290 int old_errno = errno;
291 char tmp_str1[SB_PATH_MAX], tmp_str2[SB_PATH_MAX];
292 char *dname, *bname;
293 char *filtered_path;
294
295 if (NULL == path)
296 return NULL;
297
298 filtered_path = malloc(SB_PATH_MAX * sizeof(char));
299 if (NULL == filtered_path)
300 return NULL;
301
302 if (0 == follow_link) {
303 if (-1 == canonicalize(path, filtered_path))
304 return NULL;
305 } else {
306 /* Basically we get the realpath which should resolve symlinks,
307 * etc. If that fails (might not exist), we try to get the
308 * realpath of the parent directory, as that should hopefully
309 * exist. If all else fails, just go with canonicalize */
310 if (NULL == realpath(path, filtered_path)) {
311 snprintf(tmp_str1, SB_PATH_MAX, "%s", path);
312
313 dname = dirname(tmp_str1);
314
315 /* If not, then check if we can resolve the
316 * parent directory */
317 if (NULL == realpath(dname, filtered_path)) {
318 /* Fall back to canonicalize */
319 if (-1 == canonicalize(path, filtered_path))
320 return NULL;
321 } else {
322 /* OK, now add the basename to keep our access
323 * checking happy (don't want '/usr/lib' if we
324 * tried to do something with non-existing
325 * file '/usr/lib/cf*' ...) */
326 snprintf(tmp_str2, SB_PATH_MAX, "%s", path);
327
328 bname = gbasename(tmp_str2);
329 snprintf((char *)(filtered_path + strlen(filtered_path)),
330 SB_PATH_MAX - strlen(filtered_path), "%s%s",
331 (filtered_path[strlen(filtered_path) - 1] != '/') ? "/" : "",
332 bname);
333 }
334 }
335 }
336
337 errno = old_errno;
338
339 return filtered_path;
340 }
341
342 /*
343 * Wrapper Functions
344 */
345
346 #define chmod_decl(_name) \
347 \
348 extern int _name(const char *, mode_t); \
349 static int (*true_ ## _name) (const char *, mode_t) = NULL; \
350 \
351 int _name(const char *path, mode_t mode) \
352 { \
353 int result = -1; \
354 \
355 if FUNCTION_SANDBOX_SAFE("chmod", path) { \
356 check_dlsym(_name); \
357 result = true_ ## _name(path, mode); \
358 } \
359 \
360 return result; \
361 }
362
363 #define chown_decl(_name) \
364 \
365 extern int _name(const char *, uid_t, gid_t); \
366 static int (*true_ ## _name) (const char *, uid_t, gid_t) = NULL; \
367 \
368 int _name(const char *path, uid_t owner, gid_t group) \
369 { \
370 int result = -1; \
371 \
372 if FUNCTION_SANDBOX_SAFE("chown", path) { \
373 check_dlsym(_name); \
374 result = true_ ## _name(path, owner, group); \
375 } \
376 \
377 return result; \
378 }
379
380 #define creat_decl(_name) \
381 \
382 extern int _name(const char *, mode_t); \
383 /* static int (*true_ ## _name) (const char *, mode_t) = NULL; */ \
384 \
385 int _name(const char *pathname, mode_t mode) \
386 { \
387 int result = -1; \
388 \
389 if FUNCTION_SANDBOX_SAFE("creat", pathname) { \
390 check_dlsym(open_DEFAULT); \
391 result = true_open_DEFAULT(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode); \
392 } \
393 \
394 return result; \
395 }
396
397 #define fopen_decl(_name) \
398 \
399 extern FILE *_name(const char *, const char *); \
400 static FILE * (*true_ ## _name) (const char *, const char *) = NULL; \
401 \
402 FILE *_name(const char *pathname, const char *mode) \
403 { \
404 FILE *result = NULL; \
405 \
406 if FUNCTION_SANDBOX_SAFE_OPEN_CHAR("fopen", pathname, mode) { \
407 check_dlsym(_name); \
408 result = true_ ## _name(pathname, mode); \
409 } \
410 \
411 return result; \
412 }
413
414 #define lchown_decl(_name) \
415 \
416 extern int _name(const char *, uid_t, gid_t); \
417 static int (*true_ ## _name) (const char *, uid_t, gid_t) = NULL; \
418 \
419 int _name(const char *path, uid_t owner, gid_t group) \
420 { \
421 int result = -1; \
422 \
423 if FUNCTION_SANDBOX_SAFE("lchown", path) { \
424 check_dlsym(_name); \
425 result = true_ ## _name(path, owner, group); \
426 } \
427 \
428 return result; \
429 }
430
431 #define link_decl(_name) \
432 \
433 extern int _name(const char *, const char *); \
434 static int (*true_ ## _name) (const char *, const char *) = NULL; \
435 \
436 int _name(const char *oldpath, const char *newpath) \
437 { \
438 int result = -1; \
439 \
440 if FUNCTION_SANDBOX_SAFE("link", newpath) { \
441 check_dlsym(_name); \
442 result = true_ ## _name(oldpath, newpath); \
443 } \
444 \
445 return result; \
446 }
447
448 #define mkdir_decl(_name) \
449 \
450 extern int _name(const char *, mode_t); \
451 static int (*true_ ## _name) (const char *, mode_t) = NULL; \
452 \
453 int _name(const char *pathname, mode_t mode) \
454 { \
455 struct stat st; \
456 int result = -1, my_errno = errno; \
457 char canonic[SB_PATH_MAX]; \
458 \
459 if (-1 == canonicalize(pathname, canonic)) \
460 /* Path is too long to canonicalize, do not fail, but just let
461 * the real function handle it (see bug #94630 and #21766). */ \
462 if (ENAMETOOLONG != errno) \
463 return -1; \
464 \
465 /* XXX: Hack to prevent errors if the directory exist,
466 * and are not writable - we rather return EEXIST rather
467 * than failing */ \
468 if (0 == lstat(canonic, &st)) { \
469 errno = EEXIST; \
470 return -1; \
471 } \
472 errno = my_errno; \
473 \
474 if FUNCTION_SANDBOX_SAFE("mkdir", pathname) { \
475 check_dlsym(_name); \
476 result = true_ ## _name(pathname, mode); \
477 } \
478 \
479 return result; \
480 }
481
482 #define opendir_decl(_name) \
483 \
484 extern DIR *_name(const char *); \
485 static DIR * (*true_ ## _name) (const char *) = NULL; \
486 \
487 DIR *_name(const char *name) \
488 { \
489 DIR *result = NULL; \
490 \
491 if FUNCTION_SANDBOX_SAFE("opendir", name) { \
492 check_dlsym(_name); \
493 result = true_ ## _name(name); \
494 } \
495 \
496 return result; \
497 }
498
499 #define mknod_decl(_name) \
500 \
501 extern int _name(const char *, mode_t, dev_t); \
502 static int (*true_ ## _name) (const char *, mode_t, dev_t) = NULL; \
503 \
504 int _name(const char *pathname, mode_t mode, dev_t dev) \
505 { \
506 int result = -1; \
507 \
508 if FUNCTION_SANDBOX_SAFE("mknod", pathname) { \
509 check_dlsym(_name); \
510 result = true_ ## _name(pathname, mode, dev); \
511 } \
512 \
513 return result; \
514 }
515
516 #define __xmknod_decl(_name) \
517 \
518 extern int _name(int, const char *, __mode_t, __dev_t *); \
519 static int (*true_ ## _name) (int, const char *, __mode_t, __dev_t *) = NULL; \
520 \
521 int _name(int ver, const char *pathname, __mode_t mode, __dev_t *dev) \
522 { \
523 int result = -1; \
524 \
525 if FUNCTION_SANDBOX_SAFE("mknod", pathname) { \
526 check_dlsym(_name); \
527 result = true_ ## _name(ver, pathname, mode, dev); \
528 } \
529 \
530 return result; \
531 }
532
533 #define mkfifo_decl(_name) \
534 \
535 extern int _name(const char *, mode_t); \
536 static int (*true_ ## _name) (const char *, mode_t) = NULL; \
537 \
538 int _name(const char *pathname, mode_t mode) \
539 { \
540 int result = -1; \
541 \
542 if FUNCTION_SANDBOX_SAFE("mkfifo", pathname) { \
543 check_dlsym(_name); \
544 result = true_ ## _name(pathname, mode); \
545 } \
546 \
547 return result; \
548 }
549
550 #define access_decl(_name) \
551 \
552 extern int _name(const char *, int); \
553 static int (*true_ ## _name) (const char *, int) = NULL; \
554 \
555 int _name(const char *pathname, int mode) \
556 { \
557 int result = -1; \
558 \
559 if FUNCTION_SANDBOX_SAFE_ACCESS("access", pathname, mode) { \
560 check_dlsym(_name); \
561 result = true_ ## _name(pathname, mode); \
562 } \
563 \
564 return result; \
565 }
566
567 #define open_decl(_name) \
568 \
569 extern int _name(const char *, int, ...); \
570 static int (*true_ ## _name) (const char *, int, ...) = NULL; \
571 \
572 /* Eventually, there is a third parameter: it's mode_t mode */ \
573 int _name(const char *pathname, int flags, ...) \
574 { \
575 va_list ap; \
576 int mode = 0; \
577 int result = -1; \
578 \
579 if (flags & O_CREAT) { \
580 va_start(ap, flags); \
581 mode = va_arg(ap, int); \
582 va_end(ap); \
583 } \
584 \
585 if FUNCTION_SANDBOX_SAFE_OPEN_INT("open", pathname, flags) { \
586 check_dlsym(_name); \
587 if (flags & O_CREAT) \
588 result = true_ ## _name(pathname, flags, mode); \
589 else \
590 result = true_ ## _name(pathname, flags); \
591 } \
592 \
593 return result; \
594 }
595
596 #define rename_decl(_name) \
597 \
598 extern int _name(const char *, const char *); \
599 static int (*true_ ## _name) (const char *, const char *) = NULL; \
600 \
601 int _name(const char *oldpath, const char *newpath) \
602 { \
603 int result = -1; \
604 \
605 if (FUNCTION_SANDBOX_SAFE("rename", oldpath) && \
606 FUNCTION_SANDBOX_SAFE("rename", newpath)) { \
607 check_dlsym(_name); \
608 result = true_ ## _name(oldpath, newpath); \
609 } \
610 \
611 return result; \
612 }
613
614 #define rmdir_decl(_name) \
615 \
616 extern int _name(const char *); \
617 static int (*true_ ## _name) (const char *) = NULL; \
618 \
619 int _name(const char *pathname) \
620 { \
621 int result = -1; \
622 \
623 if FUNCTION_SANDBOX_SAFE("rmdir", pathname) { \
624 check_dlsym(_name); \
625 result = true_ ## _name(pathname); \
626 } \
627 \
628 return result; \
629 }
630
631 #define symlink_decl(_name) \
632 \
633 extern int _name(const char *, const char *); \
634 static int (*true_ ## _name) (const char *, const char *) = NULL; \
635 \
636 int _name(const char *oldpath, const char *newpath) \
637 { \
638 int result = -1; \
639 \
640 if FUNCTION_SANDBOX_SAFE("symlink", newpath) { \
641 check_dlsym(_name); \
642 result = true_ ## _name(oldpath, newpath); \
643 } \
644 \
645 return result; \
646 }
647
648 #define truncate_decl(_name) \
649 \
650 extern int _name(const char *, TRUNCATE_T); \
651 static int (*true_ ## _name) (const char *, TRUNCATE_T) = NULL; \
652 \
653 int _name(const char *path, TRUNCATE_T length) \
654 { \
655 int result = -1; \
656 \
657 if FUNCTION_SANDBOX_SAFE("truncate", path) { \
658 check_dlsym(_name); \
659 result = true_ ## _name(path, length); \
660 } \
661 \
662 return result; \
663 }
664
665 #define unlink_decl(_name) \
666 \
667 extern int _name(const char *); \
668 static int (*true_ ## _name) (const char *) = NULL; \
669 \
670 int _name(const char *pathname) \
671 { \
672 int result = -1; \
673 char canonic[SB_PATH_MAX]; \
674 \
675 if (-1 == canonicalize(pathname, canonic)) \
676 /* Path is too long to canonicalize, do not fail, but just let
677 * the real function handle it (see bug #94630 and #21766). */ \
678 if (ENAMETOOLONG != errno) \
679 return -1; \
680 \
681 /* XXX: Hack to make sure sandboxed process cannot remove
682 * a device node, bug #79836. */ \
683 if ((0 == strncmp(canonic, "/dev/null", 9)) || \
684 (0 == strncmp(canonic, "/dev/zero", 9))) { \
685 errno = EACCES; \
686 return result; \
687 } \
688 \
689 if FUNCTION_SANDBOX_SAFE("unlink", pathname) { \
690 check_dlsym(_name); \
691 result = true_ ## _name(pathname); \
692 } \
693 \
694 return result; \
695 }
696
697 #define getcwd_decl(_name) \
698 \
699 extern char *_name(char *, size_t); \
700 static char * (*true_ ## _name) (char *, size_t) = NULL; \
701 \
702 char *_name(char *buf, size_t size) \
703 { \
704 char *result = NULL; \
705 \
706 /* Need to disable sandbox, as on non-linux libc's, opendir() is
707 * used by some getcwd() implementations and resolves to the sandbox
708 * opendir() wrapper, causing infinit recursion and finially crashes.
709 */ \
710 sandbox_on = 0; \
711 check_dlsym(_name); \
712 result = true_ ## _name(buf, size); \
713 sandbox_on = 1; \
714 \
715 return result; \
716 }
717
718 #define creat64_decl(_name) \
719 \
720 extern int _name(const char *, __mode_t); \
721 /* static int (*true_ ## _name) (const char *, __mode_t) = NULL; */ \
722 \
723 int _name(const char *pathname, __mode_t mode) \
724 { \
725 int result = -1; \
726 \
727 if FUNCTION_SANDBOX_SAFE("creat64", pathname) { \
728 check_dlsym(open64_DEFAULT); \
729 result = true_open64_DEFAULT(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode); \
730 } \
731 \
732 return result; \
733 }
734
735 #define fopen64_decl(_name) \
736 \
737 extern FILE *_name(const char *, const char *); \
738 static FILE * (*true_ ## _name) (const char *, const char *) = NULL; \
739 \
740 FILE *_name(const char *pathname, const char *mode) \
741 { \
742 FILE *result = NULL; \
743 \
744 if FUNCTION_SANDBOX_SAFE_OPEN_CHAR("fopen64", pathname, mode) { \
745 check_dlsym(_name); \
746 result = true_ ## _name(pathname, mode); \
747 } \
748 \
749 return result; \
750 }
751
752 #define open64_decl(_name) \
753 \
754 extern int _name(const char *, int, ...); \
755 static int (*true_ ## _name) (const char *, int, ...) = NULL; \
756 \
757 /* Eventually, there is a third parameter: it's mode_t mode */ \
758 int _name(const char *pathname, int flags, ...) \
759 { \
760 va_list ap; \
761 int mode = 0; \
762 int result = -1; \
763 \
764 if (flags & O_CREAT) { \
765 va_start(ap, flags); \
766 mode = va_arg(ap, int); \
767 va_end(ap); \
768 } \
769 \
770 if FUNCTION_SANDBOX_SAFE_OPEN_INT("open64", pathname, flags) { \
771 check_dlsym(_name); \
772 if (flags & O_CREAT) \
773 result = true_ ## _name(pathname, flags, mode); \
774 else \
775 result = true_ ## _name(pathname, flags); \
776 } \
777 \
778 return result; \
779 }
780
781 #define truncate64_decl(_name) \
782 \
783 extern int _name(const char *, __off64_t); \
784 static int (*true_ ## _name) (const char *, __off64_t) = NULL; \
785 \
786 int _name(const char *path, __off64_t length) \
787 { \
788 int result = -1; \
789 \
790 if FUNCTION_SANDBOX_SAFE("truncate64", path) { \
791 check_dlsym(_name); \
792 result = true_ ## _name(path, length); \
793 } \
794 \
795 return result; \
796 }
797
798 /*
799 * Exec Wrappers
800 */
801
802 #define execve_decl(_name) \
803 \
804 extern int _name(const char *, char *const[], char *const[]); \
805 static int (*true_ ## _name) (const char *, char *const[], char *const[]) = NULL; \
806 \
807 int _name(const char *filename, char *const argv[], char *const envp[]) \
808 { \
809 int old_errno = errno; \
810 int result = -1; \
811 int count = 0; \
812 int env_len = 0; \
813 char **my_env = NULL; \
814 int kill_env = 1; \
815 /* We limit the size LD_PRELOAD can be here, but it should be enough */ \
816 char tmp_str[SB_BUF_LEN]; \
817 \
818 if FUNCTION_SANDBOX_SAFE("execve", filename) { \
819 while (envp[count] != NULL) { \
820 /* Check if we do not have to do anything */ \
821 if (strstr(envp[count], LD_PRELOAD_EQ) == envp[count]) { \
822 if (NULL != strstr(envp[count], sandbox_lib)) { \
823 my_env = (char **)envp; \
824 kill_env = 0; \
825 goto end_loop; \
826 } \
827 } \
828 \
829 /* If LD_PRELOAD is set and sandbox_lib not in it */ \
830 if (((strstr(envp[count], LD_PRELOAD_EQ) == envp[count]) && \
831 (NULL == strstr(envp[count], sandbox_lib))) || \
832 /* Or LD_PRELOAD is not set, and this is the last loop */ \
833 ((strstr(envp[count], LD_PRELOAD_EQ) != envp[count]) && \
834 (NULL == envp[count + 1]))) { \
835 int i = 0; \
836 int add_ldpreload = 0; \
837 const int max_envp_len = strlen(envp[count]) + strlen(sandbox_lib) + 1; \
838 \
839 /* Fail safe ... */ \
840 if (max_envp_len > SB_BUF_LEN) { \
841 fprintf(stderr, "libsandbox: max_envp_len too big!\n"); \
842 errno = ENOMEM; \
843 return result; \
844 } \
845 \
846 /* Calculate envp size */ \
847 my_env = (char **)envp; \
848 do \
849 env_len++; \
850 while (NULL != *my_env++); \
851 \
852 /* Should we add LD_PRELOAD ? */ \
853 if (strstr(envp[count], LD_PRELOAD_EQ) != envp[count]) \
854 add_ldpreload = 1; \
855 \
856 my_env = (char **)calloc(env_len + add_ldpreload, sizeof(char *)); \
857 if (NULL == my_env) { \
858 errno = ENOMEM; \
859 return result; \
860 } \
861 /* Copy envp to my_env */ \
862 do \
863 /* Leave a space for LD_PRELOAD if needed */ \
864 my_env[i + add_ldpreload] = envp[i]; \
865 while (NULL != envp[i++]); \
866 \
867 /* Add 'LD_PRELOAD=' to the beginning of our new string */ \
868 snprintf(tmp_str, max_envp_len, "%s%s", LD_PRELOAD_EQ, sandbox_lib); \
869 \
870 /* LD_PRELOAD already have variables other than sandbox_lib,
871 * thus we have to add sandbox_lib seperated via a whitespace. */ \
872 if (0 == add_ldpreload) { \
873 snprintf((char *)(tmp_str + strlen(tmp_str)), \
874 max_envp_len - strlen(tmp_str) + 1, " %s", \
875 (char *)(envp[count] + strlen(LD_PRELOAD_EQ))); \
876 } \
877 \
878 /* Valid string? */ \
879 tmp_str[max_envp_len] = '\0'; \
880 \
881 /* Ok, replace my_env[count] with our version that contains
882 * sandbox_lib ... */ \
883 if (1 == add_ldpreload) \
884 /* We reserved a space for LD_PRELOAD above */ \
885 my_env[0] = tmp_str; \
886 else \
887 my_env[count] = tmp_str; \
888 \
889 goto end_loop; \
890 } \
891 count++; \
892 } \
893 \
894 end_loop: \
895 errno = old_errno; \
896 check_dlsym(_name); \
897 result = true_ ## _name(filename, argv, my_env); \
898 old_errno = errno; \
899 \
900 if (my_env && kill_env) \
901 free(my_env); \
902 } \
903 \
904 errno = old_errno; \
905 \
906 return result; \
907 }
908
909 #include "symbols.h"
910
911 /*
912 * Internal Functions
913 */
914
915 char *egetcwd(char *buf, size_t size)
916 {
917 struct stat st;
918 char *tmpbuf, *oldbuf = buf;
919 int old_errno;
920
921 /* Need to disable sandbox, as on non-linux libc's, opendir() is
922 * used by some getcwd() implementations and resolves to the sandbox
923 * opendir() wrapper, causing infinit recursion and finially crashes.
924 */
925 sandbox_on = 0;
926 check_dlsym(getcwd_DEFAULT);
927 errno = 0;
928 tmpbuf = true_getcwd_DEFAULT(buf, size);
929 sandbox_on = 1;
930
931 /* We basically try to figure out if we can trust what getcwd()
932 * returned. If one of the following happens kernel/libc side,
933 * bad things will happen, but not much we can do about it:
934 * - Invalid pointer with errno = 0
935 * - Truncated path with errno = 0
936 * - Whatever I forgot about
937 */
938 if ((tmpbuf) && (errno == 0)) {
939 old_errno = errno;
940 lstat(buf, &st);
941
942 if (errno == ENOENT) {
943 /* If getcwd() allocated the buffer, free it. */
944 if (NULL == oldbuf)
945 free(tmpbuf);
946
947 /* If lstat() failed with eerror = ENOENT, then its
948 * possible that we are running on an older kernel
949 * which had issues with returning invalid paths if
950 * they got too long. Return with errno = ENAMETOOLONG,
951 * so that canonicalize() and check_syscall() know
952 * what the issue is.
953 */
954 errno = ENAMETOOLONG;
955 return NULL;
956 } else if (errno != 0) {
957 /* If getcwd() allocated the buffer, free it. */
958 if (NULL == oldbuf)
959 free(tmpbuf);
960
961 /* Not sure if we should quit here, but I guess if
962 * lstat() fails, getcwd could have messed up. Not
963 * sure what to do about errno - use lstat()'s for
964 * now.
965 */
966 return NULL;
967 }
968
969 errno = old_errno;
970 } else if (errno != 0) {
971
972 /* Make sure we do not return garbage if the current libc or
973 * kernel's getcwd() is buggy.
974 */
975 return NULL;
976 }
977
978 return tmpbuf;
979 }
980
981 /* From procps */
982 static char** file2strvec(const char *directory, const char *what)
983 {
984 char buf[2048]; /* read buf bytes at a time */
985 char *p, *rbuf = 0, *endbuf, **q, **ret;
986 int fd, tot = 0, n, c, end_of_file = 0;
987 int align;
988
989 sprintf(buf, "%s/%s", directory, what);
990 check_dlsym(open_DEFAULT);
991 fd = true_open_DEFAULT(buf, O_RDONLY, 0);
992 if (fd==-1)
993 return NULL;
994
995 /* read whole file into a memory buffer, allocating as we go */
996 while ((n = read(fd, buf, sizeof(buf - 1))) > 0) {
997 if (n < (int)(sizeof(buf - 1)))
998 end_of_file = 1;
999 if (n == 0 && rbuf == 0)
1000 return NULL; /* process died between our open and read */
1001 if (n < 0) {
1002 if (rbuf)
1003 free(rbuf);
1004 return NULL; /* read error */
1005 }
1006 if (end_of_file && buf[n - 1]) /* last read char not null */
1007 buf[n++] = '\0'; /* so append null-terminator */
1008 rbuf = realloc(rbuf, tot + n); /* allocate more memory */
1009 if (NULL == rbuf) {
1010 errno = ENOMEM;
1011 return NULL;
1012 }
1013 memcpy(rbuf + tot, buf, n); /* copy buffer into it */
1014 tot += n; /* increment total byte ctr */
1015 if (end_of_file)
1016 break;
1017 }
1018 close(fd);
1019 if (n <= 0 && !end_of_file) {
1020 if (rbuf)
1021 free(rbuf);
1022 return NULL; /* read error */
1023 }
1024 endbuf = rbuf + tot; /* count space for pointers */
1025 align = (sizeof(char*) - 1) - ((tot + sizeof(char*) - 1) & (sizeof(char*) - 1));
1026 for (c = 0, p = rbuf; p < endbuf; p++)
1027 if (!*p)
1028 c += sizeof(char*);
1029 c += sizeof(char*); /* one extra for NULL term */
1030
1031 rbuf = realloc(rbuf, tot + c + align); /* make room for ptrs AT END */
1032 if (NULL == rbuf) {
1033 errno = ENOMEM;
1034 return NULL;
1035 }
1036 endbuf = rbuf + tot; /* addr just past data buf */
1037 q = ret = (char**) (endbuf+align); /* ==> free(*ret) to dealloc */
1038 *q++ = p = rbuf; /* point ptrs to the strings */
1039 endbuf--; /* do not traverse final NUL */
1040 while (++p < endbuf)
1041 if (!*p) /* NUL char implies that */
1042 *q++ = p+1; /* next string -> next char */
1043
1044 *q = 0; /* null ptr list terminator */
1045 return ret;
1046 }
1047
1048 static int write_logfile(const char *logfile, const char *func, const char *path,
1049 const char *apath, const char *rpath, bool access,
1050 bool color)
1051 {
1052 struct stat log_stat;
1053 int stat_ret;
1054 int logfd;
1055
1056 stat_ret = lstat(logfile, &log_stat);
1057 if ((0 == stat_ret) &&
1058 (0 == S_ISREG(log_stat.st_mode))) {
1059 EERROR(color, "SECURITY BREACH", " '%s' %s\n", logfile,
1060 "already exists and is not a regular file!");
1061 } else {
1062 check_dlsym(open_DEFAULT);
1063 logfd = true_open_DEFAULT(logfile, O_APPEND | O_WRONLY |
1064 O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP |
1065 S_IROTH);
1066 if (logfd >= 0) {
1067 char **cmdline = NULL;
1068
1069 if (0 != stat_ret) {
1070 write(logfd, LOG_STRING, strlen(LOG_STRING));
1071 write(logfd, LOG_FMT_FUNC, strlen(LOG_FMT_FUNC));
1072 write(logfd, LOG_FMT_ACCESS, strlen(LOG_FMT_ACCESS));
1073 write(logfd, LOG_FMT_PATH, strlen(LOG_FMT_PATH));
1074 write(logfd, LOG_FMT_APATH, strlen(LOG_FMT_APATH));
1075 write(logfd, LOG_FMT_RPATH, strlen(LOG_FMT_RPATH));
1076 write(logfd, LOG_FMT_CMDLINE, strlen(LOG_FMT_CMDLINE));
1077 write(logfd, "\n", 1);
1078 } else {
1079 /* Already have data in the log, so add a newline to space the
1080 * log entries.
1081 */
1082 write(logfd, "\n", 1);
1083 }
1084
1085 write(logfd, "F: ", 3);
1086 write(logfd, func, strlen(func));
1087 write(logfd, "\n", 1);
1088 write(logfd, "S: ", 3);
1089 if (access)
1090 write(logfd, "allow", 5);
1091 else
1092 write(logfd, "deny", 4);
1093 write(logfd, "\n", 1);
1094 write(logfd, "P: ", 3);
1095 write(logfd, path, strlen(path));
1096 write(logfd, "\n", 1);
1097 write(logfd, "A: ", 3);
1098 write(logfd, apath, strlen(apath));
1099 write(logfd, "\n", 1);
1100 write(logfd, "R: ", 3);
1101 write(logfd, rpath, strlen(rpath));
1102 write(logfd, "\n", 1);
1103
1104 cmdline = file2strvec("/proc/self", "cmdline");
1105 if (NULL != cmdline) {
1106 int i = 0;
1107
1108 write(logfd, "C: ", 3);
1109 while (NULL != cmdline[i]) {
1110 write(logfd, cmdline[i], strlen(cmdline[i]));
1111 if (NULL != cmdline[i + 1])
1112 write(logfd, " ", 1);
1113 i++;
1114 }
1115 write(logfd, "\n", 1);
1116
1117 free(*cmdline);
1118 } else if (ENOMEM == errno) {
1119 EERROR(color, "OUT OF MEMORY", " %s\n", __FUNCTION__);
1120 return -1;
1121 }
1122
1123
1124 close(logfd);
1125 } else {
1126 EERROR(color, "OPEN LOG FILE", " %s\n", logfile);
1127 return -1;
1128 }
1129 }
1130
1131 return 0;
1132 }
1133
1134 static void init_context(sbcontext_t * context)
1135 {
1136 context->show_access_violation = 1;
1137 context->deny_prefixes = NULL;
1138 context->num_deny_prefixes = 0;
1139 context->read_prefixes = NULL;
1140 context->num_read_prefixes = 0;
1141 context->write_prefixes = NULL;
1142 context->num_write_prefixes = 0;
1143 context->predict_prefixes = NULL;
1144 context->num_predict_prefixes = 0;
1145 context->write_denied_prefixes = NULL;
1146 context->num_write_denied_prefixes = 0;
1147 }
1148
1149 static void clean_env_entries(char ***prefixes_array, int *prefixes_num)
1150 {
1151 int old_errno = errno;
1152 int i = 0;
1153
1154 if (NULL != *prefixes_array) {
1155 for (i = 0; i < *prefixes_num; i++) {
1156 if (NULL != (*prefixes_array)[i]) {
1157 free((*prefixes_array)[i]);
1158 (*prefixes_array)[i] = NULL;
1159 }
1160 }
1161 if (NULL != *prefixes_array)
1162 free(*prefixes_array);
1163 *prefixes_array = NULL;
1164 *prefixes_num = 0;
1165 }
1166
1167 errno = old_errno;
1168 }
1169
1170 #define pfx_num (*prefixes_num)
1171 #define pfx_array (*prefixes_array)
1172 #define pfx_item ((*prefixes_array)[(*prefixes_num)])
1173
1174 static void init_env_entries(char ***prefixes_array, int *prefixes_num, const char *env, const char *prefixes_env, int warn)
1175 {
1176 char *token = NULL;
1177 char *rpath = NULL;
1178 char *buffer = NULL;
1179 char *buffer_ptr = NULL;
1180 int prefixes_env_length = strlen(prefixes_env);
1181 int num_delimiters = 0;
1182 int i = 0;
1183 int old_errno = errno;
1184
1185 if (NULL == prefixes_env) {
1186 /* Do not warn if this is in init stage, as we might get
1187 * issues due to LD_PRELOAD already set (bug #91431). */
1188 if (1 == sb_init)
1189 fprintf(stderr,
1190 "libsandbox: The '%s' env variable is not defined!\n",
1191 env);
1192 if (pfx_array) {
1193 for (i = 0; i < pfx_num; i++)
1194 free(pfx_item);
1195 free(pfx_array);
1196 }
1197 pfx_num = 0;
1198
1199 goto done;
1200 }
1201
1202 for (i = 0; i < prefixes_env_length; i++) {
1203 if (':' == prefixes_env[i])
1204 num_delimiters++;
1205 }
1206
1207 /* num_delimiters might be 0, and we need 2 entries at least */
1208 pfx_array = malloc(((num_delimiters * 2) + 2) * sizeof(char *));
1209 if (NULL == pfx_array)
1210 goto error;
1211 buffer = gstrndup(prefixes_env, prefixes_env_length);
1212 if (NULL == buffer)
1213 goto error;
1214 buffer_ptr = buffer;
1215
1216 #ifdef HAVE_STRTOK_R
1217 token = strtok_r(buffer_ptr, ":", &buffer_ptr);
1218 #else
1219 token = strtok(buffer_ptr, ":");
1220 #endif
1221
1222 while ((NULL != token) && (strlen(token) > 0)) {
1223 pfx_item = resolve_path(token, 0);
1224 if (NULL != pfx_item) {
1225 pfx_num++;
1226
1227 /* Now add the realpath if it exists and
1228 * are not a duplicate */
1229 rpath = malloc(SB_PATH_MAX * sizeof(char));
1230 if (NULL != rpath) {
1231 pfx_item = realpath(*(&(pfx_item) - 1), rpath);
1232 if ((NULL != pfx_item) &&
1233 (0 != strcmp(*(&(pfx_item) - 1), pfx_item))) {
1234 pfx_num++;
1235 } else {
1236 free(rpath);
1237 pfx_item = NULL;
1238 }
1239 } else {
1240 goto error;
1241 }
1242 }
1243
1244 #ifdef HAVE_STRTOK_R
1245 token = strtok_r(NULL, ":", &buffer_ptr);
1246 #else
1247 token = strtok(NULL, ":");
1248 #endif
1249 }
1250
1251 free(buffer);
1252
1253 done:
1254 errno = old_errno;
1255 return;
1256
1257 error:
1258 perror("libsandbox: Could not initialize environ\n");
1259 exit(EXIT_FAILURE);
1260 }
1261
1262 static int check_prefixes(char **prefixes, int num_prefixes, const char *path)
1263 {
1264 int i = 0;
1265
1266 if (NULL == prefixes)
1267 return 0;
1268
1269 for (i = 0; i < num_prefixes; i++) {
1270 if (NULL != prefixes[i]) {
1271 if (0 == strncmp(path, prefixes[i], strlen(prefixes[i])))
1272 return 1;
1273 }
1274 }
1275
1276 return 0;
1277 }
1278
1279 static int check_access(sbcontext_t * sbcontext, const char *func, const char *abs_path, const char *resolv_path)
1280 {
1281 int old_errno = errno;
1282 int result = 0;
1283 int retval;
1284
1285 retval = check_prefixes(sbcontext->deny_prefixes,
1286 sbcontext->num_deny_prefixes, resolv_path);
1287 if (1 == retval)
1288 /* Fall in a read/write denied path, Deny Access */
1289 goto out;
1290
1291 /* Hardcode denying write to log dir */
1292 if (0 == strncmp(resolv_path, SANDBOX_LOG_LOCATION,
1293 strlen(SANDBOX_LOG_LOCATION)))
1294 goto out;
1295
1296 if ((NULL != sbcontext->read_prefixes) &&
1297 ((0 == strncmp(func, "access_rd", 9)) ||
1298 (0 == strncmp(func, "open_rd", 7)) ||
1299 (0 == strncmp(func, "popen", 5)) ||
1300 (0 == strncmp(func, "opendir", 7)) ||
1301 (0 == strncmp(func, "system", 6)) ||
1302 (0 == strncmp(func, "execl", 5)) ||
1303 (0 == strncmp(func, "execlp", 6)) ||
1304 (0 == strncmp(func, "execle", 6)) ||
1305 (0 == strncmp(func, "execv", 5)) ||
1306 (0 == strncmp(func, "execvp", 6)) ||
1307 (0 == strncmp(func, "execve", 6)))) {
1308 retval = check_prefixes(sbcontext->read_prefixes,
1309 sbcontext->num_read_prefixes, resolv_path);
1310 if (1 == retval) {
1311 /* Fall in a readable path, Grant Access */
1312 result = 1;
1313 goto out;
1314 }
1315
1316 /* If we are here, and still no joy, and its the access() call,
1317 * do not log it, but just return -1 */
1318 if (0 == strncmp(func, "access_rd", 7)) {
1319 sbcontext->show_access_violation = 0;
1320 goto out;
1321 }
1322 }
1323
1324 if ((0 == strncmp(func, "access_wr", 7)) ||
1325 (0 == strncmp(func, "open_wr", 7)) ||
1326 (0 == strncmp(func, "creat", 5)) ||
1327 (0 == strncmp(func, "creat64", 7)) ||
1328 (0 == strncmp(func, "mkdir", 5)) ||
1329 (0 == strncmp(func, "mknod", 5)) ||
1330 (0 == strncmp(func, "mkfifo", 6)) ||
1331 (0 == strncmp(func, "link", 4)) ||
1332 (0 == strncmp(func, "symlink", 7)) ||
1333 (0 == strncmp(func, "rename", 6)) ||
1334 (0 == strncmp(func, "utime", 5)) ||
1335 (0 == strncmp(func, "utimes", 6)) ||
1336 (0 == strncmp(func, "unlink", 6)) ||
1337 (0 == strncmp(func, "rmdir", 5)) ||
1338 (0 == strncmp(func, "chown", 5)) ||
1339 (0 == strncmp(func, "lchown", 6)) ||
1340 (0 == strncmp(func, "chmod", 5)) ||
1341 (0 == strncmp(func, "truncate", 8)) ||
1342 (0 == strncmp(func, "ftruncate", 9)) ||
1343 (0 == strncmp(func, "truncate64", 10)) ||
1344 (0 == strncmp(func, "ftruncate64", 11))) {
1345 struct stat st;
1346 char proc_self_fd[SB_PATH_MAX];
1347
1348 retval = check_prefixes(sbcontext->write_denied_prefixes,
1349 sbcontext->num_write_denied_prefixes,
1350 resolv_path);
1351 if (1 == retval)
1352 /* Falls in a write denied path, Deny Access */
1353 goto out;
1354
1355 retval = check_prefixes(sbcontext->write_prefixes,
1356 sbcontext->num_write_prefixes, resolv_path);
1357 if (1 == retval) {
1358 /* Falls in a writable path, Grant Access */
1359 result = 1;
1360 goto out;
1361 }
1362
1363 /* XXX: Hack to enable us to remove symlinks pointing
1364 * to protected stuff. First we make sure that the
1365 * passed path is writable, and if so, check if its a
1366 * symlink, and give access only if the resolved path
1367 * of the symlink's parent also have write access. */
1368 if (((0 == strncmp(func, "unlink", 6)) ||
1369 (0 == strncmp(func, "lchown", 6)) ||
1370 (0 == strncmp(func, "rename", 6)) ||
1371 (0 == strncmp(func, "symlink", 7))) &&
1372 ((-1 != lstat(abs_path, &st)) && (S_ISLNK(st.st_mode)))) {
1373 /* Check if the symlink unresolved path have access */
1374 retval = check_prefixes(sbcontext->write_prefixes,
1375 sbcontext->num_write_prefixes, abs_path);
1376 if (1 == retval) { /* Does have write access on path */
1377 char tmp_buf[SB_PATH_MAX];
1378 char *dname, *rpath;
1379
1380 snprintf(tmp_buf, SB_PATH_MAX, "%s", abs_path);
1381
1382 dname = dirname(tmp_buf);
1383 /* Get symlink resolved path */
1384 rpath = resolve_path(dname, 1);
1385 if (NULL == rpath)
1386 /* Don't really worry here about
1387 * memory issues */
1388 goto unlink_hack_end;
1389
1390 /* Now check if the symlink resolved path have access */
1391 retval = check_prefixes(sbcontext->write_prefixes,
1392 sbcontext->num_write_prefixes,
1393 rpath);
1394 free(rpath);
1395 if (1 == retval) {
1396 /* Does have write access on path, so
1397 * enable the hack as it is a symlink */
1398 result = 1;
1399 goto out;
1400 }
1401 }
1402 }
1403 unlink_hack_end:
1404
1405 /* XXX: Hack to allow writing to '/proc/self/fd' (bug #91516)
1406 * It needs to be here, as for each process '/proc/self'
1407 * will differ ... */
1408 if ((0 == strncmp(resolv_path, "/proc", strlen("/proc"))) &&
1409 (NULL != realpath("/proc/self/fd", proc_self_fd))) {
1410 if (0 == strncmp(resolv_path, proc_self_fd,
1411 strlen(proc_self_fd))) {
1412 result = 1;
1413 goto out;
1414 }
1415 }
1416
1417 retval = check_prefixes(sbcontext->predict_prefixes,
1418 sbcontext->num_predict_prefixes, resolv_path);
1419 if (1 == retval) {
1420 /* Is a known access violation, so deny access,
1421 * and do not log it */
1422 sbcontext->show_access_violation = 0;
1423 goto out;
1424 }
1425
1426 /* If we are here, and still no joy, and its the access() call,
1427 * do not log it, but just return -1 */
1428 if (0 == strncmp(func, "access_wr", 7)) {
1429 sbcontext->show_access_violation = 0;
1430 goto out;
1431 }
1432 }
1433
1434 out:
1435 errno = old_errno;
1436
1437 return result;
1438 }
1439
1440 static int check_syscall(sbcontext_t * sbcontext, const char *func, const char *file)
1441 {
1442 char *absolute_path = NULL;
1443 char *resolved_path = NULL;
1444 char *log_path = NULL, *debug_log_path = NULL;
1445 int old_errno = errno;
1446 int result = 1;
1447 int access = 0, debug = 0, verbose = 1;
1448 int color = ((is_env_on(ENV_NOCOLOR)) ? 0 : 1);
1449
1450 absolute_path = resolve_path(file, 0);
1451 if (NULL == absolute_path)
1452 goto error;
1453 resolved_path = resolve_path(file, 1);
1454 if (NULL == resolved_path)
1455 goto error;
1456
1457 log_path = getenv(ENV_SANDBOX_LOG);
1458 if (is_env_on(ENV_SANDBOX_DEBUG)) {
1459 debug_log_path = getenv(ENV_SANDBOX_DEBUG_LOG);
1460 debug = 1;
1461 }
1462
1463 if (is_env_off(ENV_SANDBOX_VERBOSE)) {
1464 verbose = 0;
1465 }
1466
1467 result = check_access(sbcontext, func, absolute_path, resolved_path);
1468
1469 if (1 == verbose) {
1470 if ((0 == result) && (1 == sbcontext->show_access_violation)) {
1471 EERROR(color, "ACCESS DENIED", " %s:%*s%s\n",
1472 func, (int)(10 - strlen(func)), "", absolute_path);
1473 } else if ((1 == debug) && (1 == sbcontext->show_access_violation)) {
1474 EINFO(color, "ACCESS ALLOWED", " %s:%*s%s\n",
1475 func, (int)(10 - strlen(func)), "", absolute_path);
1476 } else if ((1 == debug) && (0 == sbcontext->show_access_violation)) {
1477 EWARN(color, "ACCESS PREDICTED", " %s:%*s%s\n",
1478 func, (int)(10 - strlen(func)), "", absolute_path);
1479 }
1480 }
1481
1482 if ((0 == result) && (1 == sbcontext->show_access_violation))
1483 access = 1;
1484
1485 if (((NULL != log_path) && (1 == access)) ||
1486 ((NULL != debug_log_path) && (1 == debug))) {
1487 if (1 == access) {
1488 if (-1 == write_logfile(log_path, func, file, absolute_path,
1489 resolved_path, (access == 1) ? 0 : 1,
1490 color)) {
1491 if (ENOMEM == errno)
1492 goto error;
1493 }
1494 }
1495
1496 if (1 == debug) {
1497 if (-1 == write_logfile(debug_log_path, func, file, absolute_path,
1498 resolved_path, (access == 1) ? 0 : 1,
1499 color)) {
1500 if (ENOMEM == errno)
1501 goto error;
1502 }
1503 }
1504 }
1505
1506 if (NULL != absolute_path)
1507 free(absolute_path);
1508 if (NULL != resolved_path)
1509 free(resolved_path);
1510
1511 errno = old_errno;
1512
1513 return result;
1514
1515 error:
1516 if (NULL != absolute_path)
1517 free(absolute_path);
1518 if (NULL != resolved_path)
1519 free(resolved_path);
1520
1521 /* The path is too long to be canonicalized, so just warn and let the
1522 * function handle it (see bug #94630 and #21766 for more info) */
1523 if (ENAMETOOLONG == errno) {
1524 if (0 == sb_path_size_warning) {
1525 EWARN(color, "PATH LENGTH", " %s:%*s%s\n",
1526 func, (int)(10 - strlen(func)), "", file);
1527 sb_path_size_warning = 1;
1528 }
1529
1530 return 1;
1531 }
1532
1533 return 0;
1534 }
1535
1536 static int is_sandbox_on()
1537 {
1538 int old_errno = errno;
1539
1540 /* $SANDBOX_ACTIVE is an env variable that should ONLY
1541 * be used internal by sandbox.c and libsanbox.c. External
1542 * sources should NEVER set it, else the sandbox is enabled
1543 * in some cases when run in parallel with another sandbox,
1544 * but not even in the sandbox shell.
1545 *
1546 * Azarah (3 Aug 2002)
1547 */
1548 if ((NULL != getenv(ENV_SANDBOX_ON)) &&
1549 ((0 == strncmp(getenv(ENV_SANDBOX_ON), "1", 1)) ||
1550 (0 == strncmp(getenv(ENV_SANDBOX_ON), "yes", 3))) &&
1551 (1 == sandbox_on) &&
1552 (NULL != getenv(ENV_SANDBOX_ACTIVE)) &&
1553 (0 == strncmp(getenv(ENV_SANDBOX_ACTIVE), SANDBOX_ACTIVE, 13))) {
1554 errno = old_errno;
1555 return 1;
1556 } else {
1557 errno = old_errno;
1558 return 0;
1559 }
1560 }
1561
1562 static int before_syscall(const char *func, const char *file)
1563 {
1564 int old_errno = errno;
1565 int result = 1;
1566 // static sbcontext_t sbcontext;
1567 char *deny = getenv(ENV_SANDBOX_DENY);
1568 char *read = getenv(ENV_SANDBOX_READ);
1569 char *write = getenv(ENV_SANDBOX_WRITE);
1570 char *predict = getenv(ENV_SANDBOX_PREDICT);
1571
1572 if (NULL == file || 0 == strlen(file)) {
1573 /* The file/directory does not exist */
1574 errno = ENOENT;
1575 return 0;
1576 }
1577
1578 if(0 == sb_init) {
1579 init_context(&sbcontext);
1580 cached_env_vars = malloc(sizeof(char *) * 4);
1581 cached_env_vars[0] = cached_env_vars[1] = cached_env_vars[2] = cached_env_vars[3] = NULL;
1582 sb_init = 1;
1583 }
1584
1585 if((NULL == deny && cached_env_vars[0] != deny) || NULL == cached_env_vars[0] ||
1586 strcmp(cached_env_vars[0], deny) != 0) {
1587
1588 clean_env_entries(&(sbcontext.deny_prefixes),
1589 &(sbcontext.num_deny_prefixes));
1590
1591 if(NULL != cached_env_vars[0])
1592 free(cached_env_vars[0]);
1593
1594 if(NULL != deny) {
1595 init_env_entries(&(sbcontext.deny_prefixes),
1596 &(sbcontext.num_deny_prefixes), ENV_SANDBOX_DENY, deny, 1);
1597 cached_env_vars[0] = strdup(deny);
1598 } else {
1599 cached_env_vars[0] = NULL;
1600 }
1601 }
1602
1603 if((NULL == read && cached_env_vars[1] != read) || NULL == cached_env_vars[1] ||
1604 strcmp(cached_env_vars[1], read) != 0) {
1605
1606 clean_env_entries(&(sbcontext.read_prefixes),
1607 &(sbcontext.num_read_prefixes));
1608
1609 if(NULL != cached_env_vars[1])
1610 free(cached_env_vars[1]);
1611
1612 if(NULL != read) {
1613 init_env_entries(&(sbcontext.read_prefixes),
1614 &(sbcontext.num_read_prefixes), ENV_SANDBOX_READ, read, 1);
1615 cached_env_vars[1] = strdup(read);
1616 } else {
1617 cached_env_vars[1] = NULL;
1618 }
1619 }
1620
1621 if((NULL == write && cached_env_vars[2] != write) || NULL == cached_env_vars[2] ||
1622 strcmp(cached_env_vars[2], write) != 0) {
1623
1624 clean_env_entries(&(sbcontext.write_prefixes),
1625 &(sbcontext.num_write_prefixes));
1626
1627 if(NULL != cached_env_vars[2])
1628 free(cached_env_vars[2]);
1629
1630 if(NULL != write) {
1631 init_env_entries(&(sbcontext.write_prefixes),
1632 &(sbcontext.num_write_prefixes), ENV_SANDBOX_WRITE, write, 1);
1633 cached_env_vars[2] = strdup(write);
1634 } else {
1635 cached_env_vars[2] = NULL;
1636 }
1637 }
1638
1639 if((NULL == predict && cached_env_vars[3] != predict) || NULL == cached_env_vars[3] ||
1640 strcmp(cached_env_vars[3], predict) != 0) {
1641
1642 clean_env_entries(&(sbcontext.predict_prefixes),
1643 &(sbcontext.num_predict_prefixes));
1644
1645 if(NULL != cached_env_vars[3])
1646 free(cached_env_vars[3]);
1647
1648 if(NULL != predict) {
1649 init_env_entries(&(sbcontext.predict_prefixes),
1650 &(sbcontext.num_predict_prefixes), ENV_SANDBOX_PREDICT, predict, 1);
1651 cached_env_vars[3] = strdup(predict);
1652 } else {
1653 cached_env_vars[3] = NULL;
1654 }
1655
1656 }
1657
1658 /* Might have been reset in check_access() */
1659 sbcontext.show_access_violation = 1;
1660
1661 result = check_syscall(&sbcontext, func, file);
1662
1663 errno = old_errno;
1664
1665 if (0 == result) {
1666 if ((NULL != getenv(ENV_SANDBOX_PID)) && (is_env_on(ENV_SANDBOX_ABORT)))
1667 kill(atoi(getenv(ENV_SANDBOX_PID)), SIGUSR1);
1668
1669 errno = EACCES;
1670 }
1671
1672 return result;
1673 }
1674
1675 static int before_syscall_access(const char *func, const char *file, int flags)
1676 {
1677 if (flags & W_OK) {
1678 return before_syscall("access_wr", file);
1679 } else {
1680 return before_syscall("access_rd", file);
1681 }
1682 }
1683
1684 static int before_syscall_open_int(const char *func, const char *file, int flags)
1685 {
1686 if ((flags & O_WRONLY) || (flags & O_RDWR)) {
1687 return before_syscall("open_wr", file);
1688 } else {
1689 return before_syscall("open_rd", file);
1690 }
1691 }
1692
1693 static int before_syscall_open_char(const char *func, const char *file, const char *mode)
1694 {
1695 if (*mode == 'r' && (0 == (strcmp(mode, "r")) ||
1696 /* The strspn accept args are known non-writable modifiers */
1697 (strlen(++mode) == strspn(mode, "xbtmc")))) {
1698 return before_syscall("open_rd", file);
1699 } else {
1700 return before_syscall("open_wr", file);
1701 }
1702 }
1703
1704
1705 // 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