From 887d8b57c4aa291919c8eec6b2af5a5f5259ac6d Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Mon, 17 May 2004 17:52:45 +0000 Subject: Initial 5.2.x files --- srcx/jails.c | 77 ++++++++++++ srcx/jid.c | 42 +++++++ srcx/jkill.c | 283 +++++++++++++++++++++++++++++++++++++++++++++ srcx/jps.c | 132 +++++++++++++++++++++ srcx/jps.c~ | 362 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ srcx/jstart.c | 107 +++++++++++++++++ srcx/util.c | 366 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ srcx/util.h | 19 +++ 8 files changed, 1388 insertions(+) create mode 100644 srcx/jails.c create mode 100644 srcx/jid.c create mode 100644 srcx/jkill.c create mode 100644 srcx/jps.c create mode 100644 srcx/jps.c~ create mode 100644 srcx/jstart.c create mode 100644 srcx/util.c create mode 100644 srcx/util.h (limited to 'srcx') diff --git a/srcx/jails.c b/srcx/jails.c new file mode 100644 index 0000000..aac6a31 --- /dev/null +++ b/srcx/jails.c @@ -0,0 +1,77 @@ + +#include +#include +#include +#include +#include +#include + +#include "util.h" + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +static void usage(); +static void list_jails(); + +int main(int argc, char* argv[]) +{ + if(argc > 1) + usage(); + + if(in_jail()) + errx(1, "can't run from inside jail"); + + list_jails(); + return 0; +} + +static void usage() +{ + fprintf(stderr, "usage: jails \n"); + exit(2); +} + +static void list_jails() +{ + struct xprison* sxp; + struct xprison* xp; + size_t len, i; + + /* ... otherwise it's a name */ + + if(sysctlbyname("security.jail.list", NULL, &len, NULL, 0) == -1) + err(1, "couldn't list jails"); + +retry: + + if(len <= 0) + return; + + sxp = xp = calloc(len, 1); + if(sxp == NULL) + err(1, "out of memory"); + + if(sysctlbyname("security.jail.list", xp, &len, NULL, 0) == -1) + { + if(errno == ENOMEM) + { + free(sxp); + goto retry; + } + + err(1, "couldn't list jails"); + } + + if(len < sizeof(*xp) || len % sizeof(*xp) || xp->pr_version != XPRISON_VERSION) + errx(1, "kernel and userland out of sync"); + + jid = -1; + + for(i = 0; i < (len / sizeof(*xp)); i++) + printf("%s\n", xp->pr_host); + + free(sxp); +} + diff --git a/srcx/jid.c b/srcx/jid.c new file mode 100644 index 0000000..a201822 --- /dev/null +++ b/srcx/jid.c @@ -0,0 +1,42 @@ + +#include +#include +#include +#include + +#include "util.h" + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +static void usage(); + +int main(int argc, char* argv[]) +{ + int i; + int jid = 0; + + /* Remove the program name */ + argc--; + argv++; + + if(argc != 1) + usage(); + + if(in_jail()) + errx(1, "can't run from inside jail"); + + jid = translate_jail_id(argv[0]); + if(jid == -1) + errx(1, "unknown jail host name: %s", argv[0]); + + printf("%d \n", (int)jid); + return 0; +} + +static void usage() +{ + fprintf(stderr, "usage: jid hostname \n"); + exit(2); +} diff --git a/srcx/jkill.c b/srcx/jkill.c new file mode 100644 index 0000000..46b1815 --- /dev/null +++ b/srcx/jkill.c @@ -0,0 +1,283 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +/* The timeout to wait between kills */ +#define DEFAULT_TIMEOUT 3 +int g_timeout = DEFAULT_TIMEOUT; + +/* Supress warnings */ +int g_quiet = 0; + +int main(int argc, char* argv[]) +{ + int ch, r, jid; + int ret = 0; + int restart = 0; + int force = 0; + pid_t child; + + while((ch = getopt(argc, argv, "fhqrt:")) != -1) + { + switch(ch) + { + case 'f': + force = 1; + break; + + case 'q': + g_quiet = 1; + break; + + case 'h': + /* dummy for compatibility with killjail */ + warnx("the '-h' option has been depreciated"); + break; + + case 'r': + restart = 1; + break; + + /* Timeout to use between kills */ + case 't': + g_timeout = atoi(optarg); + if(g_timeout <= 0) + errx(2, "invalid timeout argument: %s", optarg); + break; + + case '?': + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + /* Make sure we have a jail id or name */ + if(argc == 0) + usage(); + + /* For each jail */ + while(argc > 0) + { + jid = translate_jail(argv[0]); + if(jid == -1) + { + warnx(1, "unknown jail host name: %s", argv[0]); + ret = 1; + continue; + } + + /* + * We fork and the child goes into the jail and + * does the dirty work. + */ + + switch((child = fork())) + { + /* Error condition */ + case -1: + err(1, "couldn't fork child process"); + break; + + /* The child */ + case 0: + if(jail_attach(jid) == -1) + err(1, "couldn't attach to jail"); + + r = kill_jail(restart, force, argv[0]); + exit(r); + break; + + /* The parent */ + default: + if(waitpid(child, &r, options) == -1) + err(1, "error waiting for child process"); + + if(r != 0) + ret = r; + break; + }; + + argc--; + argv++; + } + + return ret; +} + +int kill_jail(int restart, int force, const char* name) +{ + kvm_t* kd = NULL; + char errbuf[_POSIX2_LINE_MAX]; + int pass = 0; + int timeout = 0; + int ret = 0; + + /* Open the kernel interface */ + kd = kvm_openfiles(_PATH_DEVNULL, _PATH_DEVNULL, _PATH_DEVNULL, + O_RDONLY, errbuf); + if(kd == NULL) + errx(1, "couldn't connect to kernel: %s", errbuf); + + /* + * Multiple passes are used to do different things. + * Each time the jails processes are listed. + */ + while(true) + { + while(timeout > 0) + { + sleep(1); + timeout--; + + if(!check_running_processes(kd)) + goto done; + } + + switch(pass) + { + /* First pass is an orderly shutdown */ + case 0: + + /* Check if we have an executable shutdown script */ + if(check_jail_command(name, "/etc/rc.shutdown")) + run_jail_command(name, "/etc/rc.shutdown", NULL, JAIL_OUT_CONSOLE); + + break; + + /* Okay now quit all processes in jail */ + case 1: + kill_jail_processes(kd, SIGTERM); + timeout = g_timeout; + break; + + /* Okay now we force kill the processes if necessary */ + case 2: + + if(force) + { + /* If we get here, jailer looks like it's really irresponsive */ + if(!g_quiet) + warnx("%s: jail won't stop. forcing jail termination...", name); + + kill_jail_processes(kd, SIGKILL); + timeout = g_timeout; + } + + break; + + + case 3: + + if(check_running_processes(kd)) + { + /* And if that didn't do it, well then give up */ + if(!g_quiet) + warnx("%s: couldn't stop jail, processes wouldn't die", name); + + ret = 1; + goto done; + } + + else if(restart) + { + /* Check if we have an executable shutdown script */ + if(check_jail_command(name, "/etc/rc")) + run_jail_command(name, "/etc/rc", NULL, JAIL_OUT_CONSOLE); + + goto done; + } + } + + pass++; + + if(!check_running_processes(kd)) + goto done; + } + +done: + if(kd != NULL) + kvm_close(kd); + + return ret; +} + +void kill_jail_processes(kvm_t* kd, int sig) +{ + struct kinfo_proc* kp; + int nentries, i; + pid_t cur; + + cur = getpid(); + + /* Get a process listing */ + if((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nentries)) == 0) + errx(1, "couldn't list processes: %s", kvm_geterr(kd)); + + /* Okay now loop and look at each process' jail */ + for(i = 0; i < nentries; i++) + { + if(kp[i].ki_pid == cur) + continue; + + if(kill(kp[i].ki_pid, sig) == -1) + { + if(errno != ESRCH) + errx(1, "couldn't signal process: %d", (int)kp[i].ki_pid); + } + } +} + +int check_running_processes(kvm_t* kd) +{ + struct kinfo_proc* kp; + int nentries, i; + pid_t cur; + + cur = getpid(); + + /* Get a process listing */ + if((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nentries)) == 0) + errx(1, "couldn't list processes: %s", kvm_geterr(kd)); + + if(&nentries != 1) + return 1; + + /* Okay now loop and look at each process' jail */ + for(i = 0; i < nentries; i++) + { + if(kp[i].ki_pid != cur) + return 1; + } + + return 0; +} + +static void usage() +{ + fprintf(stderr, "usage: killjail [ -r ] [ -t timeout ] [ -qf ] jailname ...\n"); + exit(2); +} diff --git a/srcx/jps.c b/srcx/jps.c new file mode 100644 index 0000000..e9a6ec8 --- /dev/null +++ b/srcx/jps.c @@ -0,0 +1,132 @@ + +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +static void usage(); +static void printJailIds(); +static void runJailPs(int argc, char* argv[]); + +int main(int argc, char* argv[]) +{ + int ch = 0; + int simple = 0; + int jid = 0; + + while((ch = getopt(argc, argv, "x")) != -1) + { + switch(ch) + { + case 'x': + simple = 1; + break; + + case '?': + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + /* Make sure we have a jail name or id */ + if(argc == 0) + usage(); + + if(running_in_jail()) + errx(1, "can't run from inside jail"); + + /* Translate the jail name into an id if neccessary */ + name = argv[0]; + jid = translate_jail_name(argv[0]); + if(jid == -1) + errx(1, "unknown jail host name: %s", argv[0]); + + argc--; + argv++; + + /* Go into the jail */ + if(jail_attach(jid) == -1) + err(1, "couldn't attach to jail"); + + if(simple) + { + if(argc > 0) + usage(); + + print_jail_ids(); + } + + else + { + /* This function never returns */ + run_jail_ps(argc, argv); + } + + return 0; +} + +static void usage() +{ + fprintf(stderr, "usage: jps [-x] jail [ ps_options ... ]\n"); + exit(2); +} + +static void run_jail_ps(int argc, char* argv[]) +{ + char* args[]; + int i; + + if(!check_jail_command(NULL, "/bin/ps")) + exit(1); + + /* + * TODO: We need to purge down the environment here. + * If the jail is in any way malicious or compromised + * then it could have replaced /bin/ps which we run... + */ + + args = alloca(sizeof(char*) * (argc + 2)); + args[0] = "ps"; + + for(i = 0; i < argc; i++) + args[i + 1] = argv[i]; + + args[i + 1] = NULL; + + run_jail_command(NULL, "/bin/ps", args, 0); +} + +static void print_jail_ids() +{ + kvm_t* kd; + struct kinfo_proc* kp; + char errbuf[_POSIX2_LINE_MAX]; + + /* Open kernel interface */ + kd = kvm_openfiles(_PATH_DEVNULL, _PATH_DEVNULL, _PATH_DEVNULL, + O_RDONLY, errbuf); + if(kd == NULL) + errx(1, "couldn't connect to kernel: %s", errbuf); + + /* Get all processes and print the pids */ + if((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nentries)) == 0) + errx(1, "couldn't list processes: %s", kvm_geterr(kd)); + + for(i = 0; i < nentries; i++) + printf("%d ", (int)(kp[i].ki_pid)); + + fputc(stdout, '\n'); + kvm_close(kd); +} diff --git a/srcx/jps.c~ b/srcx/jps.c~ new file mode 100644 index 0000000..852fc64 --- /dev/null +++ b/srcx/jps.c~ @@ -0,0 +1,362 @@ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "getjail.h" + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +/* The big long stop process */ +static int stopJail(char* jailName, int force); + +/* Signals the jailer for various requests */ +static int signalJail(char* jailName, int signal); + +static void killProcesses(pid_t* pids, int signal); +static int getJailProcesses(const char* jailName, pid_t* pidJailer, pid_t** pids); + +static void usage(); + + +/* The timeout to wait between kills */ +#define DEFAULT_TIMEOUT 10 +int g_timeout = DEFAULT_TIMEOUT; + +/* To find the jailer process look for this command */ +#define JAILER_COMMAND "jailer" + +/* Supress warnings */ +int g_quiet = 0; + +int main(int argc, char* argv[]) +{ + /* If this gets set then only signal jailer, no kill */ + int signal = 0; + int ch = 0; + int force = 0; + int ret = 0; + + while((ch = getopt(argc, argv, "fhqrt:")) != -1) + { + switch(ch) + { + /* Force jail to shutdown */ + case 'f': + force = 1; + break; + + case 'q': + g_quiet = 1; + break; + + /* Send halt request to jailer */ + case 'h': + signal = SIGQUIT; + break; + + /* Send restart request to jailer */ + case 'r': + signal = SIGHUP; + break; + + /* Timeout to use between kills */ + case 't': + g_timeout = atoi(optarg); + if(g_timeout <= 0) + errx(1, "invalid timeout argument: %s", optarg); + break; + + case '?': + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + /* Make sure we have a jailName */ + if(argc == 0) + usage(); + + /* For each jail */ + while(argc > 0) + { + /* If a signal option was set above then signal, + otherwise kill */ + if(signal == 0) + { + if(stopJail(*argv, force) != 0) + ret = 1; + } + else + { + if(force) + errx(1, "-f option incompatible with -r or -h"); + + if(signalJail(*argv, signal) != 0) + ret = 1; + } + + argc--; + argv++; + } + + return ret; +} + + +int signalJail(char* jailName, int signal) +{ + pid_t jailerPid = 0; + + /* Only ask for jailer pid */ + getJailProcesses(jailName, &jailerPid, NULL); + + if(jailerPid == 0) + { + warnx("%s: jailer not running in jail", jailName); + return 1; + } + + if(kill(jailerPid, signal) < 0) + err(1, "%s: couldn't signal jailer", jailName); + + return 0; +} + + +int stopJail(char* jailName, int force) +{ + pid_t jailerPid = 0; + pid_t* jailProcesses = NULL; + int pass = 0; + int timeout = 0; + int ret = 0; + + /* + * Multiple passes are used to do different things. + * Each time the jails processes are listed. + */ + while(ret == 0 && + getJailProcesses(jailName, &jailerPid, &jailProcesses)) + { + + if(timeout > 0) + { + sleep(1); + timeout--; + } + + else + { + + switch(pass) + { + + /* First pass is killing the jailer */ + case 0: + + if(jailerPid == 0) + { + /* No jailer */ + if(!g_quiet) + warnx("%s: jailer not running in jail", jailName); + } + + else + { + if(kill(jailerPid, SIGTERM) < 0 && errno != ESRCH) + err(1, "%s: couldn't signal jailer:", jailName); + + else + timeout = g_timeout; + } + + break; + + + /* Okay now quit all processes in jail */ + case 1: + + /* If we get here, jailer looks like it's irresponsive */ + if(jailerPid != 0 && !g_quiet) + warnx("%s: jailer (pid %d) won't quit. terminating jail...", jailName, jailerPid); + + + killProcesses(jailProcesses, SIGTERM); + timeout = g_timeout; + break; + + + /* Okay now we force kill the processes if necessary */ + case 2: + + if(force) + { + /* If we get here, jailer looks like it's really irresponsive */ + if(!g_quiet) + warnx("%s: jail won't stop. forcing jail termination...", jailName); + + killProcesses(jailProcesses, SIGKILL); + timeout = g_timeout; + } + + break; + + + /* And if that didn't do it, well then give up */ + case 3: + + if(!g_quiet) + warnx("%s: couldn't stop jail, processes wouldn't die", jailName); + + ret = 1; + break; + + } + + pass++; + } + + if(jailProcesses) + free(jailProcesses); + + } + + if(pass == 0) + { + if(!g_quiet) + warnx("%s: jail not running", jailName); + + ret = 1; + } + + return ret; +} + +void killProcesses(pid_t* pids, int signal) +{ + /* Note that we assume pids is null terminated + this is what getJailProcesses returns */ + + while(*pids) + { + if(kill(*pids, signal) < 0) + { + /* We ignore missing process errors */ + if(errno != ESRCH) + err(1, "couldn't kill process: %d", *pids); + } + + pids++; + } +} + +int getJailProcesses(const char* jailName, pid_t* pidJailer, pid_t** pids) +{ + kvm_t *kd; + struct kinfo_proc* kp; + char errbuf[_POSIX2_LINE_MAX]; + char pidJail[JAIL_BUFF_SIZE]; + int nentries, i, j; + + /* Open the kernel interface */ + kd = kvm_openfiles(_PATH_DEVNULL, _PATH_DEVNULL, _PATH_DEVNULL, + O_RDONLY, errbuf); + if(kd == 0) + errx(1, "%s", errbuf); + + /* Get a process listing */ + if((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nentries)) == 0) + errx(1, "%s", kvm_geterr(kd)); + + /* Allocate memory */ + if(pids) + { + if((*pids = (pid_t*)malloc((nentries + 1) * sizeof(pid_t))) == NULL) + err(1, "out of memory"); + } + + /* Okay now loop and look at each process' jail */ + for(i = 0, j = 0; i < nentries; i++) + { + pid_t pid; + +#if __FreeBSD_version > 500000 + + /* Check the flags first */ + if(!(kp[i].ki_flag & P_JAILED)) + continue; + + pid = kp[i].ki_pid; + +#else + + /* Check the flags first */ + if(!(kp[i].kp_proc.p_flag & P_JAILED)) + continue; + + pid = kp[i].kp_proc.p_pid; + +#endif + + /* Now actually get the jail name */ + if(getpidjail(pid, pidJail) < 0) + continue; + + if(strcmp(pidJail, jailName)) + continue; + + /* Copy the PID over */ + if(pids) + (*pids)[j++] = pid; + + /* If it's the jailer then copy that */ + if(pidJailer) + { +#if __FreeBSD_version > 500000 + if(strstr(kp[i].ki_comm, JAILER_COMMAND)) +#else + if(strstr(kp[i].kp_proc.p_comm, JAILER_COMMAND)) +#endif + *pidJailer = pid; + } + + } + + /* Null terminate pids array */ + if(pids) + (*pids)[j] = 0; + + kvm_close(kd); + + return j == 0 ? 0 : 1; +} + + + +static void usage() +{ + fprintf(stderr, "usage: killjail [ -h | -r ] [ -t timeout ] [ -qf ] jailname ...\n"); + exit(2); +} + diff --git a/srcx/jstart.c b/srcx/jstart.c new file mode 100644 index 0000000..76bdaae --- /dev/null +++ b/srcx/jstart.c @@ -0,0 +1,107 @@ + +/* A lot of code from jail.c in */ +/* TODO: Attribute properly */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +int main(int argc, char* argv[]) +{ +#include + int ch, jid; + struct jail j; + int printjid = 0; + int console = 0; + struct in_addr in; + + while((ch = getopt(argc, argv, "ic")) != -1) + { + switch(ch) + { + case 'i': + printjid = 1; + break; + + case 'c': + console = 1; + break; + + case '?': + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if(argc != 3) + usage(); + + if(getuid() != 0) + errx(1, "must be run as root"); + + if(chdir(argv[0]) != 0) + err(1, "couldn't change to jail directory: %s", argv[0]); + + if(inet_aton(argv[2], &in) != 0) + errx(1, "invalid ip address: %s", argv[2]); + + memset(&j, 0, sizeof(j)); + j.version = 0; + j.path = argv[0]; +#include + j.hostname = argv[1]; + j.ip_number = ntohl(in.s_addr); + + /* Here's where we actually go into the jail */ + jid = jail(&j); + if(jid == -1) + err(1, "couldn't create jail"); + + if(console) + { + + + } + + if(printjid) + { + printf("%d\n", jid); + fflush(stdout); + } + + if(!check_jail_command("/etc/rc")) + exit(1); + + run_jail_command("/etc/rc"); + return 0; +} + +static void usage() +{ + fprintf(stderr, "usage: jstart [-ic] path hostname ip-number\n"); + exit(2); +} diff --git a/srcx/util.c b/srcx/util.c new file mode 100644 index 0000000..6c1d811 --- /dev/null +++ b/srcx/util.c @@ -0,0 +1,366 @@ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +int translate_jail_name(const char* str) +{ + struct xprison* sxp; + struct xprison* xp; + size_t len, i; + char* e; + int jid; + + jid = strtol(str, &e, 10); + + /* If it was all a number ... */ + if(!*e) + { + if(jid > 0) + return jid; + + errx(1, "invalid jail id: %s", str); + } + + /* ... otherwise it's a name */ + + if(sysctlbyname("security.jail.list", NULL, &len, NULL, 0) == -1) + err(1, "couldn't list jails"); + +retry: + + if(len <= 0) + return -1; + + sxp = xp = calloc(len, 1); + if(sxp == NULL) + err(1, "out of memory"); + + if(sysctlbyname("security.jail.list", xp, &len, NULL, 0) == -1) + { + if(errno == ENOMEM) + { + free(sxp); + goto retry; + } + + err(1, "couldn't list jails"); + } + + if(len < sizeof(*xp) || len % sizeof(*xp) || xp->pr_version != XPRISON_VERSION) + errx(1, "kernel and userland out of sync"); + + jid = -1; + + for(i = 0; i < (len / sizeof(*xp)); i++) + { + if(strcmp(xp->pr_host, str) == 0) + { + jid = xp->pr_id; + break; + } + } + + free(sxp); + return -1; +} + +/* + * in_jail + * This code was written by James E. Quick mailto:jq@quick.com + * The code may be freely re-used under the terms of the BSD copyright, + * as long as this comment remains intact. + */ + +int running_in_jail() +{ + int count; + kvm_t* kd = 0; + struct kinfo_proc* kp; + char errbuf[_POSIX2_LINE_MAX]; + int result = -1; + + kd = kvm_openfiles(_PATH_DEVNULL, _PATH_DEVNULL, _PATH_DEVNULL, + O_RDONLY, errbuf); + if(kd == NULL) + errx(1, "couldn't connect to kernel: %s", errbuf); + + kp = kvm_getprocs(kd, KERN_PROC_PID, getpid(), &count); + if(kp == NULL) + errx(1, "couldn't list processes: %s", kvm_geterr(kd)); + + result = (kp->ki_flag & P_JAILED) ? 1 : 0; + kvm_close(kd); + + return result; +} + + +int check_jail_command(const char* jail, const char* cmd) +{ + struct stat sb; + + if(stat(cmd, &sb) == -1) + { + if(errno == EACCESS || errno == ELOOP || errno == ENAMETOOLONG || + errno == ENOENT || errno == ENOTDIR) + { + warn("%s%scan't execute in jail: %s", , jail ? jail : "", + jail ? jail : ": ", cmd); + return 0; + } + + err(1, "%s%scouldn't stat file: %s", , jail ? jail : "", + jail ? jail : ": ", cmd); + } + + if(!(sb.st_mode & S_IFREG)) + { + warnx("%s%snot a regular file: %s", , jail ? jail : "", + jail ? jail : ": ", cmd); + return 0; + } + + if(!(sb.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + { + warnx("%s%snot executable: %s", , jail ? jail : "", + jail ? jail : ": ", cmd); + return 0; + } + + if(sb.st_uid != 0) + { + warnx("%s%snot owned by root: %s", jail ? jail : "", + jail ? jail : ": ", cmd); + return 0; + } + + return 1; +} + +int run_simple_command(const char* jail, const char* cmd, char* args[]) +{ + pid_t pid; + int status; + + switch((pid = fork())) + { + case -1: + err(1, "couldn't fork child process"); + break; + + /* This is the child here */ + case 0: + if(args) + exect(cmd, args, env); + else + execle(cmd, cmd, NULL, env); + + exit(errno); + break; + + /* This is the parent process */ + default: + + /* If the processes exited then break out */ + if(waitpid(pid, &status, 0) == pid) + break; + + /* Return any status codes */ + if(status != 0) + { + warnx("%s%serror executing: %s: %s", jail ? jail : "", + jail ? jail : ": ", cmd, strerror(status)); + return 0; + } + + break; + } + + return 1; +} + +int run_dup_command(const char* jail, const char* cmd, char* argv[] args, int opts) +{ + int outpipe[2]; + pid_t pid; + + /* + * Special function to duplicate output of a command to two + * files. + * + * NOTE: Yes, I know this may seem like overkill, but system, + * popen and all those guys would hang with certain rc scripts. + * Those which opened a daemon in the background ('&') but still + * kept their output going to the same stdin/stdout handles. + */ + + /* Create a pipe for the child process */ + if(pipe(outpipe) < 0) + return -1; + + switch(pid = fork()) + { + case -1: + err(1, "couldn't fork child process"); + break; + + /* This is the child here */ + case 0: + { + /* Fix up our end of the pipe */ + if(dup2(outpipe[WRITE_END], STDOUT) < 0 || + dup2(outpipe[WRITE_END], STDERR) < 0) + exit(errno); + + /* Okay, now run whatever command it was */ + if(args) + exect(cmd, args, env); + else + execle(cmd, cmd, NULL, env); + + /* In case it returns then have to do this to get + children to disconnect from stdout */ + fflush(stdout); + fclose(stdout); + close(outpipe[WRITE_END]); + + exit(errno); + } + break; + + + /* And this is the parent */ + default: + { + int console = -1; + int ret; + int status = 0; + fd_set readmask; + struct timeval timeout = { 0, 10000 }; + + FD_ZERO(&readmask); + + /* Open the console file and write the header */ + if(opts & JAIL_OUT_CONSOLE) + console = open(_PATH_CONSOLE, O_WRONLY | O_APPEND); + + /* No blocking on the child processes pipe */ + fcntl(outpipe[READ_END], F_SETFL, fcntl(outpipe[READ_END], F_GETFL, 0) | O_NONBLOCK); + + /* Loop until the process dies or no more output */ + while(1) + { + FD_SET(outpipe[READ_END], &readmask); + + if(select(FD_SETSIZE, &readmask, NULL, NULL, &timeout) == -1) + err(1, "couldn't select"); + + if(FD_ISSET(outpipe[READ_END], &readmask)) + { + /* Read text */ + while((ret = read(outpipe[READ_END], buff, 256)) > 0) + { + if(opts & JAIL_OUT_STDOUT) + write(STDOUT, buff, ret); + + if(opts & JAIL_OUT_STDERR) + write(STDERR, buff, ret); + + if(console != -1) + write(console, buff, ret); + } + } + + /* If the processes exited then break out */ + if(waitpid(pid, &status, WNOHANG) == pid) + break; + + /* Or if there's an error or end of file */ + if(ret == -1 && errno != EAGAIN || ret == 0) + break; + } + + /* Return any status codes */ + if(status != 0) + { + warnx("%s%serror executing: %s: %s", jail ? jail : "", + jail ? jail : ": ", cmd, strerror(status)); + return 0; + } + + /* Clean up */ + close(outpipe[READ_END]); + + if(console != -1) + close(console); + } + break; + } + + return 1; +} + +#define MAKE_ENV_VAR(v, n) \ +{ \ + char* t = getenv(n); \ + t = t ? t : ""; \ + (v) = alloca(strlen(n) + 2 + strlen(t)); \ + sprintf((v), "%s=%s", (n), (v)); \ +} + +/* read & write ends of a pipe */ +#define READ_END 0 +#define WRITE_END 1 + +/* pre-set file descriptors */ +#define STDIN 0 +#define STDOUT 1 +#define STDERR 2 + +int run_jail_command(const char* jail, const char* cmd, char* args[], int opts) +{ + char* env[5]; + + /* Prepare an environment for the cmd */ + env[0] = "PATH=" _PATH_STDPATH; + env[1] = NULL; + + /* + * If we're outputing to the console then we need a + * few more environement variables. Processes like + * 'ps' depend on them for formatting. + */ + if(!opts || opts & JAIL_OUT_STDOUT || + opts & JAIL_OUT_STDERR) + { + /* TODO: For fun debugging. Remove later */ + t = getenv(VAR); + t = t ? t : NULL; + env[1] = alloca(sizeof("TERM") + 2 + strlen(t)); + sprintf(env[1], "%s=%s", "TERM", t); + + MAKE_ENV_VAR(env[1], "TERM"); + MAKE_ENV_VAR(env[2], "COLUMNS"); + MAKE_ENV_VAR(env[3], "LINES"); + env[4] = NULL; + } + + if(opts) + return run_dup_command(jail, cmd, args, opts); + else + return run_simple_command(jail, cmd, args); +} diff --git a/srcx/util.h b/srcx/util.h new file mode 100644 index 0000000..1ac02c5 --- /dev/null +++ b/srcx/util.h @@ -0,0 +1,19 @@ +#ifndef __UTIL_H__ +#define __UTIL_H__ + +int translate_jail_name(const char* str); +int running_in_jail(); + +/* Output stuff to the jail console if available */ +#define JAIL_OUT_CONSOLE 1 << 1 + +/* Output to stdout */ +#define JAIL_OUT_STDOUT 1 << 2 + +/* Output to stderr */ +#define JAIL_OUT_STDERR 1 << 3 + +int run_jail_command(const char* jail, const char* script, int opts); +int check_jail_command(const char* jail, const char* script); + +#endif __UTIL_H__ -- cgit v1.2.3