--- docs/compartment.c +++ docs/compartment.c @@ -0,0 +1,311 @@ +/* Compartment 1.2 (c) 2002 by Stephan Müller + * + * based on work by: + * SuSE secure compartment v1.1 (c) 2000 by Marc Heuse + * + * Description: + * Compartment provides all possibilities to securely run insecure and/or + * untrusted programs. It provides you with all necessary options to fine + * grain the tightening process as you need it. + * + * Please note that you need a Linux kernel 2.2.12 or later! + * + * Official releases and updates can be found on the SuSE Linux distribution + * from version 7.0 on and ftp.suse.com plus mirror sites. + * + * This is a patched version from Stephan Müller + * The main author above and SuSE did not respond to patches I send to them. + * + * Updates and betas can be found at http://www.chronox.de + * + * Please contact me directly at smueller@chronox.de for bugs, comments, ideas. + * + * Thanks go to Solar Designer for comments & critics ... + * + * $Id: compartment.c,v 1.4 2002/08/24 17:49:27 smueller Exp $ + * + */ + +#include +#include +#include +#include +#include +#define _LINUX_STRING_H_ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PROGRAM_NAME "SuSE secure compartment" +#define VERSION "v1.2" +#define AUTHOR "Marc Heuse " +#define AUTHOR2 "Stephan Müller " +#define POINTER "http://www.chronox.de" + +char *_env[] = { "HOME=/", "COMPARTMENT=YES", "PATH=/bin:/usr/bin:/", "" }; + +extern char **environ; +int program_params, fd; +int do_chroot, do_group, do_user, do_init, verbose, quiet; +char *chroot_path, *init_program, *prg, *user, **_argv; +long tmp; +__u32 temp, caps = 0; +uid_t set_user; +gid_t set_group; +struct passwd *pw; +struct group *gr; + +void help() { + fprintf(stderr, "%s %s %s %s %s\n\n", PROGRAM_NAME, VERSION, AUTHOR, AUTHOR2, POINTER); + fprintf(stderr, "Syntax: %s [options] /full/path/to/program\n", prg); + fprintf(stderr, "Options \n--chroot path\t chroot to path\n\t --user user\t change uid to this user\n\t --group group\t change gid to this group\n\t --init program\t execute this program/script before doing anything\n\t --verbose\t be verbose\n\t --fork\t\t fork (if everything is fine)\n\nHints: always try to chroot; use --user&group if possible; chroot and chown all\nfiles to another user than root if you use capabilties. Read the README file!\n\nKnown capset names: none"); + tmp = 0; + exit(-1); +} + +void print_msg(const char *format, ...) { + va_list arg; + if (quiet == 0) { + va_start(arg, format); + vfprintf(stderr, format, arg); + va_end(arg); + } +} + +void my_secure() { + char file[10] = "/dev/null"; + int mode = O_RDWR; + + alarm(0); + if (verbose) + print_msg("Turned possible alarm(2)s off\n"); + + environ = _env; // set a safe and clean environment + if (verbose) + print_msg("Safe environment set\n"); + + if ((fd = open(file, mode)) < 0) { + file[1] = '\0'; + mode = O_RDONLY; + if ((fd = open(file, mode)) < 0) { + print_msg("Can not open /dev/null nor /, no more fd's?\n"); + exit(-1); + } + close(fd); + } + + /* We want to have no stdout or stderr open */ + /* And we want to ensure that '/' is not opened */ + strcpy(file,"/dev/null"); + while (fd < 2) + fd = open(file, mode); + close(fd); + if (verbose) + print_msg("Ensured that fd 0-2 are open\n"); + + /* FIXME: should limits be reset or signals be blocked or ignored? */ + + return; +} + +int main (int argc, char *argv[]) { + struct stat st; + int do_fork = 0; + unsigned long int uidrange = 65535; +/* process the parameters */ + prg = argv[0]; + if (argc < 2) + help(); + if (strcmp(argv[1],"-h") == 0 || strcmp(argv[1],"--help") == 0) + help(); + + my_secure(); + + program_params = 1; + while((argc - 1) > program_params && strncmp(argv[program_params], "--", 2) == 0) { + if (strcmp(argv[program_params], "--chroot") == 0) { + chroot_path = argv[++program_params]; + do_chroot = 1; + } else if (strcmp(argv[program_params], "--user") == 0) { + if ((pw = (struct passwd *) getpwnam(argv[++program_params])) != NULL) { + user = argv[program_params]; + set_user = pw->pw_uid; + } else { + tmp = strtol(argv[program_params], (char **)NULL, 10); + if (tmp < 0 || tmp > uidrange) { + print_msg("--user is out of bounds (username or a value between 0 and %lu): %ld\n", uidrange, tmp); + exit(-1); + } + set_user = (uid_t) tmp; + } + if (verbose) + print_msg("UID will be set to %d\n", set_user); + do_user = 1; + } else if (strcmp(argv[program_params], "--group") == 0) { + if ((gr = (struct group *) getgrnam(argv[++program_params])) != NULL) { + set_group = gr->gr_gid; + } else { + tmp = strtol(argv[program_params], (char **)NULL, 10); + if (tmp < 0 || tmp > uidrange) { + print_msg("--group is out of bounds (groupname or a value between 0 and %lu): %ld\n", uidrange, tmp); + exit(-1); + } + set_group = (gid_t) tmp; + } + if (verbose) + print_msg("GID will be set to %d\n", set_group); + do_group = 1; + } else if (strcmp(argv[program_params], "--init") == 0) { + init_program = argv[++program_params]; + if (verbose) + print_msg("Initial program will be %s\n", init_program); + do_init = 1; + } else if (strcmp(argv[program_params], "--fork") == 0) { + if (verbose) + print_msg("Will fork if everything is fine\n"); + do_fork = 1; + } else if (strcmp(argv[program_params], "--verbose") == 0) { + print_msg("I am in verbose mode now\n"); + verbose = 1; + } else if (strcmp(argv[program_params], "--quiet") == 0) { + quiet = 1; + } else if (strcmp(argv[program_params], "--help") == 0) { + help(); + } else { + print_msg("Unknown parameter: %s\n\n",argv[program_params]); + help(); + } + program_params++; + } + + if ((getuid() != geteuid()) || (getgid() != getegid())) + print_msg("Warning: euid/egid and uid/gid are different, are we running suid/sgid??\nDo not do that!\n"); + +/* the first parameter without an -- has to be the program plus options to execute */ + if (program_params == argc) { + print_msg("No program given to execute.\n\n"); + help(); + } + _argv = argv; + _argv += program_params; + +/* some further insightful deductions */ + if (do_user == 0) + set_user = geteuid(); + if (do_group == 0) + set_group = getegid(); + if (user == NULL) { + if ((pw = (struct passwd *) getpwuid(set_user)) == NULL) { + print_msg("Warning: username for uid %u is not found in /etc/passwd.\n", set_user); + } else { + user = (char *) pw->pw_name; + } + } + + if (do_init) { + if (system(init_program)) { + print_msg("Error executing init program %s\n", init_program); + exit(1); + } else { + if (verbose) + print_msg("Init program %s run successfully\n", init_program); + } + } + + if (do_chroot) { + if (chroot(chroot_path)) { + print_msg("Error chrooting to %s\n", chroot_path); + exit(1); + } + if (chdir("/") != 0) { + print_msg("chdir to / in chroot failed\n"); + exit(1); + } + if (verbose) + print_msg("Chrooted sucessfully to %s\n", chroot_path); + } + + if (do_user || do_group) { +// if (user != NULL) { + if (setgroups(0, NULL)) { + print_msg("Error removing supplementary groups via setgroups().\n"); + exit(1); + } else { + if (verbose) + print_msg("setgroups() successfully set\n"); + } +// } else { +// print_msg("Can not do setgroups() because username for uid %u is not found in /etc/passwd.\n", set_user); +// } + } + + if (do_group) { + if (setgid(set_group)) { + print_msg("Error setting gid to %u\n", set_group); + exit(1); + } else { + if (verbose) + print_msg("GID successfully set to %d\n",set_group); + } + } + + if (do_user) { + if (setuid(set_user)) { + print_msg("Error setting uid to %u\n", set_user); + exit(1); + } else { + if (verbose) + print_msg("UID successfully set to %d\n", set_user); + } + } + + for (fd = 3; fd <= 1023; fd++) // set close_on_exec on all open fds > 2 + (void) fcntl(fd, F_SETFD, FD_CLOEXEC); + if (verbose) + print_msg("FD_CLOEXEC successfully set on all filedescriptors > 2\n"); + + if (stat(_argv[0], &st) < 0) { + if (do_chroot || _argv[0][0] == '/' || _argv[0][0] == '.') + print_msg("Warning: Can not find %s\n", _argv[0]); + } else { + if (do_fork) { + do_fork = fork(); + if (do_fork < 0) { + print_msg("Could not fork\n"); + exit(1); + } + if (do_fork > 0) + return 0; // this is the parent process + if (verbose) + print_msg("Successfully forked\n"); + } + //just close STDIN, STDOUT and STDERR + //otherwise if compartment is scripted + //the script may not return + close(0); + close(1); + close(2); + } + + execvp(_argv[0], _argv); + + if (do_chroot == 0) + print_msg("Could not execute %s\n", _argv[0]); + else { + if (stat(_argv[0], &st) < 0) + print_msg("Could not find file: %s\n", _argv[0]); + else + if (access(_argv[0], X_OK) < 0) + print_msg("Execute bit missing, or no permissions to execute %s\n", _argv[0]); + else + print_msg("Could not properly execute %s - the chroot environment might not be set up correctly: Create the directories /etc and /lib in chroot_dir and run \"ldd %s\" to see which libraries are needed. Copy these to chroot_dir/lib, then chdir to chroot_dir and execute \"ldconfig -X -r .\"\n", _argv[0], _argv[0]); + } + + return 1; +}