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

Contents of /trunk/libsandbox/libsandbox.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 311 - (show annotations) (download) (as text)
Sun Jul 9 19:59:15 2006 UTC (12 years, 3 months ago) by azarah
File MIME type: text/x-csrc
File size: 46303 byte(s)
Use PAGE_SIZE to read proc files.

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