blob: c8a26a9f555195c0aa7a7f9a7a4092b699756f93 [file] [log] [blame]
/*
** 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>
** $Id: /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 <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <unistd.h>
#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;
}
}
}