/* ** 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 ** $Header: /var/cvsroot/gentoo-src/portage/src/sandbox/Attic/sandbox.c,v 1.13 2002/08/05 05:51:39 drobbins Exp $ */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define LD_PRELOAD_FILE "/etc/ld.so.preload" #define LIB_NAME "libsandbox.so" #define BASHRC_NAME "sandbox.bashrc" #define PIDS_FILE "/tmp/sandboxpids.tmp" #define LOG_FILE_PREFIX "/tmp/sandbox-" #define DEBUG_LOG_FILE_PREFIX "/tmp/sandbox-debug-" #define LOG_FILE_EXT ".log" #define ENV_SANDBOX_DEBUG_LOG "SANDBOX_DEBUG_LOG" #define ENV_SANDBOX_LOG "SANDBOX_LOG" #define ENV_SANDBOX_DIR "SANDBOX_DIR" #define ENV_SANDBOX_LIB "SANDBOX_LIB" #define ENV_SANDBOX_DENY "SANDBOX_DENY" #define ENV_SANDBOX_READ "SANDBOX_READ" #define ENV_SANDBOX_WRITE "SANDBOX_WRITE" #define ENV_SANDBOX_PREDICT "SANDBOX_PREDICT" #define ENV_SANDBOX_ON "SANDBOX_ON" #define ENV_SANDBOX_BEEP "SANDBOX_BEEP" #define DEFAULT_BEEP_COUNT 3 int preload_adaptable = 1; int cleaned_up = 0; char* dirname(const char* path) { char* base = NULL; unsigned int length = 0; base = strrchr(path, '/'); if (NULL == base) { return strdup("."); } while (base > path && *base == '/') { base--; } length = (unsigned int) 1 + base - path; base = malloc(sizeof(char)*(length+1)); memmove(base, path, length); base[length] = 0; return base; } void cleanup() { int i = 0; int success = 1; FILE* preload_stream = NULL; int preload_file = -1; char preload_entry[255]; char** preload_array = NULL; int num_of_preloads = 0; FILE* pids_stream = NULL; struct stat pids_stat; int pids_file = -1; char pid_string[255]; int tmp_pid = 0; int* pids_array = NULL; int num_of_pids = 0; /* remove this sandbox's bash pid from the global pids file if it has rights to adapt the ld.so.preload file*/ if (1 == preload_adaptable && 0 == cleaned_up) { cleaned_up = 1; success = 1; if (0 == lstat(PIDS_FILE, &pids_stat) && 0 == S_ISREG(pids_stat.st_mode)) { perror(">>> pids file is not a regular file"); success = 0; } else { pids_stream = fopen(PIDS_FILE, "r+"); if (NULL == pids_stream) { perror(">>> pids file fopen"); success = 0; } else { pids_file = fileno(pids_stream); if (pids_file < 0) { perror(">>> pids file fileno"); success = 0; } else { if (flock(pids_file, LOCK_EX) < 0) { perror(">>> pids file lock"); success = 0; } else { /* check which sandbox pids are still running */ while (EOF != fscanf(pids_stream, "%d\n", &tmp_pid)) { if (0 == kill(tmp_pid, 0)) { if (NULL == pids_array) { pids_array = (int*)malloc(sizeof(int)); } else { pids_array = (int*)realloc(pids_array, sizeof(int)*(num_of_pids+1)); } pids_array[num_of_pids++] = tmp_pid; } } /* clean the /etc/ld.so.preload file if no other sandbox processes are running anymore*/ if(num_of_pids == 1) { success = 1; preload_stream = fopen("/etc/ld.so.preload", "r+"); if (NULL == preload_stream) { perror(">>> /etc/ld.so.preload file fopen"); success = 0; } else { preload_file = fileno(preload_stream); if (preload_file < 0) { perror(">>> /etc/ld.so.preload file fileno"); success = 0; } else { if (flock(preload_file, LOCK_EX) < 0) { perror(">>> /etc/ld.so.preload file lock"); success = 0; } else { /* only get the entries that don't contain the sandbox library from the /etc/ld.so.preload file */ while (EOF != fscanf(preload_stream, "%s\n", preload_entry)) { if (NULL == strstr(preload_entry, LIB_NAME)) { if (NULL == preload_array) { preload_array = (char**)malloc(sizeof(char*)); } else { preload_array = (char**)realloc(pids_array, sizeof(char*)*(num_of_preloads+1)); } preload_array[num_of_preloads++] = strdup(preload_entry); } } if (fseek(preload_stream, 0, SEEK_SET) < 0) { perror(">>> /etc/ld.so.preload file fseek"); success = 0; } else { /* empty the /etc/ld.so.preload file */ if (ftruncate(preload_file, 0) < 0) { perror(">>> /etc/ld.so.preload file ftruncate"); success = 0; } else { /* store the other preload libraries back into the /etc/ld.so.preload file */ if(num_of_preloads > 0) { for (i = 0; i < num_of_preloads; i++) { sprintf(preload_entry, "%s\n", preload_array[i]); if (write(preload_file, preload_entry, strlen(preload_entry)) != strlen(preload_entry)) { perror(">>> /etc/ld.so.preload file write"); success = 0; break; } } } } } if (NULL != preload_array) { for (i = 0; i < num_of_preloads; i++) { free(preload_array[i]); preload_array[i] = NULL; } free(preload_array); preload_array = NULL; } if (flock(preload_file, LOCK_UN) < 0) { perror(">>> /etc/ld.so.preload file unlock"); success = 0; } } } if (EOF == fclose(preload_stream)) { perror(">>> /etc/ld.so.preload file fclose"); success = 0; } preload_stream = NULL; preload_file = -1; } } if (fseek(pids_stream, 0, SEEK_SET) < 0) { perror(">>> pids file fseek"); success = 0; } else { /* empty the pids file */ if (ftruncate(pids_file, 0) < 0) { perror(">>> pids file ftruncate"); success = 0; } else { /* if pids are still running, write only the running pids back to the file */ if(num_of_pids > 1) { for (i = 0; i < num_of_pids; i++) { sprintf(pid_string, "%d\n", pids_array[i]); if (write(pids_file, pid_string, strlen(pid_string)) != strlen(pid_string)) { perror(">>> pids file write"); success = 0; break; } } } } } if (NULL != pids_array) { free(pids_array); pids_array = NULL; } if (flock(pids_file, LOCK_UN) < 0) { perror(">>> pids file unlock"); success = 0; } } } if (EOF == fclose(pids_stream)) { perror(">>> pids file fclose"); success = 0; } pids_stream = NULL; pids_file = -1; } } if (0 == success) { exit(1); } } } void stop(int signum) { cleanup(); } int main(int argc, char** argv) { int i = 0; int success = 1; int status = 0; char* run_str = "-c"; char run_arg[255]; struct stat preload_stat; FILE* preload_stream = NULL; int preload_file = -1; char preload_entry[255]; int preload_lib_present = 0; int bash_pid = 0; char* home_dir = NULL; char portage_tmp_dir[PATH_MAX]; char var_tmp_dir[PATH_MAX]; char tmp_dir[PATH_MAX]; char sandbox_write_var[255]; char sandbox_predict_var[255]; char* tmp_string = NULL; char full_sandbox_path[255]; char sandbox_log[255]; char* sandbox_log_env; struct stat sandbox_log_stat; int sandbox_log_presence = 0; int sandbox_log_file = -1; char sandbox_debug_log[255]; char sandbox_dir[255]; char sandbox_lib[255]; struct stat sandbox_lib_stat; char sandbox_rc[255]; struct stat sandbox_rc_stat; struct stat pids_stat; int pids_file = -1; char pid_string[255]; // Only print info if called with no arguments .... if (argc < 2) { printf("========================== Gentoo linux path sandbox ===========================\n"); } /* check if a sandbox is already running */ if (NULL != getenv(ENV_SANDBOX_ON)) { fprintf(stderr, "Not launching a new sandbox instance\nAnother one is already running in this process hierarchy.\n"); exit(1); } else { char* argv_bash[] = { "/bin/bash", "-rcfile", NULL, NULL, NULL, NULL }; /* determine the location of all the sandbox support files */ if (argc < 2) printf("Detection of the support files.\n"); if ('/' == argv[0][0]) { strcpy(full_sandbox_path, argv[0]); } else { tmp_string = get_current_dir_name(); strcpy(full_sandbox_path, tmp_string); free(tmp_string); tmp_string = NULL; strcat(full_sandbox_path, "/"); strcat(full_sandbox_path, argv[0]); } tmp_string = dirname(full_sandbox_path); strcpy(sandbox_dir, tmp_string); free(tmp_string); tmp_string = NULL; strcat(sandbox_dir, "/"); strcpy(sandbox_lib, "/lib/"); strcat(sandbox_lib, LIB_NAME); if (-1 == stat(sandbox_lib, &sandbox_lib_stat)) { strcpy(sandbox_lib, sandbox_dir); strcat(sandbox_lib, LIB_NAME); } strcpy(sandbox_rc, "/usr/lib/portage/lib/"); strcat(sandbox_rc, BASHRC_NAME); if (-1 == stat(sandbox_rc, &sandbox_rc_stat)) { strcpy(sandbox_rc, sandbox_dir); strcat(sandbox_rc, BASHRC_NAME); } /* verify the existance of required files */ if (argc < 2) { printf("Verification of the required files.\n"); } if (-1 == stat(sandbox_lib, &sandbox_lib_stat)) { fprintf(stderr, "Could not open the sandbox library at '%s'.\n", sandbox_lib); return -1; } else if (-1 == stat(sandbox_rc, &sandbox_rc_stat)) { fprintf(stderr, "Could not open the sandbox rc file at '%s'.\n", sandbox_rc); return -1; } else { /* ensure that the /etc/ld.so.preload file contains an entry for the sandbox lib */ if (argc < 2) { printf("Setting up the ld.so.preload file.\n"); } /* check if the /etc/ld.so.preload file exists */ if (stat("/etc/ld.so.preload", &preload_stat) < 0 && ENOENT == errno) { /* if not, try to create it and write the path of the sandbox lib to it */ success = 1; preload_file = open("/etc/ld.so.preload", O_WRONLY|O_CREAT, 0644); if (preload_file < 0) { /* if access was denied, warn the user about it */ if (EACCES == errno) { preload_adaptable = 0; printf(">>> Couldn't adapt the /etc/ld.so.preload file.\n>>> It's possible that not all function calls are trapped\n"); } else { perror(">>> /etc/ld.so.preload file open"); success = 0; } } else { if (flock(preload_file, LOCK_EX) < 0) { perror(">>> /etc/ld.so.preload file lock"); success = 0; } else { if (write(preload_file, sandbox_lib, strlen(sandbox_lib)) != strlen(sandbox_lib)) { perror(">>> /etc/ld.so.preload file write"); success = 0; } if (flock(preload_file, LOCK_UN) < 0) { perror(">>> /etc/ld.so.preload file unlock"); success = 0; } } if (close(preload_file) < 0) { perror(">>> /etc/ld.so.preload file close"); success = 0; } pids_file = -1; } if (0 == success) { exit(1); } } else { /* if the /etc/ld.so.preload file exists, try to open it in read/write mode */ success = 1; if (0 == S_ISREG(preload_stat.st_mode)) { perror(">>> /etc/ld.so.preload file is not a regular file"); success = 0; } else { preload_stream = fopen("/etc/ld.so.preload", "r+"); if (NULL == preload_stream) { if (EACCES == errno) { /* if access was denied, warn the user about it */ preload_adaptable = 0; printf(">>> Couldn't adapt the /etc/ld.so.preload file.\n>>> It's possible that not all function calls are trapped\n"); } else { perror(">>> /etc/ld.so.preload file fopen"); success = 0; } } else { preload_file = fileno(preload_stream); if (preload_file < 0) { perror(">>> /etc/ld.so.preload file fileno"); success = 0; } else { if (flock(preload_file, LOCK_EX) < 0) { perror(">>> /etc/ld.so.preload file lock"); success = 0; } else { /* check if the sandbox library is already present in the /etc/ld.so.preload file */ while (EOF != fscanf(preload_stream, "%s\n", preload_entry)) { if (NULL != strstr(preload_entry, LIB_NAME)) { preload_lib_present = 1; break; } } /* if it's not present, add the sandbox lib path to the end of the /etc/ld.so.preload file */ if (0 == preload_lib_present) { if (fseek(preload_stream, 0, SEEK_END) < 0) { perror(">>> /etc/ld.so.preload file fseek"); success = 0; } else { if (write(preload_file, sandbox_lib, strlen(sandbox_lib)) != strlen(sandbox_lib)) { perror(">>> /etc/ld.so.preload file write"); success = 0; } } } if (flock(preload_file, LOCK_UN) < 0) { perror(">>> /etc/ld.so.preload file unlock"); success = 0; } } } if (EOF == fclose(preload_stream)) { perror(">>> /etc/ld.so.preload file fclose"); success = 0; } preload_stream = NULL; preload_file = -1; } } if (0 == success) { exit(1); } } /* set up the required environment variables */ if (argc < 2) { printf("Setting up the required environment variables.\n"); } argv_bash[2] = sandbox_rc; sprintf(pid_string, "%d", getpid()); strcpy(sandbox_log, LOG_FILE_PREFIX); sandbox_log_env = getenv(ENV_SANDBOX_LOG); if (sandbox_log_env) { strcat(sandbox_log, sandbox_log_env); strcat(sandbox_log, "-"); } strcat(sandbox_log, pid_string); strcat(sandbox_log, LOG_FILE_EXT); setenv(ENV_SANDBOX_LOG, sandbox_log, 1); strcpy(sandbox_debug_log, DEBUG_LOG_FILE_PREFIX); strcat(sandbox_debug_log, pid_string); strcat(sandbox_debug_log, LOG_FILE_EXT); setenv(ENV_SANDBOX_DEBUG_LOG, sandbox_debug_log, 1); home_dir = getenv("HOME"); // drobbins: we need to expand these paths using realpath() so that PORTAGE_TMPDIR // can contain symlinks (example, /var is a symlink, /var/tmp is a symlink.) Without // this, access is denied to /var/tmp, hurtin' ebuilds. realpath(getenv("PORTAGE_TMPDIR"),portage_tmp_dir); realpath("/var/tmp",var_tmp_dir); realpath("/tmp",tmp_dir); setenv(ENV_SANDBOX_DIR, sandbox_dir, 1); setenv(ENV_SANDBOX_LIB, sandbox_lib, 1); setenv("LD_PRELOAD", sandbox_lib, 1); if (NULL == getenv(ENV_SANDBOX_DENY)) { setenv(ENV_SANDBOX_DENY, LD_PRELOAD_FILE, 1); } if (NULL == getenv(ENV_SANDBOX_READ)) { setenv(ENV_SANDBOX_READ, "/", 1); } if (NULL == getenv(ENV_SANDBOX_WRITE)) { /* these should go into make.globals later on */ strcpy(sandbox_write_var, ""); strcat(sandbox_write_var, "/dev/zero:/dev/fd/:/dev/null:/dev/pts/:/dev/vc/:/dev/tty:/tmp/"); strcat(sandbox_write_var, ":"); strcat(sandbox_write_var, "/var/log/scrollkeeper.log"); strcat(sandbox_write_var, ":"); strcat(sandbox_write_var, home_dir); strcat(sandbox_write_var, "/.gconfd/lock"); strcat(sandbox_write_var, ":"); strcat(sandbox_write_var, home_dir); strcat(sandbox_write_var, "/.bash_history"); strcat(sandbox_write_var, ":"); strcat(sandbox_write_var, "/usr/tmp/conftest"); strcat(sandbox_write_var, ":"); strcat(sandbox_write_var, "/usr/lib/conftest"); strcat(sandbox_write_var, ":"); strcat(sandbox_write_var, "/usr/tmp/cf"); strcat(sandbox_write_var, ":"); strcat(sandbox_write_var, "/usr/lib/cf"); strcat(sandbox_write_var, ":"); if (NULL == portage_tmp_dir) { strcat(sandbox_write_var, tmp_dir); strcat(sandbox_write_var, ":"); strcat(sandbox_write_var, var_tmp_dir); strcat(sandbox_write_var, ":"); strcat(sandbox_write_var, "/tmp/"); strcat(sandbox_write_var, ":"); strcat(sandbox_write_var, "/var/tmp/"); } else if (0 == strcmp(sandbox_write_var, "/var/tmp/")) { strcat(sandbox_write_var, portage_tmp_dir); strcat(sandbox_write_var, ":"); strcat(sandbox_write_var, tmp_dir); strcat(sandbox_write_var, ":"); strcat(sandbox_write_var, "/tmp/"); } else if (0 == strcmp(sandbox_write_var, "/tmp/")) { strcat(sandbox_write_var, portage_tmp_dir); strcat(sandbox_write_var, ":"); strcat(sandbox_write_var, var_tmp_dir); strcat(sandbox_write_var, ":"); strcat(sandbox_write_var, "/var/tmp/"); } else { strcat(sandbox_write_var, portage_tmp_dir); strcat(sandbox_write_var, ":"); strcat(sandbox_write_var, tmp_dir); strcat(sandbox_write_var, ":"); strcat(sandbox_write_var, var_tmp_dir); strcat(sandbox_write_var, ":"); strcat(sandbox_write_var, "/tmp/"); strcat(sandbox_write_var, ":"); strcat(sandbox_write_var, "/var/tmp/"); } /* */ setenv(ENV_SANDBOX_WRITE, sandbox_write_var, 1); } if (NULL == getenv(ENV_SANDBOX_PREDICT)) { /* these should go into make.globals later on */ strcpy(sandbox_predict_var, ""); strcat(sandbox_predict_var, home_dir); strcat(sandbox_predict_var, "/."); strcat(sandbox_predict_var, ":"); strcat(sandbox_predict_var, "/usr/lib/python2.0/"); strcat(sandbox_predict_var, ":"); strcat(sandbox_predict_var, "/usr/lib/python2.1/"); strcat(sandbox_predict_var, ":"); strcat(sandbox_predict_var, "/usr/lib/python2.2/"); setenv(ENV_SANDBOX_PREDICT, sandbox_predict_var, 1); /* */ } setenv(ENV_SANDBOX_ON, "1", 0); /* if the portage temp dir was present, cd into it */ if (NULL != portage_tmp_dir) { chdir(portage_tmp_dir); } /* adding additional bash arguments */ for (i = 1; i < argc; i++) { if (1 == i) { argv_bash[3] = run_str; argv_bash[4] = run_arg; strcpy(argv_bash[4], argv[i]); } else { strcat(argv_bash[4], " "); strcat(argv_bash[4], argv[i]); } } /* set up the required signal handlers */ signal(SIGHUP, &stop); signal(SIGINT, &stop); signal(SIGQUIT, &stop); signal(SIGTERM, &stop); /* this one should NEVER be set in ebuilds, as it is the one * private thing libsandbox.so use to test if the sandbox * should be active for this pid, or not. * * azarah (3 Aug 2002) */ setenv("SANDBOX_ACTIVE", "armedandready", 1); /* fork to executing bash */ if (argc < 2) { printf("Creating a seperate process the run the shell in.\n"); } bash_pid = fork(); if (0 == bash_pid) { /* launch bash */ execv(argv_bash[0], argv_bash); } else { int wait_pid = 0; if (argc < 2) { printf("The protected environment has been started.\n"); printf("--------------------------------------------------------------------------------\n"); } /* store his sandbox's bash pid in the global pids file if it has rights to adapt the ld.so.preload file*/ if (1 == preload_adaptable) { success = 1; if (0 == lstat(PIDS_FILE, &pids_stat) && 0 == S_ISREG(pids_stat.st_mode)) { perror(">>> pids file is not a regular file"); success = 0; } else { pids_file = open(PIDS_FILE, O_WRONLY|O_CREAT|O_APPEND, 0644); if (pids_file < 0) { perror(">>> pids file open"); success = 0; } else { if (flock(pids_file, LOCK_EX) < 0) { perror(">>> pids file lock"); success = 0; } else { sprintf(pid_string, "%d\n", getpid()); if (write(pids_file, pid_string, strlen(pid_string)) != strlen(pid_string)) { perror(">>> pids file write"); success = 0; } if (flock(pids_file, LOCK_UN) < 0) { perror(">>> pids file unlock"); success = 0; } } if (close(pids_file) < 0) { perror(">>> pids file close"); success = 0; } pids_file = -1; } } if (0 == success) { exit(1); } } /* wait until bash exits */ wait_pid = waitpid(bash_pid, &status, 0); } } cleanup(); if (argc < 2) { printf("========================== Gentoo linux path sandbox ===========================\n"); printf("The protected environment has been shut down.\n"); } if (0 == stat(sandbox_log, &sandbox_log_stat)) { sandbox_log_presence = 1; success = 1; sandbox_log_file = open(sandbox_log, O_RDONLY, 0); if (sandbox_log_file < 0) { perror(">>> sandbox log file open"); success = 0; } else { int i = 0; char* beep_count_env = NULL; int beep_count = 0; int length = 0; char buffer[255]; printf("\e[31;01m--------------------------- ACCESS VIOLATION SUMMARY ---------------------------\033[0m\n"); printf("\e[31;01mLOG FILE = \"%s\"\033[0m\n", sandbox_log); printf("\n"); while ((length = read(sandbox_log_file, buffer, sizeof(buffer)-1)) > 0) { if (length < sizeof(buffer)) { buffer[length] = 0; } printf("%s", buffer); } printf("\e[31;01m--------------------------------------------------------------------------------\033[0m\n"); if (close(sandbox_log_file) < 0) { perror(">>> sandbox log close"); success = 0; } beep_count_env = getenv(ENV_SANDBOX_BEEP); if (beep_count_env) { beep_count = atoi(beep_count_env); } else { beep_count = DEFAULT_BEEP_COUNT; } for (i = 0; i < beep_count; i++) { fputc('\a', stderr); if (i < beep_count -1) { sleep(1); } } } if (0 == success) { exit(1); } sandbox_log_file = -1; } else if (argc < 2) { printf("--------------------------------------------------------------------------------\n"); } if (status > 0 || 1 == sandbox_log_presence) { return 1; } else { return 0; } } }