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

Contents of /trunk/libsandbox/libsandbox.c

Parent Directory Parent Directory | Revision Log Revision Log


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

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

  ViewVC Help
Powered by ViewVC 1.1.20