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

Contents of /trunk/src/libsandbox.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 308 - (show annotations) (download) (as text)
Sun Jul 9 17:52:07 2006 UTC (12 years, 2 months ago) by azarah
File MIME type: text/x-csrc
File size: 46192 byte(s)
Reformat previous added code a bit, and add debug messages
and even more error checking.  Abort on some more unrecoverable
errors.

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