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

Contents of /trunk/libsandbox/libsandbox.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 331 - (show annotations) (download) (as text)
Wed Jul 12 16:53:51 2006 UTC (8 years, 5 months ago) by azarah
File MIME type: text/x-csrc
File size: 28161 byte(s)
Make sure we do not segfault with invalid mode passed to fopen() and co.  Add note about allowing different errno to be returned.

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 <errno.h>
39 #include <libgen.h>
40 #include <limits.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <sys/stat.h>
45 #include <sys/types.h>
46 #include <signal.h>
47 #include <unistd.h>
48 #include <fcntl.h>
49
50 #ifdef SB_MEM_DEBUG
51 # include <mcheck.h>
52 #endif
53
54 #undef open
55 #undef open64
56
57 #include "config.h"
58 #include "localdecls.h"
59 #include "sbutil.h"
60
61 #include "libsandbox.h"
62 #include "wrappers.h"
63
64 #define LOG_VERSION "1.0"
65 #define LOG_STRING "VERSION " LOG_VERSION "\n"
66 #define LOG_FMT_FUNC "FORMAT: F - Function called\n"
67 #define LOG_FMT_ACCESS "FORMAT: S - Access Status\n"
68 #define LOG_FMT_PATH "FORMAT: P - Path as passed to function\n"
69 #define LOG_FMT_APATH "FORMAT: A - Absolute Path (not canonical)\n"
70 #define LOG_FMT_RPATH "FORMAT: R - Canonical Path\n"
71 #define LOG_FMT_CMDLINE "FORMAT: C - Command Line\n"
72
73 #define PROC_DIR "/proc"
74 #define PROC_SELF_FD PROC_DIR "/self/fd"
75 #define PROC_SELF_CMDLINE PROC_DIR "/self/cmdline"
76
77 char sandbox_lib[SB_PATH_MAX];
78
79 typedef struct {
80 int show_access_violation;
81 char **deny_prefixes;
82 int num_deny_prefixes;
83 char **read_prefixes;
84 int num_read_prefixes;
85 char **write_prefixes;
86 int num_write_prefixes;
87 char **predict_prefixes;
88 int num_predict_prefixes;
89 char **write_denied_prefixes;
90 int num_write_denied_prefixes;
91 } sbcontext_t;
92
93 static sbcontext_t sbcontext;
94 static char **cached_env_vars;
95 volatile int sandbox_on = 1;
96 static int sb_init = 0;
97 static int sb_path_size_warning = 0;
98
99 void __attribute__ ((constructor)) libsb_init(void);
100 void __attribute__ ((destructor)) libsb_fini(void);
101
102 static char *resolve_path(const char *, int);
103 static int write_logfile(const char *, const char *, const char *,
104 const char *, const char *, bool, bool);
105 static int check_prefixes(char **, int, const char *);
106 static int check_access(sbcontext_t *, const char *, const char *, const char *);
107 static int check_syscall(sbcontext_t *, const char *, const char *);
108 static void clean_env_entries(char ***, int *);
109 static void init_context(sbcontext_t *);
110 static void init_env_entries(char ***, int *, const char *, const char *, int);
111
112
113 /*
114 * Initialize the shabang
115 */
116
117 static char log_domain[] = "libsandbox";
118
119 void __attribute__ ((destructor)) libsb_fini(void)
120 {
121 int x;
122
123 sb_init = 0;
124
125 if(NULL != cached_env_vars) {
126 for(x=0; x < 4; x++) {
127 if(NULL != cached_env_vars[x]) {
128 free(cached_env_vars[x]);
129 cached_env_vars[x] = NULL;
130 }
131 }
132 free(cached_env_vars);
133 cached_env_vars = NULL;
134 }
135
136 clean_env_entries(&(sbcontext.deny_prefixes),
137 &(sbcontext.num_deny_prefixes));
138 clean_env_entries(&(sbcontext.read_prefixes),
139 &(sbcontext.num_read_prefixes));
140 clean_env_entries(&(sbcontext.write_prefixes),
141 &(sbcontext.num_write_prefixes));
142 clean_env_entries(&(sbcontext.predict_prefixes),
143 &(sbcontext.num_predict_prefixes));
144 }
145
146 void __attribute__ ((constructor)) libsb_init(void)
147 {
148 int old_errno = errno;
149
150 #ifdef SB_MEM_DEBUG
151 mtrace();
152 #endif
153
154 rc_log_domain(log_domain);
155 sb_set_open(libsb_open);
156
157 /* Get the path and name to this library */
158 get_sandbox_lib(sandbox_lib);
159
160 // sb_init = 1;
161
162 errno = old_errno;
163 }
164
165 int canonicalize(const char *path, char *resolved_path)
166 {
167 int old_errno = errno;
168 char *retval;
169
170 *resolved_path = '\0';
171
172 /* If path == NULL, return or we get a segfault */
173 if (NULL == path) {
174 errno = EINVAL;
175 return -1;
176 }
177
178 /* Do not try to resolve an empty path */
179 if ('\0' == path[0]) {
180 errno = old_errno;
181 return 0;
182 }
183
184 retval = erealpath(path, resolved_path);
185
186 if ((NULL == retval) && (path[0] != '/')) {
187 /* The path could not be canonicalized, append it
188 * to the current working directory if it was not
189 * an absolute path
190 */
191
192 if (ENAMETOOLONG == errno)
193 return -1;
194
195 if (NULL == egetcwd(resolved_path, SB_PATH_MAX - 2))
196 return -1;
197 snprintf((char *)(resolved_path + strlen(resolved_path)),
198 SB_PATH_MAX - strlen(resolved_path), "/%s", path);
199
200 if (NULL == erealpath(resolved_path, resolved_path)) {
201 if (errno == ENAMETOOLONG) {
202 /* The resolved path is too long for the buffer to hold */
203 return -1;
204 } else {
205 /* Whatever it resolved, is not a valid path */
206 errno = ENOENT;
207 return -1;
208 }
209 }
210
211 } else if ((NULL == retval) && (path[0] == '/')) {
212 /* Whatever it resolved, is not a valid path */
213 errno = ENOENT;
214 return -1;
215 }
216
217 errno = old_errno;
218 return 0;
219 }
220
221 static char *resolve_path(const char *path, int follow_link)
222 {
223 int old_errno = errno;
224 char tmp_str1[SB_PATH_MAX], tmp_str2[SB_PATH_MAX];
225 char *dname, *bname;
226 char *filtered_path;
227
228 if (NULL == path)
229 return NULL;
230
231 filtered_path = xmalloc(SB_PATH_MAX * sizeof(char));
232 if (NULL == filtered_path)
233 return NULL;
234
235 if (0 == follow_link) {
236 if (-1 == canonicalize(path, filtered_path))
237 return NULL;
238 } else {
239 /* Basically we get the realpath which should resolve symlinks,
240 * etc. If that fails (might not exist), we try to get the
241 * realpath of the parent directory, as that should hopefully
242 * exist. If all else fails, just go with canonicalize */
243 if (NULL == realpath(path, filtered_path)) {
244 snprintf(tmp_str1, SB_PATH_MAX, "%s", path);
245
246 dname = dirname(tmp_str1);
247
248 /* If not, then check if we can resolve the
249 * parent directory */
250 if (NULL == realpath(dname, filtered_path)) {
251 /* Fall back to canonicalize */
252 if (-1 == canonicalize(path, filtered_path))
253 return NULL;
254 } else {
255 /* OK, now add the basename to keep our access
256 * checking happy (don't want '/usr/lib' if we
257 * tried to do something with non-existing
258 * file '/usr/lib/cf*' ...) */
259 snprintf(tmp_str2, SB_PATH_MAX, "%s", path);
260
261 bname = rc_basename(tmp_str2);
262 snprintf((char *)(filtered_path + strlen(filtered_path)),
263 SB_PATH_MAX - strlen(filtered_path), "%s%s",
264 (filtered_path[strlen(filtered_path) - 1] != '/') ? "/" : "",
265 bname);
266 }
267 }
268 }
269
270 errno = old_errno;
271
272 return filtered_path;
273 }
274
275 /*
276 * Internal Functions
277 */
278
279 char *egetcwd(char *buf, size_t size)
280 {
281 struct stat st;
282 char *tmpbuf, *oldbuf = buf;
283 int old_errno;
284
285 /* Need to disable sandbox, as on non-linux libc's, opendir() is
286 * used by some getcwd() implementations and resolves to the sandbox
287 * opendir() wrapper, causing infinit recursion and finially crashes.
288 */
289 sandbox_on = 0;
290 errno = 0;
291 tmpbuf = libsb_getcwd(buf, size);
292 sandbox_on = 1;
293
294 /* We basically try to figure out if we can trust what getcwd()
295 * returned. If one of the following happens kernel/libc side,
296 * bad things will happen, but not much we can do about it:
297 * - Invalid pointer with errno = 0
298 * - Truncated path with errno = 0
299 * - Whatever I forgot about
300 */
301 if ((tmpbuf) && (errno == 0)) {
302 old_errno = errno;
303 lstat(buf, &st);
304
305 if (errno == ENOENT) {
306 /* If getcwd() allocated the buffer, free it. */
307 if (NULL == oldbuf)
308 free(tmpbuf);
309
310 /* If lstat() failed with eerror = ENOENT, then its
311 * possible that we are running on an older kernel
312 * which had issues with returning invalid paths if
313 * they got too long. Return with errno = ENAMETOOLONG,
314 * so that canonicalize() and check_syscall() know
315 * what the issue is.
316 */
317 errno = ENAMETOOLONG;
318 return NULL;
319 } else if (errno != 0) {
320 /* If getcwd() allocated the buffer, free it. */
321 if (NULL == oldbuf)
322 free(tmpbuf);
323
324 /* Not sure if we should quit here, but I guess if
325 * lstat() fails, getcwd could have messed up. Not
326 * sure what to do about errno - use lstat()'s for
327 * now.
328 */
329 return NULL;
330 }
331
332 errno = old_errno;
333 } else if (errno != 0) {
334
335 /* Make sure we do not return garbage if the current libc or
336 * kernel's getcwd() is buggy.
337 */
338 return NULL;
339 }
340
341 return tmpbuf;
342 }
343
344 static char *getcmdline(void)
345 {
346 rc_dynbuf_t *proc_data;
347 struct stat st;
348 char *buf;
349 size_t n;
350 int fd;
351
352 if (-1 == stat(PROC_SELF_CMDLINE, &st)) {
353 /* Don't care if it do not exist */
354 errno = 0;
355 return NULL;
356 }
357
358 proc_data = rc_dynbuf_new();
359 if (NULL == proc_data) {
360 DBG_MSG("Could not allocate dynamic buffer!\n");
361 return NULL;
362 }
363
364 fd = sb_open(PROC_SELF_CMDLINE, O_RDONLY, 0);
365 if (fd < 0) {
366 DBG_MSG("Failed to open '%s'!\n", PROC_SELF_CMDLINE);
367 return NULL;
368 }
369
370 /* Read PAGE_SIZE at a time -- whenever EOF or an error is found
371 * (don't care) give up and return.
372 * XXX: Some linux kernels especially needed read() to read PAGE_SIZE
373 * at a time. */
374 do {
375 n = rc_dynbuf_write_fd(proc_data, fd, getpagesize());
376 if (-1 == n) {
377 DBG_MSG("Failed to read from '%s'!\n", PROC_SELF_CMDLINE);
378 goto error;
379 }
380 } while (0 < n);
381
382 sb_close(fd);
383
384 rc_dynbuf_replace_char(proc_data, '\0', ' ');
385
386 buf = rc_dynbuf_read_line(proc_data);
387 if (NULL == buf)
388 goto error;
389
390 rc_dynbuf_free(proc_data);
391
392 return buf;
393
394 error:
395 if (NULL != proc_data)
396 rc_dynbuf_free(proc_data);
397
398 return NULL;
399 }
400
401 static int write_logfile(const char *logfile, const char *func, const char *path,
402 const char *apath, const char *rpath, bool access,
403 bool color)
404 {
405 struct stat log_stat;
406 int stat_ret;
407 int logfd;
408
409 stat_ret = lstat(logfile, &log_stat);
410 /* Do not care about failure */
411 errno = 0;
412 if ((0 == stat_ret) &&
413 (0 == S_ISREG(log_stat.st_mode))) {
414 SB_EERROR(color, "SECURITY BREACH", " '%s' %s\n", logfile,
415 "already exists and is not a regular file!");
416 abort();
417 } else {
418 logfd = sb_open(logfile, O_APPEND | O_WRONLY |
419 O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP |
420 S_IROTH);
421 if (logfd >= 0) {
422 char *cmdline;
423
424 if (0 != stat_ret) {
425 SB_WRITE(logfd, LOG_STRING, strlen(LOG_STRING), error);
426 SB_WRITE(logfd, LOG_FMT_FUNC, strlen(LOG_FMT_FUNC), error);
427 SB_WRITE(logfd, LOG_FMT_ACCESS, strlen(LOG_FMT_ACCESS), error);
428 SB_WRITE(logfd, LOG_FMT_PATH, strlen(LOG_FMT_PATH), error);
429 SB_WRITE(logfd, LOG_FMT_APATH, strlen(LOG_FMT_APATH), error);
430 SB_WRITE(logfd, LOG_FMT_RPATH, strlen(LOG_FMT_RPATH), error);
431 SB_WRITE(logfd, LOG_FMT_CMDLINE, strlen(LOG_FMT_CMDLINE), error);
432 SB_WRITE(logfd, "\n", 1, error);
433 } else {
434 /* Already have data in the log, so add a newline to space the
435 * log entries.
436 */
437 SB_WRITE(logfd, "\n", 1, error);
438 }
439
440 SB_WRITE(logfd, "F: ", 3, error);
441 SB_WRITE(logfd, func, strlen(func), error);
442 SB_WRITE(logfd, "\n", 1, error);
443 SB_WRITE(logfd, "S: ", 3, error);
444 if (access)
445 SB_WRITE(logfd, "allow", 5, error);
446 else
447 SB_WRITE(logfd, "deny", 4, error);
448 SB_WRITE(logfd, "\n", 1, error);
449 SB_WRITE(logfd, "P: ", 3, error);
450 SB_WRITE(logfd, path, strlen(path), error);
451 SB_WRITE(logfd, "\n", 1, error);
452 SB_WRITE(logfd, "A: ", 3, error);
453 SB_WRITE(logfd, apath, strlen(apath), error);
454 SB_WRITE(logfd, "\n", 1, error);
455 SB_WRITE(logfd, "R: ", 3, error);
456 SB_WRITE(logfd, rpath, strlen(rpath), error);
457 SB_WRITE(logfd, "\n", 1, error);
458
459 cmdline = getcmdline();
460 if (NULL != cmdline) {
461 SB_WRITE(logfd, "C: ", 3, error);
462 SB_WRITE(logfd, cmdline, strlen(cmdline),
463 error);
464 SB_WRITE(logfd, "\n", 1, error);
465
466 free(cmdline);
467 } else if (0 != errno) {
468 goto error;
469 }
470
471
472 sb_close(logfd);
473 } else {
474 goto error;
475 }
476 }
477
478 return 0;
479
480 error:
481 return -1;
482 }
483
484 static void init_context(sbcontext_t * context)
485 {
486 context->show_access_violation = 1;
487 context->deny_prefixes = NULL;
488 context->num_deny_prefixes = 0;
489 context->read_prefixes = NULL;
490 context->num_read_prefixes = 0;
491 context->write_prefixes = NULL;
492 context->num_write_prefixes = 0;
493 context->predict_prefixes = NULL;
494 context->num_predict_prefixes = 0;
495 context->write_denied_prefixes = NULL;
496 context->num_write_denied_prefixes = 0;
497 }
498
499 static void clean_env_entries(char ***prefixes_array, int *prefixes_num)
500 {
501 int old_errno = errno;
502 int i = 0;
503
504 if (NULL != *prefixes_array) {
505 for (i = 0; i < *prefixes_num; i++) {
506 if (NULL != (*prefixes_array)[i]) {
507 free((*prefixes_array)[i]);
508 (*prefixes_array)[i] = NULL;
509 }
510 }
511 if (NULL != *prefixes_array)
512 free(*prefixes_array);
513 *prefixes_array = NULL;
514 *prefixes_num = 0;
515 }
516
517 errno = old_errno;
518 }
519
520 #define pfx_num (*prefixes_num)
521 #define pfx_array (*prefixes_array)
522 #define pfx_item ((*prefixes_array)[(*prefixes_num)])
523
524 static void init_env_entries(char ***prefixes_array, int *prefixes_num, const char *env, const char *prefixes_env, int warn)
525 {
526 char *token = NULL;
527 char *rpath = NULL;
528 char *buffer = NULL;
529 char *buffer_ptr = NULL;
530 int prefixes_env_length = strlen(prefixes_env);
531 int num_delimiters = 0;
532 int i = 0;
533 int old_errno = errno;
534
535 if (NULL == prefixes_env) {
536 /* Do not warn if this is in init stage, as we might get
537 * issues due to LD_PRELOAD already set (bug #91431). */
538 if (1 == sb_init)
539 fprintf(stderr,
540 "libsandbox: The '%s' env variable is not defined!\n",
541 env);
542 if (pfx_array) {
543 for (i = 0; i < pfx_num; i++)
544 free(pfx_item);
545 free(pfx_array);
546 }
547 pfx_num = 0;
548
549 goto done;
550 }
551
552 for (i = 0; i < prefixes_env_length; i++) {
553 if (':' == prefixes_env[i])
554 num_delimiters++;
555 }
556
557 /* num_delimiters might be 0, and we need 2 entries at least */
558 pfx_array = xmalloc(((num_delimiters * 2) + 2) * sizeof(char *));
559 if (NULL == pfx_array)
560 goto error;
561 buffer = rc_strndup(prefixes_env, prefixes_env_length);
562 if (NULL == buffer)
563 goto error;
564 buffer_ptr = buffer;
565
566 #ifdef HAVE_STRTOK_R
567 token = strtok_r(buffer_ptr, ":", &buffer_ptr);
568 #else
569 token = strtok(buffer_ptr, ":");
570 #endif
571
572 while ((NULL != token) && (strlen(token) > 0)) {
573 pfx_item = resolve_path(token, 0);
574 /* We do not care about errno here */
575 errno = 0;
576 if (NULL != pfx_item) {
577 pfx_num++;
578
579 /* Now add the realpath if it exists and
580 * are not a duplicate */
581 rpath = xmalloc(SB_PATH_MAX * sizeof(char));
582 if (NULL != rpath) {
583 pfx_item = realpath(*(&(pfx_item) - 1), rpath);
584 if ((NULL != pfx_item) &&
585 (0 != strcmp(*(&(pfx_item) - 1), pfx_item))) {
586 pfx_num++;
587 } else {
588 free(rpath);
589 pfx_item = NULL;
590 }
591 } else {
592 goto error;
593 }
594 }
595
596 #ifdef HAVE_STRTOK_R
597 token = strtok_r(NULL, ":", &buffer_ptr);
598 #else
599 token = strtok(NULL, ":");
600 #endif
601 }
602
603 free(buffer);
604
605 done:
606 errno = old_errno;
607 return;
608
609 error:
610 DBG_MSG("Unrecoverable error!\n");
611 abort();
612 }
613
614 static int check_prefixes(char **prefixes, int num_prefixes, const char *path)
615 {
616 int i = 0;
617
618 if (NULL == prefixes)
619 return 0;
620
621 for (i = 0; i < num_prefixes; i++) {
622 if (NULL != prefixes[i]) {
623 if (0 == strncmp(path, prefixes[i], strlen(prefixes[i])))
624 return 1;
625 }
626 }
627
628 return 0;
629 }
630
631 static int check_access(sbcontext_t * sbcontext, const char *func, const char *abs_path, const char *resolv_path)
632 {
633 int old_errno = errno;
634 int result = 0;
635 int retval;
636
637 retval = check_prefixes(sbcontext->deny_prefixes,
638 sbcontext->num_deny_prefixes, resolv_path);
639 if (1 == retval)
640 /* Fall in a read/write denied path, Deny Access */
641 goto out;
642
643 /* Hardcode denying write to log dir */
644 if (0 == strncmp(resolv_path, SANDBOX_LOG_LOCATION,
645 strlen(SANDBOX_LOG_LOCATION)))
646 goto out;
647
648 if ((NULL != sbcontext->read_prefixes) &&
649 ((0 == strncmp(func, "access_rd", 9)) ||
650 (0 == strncmp(func, "open_rd", 7)) ||
651 (0 == strncmp(func, "popen", 5)) ||
652 (0 == strncmp(func, "opendir", 7)) ||
653 (0 == strncmp(func, "system", 6)) ||
654 (0 == strncmp(func, "execl", 5)) ||
655 (0 == strncmp(func, "execlp", 6)) ||
656 (0 == strncmp(func, "execle", 6)) ||
657 (0 == strncmp(func, "execv", 5)) ||
658 (0 == strncmp(func, "execvp", 6)) ||
659 (0 == strncmp(func, "execve", 6)))) {
660 retval = check_prefixes(sbcontext->read_prefixes,
661 sbcontext->num_read_prefixes, resolv_path);
662 if (1 == retval) {
663 /* Fall in a readable path, Grant Access */
664 result = 1;
665 goto out;
666 }
667
668 /* If we are here, and still no joy, and its the access() call,
669 * do not log it, but just return -1 */
670 if (0 == strncmp(func, "access_rd", 9)) {
671 sbcontext->show_access_violation = 0;
672 goto out;
673 }
674 }
675
676 if ((0 == strncmp(func, "access_wr", 9)) ||
677 (0 == strncmp(func, "open_wr", 7)) ||
678 (0 == strncmp(func, "creat", 5)) ||
679 (0 == strncmp(func, "creat64", 7)) ||
680 (0 == strncmp(func, "mkdir", 5)) ||
681 (0 == strncmp(func, "mknod", 5)) ||
682 (0 == strncmp(func, "mkfifo", 6)) ||
683 (0 == strncmp(func, "link", 4)) ||
684 (0 == strncmp(func, "symlink", 7)) ||
685 (0 == strncmp(func, "rename", 6)) ||
686 (0 == strncmp(func, "utime", 5)) ||
687 (0 == strncmp(func, "utimes", 6)) ||
688 (0 == strncmp(func, "unlink", 6)) ||
689 (0 == strncmp(func, "rmdir", 5)) ||
690 (0 == strncmp(func, "chown", 5)) ||
691 (0 == strncmp(func, "lchown", 6)) ||
692 (0 == strncmp(func, "chmod", 5)) ||
693 (0 == strncmp(func, "truncate", 8)) ||
694 (0 == strncmp(func, "ftruncate", 9)) ||
695 (0 == strncmp(func, "truncate64", 10)) ||
696 (0 == strncmp(func, "ftruncate64", 11))) {
697 struct stat st;
698 char proc_self_fd[SB_PATH_MAX];
699
700 retval = check_prefixes(sbcontext->write_denied_prefixes,
701 sbcontext->num_write_denied_prefixes,
702 resolv_path);
703 if (1 == retval)
704 /* Falls in a write denied path, Deny Access */
705 goto out;
706
707 retval = check_prefixes(sbcontext->write_prefixes,
708 sbcontext->num_write_prefixes, resolv_path);
709 if (1 == retval) {
710 /* Falls in a writable path, Grant Access */
711 result = 1;
712 goto out;
713 }
714
715 /* XXX: Hack to enable us to remove symlinks pointing
716 * to protected stuff. First we make sure that the
717 * passed path is writable, and if so, check if its a
718 * symlink, and give access only if the resolved path
719 * of the symlink's parent also have write access. */
720 if (((0 == strncmp(func, "unlink", 6)) ||
721 (0 == strncmp(func, "lchown", 6)) ||
722 (0 == strncmp(func, "rename", 6)) ||
723 (0 == strncmp(func, "symlink", 7))) &&
724 ((-1 != lstat(abs_path, &st)) && (S_ISLNK(st.st_mode)))) {
725 /* Check if the symlink unresolved path have access */
726 retval = check_prefixes(sbcontext->write_prefixes,
727 sbcontext->num_write_prefixes, abs_path);
728 if (1 == retval) { /* Does have write access on path */
729 char tmp_buf[SB_PATH_MAX];
730 char *dname, *rpath;
731
732 snprintf(tmp_buf, SB_PATH_MAX, "%s", abs_path);
733
734 dname = dirname(tmp_buf);
735 /* Get symlink resolved path */
736 rpath = resolve_path(dname, 1);
737 if (NULL == rpath)
738 /* Don't really worry here about
739 * memory issues */
740 goto unlink_hack_end;
741
742 /* Now check if the symlink resolved path have access */
743 retval = check_prefixes(sbcontext->write_prefixes,
744 sbcontext->num_write_prefixes,
745 rpath);
746 free(rpath);
747 if (1 == retval) {
748 /* Does have write access on path, so
749 * enable the hack as it is a symlink */
750 result = 1;
751 goto out;
752 }
753 }
754 }
755 unlink_hack_end:
756
757 /* XXX: Hack to allow writing to '/proc/self/fd' (bug #91516)
758 * It needs to be here, as for each process '/proc/self'
759 * will differ ... */
760 if ((0 == strncmp(resolv_path, PROC_DIR, strlen(PROC_DIR))) &&
761 (NULL != realpath(PROC_SELF_FD, proc_self_fd))) {
762 if (0 == strncmp(resolv_path, proc_self_fd,
763 strlen(proc_self_fd))) {
764 result = 1;
765 goto out;
766 }
767 }
768
769 retval = check_prefixes(sbcontext->predict_prefixes,
770 sbcontext->num_predict_prefixes, resolv_path);
771 if (1 == retval) {
772 /* Is a known access violation, so deny access,
773 * and do not log it */
774 sbcontext->show_access_violation = 0;
775 goto out;
776 }
777
778 /* If we are here, and still no joy, and its the access() call,
779 * do not log it, but just return -1 */
780 if (0 == strncmp(func, "access_wr", 9)) {
781 sbcontext->show_access_violation = 0;
782 goto out;
783 }
784 }
785
786 out:
787 errno = old_errno;
788
789 return result;
790 }
791
792 static int check_syscall(sbcontext_t * sbcontext, const char *func, const char *file)
793 {
794 char *absolute_path = NULL;
795 char *resolved_path = NULL;
796 char *log_path = NULL, *debug_log_path = NULL;
797 int old_errno = errno;
798 int result = 1;
799 int access = 0, debug = 0, verbose = 1;
800 int color = ((is_env_on(ENV_NOCOLOR)) ? 0 : 1);
801
802 absolute_path = resolve_path(file, 0);
803 if (NULL == absolute_path)
804 goto error;
805 resolved_path = resolve_path(file, 1);
806 if (NULL == resolved_path)
807 goto error;
808
809 log_path = getenv(ENV_SANDBOX_LOG);
810 if (is_env_on(ENV_SANDBOX_DEBUG)) {
811 debug_log_path = getenv(ENV_SANDBOX_DEBUG_LOG);
812 debug = 1;
813 }
814
815 if (is_env_off(ENV_SANDBOX_VERBOSE)) {
816 verbose = 0;
817 }
818
819 result = check_access(sbcontext, func, absolute_path, resolved_path);
820
821 if (1 == verbose) {
822 if ((0 == result) && (1 == sbcontext->show_access_violation)) {
823 SB_EERROR(color, "ACCESS DENIED", " %s:%*s%s\n",
824 func, (int)(10 - strlen(func)), "", absolute_path);
825 } else if ((1 == debug) && (1 == sbcontext->show_access_violation)) {
826 SB_EINFO(color, "ACCESS ALLOWED", " %s:%*s%s\n",
827 func, (int)(10 - strlen(func)), "", absolute_path);
828 } else if ((1 == debug) && (0 == sbcontext->show_access_violation)) {
829 SB_EWARN(color, "ACCESS PREDICTED", " %s:%*s%s\n",
830 func, (int)(10 - strlen(func)), "", absolute_path);
831 }
832 }
833
834 if ((0 == result) && (1 == sbcontext->show_access_violation))
835 access = 1;
836
837 if ((NULL != log_path) && (1 == access)) {
838 if (-1 == write_logfile(log_path, func, file, absolute_path,
839 resolved_path, (access == 1) ? 0 : 1,
840 color)) {
841 if (0 != errno)
842 goto error;
843 }
844 }
845
846 if ((NULL != debug_log_path) && (1 == debug)) {
847 if (-1 == write_logfile(debug_log_path, func, file, absolute_path,
848 resolved_path, (access == 1) ? 0 : 1,
849 color)) {
850 if (0 != errno)
851 goto error;
852 }
853 }
854
855 if (NULL != absolute_path)
856 free(absolute_path);
857 if (NULL != resolved_path)
858 free(resolved_path);
859
860 errno = old_errno;
861
862 return result;
863
864 error:
865 if (NULL != absolute_path)
866 free(absolute_path);
867 if (NULL != resolved_path)
868 free(resolved_path);
869
870 /* The path is too long to be canonicalized, so just warn and let the
871 * function handle it (see bug #94630 and #21766 for more info) */
872 if (ENAMETOOLONG == errno) {
873 if (0 == sb_path_size_warning) {
874 SB_EWARN(color, "PATH LENGTH", " %s:%*s%s\n",
875 func, (int)(10 - strlen(func)), "", file);
876 sb_path_size_warning = 1;
877 }
878
879 return 1;
880 }
881
882 /* If we get here, something bad happened */
883 DBG_MSG("Unrecoverable error!\n");
884 abort();
885 }
886
887 int is_sandbox_on()
888 {
889 int old_errno = errno;
890
891 /* $SANDBOX_ACTIVE is an env variable that should ONLY
892 * be used internal by sandbox.c and libsanbox.c. External
893 * sources should NEVER set it, else the sandbox is enabled
894 * in some cases when run in parallel with another sandbox,
895 * but not even in the sandbox shell.
896 *
897 * Azarah (3 Aug 2002)
898 */
899 if ((is_env_on(ENV_SANDBOX_ON)) &&
900 (1 == sandbox_on) &&
901 (NULL != getenv(ENV_SANDBOX_ACTIVE)) &&
902 (0 == strncmp(getenv(ENV_SANDBOX_ACTIVE), SANDBOX_ACTIVE, 13))) {
903 errno = old_errno;
904 return 1;
905 } else {
906 errno = old_errno;
907 return 0;
908 }
909 }
910
911 int before_syscall(const char *func, const char *file)
912 {
913 int old_errno = errno;
914 int result = 1;
915 // static sbcontext_t sbcontext;
916 char *deny = getenv(ENV_SANDBOX_DENY);
917 char *read = getenv(ENV_SANDBOX_READ);
918 char *write = getenv(ENV_SANDBOX_WRITE);
919 char *predict = getenv(ENV_SANDBOX_PREDICT);
920
921 if (NULL == file || 0 == strlen(file)) {
922 /* The file/directory does not exist */
923 errno = ENOENT;
924 return 0;
925 }
926
927 if(0 == sb_init) {
928 init_context(&sbcontext);
929 cached_env_vars = xmalloc(sizeof(char *) * 4);
930 if (NULL == cached_env_vars) {
931 DBG_MSG("Unrecoverable error!\n");
932 abort();
933 }
934
935 cached_env_vars[0] = cached_env_vars[1] = cached_env_vars[2] = cached_env_vars[3] = NULL;
936 sb_init = 1;
937 }
938
939 if((NULL == deny && cached_env_vars[0] != deny) || NULL == cached_env_vars[0] ||
940 strcmp(cached_env_vars[0], deny) != 0) {
941
942 clean_env_entries(&(sbcontext.deny_prefixes),
943 &(sbcontext.num_deny_prefixes));
944
945 if(NULL != cached_env_vars[0])
946 free(cached_env_vars[0]);
947
948 if(NULL != deny) {
949 init_env_entries(&(sbcontext.deny_prefixes),
950 &(sbcontext.num_deny_prefixes), ENV_SANDBOX_DENY, deny, 1);
951 cached_env_vars[0] = strdup(deny);
952 } else {
953 cached_env_vars[0] = NULL;
954 }
955 }
956
957 if((NULL == read && cached_env_vars[1] != read) || NULL == cached_env_vars[1] ||
958 strcmp(cached_env_vars[1], read) != 0) {
959
960 clean_env_entries(&(sbcontext.read_prefixes),
961 &(sbcontext.num_read_prefixes));
962
963 if(NULL != cached_env_vars[1])
964 free(cached_env_vars[1]);
965
966 if(NULL != read) {
967 init_env_entries(&(sbcontext.read_prefixes),
968 &(sbcontext.num_read_prefixes), ENV_SANDBOX_READ, read, 1);
969 cached_env_vars[1] = strdup(read);
970 } else {
971 cached_env_vars[1] = NULL;
972 }
973 }
974
975 if((NULL == write && cached_env_vars[2] != write) || NULL == cached_env_vars[2] ||
976 strcmp(cached_env_vars[2], write) != 0) {
977
978 clean_env_entries(&(sbcontext.write_prefixes),
979 &(sbcontext.num_write_prefixes));
980
981 if(NULL != cached_env_vars[2])
982 free(cached_env_vars[2]);
983
984 if(NULL != write) {
985 init_env_entries(&(sbcontext.write_prefixes),
986 &(sbcontext.num_write_prefixes), ENV_SANDBOX_WRITE, write, 1);
987 cached_env_vars[2] = strdup(write);
988 } else {
989 cached_env_vars[2] = NULL;
990 }
991 }
992
993 if((NULL == predict && cached_env_vars[3] != predict) || NULL == cached_env_vars[3] ||
994 strcmp(cached_env_vars[3], predict) != 0) {
995
996 clean_env_entries(&(sbcontext.predict_prefixes),
997 &(sbcontext.num_predict_prefixes));
998
999 if(NULL != cached_env_vars[3])
1000 free(cached_env_vars[3]);
1001
1002 if(NULL != predict) {
1003 init_env_entries(&(sbcontext.predict_prefixes),
1004 &(sbcontext.num_predict_prefixes), ENV_SANDBOX_PREDICT, predict, 1);
1005 cached_env_vars[3] = strdup(predict);
1006 } else {
1007 cached_env_vars[3] = NULL;
1008 }
1009
1010 }
1011
1012 /* Might have been reset in check_access() */
1013 sbcontext.show_access_violation = 1;
1014
1015 result = check_syscall(&sbcontext, func, file);
1016
1017 errno = old_errno;
1018
1019 if (0 == result) {
1020 if ((NULL != getenv(ENV_SANDBOX_PID)) && (is_env_on(ENV_SANDBOX_ABORT)))
1021 kill(atoi(getenv(ENV_SANDBOX_PID)), SIGUSR1);
1022
1023 /* FIXME: Should probably audit errno, and enable some other
1024 * error to be returned (EINVAL for invalid mode for
1025 * fopen() and co, ETOOLONG, etc). */
1026 errno = EACCES;
1027 }
1028
1029 return result;
1030 }
1031
1032 int before_syscall_access(const char *func, const char *file, int flags)
1033 {
1034 if (flags & W_OK) {
1035 return before_syscall("access_wr", file);
1036 } else {
1037 return before_syscall("access_rd", file);
1038 }
1039 }
1040
1041 int before_syscall_open_int(const char *func, const char *file, int flags)
1042 {
1043 if ((flags & O_WRONLY) || (flags & O_RDWR)) {
1044 return before_syscall("open_wr", file);
1045 } else {
1046 return before_syscall("open_rd", file);
1047 }
1048 }
1049
1050 int before_syscall_open_char(const char *func, const char *file, const char *mode)
1051 {
1052 if (NULL == mode)
1053 return 0;
1054
1055 if ((*mode == 'r') && ((0 == (strcmp(mode, "r"))) ||
1056 /* The strspn accept args are known non-writable modifiers */
1057 (strlen(++mode) == strspn(mode, "xbtmc")))) {
1058 return before_syscall("open_rd", file);
1059 } else {
1060 return before_syscall("open_wr", file);
1061 }
1062 }
1063
1064
1065 // 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