diff --git a/policycoreutils/Makefile b/policycoreutils/Makefile index 86ed03f..3e95698 100644 --- a/policycoreutils/Makefile +++ b/policycoreutils/Makefile @@ -1,4 +1,4 @@ -SUBDIRS = setfiles semanage load_policy newrole run_init sandbox secon audit2allow audit2why scripts sestatus semodule_package semodule semodule_link semodule_expand semodule_deps setsebool po +SUBDIRS = setfiles semanage semanage/default_encoding load_policy newrole run_init sandbox secon audit2allow audit2why scripts sestatus semodule_package semodule semodule_link semodule_expand semodule_deps sepolgen-ifgen setsebool po INOTIFYH = $(shell ls /usr/include/sys/inotify.h 2>/dev/null) diff --git a/policycoreutils/audit2allow/audit2allow b/policycoreutils/audit2allow/audit2allow index 5435e9d..c60490b 100644 --- a/policycoreutils/audit2allow/audit2allow +++ b/policycoreutils/audit2allow/audit2allow @@ -1,4 +1,4 @@ -#! /usr/bin/python -E +#! /usr/bin/python -Es # Authors: Karl MacMillan # # Copyright (C) 2006-2007 Red Hat @@ -28,6 +28,7 @@ import sepolgen.objectmodel as objectmodel import sepolgen.defaults as defaults import sepolgen.module as module from sepolgen.sepolgeni18n import _ +import selinux.audit2why as audit2why class AuditToPolicy: VERSION = "%prog .1" @@ -46,6 +47,7 @@ class AuditToPolicy: help="audit messages since last boot conflicts with -i") parser.add_option("-a", "--all", action="store_true", dest="audit", default=False, help="read input from audit log - conflicts with -i") + parser.add_option("-p", "--policy", dest="policy", default=None, help="Policy file to use for analysis") parser.add_option("-d", "--dmesg", action="store_true", dest="dmesg", default=False, help="read input from dmesg - conflicts with --all and --input") parser.add_option("-i", "--input", dest="input", @@ -231,29 +233,12 @@ class AuditToPolicy: def __output_audit2why(self): import selinux - import selinux.audit2why as audit2why import seobject - audit2why.init() for i in self.__parser.avc_msgs: - rc, bools = audit2why.analyze(i.scontext.to_string(), i.tcontext.to_string(), i.tclass, i.accesses) + rc = i.type + bools = i.bools if rc >= 0: print "%s\n\tWas caused by:" % i.message - if rc == audit2why.NOPOLICY: - raise RuntimeError("Must call policy_init first") - if rc == audit2why.BADTCON: - print "Invalid Target Context %s\n" % i.tcontext - continue - if rc == audit2why.BADSCON: - print "Invalid Source Context %s\n" % i.scontext - continue - if rc == audit2why.BADSCON: - print "Invalid Type Class %s\n" % i.tclass - continue - if rc == audit2why.BADPERM: - print "Invalid permission %s\n" % i.accesses - continue - if rc == audit2why. BADCOMPUTE: - raise RuntimeError("Error during access vector computation") if rc == audit2why.ALLOW: print "\t\tUnknown - would be allowed by active policy\n", print "\t\tPossible mismatch between this policy and the one under which the audit message was generated.\n" @@ -350,11 +335,19 @@ class AuditToPolicy: def main(self): try: self.__parse_options() + if self.__options.policy: + audit2why.init(self.__options.policy) + else: + audit2why.init() + self.__read_input() self.__process_input() self.__output() except KeyboardInterrupt: sys.exit(0) + except ValueError, e: + print e + sys.exit(1) if __name__ == "__main__": app = AuditToPolicy() diff --git a/policycoreutils/audit2allow/audit2allow.1 b/policycoreutils/audit2allow/audit2allow.1 index fd9eb88..a854a45 100644 --- a/policycoreutils/audit2allow/audit2allow.1 +++ b/policycoreutils/audit2allow/audit2allow.1 @@ -67,6 +67,9 @@ Generate module/require output .B "\-M " Generate loadable module package, conflicts with -o .TP +.B "\-p " | "\-\-policy " +Policy file to use for analysis +.TP .B "\-o " | "\-\-output " append output to .I diff --git a/policycoreutils/audit2allow/sepolgen-ifgen b/policycoreutils/audit2allow/sepolgen-ifgen index 0acbf7e..ef4bec3 100644 --- a/policycoreutils/audit2allow/sepolgen-ifgen +++ b/policycoreutils/audit2allow/sepolgen-ifgen @@ -28,6 +28,10 @@ import sys import os +import tempfile +import subprocess + +import selinux import sepolgen.refparser as refparser import sepolgen.defaults as defaults @@ -35,6 +39,7 @@ import sepolgen.interfaces as interfaces VERSION = "%prog .1" +ATTR_HELPER = "/usr/bin/sepolgen-ifgen-attr-helper" def parse_options(): from optparse import OptionParser @@ -44,14 +49,58 @@ def parse_options(): help="filename to store output") parser.add_option("-i", "--interfaces", dest="headers", default=defaults.headers(), help="location of the interface header files") + parser.add_option("-a", "--attribute_info", dest="attribute_info") + parser.add_option("-p", "--policy", dest="policy_path") parser.add_option("-v", "--verbose", action="store_true", default=False, help="print debuging output") parser.add_option("-d", "--debug", action="store_true", default=False, help="extra debugging output") + parser.add_option("--no_attrs", action="store_true", default=False, + help="do not retrieve attribute access from kernel policy") options, args = parser.parse_args() return options +def get_policy(): + i = selinux.security_policyvers() + p = selinux.selinux_binary_policy_path() + "." + str(i) + while i > 0 and not os.path.exists(p): + i = i - 1 + p = selinux.selinux_binary_policy_path() + "." + str(i) + if i > 0: + return p + return None + +def get_attrs(policy_path): + try: + if not policy_path: + policy_path = get_policy() + if not policy_path: + sys.stderr.write("No installed policy to check\n") + return None + outfile = tempfile.NamedTemporaryFile() + except IOError, e: + sys.stderr.write("could not open attribute output file\n") + return None + except OSError: + # SELinux Disabled Machine + return None + + fd = open("/dev/null","w") + ret = subprocess.Popen([ATTR_HELPER, policy_path, outfile.name], stdout=fd).wait() + fd.close() + if ret != 0: + sys.stderr.write("could not run attribute helper") + return None + + attrs = interfaces.AttributeSet() + try: + attrs.from_file(outfile) + except: + print "error parsing attribute info" + return None + + return attrs def main(): options = parse_options() @@ -68,6 +117,14 @@ def main(): else: log = None + # Get the attibutes from the binary + attrs = None + if not options.no_attrs: + attrs = get_attrs(options.policy_path) + if attrs is None: + return 1 + + # Parse the headers try: headers = refparser.parse_headers(options.headers, output=log, debug=options.debug) except ValueError, e: @@ -76,7 +133,7 @@ def main(): return 1 if_set = interfaces.InterfaceSet(output=log) - if_set.add_headers(headers) + if_set.add_headers(headers, attributes=attrs) if_set.to_file(f) f.close() diff --git a/policycoreutils/newrole/newrole.c b/policycoreutils/newrole/newrole.c index 99d0ed7..3f08d37 100644 --- a/policycoreutils/newrole/newrole.c +++ b/policycoreutils/newrole/newrole.c @@ -1030,10 +1030,11 @@ int main(int argc, char *argv[]) * if it makes sense to continue to run newrole, and setting up * a scrubbed environment. */ - if (drop_capabilities(FALSE)) { +/* if (drop_capabilities(FALSE)) { perror(_("Sorry, newrole failed to drop capabilities\n")); return -1; } +*/ if (set_signal_handles()) return -1; diff --git a/policycoreutils/restorecond/Makefile b/policycoreutils/restorecond/Makefile index 3f235e6..03a4544 100644 --- a/policycoreutils/restorecond/Makefile +++ b/policycoreutils/restorecond/Makefile @@ -1,17 +1,28 @@ # Installation directories. PREFIX ?= ${DESTDIR}/usr SBINDIR ?= $(PREFIX)/sbin +LIBDIR ?= $(PREFIX)/lib MANDIR = $(PREFIX)/share/man +AUTOSTARTDIR = $(DESTDIR)/etc/xdg/autostart +DBUSSERVICEDIR = $(DESTDIR)/usr/share/dbus-1/services + +autostart_DATA = sealertauto.desktop INITDIR = $(DESTDIR)/etc/rc.d/init.d SELINUXDIR = $(DESTDIR)/etc/selinux +DBUSFLAGS = -DHAVE_DBUS -I/usr/include/dbus-1.0 -I/usr/lib64/dbus-1.0/include -I/usr/lib/dbus-1.0/include +DBUSLIB = -ldbus-glib-1 -ldbus-1 + CFLAGS ?= -g -Werror -Wall -W -override CFLAGS += -I$(PREFIX)/include -D_FILE_OFFSET_BITS=64 -LDLIBS += -lselinux -L$(PREFIX)/lib +override CFLAGS += -I$(PREFIX)/include $(DBUSFLAGS) -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include -I/usr/lib/glib-2.0/include + +LDLIBS += -lselinux $(DBUSLIB) -lglib-2.0 -L$(LIBDIR) all: restorecond -restorecond: restorecond.o utmpwatcher.o stringslist.o +restorecond.o utmpwatcher.o stringslist.o user.o watch.o: restorecond.h + +restorecond: ../setfiles/restore.o restorecond.o utmpwatcher.o stringslist.o user.o watch.o $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) install: all @@ -22,7 +33,12 @@ install: all -mkdir -p $(INITDIR) install -m 755 restorecond.init $(INITDIR)/restorecond -mkdir -p $(SELINUXDIR) - install -m 600 restorecond.conf $(SELINUXDIR)/restorecond.conf + install -m 644 restorecond.conf $(SELINUXDIR)/restorecond.conf + install -m 644 restorecond_user.conf $(SELINUXDIR)/restorecond_user.conf + -mkdir -p $(AUTOSTARTDIR) + install -m 644 restorecond.desktop $(AUTOSTARTDIR)/restorecond.desktop + -mkdir -p $(DBUSSERVICEDIR) + install -m 600 org.selinux.Restorecond.service $(DBUSSERVICEDIR)/org.selinux.Restorecond.service relabel: install /sbin/restorecon $(SBINDIR)/restorecond diff --git a/policycoreutils/restorecond/org.selinux.Restorecond.service b/policycoreutils/restorecond/org.selinux.Restorecond.service new file mode 100644 index 0000000..0ef5f0b --- /dev/null +++ b/policycoreutils/restorecond/org.selinux.Restorecond.service @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.selinux.Restorecond +Exec=/usr/sbin/restorecond -u diff --git a/policycoreutils/restorecond/restorecond.8 b/policycoreutils/restorecond/restorecond.8 index b149dcb..4622d2b 100644 --- a/policycoreutils/restorecond/restorecond.8 +++ b/policycoreutils/restorecond/restorecond.8 @@ -3,7 +3,7 @@ restorecond \- daemon that watches for file creation and then sets the default SELinux file context .SH "SYNOPSIS" -.B restorecond [\-d] +.B restorecond [\-d] [\-f restorecond_file ] [\-u] [\-v] .P .SH "DESCRIPTION" @@ -19,13 +19,22 @@ the correct file context associated with the policy. .B \-d Turns on debugging mode. Application will stay in the foreground and lots of debugs messages start printing. +.TP +.B \-f restorecond_file +Use alternative restorecond.conf file. +.TP +.B \-u +Turns on user mode. Runs restorecond in the user session and reads /etc/selinux/restorecond_user.conf. Uses dbus to make sure only one restorecond is running per user session. +.TP +.B \-v +Turns on verbose debugging. (Report missing files) .SH "AUTHOR" -This man page was written by Dan Walsh . -The program was written by Dan Walsh . +This man page and program was written by Dan Walsh . .SH "FILES" /etc/selinux/restorecond.conf +/etc/selinux/restorecond_user.conf .SH "SEE ALSO" .BR restorecon (8), diff --git a/policycoreutils/restorecond/restorecond.c b/policycoreutils/restorecond/restorecond.c index 4952632..89f5d97 100644 --- a/policycoreutils/restorecond/restorecond.c +++ b/policycoreutils/restorecond/restorecond.c @@ -30,9 +30,11 @@ * and makes sure that there security context matches the systems defaults * * USAGE: - * restorecond [-d] [-v] + * restorecond [-d] [-u] [-v] [-f restorecond_file ] * * -d Run in debug mode + * -f Use alternative restorecond_file + * -u Run in user mode * -v Run in verbose mode (Report missing files) * * EXAMPLE USAGE: @@ -48,297 +50,38 @@ #include #include #include -#include +#include "../setfiles/restore.h" #include -#include #include #include +#include +#include +#include +#include #include - #include "restorecond.h" -#include "stringslist.h" #include "utmpwatcher.h" -extern char *dirname(char *path); +const char *homedir; static int master_fd = -1; -static int master_wd = -1; -static int terminate = 0; - -#include -#include - -/* size of the event structure, not counting name */ -#define EVENT_SIZE (sizeof (struct inotify_event)) -/* reasonable guess as to size of 1024 events */ -#define BUF_LEN (1024 * (EVENT_SIZE + 16)) -static int debug_mode = 0; -static int verbose_mode = 0; - -static void restore(const char *filename, int exact); - -struct watchList { - struct watchList *next; - int wd; - char *dir; - struct stringsList *files; -}; -struct watchList *firstDir = NULL; - -/* Compare two contexts to see if their differences are "significant", - * or whether the only difference is in the user. */ -static int only_changed_user(const char *a, const char *b) -{ - char *rest_a, *rest_b; /* Rest of the context after the user */ - if (!a || !b) - return 0; - rest_a = strchr(a, ':'); - rest_b = strchr(b, ':'); - if (!rest_a || !rest_b) - return 0; - return (strcmp(rest_a, rest_b) == 0); -} +static char *server_watch_file = "/etc/selinux/restorecond.conf"; +static char *user_watch_file = "/etc/selinux/restorecond_user.conf"; +static char *watch_file; +static struct restore_opts r_opts; -/* - A file was in a direcroty has been created. This function checks to - see if it is one that we are watching. -*/ - -static int watch_list_find(int wd, const char *file) -{ - struct watchList *ptr = NULL; - ptr = firstDir; - - if (debug_mode) - printf("%d: File=%s\n", wd, file); - while (ptr != NULL) { - if (ptr->wd == wd) { - int exact=0; - if (strings_list_find(ptr->files, file, &exact) == 0) { - char *path = NULL; - if (asprintf(&path, "%s/%s", ptr->dir, file) < - 0) - exitApp("Error allocating memory."); - restore(path, exact); - free(path); - return 0; - } - if (debug_mode) - strings_list_print(ptr->files); - - /* Not found in this directory */ - return -1; - } - ptr = ptr->next; - } - /* Did not find a directory */ - return -1; -} - -static void watch_list_free(int fd) -{ - struct watchList *ptr = NULL; - struct watchList *prev = NULL; - ptr = firstDir; - - while (ptr != NULL) { - inotify_rm_watch(fd, ptr->wd); - strings_list_free(ptr->files); - free(ptr->dir); - prev = ptr; - ptr = ptr->next; - free(prev); - } - firstDir = NULL; -} - -/* - Set the file context to the default file context for this system. - Same as restorecon. -*/ -static void restore(const char *filename, int exact) -{ - int retcontext = 0; - security_context_t scontext = NULL; - security_context_t prev_context = NULL; - struct stat st; - int fd = -1; - if (debug_mode) - printf("restore %s\n", filename); - - fd = open(filename, O_NOFOLLOW | O_RDONLY); - if (fd < 0) { - if (verbose_mode) - syslog(LOG_ERR, "Unable to open file (%s) %s\n", - filename, strerror(errno)); - return; - } - - if (fstat(fd, &st) != 0) { - syslog(LOG_ERR, "Unable to stat file (%s) %s\n", filename, - strerror(errno)); - close(fd); - return; - } - - if (!(st.st_mode & S_IFDIR) && st.st_nlink > 1) { - if (exact) { - syslog(LOG_ERR, - "Will not restore a file with more than one hard link (%s) %s\n", - filename, strerror(errno)); - } - close(fd); - return; - } - - if (matchpathcon(filename, st.st_mode, &scontext) < 0) { - if (errno == ENOENT) - return; - syslog(LOG_ERR, "matchpathcon(%s) failed %s\n", filename, - strerror(errno)); - return; - } - retcontext = fgetfilecon_raw(fd, &prev_context); - - if (retcontext >= 0 || errno == ENODATA) { - if (retcontext < 0) - prev_context = NULL; - if (retcontext < 0 || (strcmp(prev_context, scontext) != 0)) { - - if (only_changed_user(scontext, prev_context) != 0) { - free(scontext); - free(prev_context); - close(fd); - return; - } - - if (fsetfilecon(fd, scontext) < 0) { - if (errno != EOPNOTSUPP) - syslog(LOG_ERR, - "set context %s->%s failed:'%s'\n", - filename, scontext, strerror(errno)); - if (retcontext >= 0) - free(prev_context); - free(scontext); - close(fd); - return; - } - syslog(LOG_WARNING, "Reset file context %s: %s->%s\n", - filename, prev_context, scontext); - } - if (retcontext >= 0) - free(prev_context); - } else { - if (errno != EOPNOTSUPP) - syslog(LOG_ERR, "get context on %s failed: '%s'\n", - filename, strerror(errno)); - } - free(scontext); - close(fd); -} - -static void process_config(int fd, FILE * cfg) -{ - char *line_buf = NULL; - size_t len = 0; - - while (getline(&line_buf, &len, cfg) > 0) { - char *buffer = line_buf; - while (isspace(*buffer)) - buffer++; - if (buffer[0] == '#') - continue; - int l = strlen(buffer) - 1; - if (l <= 0) - continue; - buffer[l] = 0; - if (buffer[0] == '~') - utmpwatcher_add(fd, &buffer[1]); - else { - watch_list_add(fd, buffer); - } - } - free(line_buf); -} - -/* - Read config file ignoring Comment lines - Files specified one per line. Files with "~" will be expanded to the logged in users - homedirs. -*/ - -static void read_config(int fd) -{ - char *watch_file_path = "/etc/selinux/restorecond.conf"; - - FILE *cfg = NULL; - if (debug_mode) - printf("Read Config\n"); - - watch_list_free(fd); - - cfg = fopen(watch_file_path, "r"); - if (!cfg) - exitApp("Error reading config file."); - process_config(fd, cfg); - fclose(cfg); - - inotify_rm_watch(fd, master_wd); - master_wd = - inotify_add_watch(fd, watch_file_path, IN_MOVED_FROM | IN_MODIFY); - if (master_wd == -1) - exitApp("Error watching config file."); -} - -/* - Inotify watch loop -*/ -static int watch(int fd) -{ - char buf[BUF_LEN]; - int len, i = 0; - len = read(fd, buf, BUF_LEN); - if (len < 0) { - if (terminate == 0) { - syslog(LOG_ERR, "Read error (%s)", strerror(errno)); - return 0; - } - syslog(LOG_ERR, "terminated"); - return -1; - } else if (!len) - /* BUF_LEN too small? */ - return -1; - while (i < len) { - struct inotify_event *event; - event = (struct inotify_event *)&buf[i]; - if (debug_mode) - printf("wd=%d mask=%u cookie=%u len=%u\n", - event->wd, event->mask, - event->cookie, event->len); - - if (event->mask & ~IN_IGNORED) { - if (event->wd == master_wd) - read_config(fd); - else { - switch (utmpwatcher_handle(fd, event->wd)) { - case -1: /* Message was not for utmpwatcher */ - if (event->len) - watch_list_find(event->wd, event->name); - break; - - case 1: /* utmp has changed need to reload */ - read_config(fd); - break; +#include - default: /* No users logged in or out */ - break; - } - } - } +int debug_mode = 0; +int terminate = 0; +int master_wd = -1; +int run_as_user = 0; - i += EVENT_SIZE + event->len; - } - return 0; +static void done(void) { + watch_list_free(master_fd); + close(master_fd); + utmpwatcher_free(); + matchpathcon_fini(); } static const char *pidfile = "/var/run/restorecond.pid"; @@ -377,7 +120,7 @@ static void term_handler() static void usage(char *program) { - printf("%s [-d] [-v] \n", program); + printf("%s [-d] [-f restorecond_file ] [-u] [-v] \n", program); exit(0); } @@ -393,74 +136,35 @@ void exitApp(const char *msg) to see if it is one that we are watching. */ -void watch_list_add(int fd, const char *path) -{ - struct watchList *ptr = NULL; - struct watchList *prev = NULL; - char *x = strdup(path); - if (!x) - exitApp("Out of Memory"); - char *dir = dirname(x); - char *file = basename(path); - ptr = firstDir; - - restore(path, 1); - - while (ptr != NULL) { - if (strcmp(dir, ptr->dir) == 0) { - strings_list_add(&ptr->files, file); - free(x); - return; - } - prev = ptr; - ptr = ptr->next; - } - ptr = calloc(1, sizeof(struct watchList)); - - if (!ptr) - exitApp("Out of Memory"); - - ptr->wd = inotify_add_watch(fd, dir, IN_CREATE | IN_MOVED_TO); - if (ptr->wd == -1) { - free(ptr); - syslog(LOG_ERR, "Unable to watch (%s) %s\n", - path, strerror(errno)); - return; - } - - ptr->dir = strdup(dir); - if (!ptr->dir) - exitApp("Out of Memory"); - - strings_list_add(&ptr->files, file); - if (prev) - prev->next = ptr; - else - firstDir = ptr; - - if (debug_mode) - printf("%d: Dir=%s, File=%s\n", ptr->wd, ptr->dir, file); - - free(x); -} - int main(int argc, char **argv) { int opt; struct sigaction sa; -#ifndef DEBUG - /* Make sure we are root */ - if (getuid() != 0) { - fprintf(stderr, "You must be root to run this program.\n"); - return 1; - } -#endif - /* Make sure we are root */ - if (is_selinux_enabled() != 1) { - fprintf(stderr, "Daemon requires SELinux be enabled to run.\n"); - return 1; - } + memset(&r_opts, 0, sizeof(r_opts)); + + r_opts.progress = 0; + r_opts.count = 0; + r_opts.debug = 0; + r_opts.change = 1; + r_opts.verbose = 0; + r_opts.logging = 0; + r_opts.rootpath = NULL; + r_opts.rootpathlen = 0; + r_opts.outfile = NULL; + r_opts.force = 0; + r_opts.hard_links = 0; + r_opts.abort_on_error = 0; + r_opts.add_assoc = 0; + r_opts.expand_realpath = 0; + r_opts.fts_flags = FTS_PHYSICAL; + r_opts.selabel_opt_validate = NULL; + r_opts.selabel_opt_path = NULL; + r_opts.ignore_enoent = 1; + + restore_init(&r_opts); + /* If we are not running SELinux then just exit */ + if (is_selinux_enabled() != 1) return 0; /* Register sighandlers */ sa.sa_flags = 0; @@ -470,36 +174,59 @@ int main(int argc, char **argv) set_matchpathcon_flags(MATCHPATHCON_NOTRANS); - master_fd = inotify_init(); - if (master_fd < 0) - exitApp("inotify_init"); - - while ((opt = getopt(argc, argv, "dv")) > 0) { + exclude_non_seclabel_mounts(); + atexit( done ); + while ((opt = getopt(argc, argv, "df:uv")) > 0) { switch (opt) { case 'd': debug_mode = 1; break; + case 'f': + watch_file = optarg; + break; + case 'u': + run_as_user = 1; + break; case 'v': - verbose_mode = 1; + r_opts.verbose++; break; case '?': usage(argv[0]); } } - read_config(master_fd); + + master_fd = inotify_init(); + if (master_fd < 0) + exitApp("inotify_init"); + + uid_t uid = getuid(); + struct passwd *pwd = getpwuid(uid); + if (!pwd) + exitApp("getpwuid"); + + homedir = pwd->pw_dir; + if (uid != 0) { + if (run_as_user) + return server(master_fd, user_watch_file); + if (start() != 0) + return server(master_fd, user_watch_file); + return 0; + } + + watch_file = server_watch_file; + read_config(master_fd, watch_file); if (!debug_mode) daemon(0, 0); write_pid_file(); - while (watch(master_fd) == 0) { + while (watch(master_fd, watch_file) == 0) { }; watch_list_free(master_fd); close(master_fd); matchpathcon_fini(); - utmpwatcher_free(); if (pidfile) unlink(pidfile); diff --git a/policycoreutils/restorecond/restorecond.conf b/policycoreutils/restorecond/restorecond.conf index 3fc9376..58b723a 100644 --- a/policycoreutils/restorecond/restorecond.conf +++ b/policycoreutils/restorecond/restorecond.conf @@ -4,8 +4,5 @@ /etc/mtab /var/run/utmp /var/log/wtmp -~/* -/root/.ssh +/root/* /root/.ssh/* - - diff --git a/policycoreutils/restorecond/restorecond.desktop b/policycoreutils/restorecond/restorecond.desktop new file mode 100644 index 0000000..23ff89d --- /dev/null +++ b/policycoreutils/restorecond/restorecond.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Name=File Context maintainer +Exec=/usr/sbin/restorecond -u +Comment=Fix file context in owned by the user +Encoding=UTF-8 +Type=Application +StartupNotify=false diff --git a/policycoreutils/restorecond/restorecond.h b/policycoreutils/restorecond/restorecond.h index e1666bf..8c85ef0 100644 --- a/policycoreutils/restorecond/restorecond.h +++ b/policycoreutils/restorecond/restorecond.h @@ -24,7 +24,22 @@ #ifndef RESTORED_CONFIG_H #define RESTORED_CONFIG_H -void exitApp(const char *msg); -void watch_list_add(int inotify_fd, const char *path); +extern int debug_mode; +extern const char *homedir; +extern int terminate; +extern int master_wd; +extern int run_as_user; + +extern int start(void); +extern int server(int, const char *watch_file); + +extern void exitApp(const char *msg); +extern void read_config(int fd, const char *watch_file); + +extern int watch(int fd, const char *watch_file); +extern void watch_list_add(int inotify_fd, const char *path); +extern int watch_list_find(int wd, const char *file); +extern void watch_list_free(int fd); +extern int watch_list_isempty(); #endif diff --git a/policycoreutils/restorecond/restorecond.init b/policycoreutils/restorecond/restorecond.init index b966db6..775c52b 100644 --- a/policycoreutils/restorecond/restorecond.init +++ b/policycoreutils/restorecond/restorecond.init @@ -26,7 +26,7 @@ PATH=/sbin:/bin:/usr/bin:/usr/sbin # Source function library. . /etc/rc.d/init.d/functions -[ -x /usr/sbin/selinuxenabled ] && /usr/sbin/selinuxenabled || exit 0 +[ -x /usr/sbin/selinuxenabled ] && /usr/sbin/selinuxenabled || exit 7 # Check that we are root ... so non-root users stop here test $EUID = 0 || exit 4 @@ -75,16 +75,15 @@ case "$1" in status restorecond RETVAL=$? ;; - restart|reload) + force-reload|restart|reload) restart ;; condrestart) [ -e /var/lock/subsys/restorecond ] && restart || : ;; *) - echo $"Usage: $0 {start|stop|restart|reload|condrestart}" + echo $"Usage: $0 {start|stop|restart|force-reload|status|condrestart}" RETVAL=3 esac exit $RETVAL - diff --git a/policycoreutils/restorecond/restorecond_user.conf b/policycoreutils/restorecond/restorecond_user.conf new file mode 100644 index 0000000..e0c2871 --- /dev/null +++ b/policycoreutils/restorecond/restorecond_user.conf @@ -0,0 +1,7 @@ +~/* +~/public_html/* +~/.gnome2/* +~/local/* +~/.fonts/* +~/.cache/* +~/.config/* diff --git a/policycoreutils/restorecond/user.c b/policycoreutils/restorecond/user.c new file mode 100644 index 0000000..ade3fb8 --- /dev/null +++ b/policycoreutils/restorecond/user.c @@ -0,0 +1,246 @@ +/* + * restorecond + * + * Copyright (C) 2006-2009 Red Hat + * see file 'COPYING' for use and warranty information + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +.* + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + * + * Authors: + * Dan Walsh + * +*/ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "restorecond.h" +#include "stringslist.h" +#include +#ifdef HAVE_DBUS +#include +#include +#include + +static DBusHandlerResult signal_filter (DBusConnection *connection, DBusMessage *message, void *user_data); + +static const char *PATH="/org/selinux/Restorecond"; +//static const char *BUSNAME="org.selinux.Restorecond"; +static const char *INTERFACE="org.selinux.RestorecondIface"; +static const char *RULE="type='signal',interface='org.selinux.RestorecondIface'"; + + +static DBusHandlerResult +signal_filter (DBusConnection *connection __attribute__ ((__unused__)), DBusMessage *message, void *user_data) +{ + /* User data is the event loop we are running in */ + GMainLoop *loop = user_data; + + /* A signal from the bus saying we are about to be disconnected */ + if (dbus_message_is_signal + (message, INTERFACE, "Stop")) { + + /* Tell the main loop to quit */ + g_main_loop_quit (loop); + /* We have handled this message, don't pass it on */ + return DBUS_HANDLER_RESULT_HANDLED; + } + /* A Ping signal on the com.burtonini.dbus.Signal interface */ + else if (dbus_message_is_signal (message, INTERFACE, "Start")) { + DBusError error; + dbus_error_init (&error); + g_print("Start received\n"); + return DBUS_HANDLER_RESULT_HANDLED; + } + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static int dbus_server(GMainLoop *loop) { + DBusConnection *bus; + DBusError error; + dbus_error_init (&error); + bus = dbus_bus_get (DBUS_BUS_SESSION, &error); + if (bus) { + dbus_connection_setup_with_g_main (bus, NULL); + + /* listening to messages from all objects as no path is specified */ + dbus_bus_add_match (bus, RULE, &error); // see signals from the given interfacey + dbus_connection_add_filter (bus, signal_filter, loop, NULL); + return 0; + } + return -1; +} + +#endif +#include +#include + +/* size of the event structure, not counting name */ +#define EVENT_SIZE (sizeof (struct inotify_event)) +/* reasonable guess as to size of 1024 events */ +#define BUF_LEN (1024 * (EVENT_SIZE + 16)) + +static gboolean +io_channel_callback + (GIOChannel *source, + GIOCondition condition, + gpointer data __attribute__((__unused__))) +{ + + char buffer[BUF_LEN+1]; + gsize bytes_read; + unsigned int i = 0; + + if (condition & G_IO_IN) { + /* Data is available. */ + g_io_channel_read + (source, buffer, + sizeof (buffer), + &bytes_read); + + while (i < bytes_read) { + struct inotify_event *event; + event = (struct inotify_event *)&buffer[i]; + if (debug_mode) + printf("wd=%d mask=%u cookie=%u len=%u\n", + event->wd, event->mask, + event->cookie, event->len); + if (event->len) + watch_list_find(event->wd, event->name); + + i += EVENT_SIZE + event->len; + } + } + + /* An error happened while reading + the file. */ + + if (condition & G_IO_NVAL) + return FALSE; + + /* We have reached the end of the + file. */ + + if (condition & G_IO_HUP) { + g_io_channel_close (source); + return FALSE; + } + + /* Returning TRUE will make sure + the callback remains associated + to the channel. */ + + return TRUE; +} + +int start() { +#ifdef HAVE_DBUS + DBusConnection *bus; + DBusError error; + DBusMessage *message; + + /* Get a connection to the session bus */ + dbus_error_init (&error); + bus = dbus_bus_get (DBUS_BUS_SESSION, &error); + if (!bus) { + if (debug_mode) + g_warning ("Failed to connect to the D-BUS daemon: %s", error.message); + dbus_error_free (&error); + return 1; + } + + + /* Create a new signal "Start" on the interface, + * from the object */ + message = dbus_message_new_signal (PATH, + INTERFACE, "Start"); + /* Send the signal */ + dbus_connection_send (bus, message, NULL); + /* Free the signal now we have finished with it */ + dbus_message_unref (message); +#endif /* HAVE_DBUS */ + return 0; +} + +static int local_server() { + // ! dbus, run as local service + char *ptr=NULL; + if (asprintf(&ptr, "%s/.restorecond", homedir) < 0) { + if (debug_mode) + perror("asprintf"); + return -1; + } + int fd = open(ptr, O_CREAT | O_WRONLY | O_NOFOLLOW, S_IRUSR | S_IWUSR); + if (debug_mode) + g_warning ("Lock file: %s", ptr); + + free(ptr); + if (fd < 0) { + if (debug_mode) + perror("open"); + return -1; + } + if (flock(fd, LOCK_EX | LOCK_NB) < 0) { + if (debug_mode) + perror("flock"); + return -1; + } + return 0; +} + +int server(int master_fd, const char *watch_file) { + GMainLoop *loop; + + loop = g_main_loop_new (NULL, FALSE); + +#ifdef HAVE_DBUS + if (dbus_server(loop) != 0) +#endif /* HAVE_DBUS */ + if (local_server(loop)) + goto end; + + read_config(master_fd, watch_file); + + if (watch_list_isempty()) goto end; + + set_matchpathcon_flags(MATCHPATHCON_NOTRANS); + + GIOChannel *c = g_io_channel_unix_new(master_fd); + + g_io_add_watch_full( c, + G_PRIORITY_HIGH, + G_IO_IN|G_IO_ERR|G_IO_HUP, + io_channel_callback, NULL, NULL); + + g_main_loop_run (loop); + +end: + g_main_loop_unref (loop); + return 0; +} + diff --git a/policycoreutils/restorecond/watch.c b/policycoreutils/restorecond/watch.c new file mode 100644 index 0000000..6a833c3 --- /dev/null +++ b/policycoreutils/restorecond/watch.c @@ -0,0 +1,272 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../setfiles/restore.h" +#include +#include +#include +#include +#include +#include +#include +#include "restorecond.h" +#include "stringslist.h" +#include "utmpwatcher.h" + +/* size of the event structure, not counting name */ +#define EVENT_SIZE (sizeof (struct inotify_event)) +/* reasonable guess as to size of 1024 events */ +#define BUF_LEN (1024 * (EVENT_SIZE + 16)) + + +struct watchList { + struct watchList *next; + int wd; + char *dir; + struct stringsList *files; +}; +struct watchList *firstDir = NULL; + +int watch_list_isempty() { + return firstDir == NULL; +} + +void watch_list_add(int fd, const char *path) +{ + struct watchList *ptr = NULL; + size_t i = 0; + struct watchList *prev = NULL; + glob_t globbuf; + char *x = strdup(path); + if (!x) exitApp("Out of Memory"); + char *file = basename(x); + char *dir = dirname(x); + ptr = firstDir; + + if (exclude(path)) goto end; + + globbuf.gl_offs = 1; + if (glob(path, + GLOB_TILDE | GLOB_PERIOD, + NULL, + &globbuf) >= 0) { + for (i=0; i < globbuf.gl_pathc; i++) { + int len = strlen(globbuf.gl_pathv[i]) -2; + if (len > 0 && strcmp(&globbuf.gl_pathv[i][len--], "/.") == 0) continue; + if (len > 0 && strcmp(&globbuf.gl_pathv[i][len], "/..") == 0) continue; + if (process_one_realpath(globbuf.gl_pathv[i], 0) > 0) + process_one_realpath(globbuf.gl_pathv[i], 1); + } + globfree(&globbuf); + } + + while (ptr != NULL) { + if (strcmp(dir, ptr->dir) == 0) { + strings_list_add(&ptr->files, file); + goto end; + } + prev = ptr; + ptr = ptr->next; + } + ptr = calloc(1, sizeof(struct watchList)); + + if (!ptr) exitApp("Out of Memory"); + + ptr->wd = inotify_add_watch(fd, dir, IN_CREATE | IN_MOVED_TO); + if (ptr->wd == -1) { + free(ptr); + if (! run_as_user) + syslog(LOG_ERR, "Unable to watch (%s) %s\n", + path, strerror(errno)); + goto end; + } + + ptr->dir = strdup(dir); + if (!ptr->dir) + exitApp("Out of Memory"); + + strings_list_add(&ptr->files, file); + if (prev) + prev->next = ptr; + else + firstDir = ptr; + + if (debug_mode) + printf("%d: Dir=%s, File=%s\n", ptr->wd, ptr->dir, file); + +end: + free(x); + return; +} + +/* + A file was in a direcroty has been created. This function checks to + see if it is one that we are watching. +*/ + +int watch_list_find(int wd, const char *file) +{ + struct watchList *ptr = NULL; + ptr = firstDir; + if (debug_mode) + printf("%d: File=%s\n", wd, file); + while (ptr != NULL) { + if (ptr->wd == wd) { + int exact=0; + if (strings_list_find(ptr->files, file, &exact) == 0) { + char *path = NULL; + if (asprintf(&path, "%s/%s", ptr->dir, file) < + 0) + exitApp("Error allocating memory."); + + process_one_realpath(path, 0); + free(path); + return 0; + } + if (debug_mode) + strings_list_print(ptr->files); + + /* Not found in this directory */ + return -1; + } + ptr = ptr->next; + } + /* Did not find a directory */ + return -1; +} + +void watch_list_free(int fd) +{ + struct watchList *ptr = NULL; + struct watchList *prev = NULL; + ptr = firstDir; + + while (ptr != NULL) { + inotify_rm_watch(fd, ptr->wd); + strings_list_free(ptr->files); + free(ptr->dir); + prev = ptr; + ptr = ptr->next; + free(prev); + } + firstDir = NULL; +} + +/* + Inotify watch loop +*/ +int watch(int fd, const char *watch_file) +{ + char buf[BUF_LEN]; + int len, i = 0; + if (firstDir == NULL) return 0; + + len = read(fd, buf, BUF_LEN); + if (len < 0) { + if (terminate == 0) { + syslog(LOG_ERR, "Read error (%s)", strerror(errno)); + return 0; + } + syslog(LOG_ERR, "terminated"); + return -1; + } else if (!len) + /* BUF_LEN too small? */ + return -1; + while (i < len) { + struct inotify_event *event; + event = (struct inotify_event *)&buf[i]; + if (debug_mode) + printf("wd=%d mask=%u cookie=%u len=%u\n", + event->wd, event->mask, + event->cookie, event->len); + if (event->wd == master_wd) + read_config(fd, watch_file); + else { + switch (utmpwatcher_handle(fd, event->wd)) { + case -1: /* Message was not for utmpwatcher */ + if (event->len) + watch_list_find(event->wd, event->name); + break; + case 1: /* utmp has changed need to reload */ + read_config(fd, watch_file); + break; + + default: /* No users logged in or out */ + break; + } + } + + i += EVENT_SIZE + event->len; + } + return 0; +} + +static void process_config(int fd, FILE * cfg) +{ + char *line_buf = NULL; + size_t len = 0; + + while (getline(&line_buf, &len, cfg) > 0) { + char *buffer = line_buf; + while (isspace(*buffer)) + buffer++; + if (buffer[0] == '#') + continue; + int l = strlen(buffer) - 1; + if (l <= 0) + continue; + buffer[l] = 0; + if (buffer[0] == '~') { + if (run_as_user) { + char *ptr=NULL; + if (asprintf(&ptr, "%s%s", homedir, &buffer[1]) < 0) + exitApp("Error allocating memory."); + + watch_list_add(fd, ptr); + free(ptr); + } else { + utmpwatcher_add(fd, &buffer[1]); + } + } else { + watch_list_add(fd, buffer); + } + } + free(line_buf); +} + +/* + Read config file ignoring Comment lines + Files specified one per line. Files with "~" will be expanded to the logged in users + homedirs. +*/ + +void read_config(int fd, const char *watch_file_path) +{ + + FILE *cfg = NULL; + if (debug_mode) + printf("Read Config\n"); + + watch_list_free(fd); + + cfg = fopen(watch_file_path, "r"); + if (!cfg){ + perror(watch_file_path); + exitApp("Error reading config file"); + } + process_config(fd, cfg); + fclose(cfg); + + inotify_rm_watch(fd, master_wd); + master_wd = + inotify_add_watch(fd, watch_file_path, IN_MOVED_FROM | IN_MODIFY); + if (master_wd == -1) + exitApp("Error watching config file."); +} diff --git a/policycoreutils/run_init/run_init.c b/policycoreutils/run_init/run_init.c index 9db766c..068e24c 100644 --- a/policycoreutils/run_init/run_init.c +++ b/policycoreutils/run_init/run_init.c @@ -414,10 +414,17 @@ int main(int argc, char *argv[]) * execvp or using a exec(1) recycles pty's, and does not open a new * one. */ +#ifdef USE_OPEN_INIT_PTY if (execvp("/usr/sbin/open_init_pty", argv)) { perror("execvp"); exit(-1); } +#else + if (execvp(argv[1], argv + 1)) { + perror("execvp"); + exit(-1); + } +#endif return 0; } /* main() */ diff --git a/policycoreutils/sandbox/Makefile b/policycoreutils/sandbox/Makefile index ff0ee7c..924999d 100644 --- a/policycoreutils/sandbox/Makefile +++ b/policycoreutils/sandbox/Makefile @@ -7,10 +7,10 @@ SBINDIR ?= $(PREFIX)/sbin MANDIR ?= $(PREFIX)/share/man LOCALEDIR ?= /usr/share/locale SHAREDIR ?= $(PREFIX)/share/sandbox -override CFLAGS += $(LDFLAGS) -I$(PREFIX)/include -DPACKAGE="\"policycoreutils\"" -LDLIBS += -lselinux -lcap-ng +override CFLAGS += $(LDFLAGS) -I$(PREFIX)/include -DPACKAGE="\"policycoreutils\"" -Wall -Werror -Wextra +LDLIBS += -lcgroup -lselinux -lcap-ng -all: sandbox seunshare sandboxX.sh +all: sandbox seunshare sandboxX.sh start seunshare: seunshare.o $(EXTRA_OBJS) $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) @@ -20,14 +20,18 @@ install: all install -m 755 sandbox $(BINDIR) -mkdir -p $(MANDIR)/man8 install -m 644 sandbox.8 $(MANDIR)/man8/ + install -m 644 seunshare.8 $(MANDIR)/man8/ + -mkdir -p $(MANDIR)/man5 + install -m 644 sandbox.conf.5 $(MANDIR)/man5/sandbox.5 -mkdir -p $(SBINDIR) install -m 4755 seunshare $(SBINDIR)/ -mkdir -p $(SHAREDIR) install -m 755 sandboxX.sh $(SHAREDIR) + install -m 755 start $(SHAREDIR) -mkdir -p $(INITDIR) install -m 755 sandbox.init $(INITDIR)/sandbox -mkdir -p $(SYSCONFDIR) - install -m 644 sandbox.config $(SYSCONFDIR)/sandbox + install -m 644 sandbox.conf $(SYSCONFDIR)/sandbox test: @python test_sandbox.py -v diff --git a/policycoreutils/sandbox/sandbox b/policycoreutils/sandbox/sandbox index 0b89e9a..481034c 100644 --- a/policycoreutils/sandbox/sandbox +++ b/policycoreutils/sandbox/sandbox @@ -1,5 +1,6 @@ #! /usr/bin/python -Es # Authors: Dan Walsh +# Authors: Thomas Liu # Authors: Josh Cogliati # # Copyright (C) 2009,2010 Red Hat @@ -19,15 +20,17 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -import os, sys, socket, random, fcntl, shutil, re, subprocess +import os, stat, sys, socket, random, fcntl, shutil, re, subprocess import selinux import signal from tempfile import mkdtemp import pwd +import commands +import setools PROGNAME = "policycoreutils" -HOMEDIR=pwd.getpwuid(os.getuid()).pw_dir - +SEUNSHARE = "/usr/sbin/seunshare" +SANDBOXSH = "/usr/share/sandbox/sandboxX.sh" import gettext gettext.bindtextdomain(PROGNAME, "/usr/share/locale") gettext.textdomain(PROGNAME) @@ -41,6 +44,7 @@ except IOError: import __builtin__ __builtin__.__dict__['_'] = unicode +DEFAULT_WINDOWSIZE = "1000x700" DEFAULT_TYPE = "sandbox_t" DEFAULT_X_TYPE = "sandbox_x_t" SAVE_FILES = {} @@ -63,15 +67,15 @@ def error_exit(msg): sys.stderr.flush() sys.exit(1) -def copyfile(file, dir, dest): +def copyfile(file, srcdir, dest): import re - if file.startswith(dir): + if file.startswith(srcdir): dname = os.path.dirname(file) bname = os.path.basename(file) - if dname == dir: + if dname == srcdir: dest = dest + "/" + bname else: - newdir = re.sub(dir, dest, dname) + newdir = re.sub(srcdir, dest, dname) if not os.path.exists(newdir): os.makedirs(newdir) dest = newdir + "/" + bname @@ -81,9 +85,10 @@ def copyfile(file, dir, dest): shutil.copytree(file, dest) else: shutil.copy2(file, dest) + except shutil.Error, elist: - for e in elist: - sys.stderr.write(e[1]) + for e in elist.message: + sys.stderr.write(e[2]) SAVE_FILES[file] = (dest, os.path.getmtime(dest)) @@ -161,10 +166,10 @@ class Sandbox: if not self.__options.homedir or not self.__options.tmpdir: self.usage(_("Homedir and tempdir required for level mounts")) - if not os.path.exists("/usr/sbin/seunshare"): + if not os.path.exists(SEUNSHARE): raise ValueError(_(""" -/usr/sbin/seunshare is required for the action you want to perform. -""")) +%s is required for the action you want to perform. +""") % SEUNSHARE) def __mount_callback(self, option, opt, value, parser): self.__mount = True @@ -172,6 +177,15 @@ class Sandbox: def __x_callback(self, option, opt, value, parser): self.__mount = True setattr(parser.values, option.dest, True) + if not os.path.exists(SEUNSHARE): + raise ValueError(_(""" +%s is required for the action you want to perform. +""") % SEUNSHARE) + + if not os.path.exists(SANDBOXSH): + raise ValueError(_(""" +%s is required for the action you want to perform. +""") % SANDBOXSH) def __validdir(self, option, opt, value, parser): if not os.path.isdir(value): @@ -194,6 +208,8 @@ class Sandbox: self.__include(option, opt, i[:-1], parser) except IOError, e: sys.stderr.write(str(e)) + except TypeError, e: + sys.stderr.write(str(e)) fd.close() def __copyfiles(self): @@ -212,13 +228,15 @@ class Sandbox: /etc/gdm/Xsession """) else: - command = " ".join(self.__paths) + command = self.__paths[0] + " " + for p in self.__paths[1:]: + command += "'%s' " % p fd.write("""#! /bin/sh #TITLE: %s /usr/bin/test -r ~/.xmodmap && /usr/bin/xmodmap ~/.xmodmap %s & WM_PID=$! -%s +dbus-launch --exit-with-session %s kill -TERM $WM_PID 2> /dev/null """ % (command, wm, command)) fd.close() @@ -229,11 +247,22 @@ kill -TERM $WM_PID 2> /dev/null def __parse_options(self): from optparse import OptionParser + types = "" + try: + types = _(""" +Policy defines the following types for use with the -t: +\t%s +""") % "\n\t".join(setools.seinfo(setools.ATTRIBUTE, "sandbox_type")[0]['types']) + except RuntimeError: + pass + usage = _(""" -sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [-W windowmanager ] [[-i file ] ...] [ -t type ] command +sandbox [-h] [-c] [-l level ] [-[X|M] [-H homedir] [-T tempdir]] [-I includefile ] [-W windowmanager ] [ -w windowsize ] [[-i file ] ...] [ -t type ] command + +sandbox [-h] [-c] [-l level ] [-[X|M] [-H homedir] [-T tempdir]] [-I includefile ] [-W windowmanager ] [ -w windowsize ] [[-i file ] ...] [ -t type ] -S +%s +""") % types -sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [-W windowmanager ] [[-i file ] ...] [ -t type ] -S -""") parser = OptionParser(version=self.VERSION, usage=usage) parser.disable_interspersed_args() @@ -260,14 +289,18 @@ sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [- parser.add_option("-H", "--homedir", action="callback", callback=self.__validdir, type="string", - dest="homedir", + dest="homedir", help=_("alternate home directory to use for mounting")) - parser.add_option("-T", "--tmpdir", dest="tmpdir", + parser.add_option("-T", "--tmpdir", dest="tmpdir", type="string", action="callback", callback=self.__validdir, help=_("alternate /tmp directory to use for mounting")) + parser.add_option("-w", "--windowsize", dest="windowsize", + type="string", default=DEFAULT_WINDOWSIZE, + help="size of the sandbox window") + parser.add_option("-W", "--windowmanager", dest="wm", type="string", default="/usr/bin/matchbox-window-manager -use_titlebar no", @@ -276,13 +309,21 @@ sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [- parser.add_option("-l", "--level", dest="level", help=_("MCS/MLS level for the sandbox")) + parser.add_option("-c", "--cgroups", + action="store_true", dest="usecgroup", default=False, + help=_("Use cgroups to limit this sandbox.")) + + parser.add_option("-C", "--capabilities", + action="store_true", dest="usecaps", default=False, + help="Allow apps requiring capabilities to run within the sandbox.") + self.__parser=parser self.__options, cmds = parser.parse_args() if self.__options.X_ind: self.setype = DEFAULT_X_TYPE - + self.dpi=commands.getoutput("xrdb -query | grep dpi | /bin/cut -f 2") if self.__options.setype: self.setype = self.__options.setype @@ -300,6 +341,10 @@ sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [- self.__homedir = self.__options.homedir self.__tmpdir = self.__options.tmpdir else: + if self.__options.level: + self.__homedir = self.__options.homedir + self.__tmpdir = self.__options.tmpdir + if len(cmds) == 0: self.usage(_("Command required")) cmds[0] = fullpath(cmds[0]) @@ -323,50 +368,51 @@ sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [- con = selinux.getcon()[1].split(":") self.__execcon = "%s:%s:%s:%s" % (con[0], con[1], self.setype, level) - self.__filecon = "%s:%s:%s:%s" % (con[0], "object_r", - "%s_file_t" % self.setype[:-2], + self.__filecon = "%s:%s:%s:%s" % (con[0], "object_r", + "%s_file_t" % self.setype[:-2], level) def __setup_dir(self): if self.__options.level or self.__options.session: return - sandboxdir = HOMEDIR + "/.sandbox" - if not os.path.exists(sandboxdir): - os.mkdir(sandboxdir) if self.__options.homedir: selinux.chcon(self.__options.homedir, self.__filecon, recursive=True) self.__homedir = self.__options.homedir else: selinux.setfscreatecon(self.__filecon) - self.__homedir = mkdtemp(dir=sandboxdir, prefix=".sandbox") + self.__homedir = mkdtemp(dir="/tmp", prefix=".sandbox_home_") if self.__options.tmpdir: selinux.chcon(self.__options.tmpdir, self.__filecon, recursive=True) self.__tmpdir = self.__options.tmpdir else: selinux.setfscreatecon(self.__filecon) - self.__tmpdir = mkdtemp(dir="/tmp", prefix=".sandbox") + self.__tmpdir = mkdtemp(dir="/tmp", prefix=".sandbox_tmp_") selinux.setfscreatecon(None) self.__copyfiles() def __execute(self): try: - if self.__options.X_ind: - xmodmapfile = self.__homedir + "/.xmodmap" - xd = open(xmodmapfile,"w") - subprocess.Popen(["/usr/bin/xmodmap","-pke"],stdout=xd).wait() - xd.close() - - self.__setup_sandboxrc(self.__options.wm) - - cmds = [ '/usr/sbin/seunshare', "-t", self.__tmpdir, "-h", self.__homedir, "--", self.__execcon, "/usr/share/sandbox/sandboxX.sh" ] - rc = subprocess.Popen(cmds).wait() - return rc - + cmds = [ SEUNSHARE, "-Z", self.__execcon ] + if self.__options.usecgroup: + cmds.append('-c') + if self.__options.usecaps: + cmds.append('-C') if self.__mount: - cmds = [ '/usr/sbin/seunshare', "-t", self.__tmpdir, "-h", self.__homedir, "--", self.__execcon ] + self.__paths - rc = subprocess.Popen(cmds).wait() - return rc + cmds += [ "-t", self.__tmpdir, "-h", self.__homedir ] + + if self.__options.X_ind: + xmodmapfile = self.__homedir + "/.xmodmap" + xd = open(xmodmapfile,"w") + subprocess.Popen(["/usr/bin/xmodmap","-pke"],stdout=xd).wait() + xd.close() + + self.__setup_sandboxrc(self.__options.wm) + + cmds += [ "--", SANDBOXSH, self.__options.windowsize, self.dpi ] + else: + cmds += [ "--" ] + self.__paths + return subprocess.Popen(cmds).wait() selinux.setexeccon(self.__execcon) rc = subprocess.Popen(self.__cmds).wait() @@ -404,7 +450,7 @@ if __name__ == '__main__': sandbox = Sandbox() rc = sandbox.main() except OSError, error: - error_exit(error.args[1]) + error_exit(error) except ValueError, error: error_exit(error.args[0]) except KeyError, error: diff --git a/policycoreutils/sandbox/sandbox.8 b/policycoreutils/sandbox/sandbox.8 index 1479364..2b37e63 100644 --- a/policycoreutils/sandbox/sandbox.8 +++ b/policycoreutils/sandbox/sandbox.8 @@ -1,10 +1,13 @@ -.TH SANDBOX "8" "May 2009" "chcat" "User Commands" +.TH SANDBOX "8" "May 2010" "sandbox" "User Commands" .SH NAME sandbox \- Run cmd under an SELinux sandbox .SH SYNOPSIS .B sandbox -[-l level ] [[-M | -X] -H homedir -T tempdir ] [-I includefile ] [ -W windowmanager ] [[-i file ]...] [ -t type ] cmd -[-l level ] [[-M | -X] -H homedir -T tempdir ] [-I includefile ] [ -W windowmanager ] [[-i file ]...] [ -t type ] -S +[-C] [-c] [-l level ] [[-M | -X] -H homedir -T tempdir ] [-I includefile ] [ -W windowmanager ] [ -w windowsize ] [[-i file ]...] [ -t type ] cmd + +.br +.B sandbox +[-C] [-c] [-l level ] [[-M | -X] -H homedir -T tempdir ] [-I includefile ] [ -W windowmanager ] [ -w windowsize ] [[-i file ]...] [ -t type ] -S .br .SH DESCRIPTION .PP @@ -42,6 +45,12 @@ Use alternate sandbox type, defaults to sandbox_t or sandbox_x_t for -X. \fB\-T\ tmpdir Use alternate tempory directory to mount on /tmp. Defaults to tmpfs. Requires -X or -M. .TP +\fB\-S +Run a full desktop session, Requires level, and home and tmpdir. +.TP +\fB\-w windowsize\fR +Specifies the windowsize when creating an X based Sandbox. The default windowsize is 1000x700. +.TP \fB\-W windowmanager\fR Select alternative window manager to run within .B sandbox -X. @@ -50,8 +59,20 @@ Default to /usr/bin/matchbox-window-manager. \fB\-X\fR Create an X based Sandbox for gui apps, temporary files for $HOME and /tmp, secondary Xserver, defaults to sandbox_x_t +.TP +\fB\-c\fR +Use control groups to control this copy of sandbox. Specify parameters in /etc/sysconfig/sandbox. Max memory usage and cpu usage are to be specified in percent. You can specify which CPUs to use by numbering them 0,1,2... etc. +.TP +\fB\-C\fR +Use capabilities within the sandbox. By default applications executed within the sandbox will not be allowed to use capabilities (setuid apps), with the -C flag, you can use programs requiring capabilities. .PP .SH "SEE ALSO" .TP -runcon(1) +runcon(1), seunshare(8), selinux(8) .PP + +.SH AUTHOR +This manual page was written by +.I Dan Walsh +and +.I Thomas Liu diff --git a/policycoreutils/sandbox/sandbox.conf b/policycoreutils/sandbox/sandbox.conf new file mode 100644 index 0000000..7c35808 --- /dev/null +++ b/policycoreutils/sandbox/sandbox.conf @@ -0,0 +1,7 @@ +# Space separate list of homedirs +HOMEDIRS="/home" +# Control group configuration +NAME=sandbox +CPUAFFINITY=ALL +MEMUSAGE=80% +CPUUSAGE=80% diff --git a/policycoreutils/sandbox/sandbox.conf.5 b/policycoreutils/sandbox/sandbox.conf.5 new file mode 100644 index 0000000..b3ee67d --- /dev/null +++ b/policycoreutils/sandbox/sandbox.conf.5 @@ -0,0 +1,40 @@ +.TH sandbox.conf "5" "June 2010" "sandbox.conf" "Linux System Administration" +.SH NAME +sandbox.conf \- user config file for the SELinux sandbox +.SH DESCRIPTION +.PP +When running sandbox with the -C argument, it will be confined using control groups and a system administrator can specify how the sandbox is confined. + +.PP +Everything after "#" is ignored, as are empty lines. All arguments should be separated by and equals sign ("="). + +.PP +These keywords are allowed. + +.RS +.TP +.B NAME +The name of the sandbox control group. Default is "sandbox". + +.TP +.B CPUAFFINITY +Which cpus to assign sandbox to. The default is ALL, but users can specify a comma-separated list with dashes ("-") to represent ranges. Ex: 0-2,5 + +.TP +.B MEMUSAGE +How much memory to allow sandbox to use. The default is 80%. Users can specify either a percentage or a value in the form of a number followed by one of the suffixes K, M, G to denote kilobytes, megabytes or gigabytes respectively. Ex: 50% or 100M + +.TP +.B CPUUSAGE +Percentage of cpu sandbox should be allowed to use. The default is 80%. Specify a value followed by a percent sign ("%"). Ex: 50% + + + +.SH "SEE ALSO" +.TP +sandbox(8) +.PP + +.SH AUTHOR +This manual page was written by +.I Thomas Liu diff --git a/policycoreutils/sandbox/sandbox.init b/policycoreutils/sandbox/sandbox.init index ff8b3ef..66aadfd 100644 --- a/policycoreutils/sandbox/sandbox.init +++ b/policycoreutils/sandbox/sandbox.init @@ -10,17 +10,12 @@ # # chkconfig: 345 1 99 # -# Description: sandbox and other apps that want to use pam_namespace -# on /var/tmp, /tmp and home directories, requires this script -# to be run at boot time. -# This script sets up the / mount point and all of its -# subdirectories as shared. The script sets up -# /tmp, /var/tmp, /home and any homedirs listed in -# /etc/sysconfig/sandbox and all of their subdirectories -# as unshared. -# All processes that use pam_namespace will see -# modifications to the global mountspace, except for the -# unshared directories. +# description: sandbox, xguest and other apps that want to use pam_namespace \ +# require this script be run at boot. This service script does \ +# not actually run any service but sets up: \ +# /var/tmp, /tmp and home directories to be used by these tools.\ +# If you do not use sandbox, xguest or pam_namespace you can turn \ +# this service off.\ # # Source function library. @@ -41,15 +36,6 @@ start() { touch $LOCKFILE mount --make-rshared / || return $? - mount --rbind /tmp /tmp || return $? - mount --rbind /var/tmp /var/tmp || return $? - mount --make-private /tmp || return $? - mount --make-private /var/tmp || return $? - for h in $HOMEDIRS; do - mount --rbind $h $h || return $? - mount --make-private $h || return $? - done - return 0 } diff --git a/policycoreutils/sandbox/sandboxX.sh b/policycoreutils/sandbox/sandboxX.sh index 8338203..88ebfee 100644 --- a/policycoreutils/sandbox/sandboxX.sh +++ b/policycoreutils/sandbox/sandboxX.sh @@ -1,15 +1,21 @@ -#!/bin/bash +#!/bin/bash +trap "" TERM context=`id -Z | secon -t -l -P` export TITLE="Sandbox $context -- `grep ^#TITLE: ~/.sandboxrc | /usr/bin/cut -b8-80`" -export SCREENSIZE="1000x700" -#export SCREENSIZE=`xdpyinfo | awk '/dimensions/ { print $2 }'` +[ -z $1 ] && export SCREENSIZE="1000x700" || export SCREENSIZE="$1" +[ -z $2 ] && export DPI="96" || export DPI="$2" trap "exit 0" HUP -(/usr/bin/Xephyr -title "$TITLE" -terminate -screen $SCREENSIZE -displayfd 5 5>&1 2>/dev/null) | while read D; do +(/usr/bin/Xephyr -title "$TITLE" -terminate -screen $SCREENSIZE -dpi $DPI -displayfd 5 5>&1 2>/dev/null) | while read D; do export DISPLAY=:$D - python -c 'import gtk, os, commands; commands.getstatusoutput("%s/.sandboxrc" % os.environ["HOME"])' + cat > ~/seremote << __EOF +#!/bin/sh +DISPLAY=$DISPLAY "\$@" +__EOF + chmod +x ~/seremote + /usr/share/sandbox/start $HOME/.sandboxrc export EXITCODE=$? - kill -HUP 0 + kill -TERM 0 break done exit 0 diff --git a/policycoreutils/sandbox/seunshare.8 b/policycoreutils/sandbox/seunshare.8 new file mode 100644 index 0000000..06610c0 --- /dev/null +++ b/policycoreutils/sandbox/seunshare.8 @@ -0,0 +1,43 @@ +.TH SEUNSHARE "8" "May 2010" "seunshare" "User Commands" +.SH NAME +seunshare \- Run cmd with alternate homedir, tmpdir and/or SELinux context +.SH SYNOPSIS +.B seunshare +[ -v ] [ -c ] [ -C ] [ -k ] [ -t tmpdir ] [ -h homedir ] [ -Z context ] -- executable [args] +.br +.SH DESCRIPTION +.PP +Run the +.I executable +within the specified context, using the alternate home directory and /tmp directory. The seunshare command unshares from the default namespace, then mounts the specified homedir and tmpdir over the default homedir and /tmp. Finally it tells the kernel to execute the application under the specified SELinux context. + +.TP +\fB\-h homedir\fR +Alternate homedir to be used by the application. Homedir must be owned by the user. +.TP +\fB\-t\ tmpdir +Use alternate tempory directory to mount on /tmp. tmpdir must be owned by the user. +.TP +\fB\-c --cgroups\fR +Use cgroups to control this copy of seunshare. Specify parameters in /etc/sysconfig/sandbox. Max memory usage and cpu usage are to be specified in percent. You can specify which CPUs to use by numbering them 0,1,2... etc. +.TP +\fB\-C --capabilities\fR +Allow apps executed within the namespace to use capabilities. Default is no capabilities. +.TP +\fB\-k --kill\fR +Kill all processes with matching MCS level. +.TP +\fB\-Z\ context +Use alternate SELinux context while runing the executable. +.TP +\fB\-v\fR +Verbose output +.SH "SEE ALSO" +.TP +runcon(1), sandbox(8), selinux(8) +.PP +.SH AUTHOR +This manual page was written by +.I Dan Walsh +and +.I Thomas Liu diff --git a/policycoreutils/sandbox/seunshare.c b/policycoreutils/sandbox/seunshare.c index e713b74..1a0a488 100644 --- a/policycoreutils/sandbox/seunshare.c +++ b/policycoreutils/sandbox/seunshare.c @@ -1,27 +1,35 @@ +/* + * Authors: Dan Walsh + * Authors: Thomas Liu + */ + #define _GNU_SOURCE #include #include +#include #include #include #include +#include #include #include +#include #include #include +#include #include +#include #include #include #include /* for getopt_long() form of getopt() */ #include #include #include +#include #include #include /* for context-mangling functions */ - -#include -#include -#include +#include #ifdef USE_NLS #include /* for setlocale() */ @@ -39,29 +47,55 @@ #define MS_PRIVATE 1<<18 #endif +#ifndef PACKAGE +#define PACKAGE "policycoreutils" /* the name of this package lang translation */ +#endif + +#define BUF_SIZE 1024 +#define DEFAULT_PATH "/usr/bin:/bin" +#define USAGE_STRING _("USAGE: seunshare [ -v ] [ -c ] [ -k ] [ -C ] [ -t tmpdir] [ -h homedir ] [ -Z context ] -- executable [args]") + +static int verbose = 0; +static int child = 0; + +static capng_select_t cap_set = CAPNG_SELECT_BOTH; + /** - * This function will drop all capabilities - * Returns zero on success, non-zero otherwise + * This function will drop all capabilities. */ -static int drop_capabilities(uid_t uid) +static int drop_caps() { - capng_clear(CAPNG_SELECT_BOTH); - - if (capng_lock() < 0) + if (capng_have_capabilities(cap_set) == CAPNG_NONE) + return 0; + capng_clear(cap_set); + if (capng_lock() == -1 || capng_apply(cap_set) == -1) { + fprintf(stderr, _("Failed to drop all capabilities\n")); return -1; - /* Change uid */ - if (setresuid(uid, uid, uid)) { - fprintf(stderr, _("Error changing uid, aborting.\n")); + } + return 0; +} + +/** + * This function will drop all privileges. + */ +static int drop_privs(uid_t uid) +{ + if (drop_caps() == -1 || setresuid(uid, uid, uid) == -1) { + fprintf(stderr, _("Failed to drop privileges\n")); return -1; } - return capng_apply(CAPNG_SELECT_BOTH); + return 0; } -#define DEFAULT_PATH "/usr/bin:/bin" -static int verbose = 0; +/** + * If the user sends a siginto to seunshare, kill the child's session + */ +void handler(int sig) { + if (child > 0) kill(-child,sig); +} /** - * Take care of any signal setup + * Take care of any signal setup. */ static int set_signal_handles(void) { @@ -75,32 +109,117 @@ static int set_signal_handles(void) (void)sigprocmask(SIG_SETMASK, &empty, NULL); - /* Terminate on SIGHUP. */ + /* Terminate on SIGHUP */ if (signal(SIGHUP, SIG_DFL) == SIG_ERR) { perror("Unable to set SIGHUP handler"); return -1; } + if (signal(SIGINT, handler) == SIG_ERR) { + perror("Unable to set SIGINT handler"); + return -1; + } + + return 0; +} + +#define status_to_retval(status,retval) do { \ + if ((status) == -1) \ + retval = -1; \ + else if (WIFEXITED((status))) \ + retval = WEXITSTATUS((status)); \ + else if (WIFSIGNALED((status))) \ + retval = 128 + WTERMSIG((status)); \ + else \ + retval = -1; \ + } while(0) + +/** + * Spawn external command using system() with dropped privileges. + * TODO: avoid system() and use exec*() instead + */ +static int spawn_command(const char *cmd, uid_t uid){ + int child; + int status = -1; + + if (verbose > 1) + printf("spawn_command: %s\n", cmd); + + child = fork(); + if (child == -1) { + perror(_("Unable to fork")); + return status; + } + + if (child == 0) { + if (drop_privs(uid) != 0) exit(-1); + + status = system(cmd); + status_to_retval(status, status); + exit(status); + } + + waitpid(child, &status, 0); + status_to_retval(status, status); + return status; +} + +/** + * Check file/directory ownership, struct stat * must be passed to the + * functions. + */ +static int check_owner_uid(uid_t uid, const char *file, struct stat *st) { + if (S_ISLNK(st->st_mode)) { + fprintf(stderr, _("Error: %s must not be a symbolic link\n"), file); + return -1; + } + if (st->st_uid != uid) { + fprintf(stderr, _("Error: %s not owned by UID %d\n"), file, uid); + return -1; + } + return 0; +} + +static int check_owner_gid(gid_t gid, const char *file, struct stat *st) { + if (S_ISLNK(st->st_mode)) { + fprintf(stderr, _("Error: %s must not be a symbolic link\n"), file); + return -1; + } + if (st->st_gid != gid) { + fprintf(stderr, _("Error: %s not owned by GID %d\n"), file, gid); + return -1; + } return 0; } +#define equal_stats(one,two) \ + ((one)->st_dev == (two)->st_dev && (one)->st_ino == (two)->st_ino && \ + (one)->st_uid == (two)->st_uid && (one)->st_gid == (two)->st_gid && \ + (one)->st_mode == (two)->st_mode) + /** - * This function makes sure the mounted directory is owned by the user executing - * seunshare. - * If so, it returns 0. If it can not figure this out or they are different, it returns -1. + * Sanity check specified directory. Store stat info for future comparison, or + * compare with previously saved info to detect replaced directories. + * Note: This function does not perform owner checks. */ -static int verify_mount(const char *mntdir, struct passwd *pwd) { +static int verify_directory(const char *dir, struct stat *st_in, struct stat *st_out) { struct stat sb; - if (stat(mntdir, &sb) == -1) { - fprintf(stderr, _("Invalid mount point %s: %s\n"), mntdir, strerror(errno)); + + if (st_out == NULL) st_out = &sb; + + if (lstat(dir, st_out) == -1) { + fprintf(stderr, _("Failed to stat %s: %s\n"), dir, strerror(errno)); + return -1; + } + if (! S_ISDIR(st_out->st_mode)) { + fprintf(stderr, _("Error: %s is not a directory: %s\n"), dir, strerror(errno)); return -1; } - if (sb.st_uid != pwd->pw_uid) { - errno = EPERM; - syslog(LOG_AUTHPRIV | LOG_ALERT, "%s attempted to mount an invalid directory, %s", pwd->pw_name, mntdir); - perror(_("Invalid mount point, reporting to administrator")); + if (st_in && !equal_stats(st_in, st_out)) { + fprintf(stderr, _("Error: %s was replaced by a different directory\n"), dir); return -1; } + return 0; } @@ -123,7 +242,7 @@ static int verify_shell(const char *shell_name) /* check the shell skipping newline char */ if (!strcmp(shell_name, buf)) { - rc = 1; + rc = 0; break; } } @@ -131,54 +250,618 @@ static int verify_shell(const char *shell_name) return rc; } -static int seunshare_mount(const char *src, const char *dst, struct passwd *pwd) { +/** + * Mount directory and check that we mounted the right directory. + */ +static int seunshare_mount(const char *src, const char *dst, struct stat *src_st) +{ + int flags = MS_REC; + int is_tmp = 0; + if (verbose) - printf("Mount %s on %s\n", src, dst); - if (mount(dst, dst, NULL, MS_BIND | MS_REC, NULL) < 0) { + printf(_("Mounting %s on %s\n"), src, dst); + + if (strcmp("/tmp", dst) == 0) { + flags = flags | MS_NODEV | MS_NOSUID | MS_NOEXEC; + is_tmp = 1; + } + + /* mount directory */ + if (mount(dst, dst, NULL, MS_BIND | flags, NULL) < 0) { fprintf(stderr, _("Failed to mount %s on %s: %s\n"), dst, dst, strerror(errno)); return -1; } - - if (mount(dst, dst, NULL, MS_PRIVATE | MS_REC, NULL) < 0) { + if (mount(dst, dst, NULL, MS_PRIVATE | flags, NULL) < 0) { fprintf(stderr, _("Failed to make %s private: %s\n"), dst, strerror(errno)); return -1; } - - if (mount(src, dst, NULL, MS_BIND | MS_REC, NULL) < 0) { + if (mount(src, dst, NULL, MS_BIND | flags, NULL) < 0) { fprintf(stderr, _("Failed to mount %s on %s: %s\n"), src, dst, strerror(errno)); return -1; } - if (verify_mount(dst, pwd) < 0) + /* verify whether we mounted what we expected to mount */ + if (verify_directory(dst, src_st, NULL) < 0) return -1; + + /* bind mount /tmp on /var/tmp too */ + if (is_tmp) { + if (verbose) + printf(_("Mounting /tmp on /var/tmp\n")); + + if (mount("/var/tmp", "/var/tmp", NULL, MS_BIND | flags, NULL) < 0) { + fprintf(stderr, _("Failed to mount /var/tmp on /var/tmp: %s\n"), strerror(errno)); + return -1; + } + if (mount("/var/tmp", "/var/tmp", NULL, MS_PRIVATE | flags, NULL) < 0) { + fprintf(stderr, _("Failed to make /var/tmp private: %s\n"), strerror(errno)); + return -1; + } + if (mount("/tmp", "/var/tmp", NULL, MS_BIND | flags, NULL) < 0) { + fprintf(stderr, _("Failed to mount /tmp on /var/tmp: %s\n"), strerror(errno)); + return -1; + } + } + + return 0; + +} + +/** + * Error logging used by cgroups code. + */ +static int sandbox_error(const char *string) +{ + fprintf(stderr, string); + syslog(LOG_AUTHPRIV | LOG_ALERT, string); + exit(-1); +} + +/** + * Regular expression match. + */ +static int match(const char *string, char *pattern) +{ + int status; + regex_t re; + if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB) != 0) { + return 0; + } + status = regexec(&re, string, (size_t)0, NULL, 0); + regfree(&re); + if (status != 0) { + return 0; + } + return 1; +} + +/** + * Apply cgroups settings from the /etc/sysconfig/sandbox config file. + */ +static int setup_cgroups() +{ + char *cpus = NULL; /* which CPUs to use */ + char *cgroupname = NULL;/* name for the cgroup */ + char *mem = NULL; /* string for memory amount to pass to cgroup */ + int64_t memusage = 0; /* amount of memory to use max (percent) */ + int cpupercentage = 0; /* what percentage of cpu to allow usage */ + FILE* fp; + char buf[BUF_SIZE]; + char *tok = NULL; + int rc = -1; + char *str = NULL; + const char* fname = "/etc/sysconfig/sandbox"; + + if ((fp = fopen(fname, "rt")) == NULL) { + fprintf(stderr, "Error opening sandbox config file."); + return rc; + } + while(fgets(buf, BUF_SIZE, fp) != NULL) { + /* Skip comments */ + if (buf[0] == '#') continue; + + /* Copy the string, ignoring whitespace */ + int len = strlen(buf); + free(str); + str = malloc((len + 1) * sizeof(char)); + if (!str) + goto err; + + int ind = 0; + int i; + for (i = 0; i < len; i++) { + char cur = buf[i]; + if (cur != ' ' && cur != '\t') { + str[ind] = cur; + ind++; + } + } + str[ind] = '\0'; + + tok = strtok(str, "=\n"); + if (tok != NULL) { + if (!strcmp(tok, "CPUAFFINITY")) { + tok = strtok(NULL, "=\n"); + cpus = strdup(tok); + if (!strcmp(cpus, "ALL")) { + free(cpus); + cpus = NULL; + } + } else if (!strcmp(tok, "MEMUSAGE")) { + tok = strtok(NULL, "=\n"); + if (match(tok, "^[0-9]+[kKmMgG%]")) { + char *ind = strchr(tok, '%'); + if (ind != NULL) { + *ind = '\0';; + memusage = atoi(tok); + } else { + mem = strdup(tok); + } + } else { + fprintf(stderr, "Error parsing config file."); + goto err; + } + + } else if (!strcmp(tok, "CPUUSAGE")) { + tok = strtok(NULL, "=\n"); + if (match(tok, "^[0-9]+\%")) { + char* ind = strchr(tok, '%'); + *ind = '\0'; + cpupercentage = atoi(tok); + } else { + fprintf(stderr, "Error parsing config file."); + goto err; + } + } else if (!strcmp(tok, "NAME")) { + tok = strtok(NULL, "=\n"); + cgroupname = strdup(tok); + } else { + continue; + } + } + + } + if (mem == NULL) { + long phypz = sysconf(_SC_PHYS_PAGES); + long psize = sysconf(_SC_PAGE_SIZE); + memusage = phypz * psize * (float) memusage / 100.0; + } + + cgroup_init(); + + int64_t current_runtime = 0; + int64_t current_period = 0 ; + int64_t current_mem = 0; + char *curr_cpu_path = NULL; + char *curr_mem_path = NULL; + int ret = cgroup_get_current_controller_path(getpid(), "cpu", &curr_cpu_path); + if (ret) { + sandbox_error("Error while trying to get current controller path.\n"); + } else { + struct cgroup *curr = cgroup_new_cgroup(curr_cpu_path); + cgroup_get_cgroup(curr); + cgroup_get_value_int64(cgroup_get_controller(curr, "cpu"), "cpu.rt_runtime_us", ¤t_runtime); + cgroup_get_value_int64(cgroup_get_controller(curr, "cpu"), "cpu.rt_period_us", ¤t_period); + } + + ret = cgroup_get_current_controller_path(getpid(), "memory", &curr_mem_path); + if (ret) { + sandbox_error("Error while trying to get current controller path.\n"); + } else { + struct cgroup *curr = cgroup_new_cgroup(curr_mem_path); + cgroup_get_cgroup(curr); + cgroup_get_value_int64(cgroup_get_controller(curr, "memory"), "memory.limit_in_bytes", ¤t_mem); + } + + if (((float) cpupercentage) / 100.0> (float)current_runtime / (float) current_period) { + sandbox_error("CPU usage restricted!\n"); + goto err; + } + + if (mem == NULL) { + if (memusage > current_mem) { + sandbox_error("Attempting to use more memory than allowed!"); + goto err; + } + } + + long nprocs = sysconf(_SC_NPROCESSORS_ONLN); + + struct sched_param sp; + sp.sched_priority = sched_get_priority_min(SCHED_FIFO); + sched_setscheduler(getpid(), SCHED_FIFO, &sp); + struct cgroup *sandbox_group = cgroup_new_cgroup(cgroupname); + cgroup_add_controller(sandbox_group, "memory"); + cgroup_add_controller(sandbox_group, "cpu"); + + if (mem == NULL) { + if (memusage > 0) { + cgroup_set_value_uint64(cgroup_get_controller(sandbox_group, "memory"), "memory.limit_in_bytes", memusage); + } + } else { + cgroup_set_value_string(cgroup_get_controller(sandbox_group, "memory"), "memory.limit_in_bytes", mem); + } + if (cpupercentage > 0) { + cgroup_set_value_uint64(cgroup_get_controller(sandbox_group, "cpu"), "cpu.rt_runtime_us", + (float) cpupercentage / 100.0 * 60000); + cgroup_set_value_uint64(cgroup_get_controller(sandbox_group, "cpu"), "cpu.rt_period_us",60000 * nprocs); + } + if (cpus != NULL) { + cgroup_set_value_string(cgroup_get_controller(sandbox_group, "cpu"), "cgroup.procs",cpus); + } + + uint64_t allocated_mem; + if (cgroup_get_value_uint64(cgroup_get_controller(sandbox_group, "memory"), "memory.limit_in_bytes", &allocated_mem) > current_mem) { + sandbox_error("Attempting to use more memory than allowed!\n"); + goto err; + } + + rc = cgroup_create_cgroup(sandbox_group, 1); + if (rc != 0) { + sandbox_error("Failed to create group. Ensure that cgconfig service is running. \n"); + goto err; + } + + cgroup_attach_task(sandbox_group); + + rc = 0; +err: + fclose(fp); + free(str); + free(mem); + free(cgroupname); + free(cpus); + return rc; +} + +/* + If path is empy or ends with "/." or "/.. return -1 else return 0; + */ +static int bad_path(const char *path) { + const char *ptr; + ptr = path; + while (*ptr) ptr++; + if (ptr == path) return -1; // ptr null + ptr--; + if (ptr != path && *ptr == '.') { + ptr--; + if (*ptr == '/') return -1; // path ends in /. + if (*ptr == '.') { + if (ptr != path) { + ptr--; + if (*ptr == '/') return -1; // path ends in /.. + } + } + } + return 0; +} + +static int rsynccmd(const char * src, const char *dst, char **cmdbuf) +{ + char *buf = NULL; + char *newbuf = NULL; + glob_t fglob; + fglob.gl_offs = 0; + int flags = GLOB_PERIOD; + unsigned int i = 0; + int rc = -1; + + /* match glob for all files in src dir */ + if (asprintf(&buf, "%s/*", src) == -1) { + fprintf(stderr, "Out of memory\n"); + return -1; + } + + if (glob(buf, flags, NULL, &fglob) != 0) { + free(buf); buf = NULL; return -1; + } + + free(buf); buf = NULL; + + for ( i=0; i < fglob.gl_pathc; i++) { + const char *path = fglob.gl_pathv[i]; + + if (bad_path(path)) continue; + + if (!buf) { + if (asprintf(&newbuf, "\'%s\'", path) == -1) { + fprintf(stderr, "Out of memory\n"); + goto err; + } + } else { + if (asprintf(&newbuf, "%s \'%s\'", buf, path) == -1) { + fprintf(stderr, "Out of memory\n"); + goto err; + } + } + + free(buf); buf = newbuf; + newbuf = NULL; + } + + if (buf) { + if (asprintf(&newbuf, "/usr/bin/rsync -trlHDq %s '%s'", buf, dst) == -1) { + fprintf(stderr, "Out of memory\n"); + goto err; + } + *cmdbuf=newbuf; + } + else { + *cmdbuf=NULL; + } + rc = 0; + +err: + free(buf); buf = NULL; + globfree(&fglob); + return rc; +} + +/** + * Clean up runtime temporary directory. Returns 0 if no problem was detected, + * >0 if some error was detected, but errors here are treated as non-fatal and + * left to tmpwatch to finish incomplete cleanup. + */ +static int cleanup_tmpdir(const char *tmpdir, const char *src, + struct passwd *pwd, int copy_content) +{ + char *cmdbuf = NULL; + int rc = 0; + + /* rsync files back */ + if (copy_content) { + if (asprintf(&cmdbuf, "/usr/bin/rsync --exclude=.X11-unix -utrlHDq --delete '%s/' '%s/'", tmpdir, src) == -1) { + fprintf(stderr, _("Out of memory\n")); + cmdbuf = NULL; + rc++; + } + if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) { + fprintf(stderr, _("Failed to copy files from the runtime temporary directory\n")); + rc++; + } + free(cmdbuf); cmdbuf = NULL; + } + + /* remove files from the runtime temporary directory */ + if (asprintf(&cmdbuf, "/bin/rm -r '%s/' 2>/dev/null", tmpdir) == -1) { + fprintf(stderr, _("Out of memory\n")); + cmdbuf = NULL; + rc++; + } + /* this may fail if there's root-owned file left in the runtime tmpdir */ + if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) rc++; + free(cmdbuf); cmdbuf = NULL; + + /* remove runtime temporary directory */ + setfsuid(0); + if (rmdir(tmpdir) == -1) + fprintf(stderr, _("Failed to remove directory %s: %s\n"), tmpdir, strerror(errno)); + setfsuid(pwd->pw_uid); + + return 0; +} + +/** + * seunshare will create a tmpdir in /tmp, with root ownership. The parent + * process waits for it child to exit to attempt to remove the directory. If + * it fails to remove the directory, we will need to rely on tmpreaper/tmpwatch + * to clean it up. + */ +static char *create_tmpdir(const char *src, struct stat *src_st, + struct stat *out_st, struct passwd *pwd, security_context_t execcon) +{ + char *tmpdir = NULL; + char *cmdbuf = NULL; + int fd_t = -1, fd_s = -1; + struct stat tmp_st; + security_context_t con = NULL; + + /* get selinux context */ + if (execcon) { + setfsuid(pwd->pw_uid); + if ((fd_s = open(src, O_RDONLY)) < 0) { + fprintf(stderr, _("Failed to open directory %s: %s\n"), src, strerror(errno)); + goto err; + } + if (fstat(fd_s, &tmp_st) == -1) { + fprintf(stderr, _("Failed to stat directory %s: %s\n"), src, strerror(errno)); + goto err; + } + if (!equal_stats(src_st, &tmp_st)) { + fprintf(stderr, _("Error: %s was replaced by a different directory\n"), src); + goto err; + } + if (fgetfilecon(fd_s, &con) == -1) { + fprintf(stderr, _("Failed to get context of the directory %s: %s\n"), src, strerror(errno)); + goto err; + } + + /* ok to not reach this if there is an error */ + setfsuid(0); + } + + if (asprintf(&tmpdir, "/tmp/.sandbox-%s-XXXXXX", pwd->pw_name) == -1) { + fprintf(stderr, _("Out of memory\n")); + tmpdir = NULL; + goto err; + } + if (mkdtemp(tmpdir) == NULL) { + fprintf(stderr, _("Failed to create temporary directory: %s\n"), strerror(errno)); + goto err; + } + + /* temporary directory must be owned by root:user */ + if (verify_directory(tmpdir, NULL, out_st) < 0) { + goto err; + } + + if (check_owner_uid(0, tmpdir, out_st) < 0) + goto err; + + if (check_owner_gid(getgid(), tmpdir, out_st) < 0) + goto err; + + /* change permissions of the temporary directory */ + if ((fd_t = open(tmpdir, O_RDONLY)) < 0) { + fprintf(stderr, _("Failed to open directory %s: %s\n"), tmpdir, strerror(errno)); + goto err; + } + if (fstat(fd_t, &tmp_st) == -1) { + fprintf(stderr, _("Failed to stat directory %s: %s\n"), tmpdir, strerror(errno)); + goto err; + } + if (!equal_stats(out_st, &tmp_st)) { + fprintf(stderr, _("Error: %s was replaced by a different directory\n"), tmpdir); + goto err; + } + if (fchmod(fd_t, 01770) == -1) { + fprintf(stderr, _("Unable to change mode on %s: %s\n"), tmpdir, strerror(errno)); + goto err; + } + /* re-stat again to pick change mode */ + if (fstat(fd_t, out_st) == -1) { + fprintf(stderr, _("Failed to stat directory %s: %s\n"), tmpdir, strerror(errno)); + goto err; + } + + /* copy selinux context */ + if (execcon) { + if (fsetfilecon(fd_t, con) == -1) { + fprintf(stderr, _("Failed to set context of the directory %s: %s\n"), tmpdir, strerror(errno)); + goto err; + } + } + + setfsuid(pwd->pw_uid); + + if (rsynccmd(src, tmpdir, &cmdbuf) < 0) { + goto err; + } + + /* ok to not reach this if there is an error */ + setfsuid(0); + + if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) { + fprintf(stderr, _("Failed to populate runtime temporary directory\n")); + cleanup_tmpdir(tmpdir, src, pwd, 0); + goto err; + } + + goto good; +err: + free(tmpdir); tmpdir = NULL; +good: + free(cmdbuf); cmdbuf = NULL; + freecon(con); con = NULL; + if (fd_t >= 0) close(fd_t); + if (fd_s >= 0) close(fd_s); + return tmpdir; } -#define USAGE_STRING _("USAGE: seunshare [ -v ] [ -t tmpdir ] [ -h homedir ] -- CONTEXT executable [args] ") +#define PROC_BASE "/proc" + +static int +killall (security_context_t execcon) +{ + DIR *dir; + security_context_t scon; + struct dirent *de; + pid_t *pid_table, pid, self; + int i; + int pids, max_pids; + int running = 0; + self = getpid(); + if (!(dir = opendir(PROC_BASE))) { + return -1; + } + max_pids = 256; + pid_table = malloc(max_pids * sizeof (pid_t)); + if (!pid_table) { + (void)closedir(dir); + return -1; + } + pids = 0; + context_t con; + con = context_new(execcon); + const char *mcs = context_range_get(con); + printf("mcs=%s\n", mcs); + while ((de = readdir (dir)) != NULL) { + if (!(pid = (pid_t)atoi(de->d_name)) || pid == self) + continue; + + if (pids == max_pids) { + if (!(pid_table = realloc(pid_table, 2*pids*sizeof(pid_t)))) { + (void)closedir(dir); + return -1; + } + max_pids *= 2; + } + pid_table[pids++] = pid; + } + + (void)closedir(dir); + + for (i = 0; i < pids; i++) { + pid_t id = pid_table[i]; + + if (getpidcon(id, &scon) == 0) { + + context_t pidcon = context_new(scon); + /* Attempt to kill remaining processes */ + if (strcmp(context_range_get(pidcon), mcs) == 0) + kill(id, SIGKILL); + + context_free(pidcon); + freecon(scon); + } + running++; + } + + context_free(con); + free(pid_table); + return running; +} int main(int argc, char **argv) { - int rc; int status = -1; + security_context_t execcon = NULL; - security_context_t scontext; - - int flag_index; /* flag index in argv[] */ int clflag; /* holds codes for command line flags */ - char *tmpdir_s = NULL; /* tmpdir spec'd by user in argv[] */ + int usecgroups = 0; + int kill_all = 0; + char *homedir_s = NULL; /* homedir spec'd by user in argv[] */ + char *tmpdir_s = NULL; /* tmpdir spec'd by user in argv[] */ + char *tmpdir_r = NULL; /* tmpdir created by seunshare */ + + struct stat st_homedir; + struct stat st_tmpdir_s; + struct stat st_tmpdir_r; const struct option long_options[] = { {"homedir", 1, 0, 'h'}, {"tmpdir", 1, 0, 't'}, + {"kill", 1, 0, 'k'}, {"verbose", 1, 0, 'v'}, + {"cgroups", 1, 0, 'c'}, + {"context", 1, 0, 'Z'}, + {"capabilities", 1, 0, 'C'}, {NULL, 0, 0, 0} }; uid_t uid = getuid(); - +/* if (!uid) { fprintf(stderr, _("Must not be root")); return -1; } +*/ + +#ifdef USE_NLS + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); +#endif struct passwd *pwd=getpwuid(uid); if (!pwd) { @@ -187,34 +870,36 @@ int main(int argc, char **argv) { } if (verify_shell(pwd->pw_shell) < 0) { - fprintf(stderr, _("Error! Shell is not valid.\n")); + fprintf(stderr, _("Error: User shell is not valid\n")); return -1; } while (1) { - clflag = getopt_long(argc, argv, "h:t:", long_options, - &flag_index); + clflag = getopt_long(argc, argv, "Ccvh:t:Z:", long_options, NULL); if (clflag == -1) break; switch (clflag) { case 't': - if (!(tmpdir_s = realpath(optarg, NULL))) { - fprintf(stderr, _("Invalid mount point %s: %s\n"), optarg, strerror(errno)); - return -1; - } - if (verify_mount(tmpdir_s, pwd) < 0) return -1; + tmpdir_s = optarg; + break; + case 'k': + kill_all = 1; break; case 'h': - if (!(homedir_s = realpath(optarg, NULL))) { - fprintf(stderr, _("Invalid mount point %s: %s\n"), optarg, strerror(errno)); - return -1; - } - if (verify_mount(homedir_s, pwd) < 0) return -1; - if (verify_mount(pwd->pw_dir, pwd) < 0) return -1; + homedir_s = optarg; break; case 'v': - verbose = 1; + verbose++; + break; + case 'c': + usecgroups = 1; + break; + case 'C': + cap_set = CAPNG_SELECT_CAPS; + break; + case 'Z': + execcon = optarg; break; default: fprintf(stderr, "%s\n", USAGE_STRING); @@ -223,99 +908,131 @@ int main(int argc, char **argv) { } if (! homedir_s && ! tmpdir_s) { - fprintf(stderr, _("Error: tmpdir and/or homedir required \n"), - "%s\n", USAGE_STRING); + fprintf(stderr, _("Error: tmpdir and/or homedir required\n %s\n"), USAGE_STRING); return -1; } - if (argc - optind < 2) { - fprintf(stderr, _("Error: context and executable required \n"), - "%s\n", USAGE_STRING); + if (argc - optind < 1) { + fprintf(stderr, _("Error: executable required\n %s\n"), USAGE_STRING); return -1; } - scontext = argv[optind++]; - - if (set_signal_handles()) + if (execcon && is_selinux_enabled() != 1) { + fprintf(stderr, _("Error: execution context specified, but SELinux is not enabled\n")); return -1; + } - if (unshare(CLONE_NEWNS) < 0) { - perror(_("Failed to unshare")); + if (set_signal_handles()) return -1; - } - if (homedir_s && tmpdir_s && (strncmp(pwd->pw_dir, tmpdir_s, strlen(pwd->pw_dir)) == 0)) { - if (seunshare_mount(tmpdir_s, "/tmp", pwd) < 0) - return -1; - if (seunshare_mount(homedir_s, pwd->pw_dir, pwd) < 0) - return -1; - } else { - if (homedir_s && seunshare_mount(homedir_s, pwd->pw_dir, pwd) < 0) - return -1; - - if (tmpdir_s && seunshare_mount(tmpdir_s, "/tmp", pwd) < 0) - return -1; - } + if (usecgroups && setup_cgroups() < 0) + return -1; + + /* set fsuid to ruid */ + /* Changing fsuid is usually required when user-specified directory is + * on an NFS mount. It's also desired to avoid leaking info about + * existence of the files not accessible to the user. */ + setfsuid(uid); - if (drop_capabilities(uid)) { - perror(_("Failed to drop all capabilities")); + /* verify homedir and tmpdir */ + if (homedir_s && ( + verify_directory(homedir_s, NULL, &st_homedir) < 0 || + check_owner_uid(uid, homedir_s, &st_homedir))) return -1; + if (tmpdir_s && ( + verify_directory(tmpdir_s, NULL, &st_tmpdir_s) < 0 || + check_owner_uid(uid, tmpdir_s, &st_tmpdir_s))) return -1; + setfsuid(0); + + /* create runtime tmpdir */ + if (tmpdir_s && (tmpdir_r = create_tmpdir(tmpdir_s, &st_tmpdir_s, + &st_tmpdir_r, pwd, execcon)) == NULL) { + fprintf(stderr, _("Failed to create runtime temporary directory\n")); return -1; } - int child = fork(); + /* spawn child process */ + child = fork(); if (child == -1) { perror(_("Unable to fork")); - return -1; + goto err; } - if (!child) { - char *display=NULL; - /* Construct a new environment */ - char *d = getenv("DISPLAY"); - if (d) { - display = strdup(d); - if (!display) { - perror(_("Out of memory")); - exit(-1); - } + if (child == 0) { + char *display = NULL; + int rc = -1; + + if (unshare(CLONE_NEWNS) < 0) { + perror(_("Failed to unshare")); + goto childerr; } - if ((rc = clearenv())) { - perror(_("Unable to clear environment")); - free(display); - exit(-1); + /* assume fsuid==ruid after this point */ + setfsuid(uid); + + /* mount homedir and tmpdir, in this order */ + if (homedir_s && seunshare_mount(homedir_s, pwd->pw_dir, + &st_homedir) != 0) goto childerr; + if (tmpdir_s && seunshare_mount(tmpdir_r, "/tmp", + &st_tmpdir_r) != 0) goto childerr; + + if (drop_privs(uid) != 0) goto childerr; + + /* construct a new environment */ + if ((display = getenv("DISPLAY")) != NULL) { + if ((display = strdup(display)) == NULL) { + perror(_("Out of memory")); + goto childerr; + } } - - if (setexeccon(scontext)) { - fprintf(stderr, _("Could not set exec context to %s.\n"), - scontext); - free(display); - exit(-1); + if ((rc = clearenv()) != 0) { + perror(_("Failed to clear environment")); + goto childerr; } - - if (display) + if (display) rc |= setenv("DISPLAY", display, 1); rc |= setenv("HOME", pwd->pw_dir, 1); rc |= setenv("SHELL", pwd->pw_shell, 1); rc |= setenv("USER", pwd->pw_name, 1); rc |= setenv("LOGNAME", pwd->pw_name, 1); rc |= setenv("PATH", DEFAULT_PATH, 1); - + if (rc != 0) { + fprintf(stderr, _("Failed to construct environment\n")); + goto childerr; + } + + /* selinux context */ + if (execcon && setexeccon(execcon) != 0) { + fprintf(stderr, _("Could not set exec context to %s.\n"), execcon); + goto childerr; + } + if (chdir(pwd->pw_dir)) { perror(_("Failed to change dir to homedir")); - exit(-1); + goto childerr; } setsid(); execv(argv[optind], argv + optind); + fprintf(stderr, _("Failed to execute command %s: %s\n"), argv[optind], strerror(errno)); +childerr: free(display); - perror("execv"); exit(-1); - } else { - waitpid(child, &status, 0); } - free(tmpdir_s); - free(homedir_s); + drop_caps(); + + /* parent waits for child exit to do the cleanup */ + waitpid(child, &status, 0); + status_to_retval(status, status); + + /* Make sure all child processes exit */ + kill(-child,SIGTERM); + + if (execcon && kill_all) + killall(execcon); + + if (tmpdir_r) cleanup_tmpdir(tmpdir_r, tmpdir_s, pwd, 1); +err: + free(tmpdir_r); return status; } diff --git a/policycoreutils/sandbox/start b/policycoreutils/sandbox/start new file mode 100644 index 0000000..52950d7 --- /dev/null +++ b/policycoreutils/sandbox/start @@ -0,0 +1,9 @@ +#! /usr/bin/python -Es +import gtk, commands, sys +rc = [-1,''] +try: + rc=commands.getstatusoutput(sys.argv[1]) +except: + pass +if rc[0] == 0: + print rc[1] diff --git a/policycoreutils/scripts/fixfiles b/policycoreutils/scripts/fixfiles index e4e5f0d..27dcccf 100755 --- a/policycoreutils/scripts/fixfiles +++ b/policycoreutils/scripts/fixfiles @@ -103,7 +103,7 @@ exclude_dirs_from_relabelling() { exclude_dirs() { exclude= - for i in /home /root /tmp /dev; do + for i in /var/lib/BackupPC /home /tmp /dev; do [ -e $i ] && exclude="$exclude -e $i"; done exclude="$exclude `exclude_dirs_from_relabelling`" diff --git a/policycoreutils/semanage/default_encoding/Makefile b/policycoreutils/semanage/default_encoding/Makefile new file mode 100644 index 0000000..e15a877 --- /dev/null +++ b/policycoreutils/semanage/default_encoding/Makefile @@ -0,0 +1,8 @@ +all: + LDFLAGS="" python setup.py build + +install: all + LDFLAGS="" python setup.py install --root=$(DESTDIR)/ + +clean: + rm -rf build *~ diff --git a/policycoreutils/semanage/default_encoding/default_encoding.c b/policycoreutils/semanage/default_encoding/default_encoding.c new file mode 100644 index 0000000..2ba4870 --- /dev/null +++ b/policycoreutils/semanage/default_encoding/default_encoding.c @@ -0,0 +1,59 @@ +/* + * Authors: + * John Dennis + * + * Copyright (C) 2009 Red Hat + * see file 'COPYING' for use and warranty information + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +PyDoc_STRVAR(setdefaultencoding_doc, +"setdefaultencoding(encoding='utf-8')\n\ +\n\ +Set the current default string encoding used by the Unicode implementation.\n\ +Defaults to utf-8." +); + +static PyObject * +setdefaultencoding(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"utf-8", NULL}; + char *encoding; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s:setdefaultencoding", kwlist, &encoding)) + return NULL; + + if (PyUnicode_SetDefaultEncoding(encoding)) + return NULL; + + Py_RETURN_NONE; +} + +static PyMethodDef methods[] = { + {"setdefaultencoding", (PyCFunction)setdefaultencoding, METH_VARARGS|METH_KEYWORDS, setdefaultencoding_doc}, + {NULL, NULL} /* sentinel */ +}; + + +PyMODINIT_FUNC +initdefault_encoding_utf8(void) +{ + PyObject* m; + + PyUnicode_SetDefaultEncoding("utf-8"); + m = Py_InitModule3("default_encoding_utf8", methods, "Forces the default encoding to utf-8"); +} diff --git a/policycoreutils/semanage/default_encoding/policycoreutils/__init__.py b/policycoreutils/semanage/default_encoding/policycoreutils/__init__.py new file mode 100644 index 0000000..ccb6b8b --- /dev/null +++ b/policycoreutils/semanage/default_encoding/policycoreutils/__init__.py @@ -0,0 +1,17 @@ +# +# Copyright (C) 2006,2007,2008, 2009 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# diff --git a/policycoreutils/semanage/default_encoding/setup.py b/policycoreutils/semanage/default_encoding/setup.py new file mode 100644 index 0000000..e2befdb --- /dev/null +++ b/policycoreutils/semanage/default_encoding/setup.py @@ -0,0 +1,38 @@ +# Authors: +# John Dennis +# +# Copyright (C) 2009 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +from distutils.core import setup, Extension + +default_encoding_utf8 = Extension('policycoreutils.default_encoding_utf8', ['default_encoding.c']) + +setup(name = 'policycoreutils-default-encoding', + version = '0.1', + description = 'Forces the default encoding in Python to be utf-8', + long_description = 'Forces the default encoding in Python to be utf-8', + author = 'John Dennis', + author_email = 'jdennis@redhat.com', + maintainer = 'John Dennis', + maintainer_email = 'jdennis@redhat.com', + license = 'GPLv3+', + platforms = 'posix', + url = '', + download_url = '', + ext_modules = [default_encoding_utf8], + packages=["policycoreutils"], +) diff --git a/policycoreutils/semanage/semanage b/policycoreutils/semanage/semanage index 0140cd2..656a028 100644 --- a/policycoreutils/semanage/semanage +++ b/policycoreutils/semanage/semanage @@ -20,6 +20,7 @@ # 02111-1307 USA # # +import policycoreutils.default_encoding_utf8 import sys, getopt, re import seobject import selinux @@ -32,7 +33,7 @@ gettext.textdomain(PROGNAME) try: gettext.install(PROGNAME, localedir="/usr/share/locale", - unicode=False, + unicode=True, codeset = 'utf-8') except IOError: import __builtin__ @@ -283,11 +284,14 @@ Object-specific Options (see above): equal = a if o == "--enable": - set_action(o) + if disable: + raise ValueError(_("You can't disable and enable at the same time")) + enable = True if o == "--disable": - set_action(o) + if enable: + raise ValueError(_("You can't disable and enable at the same time")) disable = True if o == "-F" or o == "--file": @@ -338,9 +342,11 @@ Object-specific Options (see above): if o == "--on" or o == "-1": value = "on" + modify = True if o == "--off" or o == "-0": value = "off" + modify = True if object == "login": OBJECT = seobject.loginRecords(store) @@ -362,6 +368,8 @@ Object-specific Options (see above): if object == "boolean": OBJECT = seobject.booleanRecords(store) + if use_file: + modify = True if object == "module": OBJECT = seobject.moduleRecords(store) @@ -500,31 +508,36 @@ Object-specific Options (see above): if len(sys.argv) < 3: usage(_("Requires 2 or more arguments")) - gopts, cmds = getopt.getopt(sys.argv[1:], - '01adf:i:lhmno:p:s:FCDR:L:r:t:T:P:S:', - ['add', - 'delete', - 'deleteall', - 'ftype=', - 'file', - 'help', - 'input=', - 'list', - 'modify', - 'noheading', - 'localist', - 'off', - 'on', - 'output=', - 'proto=', - 'seuser=', - 'store=', - 'range=', - 'level=', - 'roles=', - 'type=', - 'prefix=' - ]) + try: + gopts, cmds = getopt.getopt(sys.argv[1:], + '01adf:i:lhmno:p:s:FCDR:L:r:t:T:P:S:', + ['add', + 'delete', + 'deleteall', + 'ftype=', + 'file', + 'help', + 'input=', + 'list', + 'modify', + 'noheading', + 'localist', + 'off', + 'on', + 'output=', + 'proto=', + 'seuser=', + 'store=', + 'range=', + 'level=', + 'roles=', + 'type=', + 'trans=', + 'prefix=' + ]) + except getopt.error, error: + usage(_("Options Error %s ") % error.msg) + for o, a in gopts: if o == "-S" or o == '--store': store = a @@ -554,8 +567,6 @@ Object-specific Options (see above): else: process_args(sys.argv[1:]) - except getopt.error, error: - usage(_("Options Error %s ") % error.msg) except ValueError, error: errorExit(error.args[0]) except KeyError, error: diff --git a/policycoreutils/semanage/seobject.py b/policycoreutils/semanage/seobject.py index 6842b07..6742fe9 100644 --- a/policycoreutils/semanage/seobject.py +++ b/policycoreutils/semanage/seobject.py @@ -30,11 +30,10 @@ from IPy import IP import gettext gettext.bindtextdomain(PROGNAME, "/usr/share/locale") gettext.textdomain(PROGNAME) -try: - gettext.install(PROGNAME, localedir = "/usr/share/locale", unicode = 1) -except IOError: - import __builtin__ - __builtin__.__dict__['_'] = unicode + +import gettext +translation=gettext.translation(PROGNAME, localedir = "/usr/share/locale", fallback=True) +_=translation.ugettext import syslog @@ -161,10 +160,12 @@ def untranslate(trans, prepend = 1): return trans else: return raw - + class semanageRecords: transaction = False handle = None + store = None + def __init__(self, store): global handle @@ -182,7 +183,7 @@ class semanageRecords: if not semanageRecords.transaction and store != "": semanage_select_store(handle, store, SEMANAGE_CON_DIRECT); - semanageRecords.store = store + semanageRecords.store = store if not semanage_is_managed(handle): semanage_handle_destroy(handle) @@ -328,6 +329,7 @@ class permissiveRecords(semanageRecords): name = semanage_module_get_name(mod) if name and name.startswith("permissive_"): l.append(name.split("permissive_")[1]) + return l def list(self, heading = 1, locallist = 0): @@ -420,7 +422,9 @@ class loginRecords(semanageRecords): if rc < 0: raise ValueError(_("Could not check if login mapping for %s is defined") % name) if exists: - raise ValueError(_("Login mapping for %s is already defined") % name) + semanage_seuser_key_free(k) + return self.__modify(name, sename, serange) + if name[0] == '%': try: grp.getgrnam(name[1:]) @@ -627,7 +631,8 @@ class seluserRecords(semanageRecords): if rc < 0: raise ValueError(_("Could not check if SELinux user %s is defined") % name) if exists: - raise ValueError(_("SELinux user %s is already defined") % name) + semanage_user_key_free(k) + return self.__modify(name, roles, selevel, serange, prefix) (rc, u) = semanage_user_create(self.sh) if rc < 0: @@ -864,6 +869,7 @@ class portRecords(semanageRecords): return ( k, proto_d, low, high ) def __add(self, port, proto, serange, type): + if is_mls_enabled == 1: if serange == "": serange = "s0" @@ -926,6 +932,7 @@ class portRecords(semanageRecords): self.commit() def __modify(self, port, proto, serange, setype): + if serange == "" and setype == "": if is_mls_enabled == 1: raise ValueError(_("Requires setype or serange")) @@ -1136,7 +1143,8 @@ class nodeRecords(semanageRecords): (rc, exists) = semanage_node_exists(self.sh, k) if exists: - raise ValueError(_("Addr %s already defined") % addr) + semanage_node_key_free(k) + return self.__modify(addr, mask, self.protocol[proto], serange, ctype) (rc, node) = semanage_node_create(self.sh) if rc < 0: @@ -1152,7 +1160,6 @@ class nodeRecords(semanageRecords): if rc < 0: raise ValueError(_("Could not set mask for %s") % addr) - rc = semanage_context_set_user(self.sh, con, "system_u") if rc < 0: raise ValueError(_("Could not set user in addr context for %s") % addr) @@ -1204,12 +1211,11 @@ class nodeRecords(semanageRecords): if not exists: raise ValueError(_("Addr %s is not defined") % addr) - (rc, node) = semanage_node_query(self.sh, k) + (rc, node) = semanage_node_query_local(self.sh, k) if rc < 0: raise ValueError(_("Could not query addr %s") % addr) con = semanage_node_get_con(node) - if serange != "": semanage_context_set_mls(self.sh, con, untranslate(serange)) if setype != "": @@ -1334,7 +1340,8 @@ class interfaceRecords(semanageRecords): if rc < 0: raise ValueError(_("Could not check if interface %s is defined") % interface) if exists: - raise ValueError(_("Interface %s already defined") % interface) + semanage_iface_key_free(k) + return self.__modify(interface, serange, ctype) (rc, iface) = semanage_iface_create(self.sh) if rc < 0: @@ -1592,7 +1599,8 @@ class fcontextRecords(semanageRecords): raise ValueError(_("Could not check if file context for %s is defined") % target) if exists: - raise ValueError(_("File context for %s already defined") % target) + semanage_fcontext_key_free(k) + return self.__modify(target, type, ftype, serange, seuser) (rc, fcontext) = semanage_fcontext_create(self.sh) if rc < 0: @@ -1783,11 +1791,11 @@ class fcontextRecords(semanageRecords): return l def list(self, heading = 1, locallist = 0 ): - if heading: - print "%-50s %-18s %s\n" % (_("SELinux fcontext"), _("type"), _("Context")) fcon_dict = self.get_all(locallist) keys = fcon_dict.keys() keys.sort() + if len(keys) > 0 and heading: + print "%-50s %-18s %s\n" % (_("SELinux fcontext"), _("type"), _("Context")) for k in keys: if fcon_dict[k]: if is_mls_enabled: @@ -1814,6 +1822,18 @@ class booleanRecords(semanageRecords): self.dict["1"] = 1 self.dict["0"] = 0 + try: + rc, self.current_booleans = selinux.security_get_boolean_names() + rc, ptype = selinux.selinux_getpolicytype() + except: + self.current_booleans = [] + ptype = None + + if self.store == None or self.store == ptype: + self.modify_local = True + else: + self.modify_local = False + def __mod(self, name, value): (rc, k) = semanage_bool_key_create(self.sh, name) if rc < 0: @@ -1833,9 +1853,10 @@ class booleanRecords(semanageRecords): else: raise ValueError(_("You must specify one of the following values: %s") % ", ".join(self.dict.keys()) ) - rc = semanage_bool_set_active(self.sh, k, b) - if rc < 0: - raise ValueError(_("Could not set active value of boolean %s") % name) + if self.modify_local and name in self.current_booleans: + rc = semanage_bool_set_active(self.sh, k, b) + if rc < 0: + raise ValueError(_("Could not set active value of boolean %s") % name) rc = semanage_bool_modify_local(self.sh, k, b) if rc < 0: raise ValueError(_("Could not modify boolean %s") % name) @@ -1918,8 +1939,12 @@ class booleanRecords(semanageRecords): value = [] name = semanage_bool_get_name(boolean) value.append(semanage_bool_get_value(boolean)) - value.append(selinux.security_get_boolean_pending(name)) - value.append(selinux.security_get_boolean_active(name)) + if self.modify_local and boolean in self.current_booleans: + value.append(selinux.security_get_boolean_pending(name)) + value.append(selinux.security_get_boolean_active(name)) + else: + value.append(value[0]) + value.append(value[0]) ddict[name] = value return ddict diff --git a/policycoreutils/semodule_package/Makefile b/policycoreutils/semodule_package/Makefile index 0a4a3a6..f84cd7e 100644 --- a/policycoreutils/semodule_package/Makefile +++ b/policycoreutils/semodule_package/Makefile @@ -9,15 +9,17 @@ CFLAGS ?= -Werror -Wall -W override CFLAGS += -I$(INCLUDEDIR) LDLIBS = -lsepol -lselinux -L$(LIBDIR) -all: semodule_package +all: semodule_package semodule_unpackage semodule_package: semodule_package.o install: all -mkdir -p $(BINDIR) install -m 755 semodule_package $(BINDIR) + install -m 755 semodule_unpackage $(BINDIR) test -d $(MANDIR)/man8 || install -m 755 -d $(MANDIR)/man8 install -m 644 semodule_package.8 $(MANDIR)/man8/ + install -m 644 semodule_unpackage.8 $(MANDIR)/man8/ relabel: diff --git a/policycoreutils/semodule_package/semodule_package.8 b/policycoreutils/semodule_package/semodule_package.8 index 29c9eb2..ddad2d2 100644 --- a/policycoreutils/semodule_package/semodule_package.8 +++ b/policycoreutils/semodule_package/semodule_package.8 @@ -44,7 +44,7 @@ File contexts file for the module (optional). netfilter context file to be included in the package. .SH SEE ALSO -.B checkmodule(8), semodule(8) +.B checkmodule(8), semodule(8), semodule_unpackage(8) .SH AUTHORS .nf This manual page was written by Dan Walsh . diff --git a/policycoreutils/semodule_package/semodule_unpackage.8 b/policycoreutils/semodule_package/semodule_unpackage.8 new file mode 100644 index 0000000..62dd53e --- /dev/null +++ b/policycoreutils/semodule_package/semodule_unpackage.8 @@ -0,0 +1,24 @@ +.TH SEMODULE_PACKAGE "8" "Nov 2005" "Security Enhanced Linux" NSA +.SH NAME +semodule_unpackage \- Extract polciy module and file context file from an SELinux policy module unpackage. + +.SH SYNOPSIS +.B semodule_unpackage [] +.br +.SH DESCRIPTION +.PP +semodule_unpackage is the tool used to extract the SELinux policy module + and file context file from an SELinux Policy Package. + +.SH EXAMPLE +.nf +# Extract the httpd module file from httpd policy package. +$ semodule_unpackage httpd.pp httpd.mod httpd.fc +.fi + +.SH SEE ALSO +.B semodule_package(8) +.SH AUTHORS +.nf +This manual page was written by Dan Walsh . +The program was written by Stephen Smalley diff --git a/policycoreutils/semodule_package/semodule_unpackage.c b/policycoreutils/semodule_package/semodule_unpackage.c new file mode 100644 index 0000000..0120ee4 --- /dev/null +++ b/policycoreutils/semodule_package/semodule_unpackage.c @@ -0,0 +1,103 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char *progname = NULL; +extern char *optarg; + +static void usage(char *progname) +{ + printf("usage: %s ppfile modfile [fcfile]\n", progname); + exit(1); +} + +static int file_to_policy_file(char *filename, struct sepol_policy_file **pf, char *mode) +{ + FILE *f; + + if (sepol_policy_file_create(pf)) { + fprintf(stderr, "%s: Out of memory\n", progname); + return -1; + } + + f = fopen(filename, mode); + if (!f) { + fprintf(stderr, "%s: Could not open file %s: %s\n", progname, strerror(errno), filename); + return -1; + } + sepol_policy_file_set_fp(*pf, f); + return 0; +} + +int main(int argc, char **argv) +{ + struct sepol_module_package *pkg; + struct sepol_policy_file *in, *out; + FILE *fp; + size_t len; + char *ppfile, *modfile, *fcfile = NULL, *fcdata; + + progname = argv[0]; + + if (argc < 3) { + usage(progname); + exit(1); + } + + ppfile = argv[1]; + modfile = argv[2]; + if (argc >= 3) + fcfile = argv[3]; + + if (file_to_policy_file(ppfile, &in, "r")) + exit(1); + + if (sepol_module_package_create(&pkg)) { + fprintf(stderr, "%s: Out of memory\n", progname); + exit(1); + } + + if (sepol_module_package_read(pkg, in, 0) == -1) { + fprintf(stderr, "%s: Error while reading policy module from %s\n", + progname, ppfile); + exit(1); + } + + if (file_to_policy_file(modfile, &out, "w")) + exit(1); + + if (sepol_policydb_write(sepol_module_package_get_policy(pkg), out)) { + fprintf(stderr, "%s: Error while writing module to %s\n", progname, modfile); + exit(1); + } + + sepol_policy_file_free(in); + sepol_policy_file_free(out); + + len = sepol_module_package_get_file_contexts_len(pkg); + if (fcfile && len) { + fp = fopen(fcfile, "w"); + if (!fp) { + fprintf(stderr, "%s: Could not open file %s: %s\n", progname, strerror(errno), fcfile); + exit(1); + } + fcdata = sepol_module_package_get_file_contexts(pkg); + if (fwrite(fcdata, 1, len, fp) != len) { + fprintf(stderr, "%s: Could not write file %s: %s\n", progname, strerror(errno), fcfile); + exit(1); + } + fclose(fp); + } + + sepol_module_package_free(pkg); + exit(0); +} diff --git a/policycoreutils/sepolgen-ifgen/.gitignore b/policycoreutils/sepolgen-ifgen/.gitignore new file mode 100644 index 0000000..3816d2e --- /dev/null +++ b/policycoreutils/sepolgen-ifgen/.gitignore @@ -0,0 +1 @@ +sepolgen-ifgen-attr-helper diff --git a/policycoreutils/sepolgen-ifgen/Makefile b/policycoreutils/sepolgen-ifgen/Makefile new file mode 100644 index 0000000..99f8fd0 --- /dev/null +++ b/policycoreutils/sepolgen-ifgen/Makefile @@ -0,0 +1,25 @@ +# Installation directories. +PREFIX ?= ${DESTDIR}/usr +BINDIR ?= $(PREFIX)/bin +LIBDIR ?= ${PREFIX}/lib +INCLUDEDIR ?= $(PREFIX)/include + +CFLAGS ?= -Werror -Wall -W +override CFLAGS += -I$(INCLUDEDIR) +LDLIBS = $(LIBDIR)/libsepol.a + +all: sepolgen-ifgen-attr-helper + +sepolgen-ifgen-attr-helper: sepolgen-ifgen-attr-helper.o + +install: all + -mkdir -p $(BINDIR) + install -m 755 sepolgen-ifgen-attr-helper $(BINDIR) + +clean: + rm -f *~ *.o sepolgen-ifgen-attr-helper + +indent: + ../../scripts/Lindent $(wildcard *.[ch]) + +relabel: ; diff --git a/policycoreutils/sepolgen-ifgen/sepolgen-ifgen-attr-helper.c b/policycoreutils/sepolgen-ifgen/sepolgen-ifgen-attr-helper.c new file mode 100644 index 0000000..1ce37b0 --- /dev/null +++ b/policycoreutils/sepolgen-ifgen/sepolgen-ifgen-attr-helper.c @@ -0,0 +1,232 @@ +/* Authors: Frank Mayer + * and Karl MacMillan + * + * Copyright (C) 2003,2010 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + * Adapted from dispol.c. + * + * This program is used by sepolgen-ifgen to get the access for all of + * the attributes in the policy so that it can resolve the + * typeattribute statements in the interfaces. + * + * It outputs the attribute access in a similar format to what sepolgen + * uses to store interface vectors: + * [Attribute sandbox_x_domain] + * sandbox_x_domain,samba_var_t,file,ioctl,read,getattr,lock,open + * sandbox_x_domain,samba_var_t,dir,getattr,search,open + * sandbox_x_domain,initrc_var_run_t,file,ioctl,read,getattr,lock,open + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +struct val_to_name { + unsigned int val; + char *name; +}; + +static int perm_name(hashtab_key_t key, hashtab_datum_t datum, void *data) +{ + struct val_to_name *v = data; + perm_datum_t *perdatum; + + perdatum = (perm_datum_t *) datum; + + if (v->val == perdatum->s.value) { + v->name = key; + return 1; + } + + return 0; +} + +int render_access_mask(uint32_t av, avtab_key_t *key, policydb_t *policydbp, + FILE *fp) +{ + struct val_to_name v; + class_datum_t *cladatum; + char *perm = NULL; + unsigned int i; + int rc; + uint32_t tclass = key->target_class; + + cladatum = policydbp->class_val_to_struct[tclass - 1]; + for (i = 0; i < cladatum->permissions.nprim; i++) { + if (av & (1 << i)) { + v.val = i + 1; + rc = hashtab_map(cladatum->permissions.table, + perm_name, &v); + if (!rc && cladatum->comdatum) { + rc = hashtab_map(cladatum->comdatum-> + permissions.table, perm_name, + &v); + } + if (rc) + perm = v.name; + if (perm) { + fprintf(fp, ",%s", perm); + } + } + } + + return 0; +} + +static int render_key(avtab_key_t *key, policydb_t *p, FILE *fp) +{ + char *stype, *ttype, *tclass; + stype = p->p_type_val_to_name[key->source_type - 1]; + ttype = p->p_type_val_to_name[key->target_type - 1]; + tclass = p->p_class_val_to_name[key->target_class - 1]; + if (stype && ttype) { + fprintf(fp, "%s,%s,%s", stype, ttype, tclass); + } else { + fprintf(stderr, "error rendering key\n"); + exit(1); + } + + return 0; +} + +struct callback_data +{ + uint32_t attr; + policydb_t *policy; + FILE *fp; +}; + +int output_avrule(avtab_key_t *key, avtab_datum_t *datum, void *args) +{ + struct callback_data *cb_data = (struct callback_data *)args; + + if (key->source_type != cb_data->attr) + return 0; + + if (!(key->specified & AVTAB_AV && key->specified & AVTAB_ALLOWED)) + return 0; + + render_key(key, cb_data->policy, cb_data->fp); + render_access_mask(datum->data, key, cb_data->policy, cb_data->fp); + fprintf(cb_data->fp, "\n"); + + return 0; +} + +static int attribute_callback(hashtab_key_t key, hashtab_datum_t datum, void *datap) +{ + struct callback_data *cb_data = (struct callback_data *)datap; + type_datum_t *t = (type_datum_t *)datum; + + if (t->flavor == TYPE_ATTRIB) { + fprintf(cb_data->fp, "[Attribute %s]\n", key); + cb_data->attr = t->s.value; + if (avtab_map(&cb_data->policy->te_avtab, output_avrule, cb_data) < 0) + return -1; + if (avtab_map(&cb_data->policy->te_cond_avtab, output_avrule, cb_data) < 0) + return -1; + } + + return 0; +} + +static policydb_t *load_policy(const char *filename) +{ + policydb_t *policydb; + struct policy_file pf; + FILE *fp; + int ret; + + fp = fopen(filename, "r"); + if (fp == NULL) { + fprintf(stderr, "Can't open '%s': %s\n", + filename, strerror(errno)); + return NULL; + } + + policy_file_init(&pf); + pf.type = PF_USE_STDIO; + pf.fp = fp; + + policydb = malloc(sizeof(policydb_t)); + if (policydb == NULL) { + fprintf(stderr, "Out of memory!\n"); + return NULL; + } + + if (policydb_init(policydb)) { + fprintf(stderr, "Out of memory!\n"); + free(policydb); + return NULL; + } + + ret = policydb_read(policydb, &pf, 1); + if (ret) { + fprintf(stderr, + "error(s) encountered while parsing configuration\n"); + free(policydb); + return NULL; + } + + fclose(fp); + + return policydb; + +} + +void usage(char *progname) +{ + printf("usage: %s policy_file out_file\n", progname); +} + +int main(int argc, char **argv) +{ + policydb_t *p; + struct callback_data cb_data; + FILE *fp; + + if (argc != 3) { + usage(argv[0]); + return -1; + } + + /* Open the policy. */ + p = load_policy(argv[1]); + if (p == NULL) + return -1; + + /* Open the output policy. */ + fp = fopen(argv[2], "w"); + if (fp == NULL) { + fprintf(stderr, "error opening output file\n"); + policydb_destroy(p); + free(p); + return -1; + } + + /* Find all of the attributes and output their access. */ + cb_data.policy = p; + cb_data.fp = fp; + + if (hashtab_map(p->p_types.table, attribute_callback, &cb_data)) { + printf("error finding attributes\n"); + } + + policydb_destroy(p); + free(p); + fclose(fp); + + return 0; +} diff --git a/policycoreutils/setfiles/restore.c b/policycoreutils/setfiles/restore.c index e05761a..5bcb44a 100644 --- a/policycoreutils/setfiles/restore.c +++ b/policycoreutils/setfiles/restore.c @@ -318,11 +318,16 @@ static int process_one(char *name, int recurse_this_path) ftsent = fts_read(fts_handle); - if (ftsent != NULL) { - /* Keep the inode of the first one. */ - dev_num = ftsent->fts_statp->st_dev; + if (ftsent == NULL) { + fprintf(stderr, + "%s: error while labeling %s: %s\n", + r_opts->progname, namelist[0], strerror(errno)); + goto err; } + /* Keep the inode of the first one. */ + dev_num = ftsent->fts_statp->st_dev; + do { rc = 0; /* Skip the post order nodes. */ @@ -368,19 +373,21 @@ int process_glob(char *name, int recurse) { int errors; memset(&globbuf, 0, sizeof(globbuf)); errors = glob(name, GLOB_TILDE | GLOB_PERIOD, NULL, &globbuf); - if (errors) - errors = process_one_realpath(name, recurse); - else { - for (i = 0; i < globbuf.gl_pathc; i++) { - int len = strlen(globbuf.gl_pathv[i]) -2; - if (len > 0 && strcmp(&globbuf.gl_pathv[i][len--], "/.") == 0) - continue; - if (len > 0 && strcmp(&globbuf.gl_pathv[i][len], "/..") == 0) - continue; - errors |= process_one_realpath(globbuf.gl_pathv[i], recurse); - } - globfree(&globbuf); + if (errors == GLOB_NOMATCH) + return 0; + + if (errors) + return errors; + + for (i = 0; i < globbuf.gl_pathc; i++) { + int len = strlen(globbuf.gl_pathv[i]) -2; + if (len > 0 && strcmp(&globbuf.gl_pathv[i][len--], "/.") == 0) + continue; + if (len > 0 && strcmp(&globbuf.gl_pathv[i][len], "/..") == 0) + continue; + errors |= process_one_realpath(globbuf.gl_pathv[i], recurse); } + globfree(&globbuf); return errors; } @@ -388,7 +395,7 @@ int process_one_realpath(char *name, int recurse) { int rc = 0; char *p; - struct stat sb; + struct stat64 sb; if (r_opts == NULL){ fprintf(stderr, @@ -399,7 +406,7 @@ int process_one_realpath(char *name, int recurse) if (!r_opts->expand_realpath) { return process_one(name, recurse); } else { - rc = lstat(name, &sb); + rc = lstat64(name, &sb); if (rc < 0) { if (r_opts->ignore_enoent && errno == ENOENT) return 0; @@ -566,7 +573,7 @@ static int filespec_add(ino_t ino, const security_context_t con, const char *fil { file_spec_t *prevfl, *fl; int h, ret; - struct stat sb; + struct stat64 sb; if (!fl_head) { fl_head = malloc(sizeof(file_spec_t) * HASH_BUCKETS); @@ -579,7 +586,7 @@ static int filespec_add(ino_t ino, const security_context_t con, const char *fil for (prevfl = &fl_head[h], fl = fl_head[h].next; fl; prevfl = fl, fl = fl->next) { if (ino == fl->ino) { - ret = lstat(fl->file, &sb); + ret = lstat64(fl->file, &sb); if (ret < 0 || sb.st_ino != ino) { freecon(fl->con); free(fl->file); @@ -631,5 +638,67 @@ static int filespec_add(ino_t ino, const security_context_t con, const char *fil return -1; } +#include +/* + Search /proc/mounts for all file systems that do not support extended + attributes and add them to the exclude directory table. File systems + that support security labels have the seclabel option. +*/ +void exclude_non_seclabel_mounts() +{ + struct utsname uts; + FILE *fp; + size_t len; + ssize_t num; + int index = 0, found = 0; + char *mount_info[4]; + char *buf = NULL, *item; + + /* Check to see if the kernel supports seclabel */ + if (uname(&uts) == 0 && strverscmp(uts.release, "2.6.30") < 0) + return; + if (is_selinux_enabled() <= 0) + return; + + fp = fopen("/proc/mounts", "r"); + if (!fp) + return; + + while ((num = getline(&buf, &len, fp)) != -1) { + found = 0; + index = 0; + item = strtok(buf, " "); + while (item != NULL) { + mount_info[index] = item; + if (index == 3) + break; + index++; + item = strtok(NULL, " "); + } + if (index < 3) { + fprintf(stderr, + "/proc/mounts record \"%s\" has incorrect format.\n", + buf); + continue; + } + /* remove pre-existing entry */ + remove_exclude(mount_info[1]); + + item = strtok(mount_info[3], ","); + while (item != NULL) { + if (strcmp(item, "seclabel") == 0) { + found = 1; + break; + } + item = strtok(NULL, ","); + } + + /* exclude mount points without the seclabel option */ + if (!found) + add_exclude(mount_info[1]); + } + + free(buf); +} diff --git a/policycoreutils/setfiles/restore.h b/policycoreutils/setfiles/restore.h index 7e988d5..ac27222 100644 --- a/policycoreutils/setfiles/restore.h +++ b/policycoreutils/setfiles/restore.h @@ -49,5 +49,6 @@ int exclude(const char *path); void remove_exclude(const char *directory); int process_one_realpath(char *name, int recurse); int process_glob(char *name, int recurse); +void exclude_non_seclabel_mounts(); #endif diff --git a/policycoreutils/setfiles/setfiles.c b/policycoreutils/setfiles/setfiles.c index d320e9f..fa0cd6a 100644 --- a/policycoreutils/setfiles/setfiles.c +++ b/policycoreutils/setfiles/setfiles.c @@ -5,7 +5,6 @@ #include #include #include -#include #define __USE_XOPEN_EXTENDED 1 /* nftw */ #include #ifdef USE_AUDIT @@ -15,8 +14,6 @@ #define AUDIT_FS_RELABEL 2309 #endif #endif -static int mass_relabel; -static int mass_relabel_errs; /* cmdline opts*/ @@ -24,7 +21,6 @@ static int mass_relabel_errs; static char *policyfile = NULL; static int warn_no_match = 0; static int null_terminated = 0; -static int errors; static struct restore_opts r_opts; #define STAT_BLOCK_SIZE 1 @@ -108,10 +104,11 @@ int canoncon(char **contextp) } #ifndef USE_AUDIT -static void maybe_audit_mass_relabel(void) +static void maybe_audit_mass_relabel(int mass_relabel __attribute__((unused)), + int mass_relabel_errs __attribute__((unused))) { #else -static void maybe_audit_mass_relabel(void) +static void maybe_audit_mass_relabel(int mass_relabel, int mass_relabel_errs) { int audit_fd = -1; int rc = 0; @@ -137,69 +134,6 @@ static void maybe_audit_mass_relabel(void) #endif } -/* - Search /proc/mounts for all file systems that do not support extended - attributes and add them to the exclude directory table. File systems - that support security labels have the seclabel option. -*/ -static void exclude_non_seclabel_mounts() -{ - struct utsname uts; - FILE *fp; - size_t len; - ssize_t num; - int index = 0, found = 0; - char *mount_info[4]; - char *buf = NULL, *item; - - /* Check to see if the kernel supports seclabel */ - if (uname(&uts) == 0 && strverscmp(uts.release, "2.6.30") < 0) - return; - if (is_selinux_enabled() <= 0) - return; - - fp = fopen("/proc/mounts", "r"); - if (!fp) - return; - - while ((num = getline(&buf, &len, fp)) != -1) { - found = 0; - index = 0; - item = strtok(buf, " "); - while (item != NULL) { - mount_info[index] = item; - if (index == 3) - break; - index++; - item = strtok(NULL, " "); - } - if (index < 3) { - fprintf(stderr, - "/proc/mounts record \"%s\" has incorrect format.\n", - buf); - continue; - } - - /* remove pre-existing entry */ - remove_exclude(mount_info[1]); - - item = strtok(mount_info[3], ","); - while (item != NULL) { - if (strcmp(item, "seclabel") == 0) { - found = 1; - break; - } - item = strtok(NULL, ","); - } - - /* exclude mount points without the seclabel option */ - if (!found) - add_exclude(mount_info[1]); - } - - free(buf); -} - int main(int argc, char **argv) { struct stat sb; @@ -210,6 +144,7 @@ int main(int argc, char **argv) size_t buf_len; int recurse; /* Recursive descent. */ char *base; + int mass_relabel = 0, errors = 0; memset(&r_opts, 0, sizeof(r_opts)); @@ -487,9 +422,7 @@ int main(int argc, char **argv) } } - if (mass_relabel) - mass_relabel_errs = errors; - maybe_audit_mass_relabel(); + maybe_audit_mass_relabel(mass_relabel, errors); if (warn_no_match) selabel_stats(r_opts.hnd);