/* * Path sandbox for the gentoo linux portage package system, initially * based on the ROCK Linux Wrapper for getting a list of created files * * to integrate with bash, bash should have been built like this * * ./configure --prefix= --host= --without-gnu-malloc * * it's very important that the --enable-static-link option is NOT specified * * Copyright (C) 2001 Geert Bevin, Uwyn, http://www.uwyn.com * Distributed under the terms of the GNU General Public License, v2 or later * Author : Geert Bevin * * Post Bevin leaving Gentoo ranks: * -------------------------------- * Ripped out all the wrappers, and implemented those of InstallWatch. * Losts of cleanups and bugfixes. Implement a execve that forces $LIBSANDBOX * in $LD_PRELOAD. Reformat the whole thing to look somewhat like the reworked * sandbox.c from Brad House . * * Martin Schlemmer (18 Aug 2002) * * Partly Copyright (C) 1998-9 Pancrazio `Ezio' de Mauro , * as some of the InstallWatch code was used. * * * $Header: /var/cvsroot/gentoo-src/portage/src/sandbox-1.1/Attic/libsandbox.c,v 1.22.2.3 2004/12/01 22:14:09 carpaski Exp $ * */ /* Uncomment below to enable wrapping of mknod(). * This is broken currently. */ /* #define WRAP_MKNOD 1 */ /* Uncomment below to enable the use of strtok_r(). */ #define REENTRANT_STRTOK 1 /* Uncomment below to enable memory debugging. */ /* #define SB_MEM_DEBUG 1 */ #define open xxx_open #define open64 xxx_open64 /* Wrapping mknod, do not have any effect, and * wrapping __xmknod causes calls to it to segfault */ #ifdef WRAP_MKNOD # define __xmknod xxx___xmknod #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SB_MEM_DEBUG # include #endif #ifdef WRAP_MKNOD # undef __xmknod #endif #undef open #undef open64 #include "localdecls.h" #include "sandbox.h" /* Macros to check if a function should be executed */ #define FUNCTION_SANDBOX_SAFE(func, path) \ ((0 == is_sandbox_on()) || (1 == before_syscall(func, path))) #define FUNCTION_SANDBOX_SAFE_INT(func, path, flags) \ ((0 == is_sandbox_on()) || (1 == before_syscall_open_int(func, path, flags))) #define FUNCTION_SANDBOX_SAFE_CHAR(func, path, mode) \ ((0 == is_sandbox_on()) || (1 == before_syscall_open_char(func, path, mode))) /* Macro to check if a wrapper is defined, if not * then try to resolve it again. */ #define check_dlsym(name) \ { \ int old_errno=errno; \ if (!true_ ## name) true_ ## name=get_dlsym(#name); \ errno=old_errno; \ } /* Macro to check if we could canonicalize a path. It returns an integer on * failure. */ #define canonicalize_int(path, resolved_path) \ { \ if (0 != canonicalize(path, resolved_path)) \ return -1; \ } /* Macro to check if we could canonicalize a path. It returns a NULL pointer on * failure. */ #define canonicalize_ptr(path, resolved_path) \ { \ if (0 != canonicalize(path, resolved_path)) \ return NULL; \ } static char sandbox_lib[255]; //static char sandbox_pids_file[255]; static char *sandbox_pids_file; typedef struct { int show_access_violation; char **deny_prefixes; int num_deny_prefixes; char **read_prefixes; int num_read_prefixes; char **write_prefixes; int num_write_prefixes; char **predict_prefixes; int num_predict_prefixes; char **write_denied_prefixes; int num_write_denied_prefixes; } sbcontext_t; /* glibc modified realpath() functions */ char *erealpath(const char *name, char *resolved); /* glibc modified getcwd() functions */ char *egetcwd(char *, size_t); static void init_wrappers(void); static void *get_dlsym(const char *); static int canonicalize(const char *, char *); static int check_access(sbcontext_t *, const char *, const char *); static int check_syscall(sbcontext_t *, const char *, const char *); static int before_syscall(const char *, const char *); static int before_syscall_open_int(const char *, const char *, int); static int before_syscall_open_char(const char *, const char *, const char *); static void clean_env_entries(char ***, int *); static void init_context(sbcontext_t *); static void init_env_entries(char ***, int *, char *, int); static char *filter_path(const char *); static int is_sandbox_on(); static int is_sandbox_pid(); /* Wrapped functions */ extern int chmod(const char *, mode_t); static int (*true_chmod) (const char *, mode_t); extern int chown(const char *, uid_t, gid_t); static int (*true_chown) (const char *, uid_t, gid_t); extern int creat(const char *, mode_t); static int (*true_creat) (const char *, mode_t); extern FILE *fopen(const char *, const char *); static FILE *(*true_fopen) (const char *, const char *); extern int lchown(const char *, uid_t, gid_t); static int (*true_lchown) (const char *, uid_t, gid_t); extern int link(const char *, const char *); static int (*true_link) (const char *, const char *); extern int mkdir(const char *, mode_t); static int (*true_mkdir) (const char *, mode_t); extern DIR *opendir(const char *); static DIR *(*true_opendir) (const char *); #ifdef WRAP_MKNOD extern int __xmknod(const char *, mode_t, dev_t); static int (*true___xmknod) (const char *, mode_t, dev_t); #endif extern int open(const char *, int, ...); static int (*true_open) (const char *, int, ...); extern int rename(const char *, const char *); static int (*true_rename) (const char *, const char *); extern int rmdir(const char *); static int (*true_rmdir) (const char *); extern int symlink(const char *, const char *); static int (*true_symlink) (const char *, const char *); extern int truncate(const char *, TRUNCATE_T); static int (*true_truncate) (const char *, TRUNCATE_T); extern int unlink(const char *); static int (*true_unlink) (const char *); #if (GLIBC_MINOR >= 1) extern int creat64(const char *, __mode_t); static int (*true_creat64) (const char *, __mode_t); extern FILE *fopen64(const char *, const char *); static FILE *(*true_fopen64) (const char *, const char *); extern int open64(const char *, int, ...); static int (*true_open64) (const char *, int, ...); extern int truncate64(const char *, __off64_t); static int (*true_truncate64) (const char *, __off64_t); #endif extern int execve(const char *filename, char *const argv[], char *const envp[]); static int (*true_execve) (const char *, char *const[], char *const[]); /* * Initialize the shabang */ static void init_wrappers(void) { void *libc_handle = NULL; #ifdef BROKEN_RTLD_NEXT // printf ("RTLD_LAZY"); libc_handle = dlopen(LIBC_VERSION, RTLD_LAZY); #else // printf ("RTLD_NEXT"); libc_handle = RTLD_NEXT; #endif true_chmod = dlsym(libc_handle, "chmod"); true_chown = dlsym(libc_handle, "chown"); true_creat = dlsym(libc_handle, "creat"); true_fopen = dlsym(libc_handle, "fopen"); true_lchown = dlsym(libc_handle, "lchown"); true_link = dlsym(libc_handle, "link"); true_mkdir = dlsym(libc_handle, "mkdir"); true_opendir = dlsym(libc_handle, "opendir"); #ifdef WRAP_MKNOD true___xmknod = dlsym(libc_handle, "__xmknod"); #endif true_open = dlsym(libc_handle, "open"); true_rename = dlsym(libc_handle, "rename"); true_rmdir = dlsym(libc_handle, "rmdir"); true_symlink = dlsym(libc_handle, "symlink"); true_truncate = dlsym(libc_handle, "truncate"); true_unlink = dlsym(libc_handle, "unlink"); #if (GLIBC_MINOR >= 1) true_creat64 = dlsym(libc_handle, "creat64"); true_fopen64 = dlsym(libc_handle, "fopen64"); true_open64 = dlsym(libc_handle, "open64"); true_truncate64 = dlsym(libc_handle, "truncate64"); #endif true_execve = dlsym(libc_handle, "execve"); } void _fini(void) { free(sandbox_pids_file); } void _init(void) { int old_errno = errno; char *tmp_string = NULL; #ifdef SB_MEM_DEBUG mtrace(); #endif init_wrappers(); /* Get the path and name to this library */ tmp_string = get_sandbox_lib("/"); strncpy(sandbox_lib, tmp_string, sizeof(sandbox_lib)-1); if (tmp_string) free(tmp_string); tmp_string = NULL; /* Generate sandbox pids-file path */ sandbox_pids_file = get_sandbox_pids_file(); errno = old_errno; } static int canonicalize(const char *path, char *resolved_path) { int old_errno = errno; char *retval; *resolved_path = '\0'; /* If path == NULL, return or we get a segfault */ if (NULL == path) { errno = EINVAL; return -1; } /* Do not try to resolve an empty path */ if ('\0' == path[0]) { errno = old_errno; return 0; } retval = erealpath(path, resolved_path); if ((!retval) && (path[0] != '/')) { /* The path could not be canonicalized, append it * to the current working directory if it was not * an absolute path */ if (errno == ENAMETOOLONG) return -1; egetcwd(resolved_path, SB_PATH_MAX - 2); strcat(resolved_path, "/"); strncat(resolved_path, path, SB_PATH_MAX - 1); if (!erealpath(resolved_path, resolved_path)) { if (errno == ENAMETOOLONG) { /* The resolved path is too long for the buffer to hold */ return -1; } else { /* Whatever it resolved, is not a valid path */ errno = ENOENT; return -1; } } } else if ((!retval) && (path[0] == '/')) { /* Whatever it resolved, is not a valid path */ errno = ENOENT; return -1; } errno = old_errno; return 0; } static void * get_dlsym(const char *symname) { void *libc_handle = NULL; void *symaddr = NULL; #ifdef BROKEN_RTLD_NEXT libc_handle = dlopen(LIBC_VERSION, RTLD_LAZY); if (!libc_handle) { printf("libsandbox.so: Can't dlopen libc: %s\n", dlerror()); abort(); } #else libc_handle = RTLD_NEXT; #endif symaddr = dlsym(libc_handle, symname); if (!symaddr) { printf("libsandbox.so: Can't resolve %s: %s\n", symname, dlerror()); abort(); } return symaddr; } /* * Wrapper Functions */ int chmod(const char *path, mode_t mode) { int result = -1; char canonic[SB_PATH_MAX]; canonicalize_int(path, canonic); if FUNCTION_SANDBOX_SAFE ("chmod", canonic) { check_dlsym(chmod); result = true_chmod(path, mode); } return result; } int chown(const char *path, uid_t owner, gid_t group) { int result = -1; char canonic[SB_PATH_MAX]; canonicalize_int(path, canonic); if FUNCTION_SANDBOX_SAFE ("chown", canonic) { check_dlsym(chown); result = true_chown(path, owner, group); } return result; } int creat(const char *pathname, mode_t mode) { /* Is it a system call? */ int result = -1; char canonic[SB_PATH_MAX]; canonicalize_int(pathname, canonic); if FUNCTION_SANDBOX_SAFE ("creat", canonic) { check_dlsym(open); result = true_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode); } return result; } FILE * fopen(const char *pathname, const char *mode) { FILE *result = NULL; char canonic[SB_PATH_MAX]; canonicalize_ptr(pathname, canonic); if FUNCTION_SANDBOX_SAFE_CHAR ("fopen", canonic, mode) { check_dlsym(fopen); result = true_fopen(pathname, mode); } return result; } int lchown(const char *path, uid_t owner, gid_t group) { int result = -1; char canonic[SB_PATH_MAX]; canonicalize_int(path, canonic); if FUNCTION_SANDBOX_SAFE ("lchown", canonic) { check_dlsym(lchown); result = true_lchown(path, owner, group); } return result; } int link(const char *oldpath, const char *newpath) { int result = -1; char old_canonic[SB_PATH_MAX], new_canonic[SB_PATH_MAX]; canonicalize_int(oldpath, old_canonic); canonicalize_int(newpath, new_canonic); if FUNCTION_SANDBOX_SAFE ("link", new_canonic) { check_dlsym(link); result = true_link(oldpath, newpath); } return result; } int mkdir(const char *pathname, mode_t mode) // returns 0 success, or -1 if an error occurred { int result = -1, my_errno = errno; char canonic[SB_PATH_MAX]; struct stat st; canonicalize_int(pathname, canonic); /* Check if the directory exist, return EEXIST rather than failing */ if (0 == lstat(canonic, &st)) { errno = EEXIST; return -1; } errno = my_errno; if FUNCTION_SANDBOX_SAFE ("mkdir", canonic) { check_dlsym(mkdir); result = true_mkdir(pathname, mode); } return result; } DIR * opendir(const char *name) { DIR *result = NULL; char canonic[SB_PATH_MAX]; canonicalize_ptr(name, canonic); if FUNCTION_SANDBOX_SAFE ("opendir", canonic) { check_dlsym(opendir); result = true_opendir(name); } return result; } #ifdef WRAP_MKNOD int __xmknod(const char *pathname, mode_t mode, dev_t dev) { int result = -1; char canonic[SB_PATH_MAX]; canonicalize_int(pathname, canonic); if FUNCTION_SANDBOX_SAFE ("__xmknod", canonic) { check_dlsym(__xmknod); result = true___xmknod(pathname, mode, dev); } return result; } #endif int open(const char *pathname, int flags, ...) { /* Eventually, there is a third parameter: it's mode_t mode */ va_list ap; mode_t mode = 0; int result = -1; char canonic[SB_PATH_MAX]; if (flags & O_CREAT) { va_start(ap, flags); mode = va_arg(ap, mode_t); va_end(ap); } canonicalize_int(pathname, canonic); if FUNCTION_SANDBOX_SAFE_INT ("open", canonic, flags) { /* We need to resolve open() realtime in some cases, * else we get a segfault when running /bin/ps, etc * in a sandbox */ check_dlsym(open); result = true_open(pathname, flags, mode); } return result; } int rename(const char *oldpath, const char *newpath) { int result = -1; char old_canonic[SB_PATH_MAX], new_canonic[SB_PATH_MAX]; canonicalize_int(oldpath, old_canonic); canonicalize_int(newpath, new_canonic); if (FUNCTION_SANDBOX_SAFE("rename", old_canonic) && FUNCTION_SANDBOX_SAFE("rename", new_canonic)) { check_dlsym(rename); result = true_rename(oldpath, newpath); } return result; } int rmdir(const char *pathname) { int result = -1; char canonic[SB_PATH_MAX]; canonicalize_int(pathname, canonic); if FUNCTION_SANDBOX_SAFE ("rmdir", canonic) { check_dlsym(rmdir); result = true_rmdir(pathname); } return result; } int symlink(const char *oldpath, const char *newpath) { int result = -1; char old_canonic[SB_PATH_MAX], new_canonic[SB_PATH_MAX]; canonicalize_int(oldpath, old_canonic); canonicalize_int(newpath, new_canonic); if FUNCTION_SANDBOX_SAFE ("symlink", new_canonic) { check_dlsym(symlink); result = true_symlink(oldpath, newpath); } return result; } int truncate(const char *path, TRUNCATE_T length) { int result = -1; char canonic[SB_PATH_MAX]; canonicalize_int(path, canonic); if FUNCTION_SANDBOX_SAFE ("truncate", canonic) { check_dlsym(truncate); result = true_truncate(path, length); } return result; } int unlink(const char *pathname) { int result = -1; char canonic[SB_PATH_MAX]; canonicalize_int(pathname, canonic); if FUNCTION_SANDBOX_SAFE ("unlink", canonic) { check_dlsym(unlink); result = true_unlink(pathname); } return result; } #if (GLIBC_MINOR >= 1) int creat64(const char *pathname, __mode_t mode) { /* Is it a system call? */ int result = -1; char canonic[SB_PATH_MAX]; canonicalize_int(pathname, canonic); if FUNCTION_SANDBOX_SAFE ("creat64", canonic) { check_dlsym(open64); result = true_open64(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode); } return result; } FILE * fopen64(const char *pathname, const char *mode) { FILE *result = NULL; char canonic[SB_PATH_MAX]; canonicalize_ptr(pathname, canonic); if FUNCTION_SANDBOX_SAFE_CHAR ("fopen64", canonic, mode) { check_dlsym(fopen64); result = true_fopen(pathname, mode); } return result; } int open64(const char *pathname, int flags, ...) { /* Eventually, there is a third parameter: it's mode_t mode */ va_list ap; mode_t mode = 0; int result = -1; char canonic[SB_PATH_MAX]; if (flags & O_CREAT) { va_start(ap, flags); mode = va_arg(ap, mode_t); va_end(ap); } canonicalize_int(pathname, canonic); if FUNCTION_SANDBOX_SAFE_INT ("open64", canonic, flags) { check_dlsym(open64); result = true_open64(pathname, flags, mode); } return result; } int truncate64(const char *path, __off64_t length) { int result = -1; char canonic[SB_PATH_MAX]; canonicalize_int(path, canonic); if FUNCTION_SANDBOX_SAFE ("truncate64", canonic) { check_dlsym(truncate64); result = true_truncate64(path, length); } return result; } #endif /* GLIBC_MINOR >= 1 */ /* * Exec Wrappers */ int execve(const char *filename, char *const argv[], char *const envp[]) { int old_errno = errno; int result = -1; int count = 0; int env_len = 0; char canonic[SB_PATH_MAX]; char **my_env = NULL; int kill_env = 1; /* We limit the size LD_PRELOAD can be here, but it should be enough */ char tmp_str[4096]; canonicalize_int(filename, canonic); if FUNCTION_SANDBOX_SAFE ("execve", canonic) { while (envp[count] != NULL) { if (strstr(envp[count], "LD_PRELOAD=") == envp[count]) { if (NULL != strstr(envp[count], sandbox_lib)) { my_env = (char **) envp; kill_env = 0; break; } else { int i = 0; const int max_envp_len = strlen(envp[count]) + strlen(sandbox_lib) + 1; /* Fail safe ... */ if (max_envp_len > 4096) { fprintf(stderr, "sandbox: max_envp_len too big!\n"); errno = ENOMEM; return result; } /* Calculate envp size */ my_env = (char **) envp; do env_len += 1; while (*my_env++); my_env = (char **) malloc((env_len + 2) * sizeof (char *)); if (NULL == my_env) { errno = ENOMEM; return result; } /* Copy envp to my_env */ do my_env[i] = envp[i]; while (envp[i++]); /* Set tmp_str to envp[count] */ strncpy(tmp_str, envp[count], max_envp_len - 1); /* LD_PRELOAD already have variables other than sandbox_lib, * thus we have to add sandbox_lib seperated via a whitespace. */ if (0 != strncmp(envp[count], "LD_PRELOAD=", max_envp_len - 1)) { strncat(tmp_str, " ", max_envp_len - strlen(tmp_str)); strncat(tmp_str, sandbox_lib, max_envp_len - strlen(tmp_str)); } else { strncat(tmp_str, sandbox_lib, max_envp_len - strlen(tmp_str)); } /* Valid string? */ tmp_str[max_envp_len] = '\0'; /* Ok, replace my_env[count] with our version that contains * sandbox_lib ... */ my_env[count] = tmp_str; break; } } count++; } errno = old_errno; check_dlsym(execve); result = true_execve(filename, argv, my_env); old_errno = errno; if (my_env && kill_env) { free(my_env); my_env = NULL; } } errno = old_errno; return result; } /* * Internal Functions */ #if (GLIBC_MINOR == 1) /* This hack is needed for glibc 2.1.1 (and others?) * (not really needed, but good example) */ extern int fclose(FILE *); static int (*true_fclose) (FILE *) = NULL; int fclose(FILE * file) { int result = -1; check_dlsym(fclose); result = true_fclose(file); return result; } #endif /* GLIBC_MINOR == 1 */ static void init_context(sbcontext_t * context) { context->show_access_violation = 1; context->deny_prefixes = NULL; context->num_deny_prefixes = 0; context->read_prefixes = NULL; context->num_read_prefixes = 0; context->write_prefixes = NULL; context->num_write_prefixes = 0; context->predict_prefixes = NULL; context->num_predict_prefixes = 0; context->write_denied_prefixes = NULL; context->num_write_denied_prefixes = 0; } static int is_sandbox_pid() { int old_errno = errno; int result = 0; FILE *pids_stream = NULL; int pids_file = -1; int current_pid = 0; int tmp_pid = 0; init_wrappers(); pids_stream = true_fopen(sandbox_pids_file, "r"); if (NULL == pids_stream) { perror(">>> pids file fopen"); } else { pids_file = fileno(pids_stream); if (pids_file < 0) { perror(">>> pids file fileno"); } else { current_pid = getpid(); while (EOF != fscanf(pids_stream, "%d\n", &tmp_pid)) { if (tmp_pid == current_pid) { result = 1; break; } } } if (EOF == fclose(pids_stream)) { perror(">>> pids file fclose"); } pids_stream = NULL; pids_file = -1; } errno = old_errno; return result; } static void clean_env_entries(char ***prefixes_array, int *prefixes_num) { int old_errno = errno; int i = 0; if (NULL != *prefixes_array) { for (i = 0; i < *prefixes_num; i++) { if (NULL != (*prefixes_array)[i]) { free((*prefixes_array)[i]); (*prefixes_array)[i] = NULL; } } if (*prefixes_array) free(*prefixes_array); *prefixes_array = NULL; *prefixes_num = 0; } errno = old_errno; } static void init_env_entries(char ***prefixes_array, int *prefixes_num, char *env, int warn) { int old_errno = errno; char *prefixes_env = getenv(env); if (NULL == prefixes_env) { fprintf(stderr, "Sandbox error : the %s environmental variable should be defined.\n", env); } else { char *buffer = NULL; int prefixes_env_length = strlen(prefixes_env); int i = 0; int num_delimiters = 0; char *token = NULL; char *prefix = NULL; for (i = 0; i < prefixes_env_length; i++) { if (':' == prefixes_env[i]) { num_delimiters++; } } if (num_delimiters > 0) { *prefixes_array = (char **) malloc((num_delimiters + 1) * sizeof (char *)); buffer = strndupa(prefixes_env, prefixes_env_length); #ifdef REENTRANT_STRTOK token = strtok_r(buffer, ":", &buffer); #else token = strtok(buffer, ":"); #endif while ((NULL != token) && (strlen(token) > 0)) { prefix = strndup(token, strlen(token)); (*prefixes_array)[(*prefixes_num)++] = filter_path(prefix); #ifdef REENTRANT_STRTOK token = strtok_r(NULL, ":", &buffer); #else token = strtok(NULL, ":"); #endif if (prefix) free(prefix); prefix = NULL; } } else if (prefixes_env_length > 0) { (*prefixes_array) = (char **) malloc(sizeof (char *)); (*prefixes_array)[(*prefixes_num)++] = filter_path(prefixes_env); } } errno = old_errno; } static char * filter_path(const char *path) { int old_errno = errno; char *filtered_path = (char *) malloc(SB_PATH_MAX * sizeof (char)); canonicalize_ptr(path, filtered_path); errno = old_errno; return filtered_path; } static int check_access(sbcontext_t * sbcontext, const char *func, const char *path) { int old_errno = errno; int result = -1; int i = 0; char *filtered_path = filter_path(path); if ('/' != filtered_path[0]) { errno = old_errno; if (filtered_path) free(filtered_path); filtered_path = NULL; return 0; } if ((0 == strncmp(filtered_path, "/etc/ld.so.preload", 18)) && (is_sandbox_pid())) { result = 1; } if (-1 == result) { if (NULL != sbcontext->deny_prefixes) { for (i = 0; i < sbcontext->num_deny_prefixes; i++) { if (NULL != sbcontext->deny_prefixes[i]) { if (0 == strncmp(filtered_path, sbcontext-> deny_prefixes[i], strlen(sbcontext->deny_prefixes[i]))) { result = 0; break; } } } } if (-1 == result) { if ((NULL != sbcontext->read_prefixes) && ((0 == strncmp(func, "open_rd", 7)) || (0 == strncmp(func, "popen", 5)) || (0 == strncmp(func, "opendir", 7)) || (0 == strncmp(func, "system", 6)) || (0 == strncmp(func, "execl", 5)) || (0 == strncmp(func, "execlp", 6)) || (0 == strncmp(func, "execle", 6)) || (0 == strncmp(func, "execv", 5)) || (0 == strncmp(func, "execvp", 6)) || (0 == strncmp(func, "execve", 6)) ) ) { for (i = 0; i < sbcontext->num_read_prefixes; i++) { if (NULL != sbcontext->read_prefixes[i]) { if (0 == strncmp(filtered_path, sbcontext-> read_prefixes[i], strlen(sbcontext->read_prefixes[i]))) { result = 1; break; } } } } else if ((NULL != sbcontext->write_prefixes) && ((0 == strncmp(func, "open_wr", 7)) || (0 == strncmp(func, "creat", 5)) || (0 == strncmp(func, "creat64", 7)) || (0 == strncmp(func, "mkdir", 5)) || (0 == strncmp(func, "mknod", 5)) || (0 == strncmp(func, "mkfifo", 6)) || (0 == strncmp(func, "link", 4)) || (0 == strncmp(func, "symlink", 7)) || (0 == strncmp(func, "rename", 6)) || (0 == strncmp(func, "utime", 5)) || (0 == strncmp(func, "utimes", 6)) || (0 == strncmp(func, "unlink", 6)) || (0 == strncmp(func, "rmdir", 5)) || (0 == strncmp(func, "chown", 5)) || (0 == strncmp(func, "lchown", 6)) || (0 == strncmp(func, "chmod", 5)) || (0 == strncmp(func, "truncate", 8)) || (0 == strncmp(func, "ftruncate", 9)) || (0 == strncmp(func, "truncate64", 10)) || (0 == strncmp(func, "ftruncate64", 11)) ) ) { struct stat tmp_stat; for (i = 0; i < sbcontext->num_write_denied_prefixes; i++) { if (NULL != sbcontext->write_denied_prefixes[i]) { if (0 == strncmp(filtered_path, sbcontext-> write_denied_prefixes [i], strlen(sbcontext->write_denied_prefixes[i]))) { result = 0; break; } } } if (-1 == result) { for (i = 0; i < sbcontext->num_write_prefixes; i++) { if (NULL != sbcontext->write_prefixes[i]) { if (0 == strncmp (filtered_path, sbcontext->write_prefixes[i], strlen(sbcontext->write_prefixes[i]))) { result = 1; break; } } } if (-1 == result) { /* hack to prevent mkdir of existing dirs to show errors */ if (0 == strncmp(func, "mkdir", 5)) { if (0 == stat(filtered_path, &tmp_stat)) { sbcontext->show_access_violation = 0; result = 0; } } if (-1 == result) { for (i = 0; i < sbcontext->num_predict_prefixes; i++) { if (NULL != sbcontext->predict_prefixes[i]) { if (0 == strncmp (filtered_path, sbcontext-> predict_prefixes[i], strlen(sbcontext->predict_prefixes[i]))) { sbcontext->show_access_violation = 0; result = 0; break; } } } } } } } } } if (-1 == result) { result = 0; } if (filtered_path) free(filtered_path); filtered_path = NULL; errno = old_errno; return result; } static int check_syscall(sbcontext_t * sbcontext, const char *func, const char *file) { int old_errno = errno; int result = 1; struct stat log_stat; char *log_path = NULL; char *absolute_path = NULL; char *tmp_buffer = NULL; int log_file = 0; struct stat debug_log_stat; char *debug_log_env = NULL; char *debug_log_path = NULL; int debug_log_file = 0; char buffer[512]; char *dpath = NULL; init_wrappers(); if ('/' == file[0]) { absolute_path = (char *) malloc((strlen(file) + 1) * sizeof (char)); sprintf(absolute_path, "%s", file); } else { tmp_buffer = (char *) malloc(SB_PATH_MAX * sizeof (char)); egetcwd(tmp_buffer, SB_PATH_MAX - 1); absolute_path = (char *) malloc((strlen(tmp_buffer) + 1 + strlen(file) + 1) * sizeof (char)); sprintf(absolute_path, "%s/%s", tmp_buffer, file); if (tmp_buffer) free(tmp_buffer); tmp_buffer = NULL; } log_path = getenv("SANDBOX_LOG"); debug_log_env = getenv("SANDBOX_DEBUG"); debug_log_path = getenv("SANDBOX_DEBUG_LOG"); if (((NULL == log_path) || (0 != strncmp(absolute_path, log_path, strlen(log_path)))) && ((NULL == debug_log_env) || (NULL == debug_log_path) || (0 != strncmp(absolute_path, debug_log_path, strlen(debug_log_path)))) && (0 == check_access(sbcontext, func, absolute_path)) ) { if (1 == sbcontext->show_access_violation) { fprintf(stderr, "\e[31;01mACCESS DENIED\033[0m %s:%*s%s\n", func, (int) (10 - strlen(func)), "", absolute_path); if (NULL != log_path) { sprintf(buffer, "%s:%*s%s\n", func, (int) (10 - strlen(func)), "", absolute_path); // log_path somehow gets corrupted. figuring out why would be good. dpath = strdup(log_path); if ((0 == lstat(log_path, &log_stat)) && (0 == S_ISREG(log_stat.st_mode)) ) { fprintf(stderr, "\e[31;01mSECURITY BREACH\033[0m %s already exists and is not a regular file.\n", dpath); } else if (0 == check_access(sbcontext, "open_wr", dpath)) { unsetenv("SANDBOX_LOG"); fprintf(stderr, "\e[31;01mSECURITY BREACH\033[0m SANDBOX_LOG %s isn't allowed via SANDBOX_WRITE\n", dpath); } else { log_file = true_open(dpath, O_APPEND | O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (log_file >= 0) { write(log_file, buffer, strlen(buffer)); close(log_file); } } free(dpath); } } result = 0; } else if (NULL != debug_log_env) { if (NULL != debug_log_path) { if (0 != strncmp(absolute_path, debug_log_path, strlen(debug_log_path))) { sprintf(buffer, "%s:%*s%s\n", func, (int) (10 - strlen(func)), "", absolute_path); //debug_log_path somehow gets corupted, same thing as log_path above. dpath = strdup(debug_log_path); if ((0 == lstat(debug_log_path, &debug_log_stat)) && (0 == S_ISREG(debug_log_stat.st_mode)) ) { fprintf(stderr, "\e[31;01mSECURITY BREACH\033[0m %s already exists and is not a regular file.\n", debug_log_path); } else if (0 == check_access(sbcontext, "open_wr", dpath)) { unsetenv("SANDBOX_DEBUG"); unsetenv("SANDBOX_DEBUG_LOG"); fprintf(stderr, "\e[31;01mSECURITY BREACH\033[0m SANDBOX_DEBUG_LOG %s isn't allowed by SANDBOX_WRITE.\n", dpath); } else { debug_log_file = true_open(dpath, O_APPEND | O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (debug_log_file >= 0) { write(debug_log_file, buffer, strlen(buffer)); close(debug_log_file); } } free(dpath); } } else { fprintf(stderr, "\e[32;01mACCESS ALLOWED\033[0m %s:%*s%s\n", func, (int) (10 - strlen(func)), "", absolute_path); } } if (absolute_path) free(absolute_path); absolute_path = NULL; errno = old_errno; return result; } static int is_sandbox_on() { int old_errno = errno; /* $SANDBOX_ACTIVE is an env variable that should ONLY * be used internal by sandbox.c and libsanbox.c. External * sources should NEVER set it, else the sandbox is enabled * in some cases when run in parallel with another sandbox, * but not even in the sandbox shell. * * Azarah (3 Aug 2002) */ if ((NULL != getenv("SANDBOX_ON")) && (0 == strncmp(getenv("SANDBOX_ON"), "1", 1)) && (NULL != getenv("SANDBOX_ACTIVE")) && (0 == strncmp(getenv("SANDBOX_ACTIVE"), "armedandready", 13)) ) { errno = old_errno; return 1; } else { errno = old_errno; return 0; } } static int before_syscall(const char *func, const char *file) { int old_errno = errno; int result = 1; sbcontext_t sbcontext; if (!strlen(file)) { /* The file/directory does not exist */ errno = ENOENT; return 0; } init_context(&sbcontext); init_env_entries(&(sbcontext.deny_prefixes), &(sbcontext.num_deny_prefixes), "SANDBOX_DENY", 1); init_env_entries(&(sbcontext.read_prefixes), &(sbcontext.num_read_prefixes), "SANDBOX_READ", 1); init_env_entries(&(sbcontext.write_prefixes), &(sbcontext.num_write_prefixes), "SANDBOX_WRITE", 1); init_env_entries(&(sbcontext.predict_prefixes), &(sbcontext.num_predict_prefixes), "SANDBOX_PREDICT", 1); result = check_syscall(&sbcontext, func, file); clean_env_entries(&(sbcontext.deny_prefixes), &(sbcontext.num_deny_prefixes)); clean_env_entries(&(sbcontext.read_prefixes), &(sbcontext.num_read_prefixes)); clean_env_entries(&(sbcontext.write_prefixes), &(sbcontext.num_write_prefixes)); clean_env_entries(&(sbcontext.predict_prefixes), &(sbcontext.num_predict_prefixes)); errno = old_errno; if (0 == result) { errno = EACCES; } return result; } static int before_syscall_open_int(const char *func, const char *file, int flags) { if ((flags & O_WRONLY) || (flags & O_RDWR)) { return before_syscall("open_wr", file); } else { return before_syscall("open_rd", file); } } static int before_syscall_open_char(const char *func, const char *file, const char *mode) { if (*mode == 'r' && ((strcmp(mode, "r") == 0) || /* The strspn accept args are known non-writable modifiers */ (strlen(++mode) == strspn(mode, "xbtmc")))) { return before_syscall("open_rd", file); } else { return before_syscall("open_wr", file); } } #include "getcwd.c" #include "canonicalize.c" // vim:expandtab noai:cindent ai