diff options
Diffstat (limited to 'src/sandbox-dev/libsandbox.c')
-rw-r--r-- | src/sandbox-dev/libsandbox.c | 1214 |
1 files changed, 1214 insertions, 0 deletions
diff --git a/src/sandbox-dev/libsandbox.c b/src/sandbox-dev/libsandbox.c new file mode 100644 index 000000000..ffb503592 --- /dev/null +++ b/src/sandbox-dev/libsandbox.c @@ -0,0 +1,1214 @@ +/* + * 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=<prefix> --host=<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 <gbevin@uwyn.com> + * + * 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 <brad@mainstreetsoftworks.com>. + * + * Martin Schlemmer <azarah@gentoo.org> (18 Aug 2002) + * + * Partly Copyright (C) 1998-9 Pancrazio `Ezio' de Mauro <p@demauro.net>, + * as some of the InstallWatch code was used. + * + * + * $Header: /var/cvsroot/gentoo-src/portage/src/sandbox-dev/Attic/libsandbox.c,v 1.4 2002/12/16 22:28:05 jrray Exp $ + * + */ + +/* Uncomment below to enable wrapping of mknod(). + * This is broken currently. */ +/* #define WRAP_MKNOD */ + + +#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 <dirent.h> +#include <dlfcn.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/param.h> +#include <unistd.h> +#include <utime.h> +#include <semaphore.h> + +#ifdef WRAP_MKNOD +# undef __xmknod +#endif + +#undef open +#undef open64 + +#include "localdecls.h" +#include "sandbox.h" + +#define PIDS_FILE "/tmp/sandboxpids.tmp" + +#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; \ +} + +static char sandbox_lib[255]; + +typedef struct { + char *last_env; + int count; + char **strs; +} sbprefix_t; + +typedef struct { + int show_access_violation; + sbprefix_t deny; + sbprefix_t read; + sbprefix_t write; + sbprefix_t predict; +} sbcontext_t; + +/* glibc modified realpath() functions */ +char *erealpath (const char *name, char *resolved); + +static void init_wrappers(void); +static void *get_dlsym(const char *); +static void 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(sbprefix_t *); +static void init_context(sbcontext_t *); +static void init_env_entries(sbprefix_t *, char *); +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 []); + +static sbcontext_t* sbcontext = NULL; +static sem_t ctxsem; + +/* + * 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 _init(void) +{ + int old_errno = errno; + char *tmp_string = NULL; + + if (sem_init(&ctxsem, 0, 1)) { + fprintf(stderr, "Failed to create semaphore\n"); + abort(); + } + + init_wrappers(); + + /* Get the path and name to this library */ + tmp_string = get_sandbox_lib("/"); + strncpy(sandbox_lib, tmp_string, 254); + + if (tmp_string) free(tmp_string); + tmp_string = NULL; + + errno = old_errno; +} + +void _fini(void) +{ + if (sbcontext) { + clean_env_entries(&sbcontext->deny); + clean_env_entries(&sbcontext->read); + clean_env_entries(&sbcontext->write); + clean_env_entries(&sbcontext->predict); + free(sbcontext); + sbcontext = NULL; + } + + /* free the semaphore */ + sem_destroy(&ctxsem); +} + +static void canonicalize(const char *path, char *resolved_path) +{ + int old_errno = errno; + + /* If path == NULL, return or we get a segfault */ + if (NULL == path) return; + + if(!erealpath(path, resolved_path) && (path[0] != '/')) { + /* The path could not be canonicalized, append it + * to the current working directory if it was not + * an absolute path + */ + getcwd(resolved_path, MAXPATHLEN - 2); + strcat(resolved_path, "/"); + strncat(resolved_path, path, MAXPATHLEN - 1 - strlen(resolved_path)); + erealpath(resolved_path, resolved_path); + } + + errno = old_errno; +} + +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[MAXPATHLEN]; + + canonicalize(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[MAXPATHLEN]; + + canonicalize(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[MAXPATHLEN]; + + canonicalize(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[MAXPATHLEN]; + + canonicalize(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) +{ +/* Linux specific? */ + int result = -1; + char canonic[MAXPATHLEN]; + + canonicalize(path, canonic); + + if FUNCTION_SANDBOX_SAFE("lchown", canonic) { + check_dlsym(chown); + result = true_chown(path, owner, group); + } + + return result; +} + +int link(const char *oldpath, const char *newpath) +{ + int result = -1; + char old_canonic[MAXPATHLEN], new_canonic[MAXPATHLEN]; + + canonicalize(oldpath, old_canonic); + canonicalize(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) +{ + int result = -1; + char canonic[MAXPATHLEN]; + + canonicalize(pathname, canonic); + + 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[MAXPATHLEN]; + + canonicalize(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[MAXPATHLEN]; + + canonicalize(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[MAXPATHLEN]; + + if (flags & O_CREAT) { + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); + } + + canonicalize(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[MAXPATHLEN], new_canonic[MAXPATHLEN]; + + canonicalize(oldpath, old_canonic); + canonicalize(newpath, new_canonic); + + if 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[MAXPATHLEN]; + + canonicalize(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[MAXPATHLEN], new_canonic[MAXPATHLEN]; + + canonicalize(oldpath, old_canonic); + canonicalize(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[MAXPATHLEN]; + + canonicalize(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[MAXPATHLEN]; + + canonicalize(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[MAXPATHLEN]; + + canonicalize(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[MAXPATHLEN]; + + canonicalize(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[MAXPATHLEN]; + + if (flags & O_CREAT) { + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); + } + + canonicalize(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[MAXPATHLEN]; + + canonicalize(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; + char canonic[MAXPATHLEN]; + char *old_envp = NULL; + char *new_envp = NULL; + + canonicalize(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)) { + break; + } else { + const int max_envp_len = strlen(envp[count]) + strlen(sandbox_lib) + 1; + + /* Backup envp[count], and set it to our own one which + * contains sandbox_lib */ + old_envp = envp[count]; + new_envp = strndupa(old_envp, max_envp_len - 1); + + /* LD_PRELOAD already have variables other than sandbox_lib, + * thus we have to add sandbox_lib via a white space. */ + if (0 != strcmp(envp[count], "LD_PRELOAD=")) { + strncpy(new_envp + strlen(old_envp), ":", + max_envp_len - strlen(new_envp)); + strncpy(new_envp + strlen(old_envp) + 1, sandbox_lib, + max_envp_len - strlen(new_envp)); + } else { + strncpy(new_envp + strlen(old_envp), sandbox_lib, + max_envp_len - strlen(new_envp)); + } + + /* Valid string? */ + new_envp[max_envp_len] = '\0'; + + /* envp[count] = new_envp; + * + * Get rid of the "read-only" warnings */ + memcpy((void *)&envp[count], &new_envp, sizeof(new_envp)); + + break; + } + } + count++; + } + + errno = old_errno; + check_dlsym(execve); + result = true_execve(filename, argv, envp); + old_errno = errno; + + if (old_envp) { + /* Restore envp[count] again. + * + * envp[count] = old_envp; */ + memcpy((void *)&envp[count], &old_envp, sizeof(old_envp)); + old_envp = 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) +{ + memset(context, 0, sizeof(sbcontext_t)); + context->show_access_violation = 1; +} + +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(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(sbprefix_t* prefix) +{ + int old_errno = errno; + int i = 0; + + if (NULL != prefix->strs) { + for (i = 0; i < prefix->count; i++) { + if (NULL != prefix->strs[i]) { + free(prefix->strs[i]); + prefix->strs[i] = NULL; + } + } + free(prefix->strs); + prefix->strs = NULL; + prefix->count = 0; + } + if (prefix->last_env) { + free(prefix->last_env); + prefix->last_env = NULL; + } + + errno = old_errno; +} + +static void init_env_entries(sbprefix_t* prefix, char* env) +{ + 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 *ptr; + int num_colons = 0; + + /* Check to see if the env value has changed since the + last time this was initalized, don't do the work again + if it hasn't. + */ + + if (prefix->last_env && !strcmp(prefix->last_env, prefixes_env)) { + errno = old_errno; + return; + } + + /* Clean any existing entries */ + clean_env_entries(prefix); + + /* Env value is different, update the cached copy */ + prefix->last_env = strdup(prefixes_env); + + ptr = prefixes_env; + while (*ptr) { + if (*ptr++ == ':') ++num_colons; + } + + if (prefix->strs) { + free(prefix->strs); + prefix->strs = 0; + } + prefix->strs = (char**)malloc((num_colons+1) * sizeof(char*)); + if (!prefix->strs) return; + memset(prefix->strs, 0, (num_colons+1) * sizeof(char*)); + prefix->count = 0; + + ptr = prefixes_env; + while (*ptr) { + char *next_colon = strchr(ptr, ':'); + if (next_colon) { + if (next_colon != ptr) { + char *str = strndup(ptr, next_colon-ptr); + if (!str) return; + prefix->strs[prefix->count++] = filter_path(str); + free(str); + } + } else { + prefix->strs[prefix->count++] = filter_path(ptr); + break; + } + + ptr = next_colon+1; + } + } + errno = old_errno; +} + +static char* filter_path(const char* path) +{ + int old_errno = errno; + char* filtered_path = (char *)malloc(MAXPATHLEN * sizeof(char)); + filtered_path[0] = 0; + + canonicalize(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) { + errno = old_errno; + return 0; + } + + if ('/' != filtered_path[0]) { + free(filtered_path); + errno = old_errno; + return 0; + } + + if ((0 == strncmp(filtered_path, "/etc/ld.so.preload", 18)) && (is_sandbox_pid())) { + result = 1; + } + + if (-1 == result) { + if (NULL != sbcontext->deny.strs) { + for (i = 0; i < sbcontext->deny.count; i++) { + if (NULL != sbcontext->deny.strs[i]) { + if (0 == strncmp(filtered_path, + sbcontext->deny.strs[i], + strlen(sbcontext->deny.strs[i]))) { + result = 0; + break; + } + } + } + } + + if (-1 == result) { + if ((NULL != sbcontext->read.strs) && + ((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->read.count; i++) { + if (NULL != sbcontext->read.strs[i]) { + if (0 == strncmp(filtered_path, + sbcontext->read.strs[i], + strlen(sbcontext->read.strs[i]))) { + result = 1; + break; + } + } + } + } + else if ((NULL != sbcontext->write.strs) && + ((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; + +#if 0 // write_denied is never set + + for (i = 0; i < sbcontext->write_denied.count; i++) { + if (NULL != sbcontext->write_denied.strs[i]) { + if (0 == strncmp(filtered_path, + sbcontext->write_denied.strs[i], + strlen(sbcontext->write_denied.strs[i]))) { + result = 0; + break; + } + } + } +#endif + + if (-1 == result) { + for (i = 0; i < sbcontext->write.count; i++) { + if (NULL != sbcontext->write.strs[i]) { + if (0 == strncmp(filtered_path, + sbcontext->write.strs[i], + strlen(sbcontext->write.strs[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->predict.count; i++) { + if (NULL != sbcontext->predict.strs[i]) { + if (0 == strncmp(filtered_path, + sbcontext->predict.strs[i], + strlen(sbcontext->predict.strs[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]; + + init_wrappers(); + + if ('/' == file[0]) { + absolute_path = (char *)malloc((strlen(file) + 1) * sizeof(char)); + sprintf(absolute_path, "%s", file); + } else { + tmp_buffer = get_current_dir_name(); + 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); + + 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", + log_path); + } else { + log_file = true_open(log_path, + 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); + } + } + } + } + + 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); + + 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", + log_path); + } else { + debug_log_file = true_open(debug_log_path, + 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); + } + } + } + } 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; + + /* Only allow one thread to access sbcontext at a time */ + sem_wait(&ctxsem); + + if (!sbcontext) { + sbcontext = (sbcontext_t*)malloc(sizeof(sbcontext_t)); + init_context(sbcontext); + } else { + /* sometimes this value gets set to 0 */ + sbcontext->show_access_violation = 1; + } + + init_env_entries(&sbcontext->deny, "SANDBOX_DENY"); + init_env_entries(&sbcontext->read, "SANDBOX_READ"); + init_env_entries(&sbcontext->write, "SANDBOX_WRITE"); + init_env_entries(&sbcontext->predict, "SANDBOX_PREDICT"); + + result = check_syscall(sbcontext, func, file); + + if (sem_post(&ctxsem)) { + fprintf(stderr, "Failed trying to release semaphore\n"); + } + + 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 ((strcmp(mode, "r") == 0) || (strcmp(mode, "rb") == 0)) { + return before_syscall("open_rd", file); + } else { + return before_syscall("open_wr", file); + } +} + + +// vim:expandtab noai:cindent ai |