a648c6f239
Also add signal handler to catch sigint, so if user enters ctrl-C sandbox will shutdown.
5995 lines
187 KiB
Diff
5995 lines
187 KiB
Diff
diff --git a/policycoreutils/Makefile b/policycoreutils/Makefile
|
|
index 86ed03f..67d0ee8 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 gui
|
|
|
|
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 <kmacmillan@mentalrootkit.com>
|
|
#
|
|
# 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 6178cc8..b6f386d 100644
|
|
--- a/policycoreutils/audit2allow/audit2allow.1
|
|
+++ b/policycoreutils/audit2allow/audit2allow.1
|
|
@@ -1,5 +1,6 @@
|
|
.\" Hey, Emacs! This is an -*- nroff -*- source file.
|
|
.\" Copyright (c) 2005 Manoj Srivastava <srivasta@debian.org>
|
|
+.\" Copyright (c) 2010 Dan Walsh <dwalsh@redhat.com>
|
|
.\"
|
|
.\" This is free documentation; you can redistribute it and/or
|
|
.\" modify it under the terms of the GNU General Public License as
|
|
@@ -22,7 +23,7 @@
|
|
.\" USA.
|
|
.\"
|
|
.\"
|
|
-.TH AUDIT2ALLOW "1" "January 2005" "Security Enhanced Linux" NSA
|
|
+.TH AUDIT2ALLOW "1" "October 2010" "Security Enhanced Linux" NSA
|
|
.SH NAME
|
|
.BR audit2allow
|
|
\- generate SELinux policy allow/dontaudit rules from logs of denied operations
|
|
@@ -66,6 +67,9 @@ Generate module/require output <modulename>
|
|
.B "\-M <modulename>"
|
|
Generate loadable module package, conflicts with -o
|
|
.TP
|
|
+.B "\-p <policyfile>" | "\-\-policy <policyfile>"
|
|
+Policy file to use for analysis
|
|
+.TP
|
|
.B "\-o <outputfile>" | "\-\-output <outputfile>"
|
|
append output to
|
|
.I <outputfile>
|
|
@@ -117,14 +121,6 @@ an 'allow' rule.
|
|
.B Please substitute /var/log/messages for /var/log/audit/audit.log in the
|
|
.B examples.
|
|
.PP
|
|
-.B Using audit2allow to generate monolithic (non-module) policy
|
|
-$ cd /etc/selinux/$SELINUXTYPE/src/policy
|
|
-$ cat /var/log/audit/audit.log | audit2allow >> domains/misc/local.te
|
|
-$ cat domains/misc/local.te
|
|
-allow cupsd_config_t unconfined_t:fifo_file { getattr ioctl };
|
|
-<review domains/misc/local.te and customize as desired>
|
|
-$ make load
|
|
-
|
|
.B Using audit2allow to generate module policy
|
|
|
|
$ cat /var/log/audit/audit.log | audit2allow -m local > local.te
|
|
@@ -132,20 +128,38 @@ $ cat local.te
|
|
module local 1.0;
|
|
|
|
require {
|
|
- role system_r;
|
|
+ class file { getattr open read };
|
|
|
|
|
|
- class fifo_file { getattr ioctl };
|
|
+ type myapp_t;
|
|
+ type etc_t;
|
|
+ };
|
|
|
|
|
|
- type cupsd_config_t;
|
|
- type unconfined_t;
|
|
- };
|
|
+allow myapp_t etc_t:file { getattr open read };
|
|
+<review local.te and customize as desired>
|
|
|
|
+.B Using audit2allow to generate module policy using reference policy
|
|
|
|
-allow cupsd_config_t unconfined_t:fifo_file { getattr ioctl };
|
|
+$ cat /var/log/audit/audit.log | audit2allow -R -m local > local.te
|
|
+$ cat local.te
|
|
+policy_module(local, 1.0)
|
|
+
|
|
+gen_require(`
|
|
+ type myapp_t;
|
|
+ type etc_t;
|
|
+ };
|
|
+
|
|
+files_read_etc_files(myapp_t)
|
|
<review local.te and customize as desired>
|
|
|
|
+.B Building module policy using Makefile
|
|
+
|
|
+# SELinux provides a policy devel environment under /usr/share/selinux/devel
|
|
+# You can create a te file and compile it by executing
|
|
+$ make -f /usr/share/selinux/devel/Makefile
|
|
+$ semodule -i local.pp
|
|
+
|
|
.B Building module policy manually
|
|
|
|
# Compile the module
|
|
@@ -168,6 +182,14 @@ you are required to execute
|
|
|
|
semodule -i local.pp
|
|
|
|
+.B Using audit2allow to generate monolithic (non-module) policy
|
|
+$ cd /etc/selinux/$SELINUXTYPE/src/policy
|
|
+$ cat /var/log/audit/audit.log | audit2allow >> domains/misc/local.te
|
|
+$ cat domains/misc/local.te
|
|
+allow cupsd_config_t unconfined_t:fifo_file { getattr ioctl };
|
|
+<review domains/misc/local.te and customize as desired>
|
|
+$ make load
|
|
+
|
|
.fi
|
|
.PP
|
|
.SH AUTHOR
|
|
diff --git a/policycoreutils/audit2allow/sepolgen-ifgen b/policycoreutils/audit2allow/sepolgen-ifgen
|
|
index 03f95a1..dad2009 100644
|
|
--- a/policycoreutils/audit2allow/sepolgen-ifgen
|
|
+++ b/policycoreutils/audit2allow/sepolgen-ifgen
|
|
@@ -1,4 +1,4 @@
|
|
-#! /usr/bin/python -E
|
|
+#! /usr/bin/python -Es
|
|
#
|
|
# Authors: Karl MacMillan <kmacmillan@mentalrootkit.com>
|
|
#
|
|
@@ -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 2d31d64..e985289 100644
|
|
--- a/policycoreutils/newrole/newrole.c
|
|
+++ b/policycoreutils/newrole/newrole.c
|
|
@@ -586,7 +586,7 @@ static int drop_capabilities(int full)
|
|
return -1;
|
|
}
|
|
if (! full)
|
|
- capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, CAP_SYS_ADMIN | CAP_FOWNER | CAP_CHOWN | CAP_DAC_OVERRIDE);
|
|
+ capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, CAP_SYS_ADMIN | CAP_FOWNER | CAP_CHOWN | CAP_DAC_OVERRIDE | CAP_SETPCAP );
|
|
return capng_apply(CAPNG_SELECT_BOTH);
|
|
}
|
|
|
|
@@ -1030,8 +1030,13 @@ 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)) {
|
|
+ fprintf(stderr, _("Sorry, newrole failed to drop capabilities\n"));
|
|
+ perror("");
|
|
return -1;
|
|
+ }
|
|
+*/
|
|
if (set_signal_handles())
|
|
return -1;
|
|
|
|
diff --git a/policycoreutils/restorecond/Makefile b/policycoreutils/restorecond/Makefile
|
|
index 3f235e6..7552668 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..0c14c94 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 <dwalsh@redhat.com>.
|
|
-The program was written by Dan Walsh <dwalsh@redhat.com>.
|
|
+This man page and program was written by Dan Walsh <dwalsh@redhat.com>.
|
|
|
|
.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 58774e6..a588e5e 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,294 +50,38 @@
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
-#include <ctype.h>
|
|
+#include "../setfiles/restore.h"
|
|
#include <sys/types.h>
|
|
-#include <sys/stat.h>
|
|
#include <syslog.h>
|
|
#include <limits.h>
|
|
+#include <pwd.h>
|
|
+#include <sys/stat.h>
|
|
+#include <string.h>
|
|
+#include <stdio.h>
|
|
#include <fcntl.h>
|
|
-
|
|
#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 <selinux/selinux.h>
|
|
-#include <utmp.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))
|
|
-
|
|
-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);
|
|
-}
|
|
-
|
|
-/*
|
|
- 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->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;
|
|
+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;
|
|
|
|
- case 1: /* utmp has changed need to reload */
|
|
- read_config(fd);
|
|
- break;
|
|
+#include <selinux/selinux.h>
|
|
|
|
- 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";
|
|
@@ -374,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);
|
|
}
|
|
|
|
@@ -390,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;
|
|
@@ -467,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..8cf2f20
|
|
--- /dev/null
|
|
+++ b/policycoreutils/restorecond/user.c
|
|
@@ -0,0 +1,242 @@
|
|
+/*
|
|
+ * 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 <dwalsh@redhat.com>
|
|
+ *
|
|
+*/
|
|
+
|
|
+#define _GNU_SOURCE
|
|
+#include <sys/inotify.h>
|
|
+#include <errno.h>
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <signal.h>
|
|
+#include <string.h>
|
|
+#include <unistd.h>
|
|
+#include <ctype.h>
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
+#include <syslog.h>
|
|
+#include <limits.h>
|
|
+#include <fcntl.h>
|
|
+
|
|
+#include "restorecond.h"
|
|
+#include "stringslist.h"
|
|
+#include <glib.h>
|
|
+#ifdef HAVE_DBUS
|
|
+#include <dbus/dbus.h>
|
|
+#include <dbus/dbus-glib.h>
|
|
+#include <dbus/dbus-glib-lowlevel.h>
|
|
+
|
|
+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 <selinux/selinux.h>
|
|
+#include <sys/file.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))
|
|
+
|
|
+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;
|
|
+ asprintf(&ptr, "%s/.restorecond", homedir);
|
|
+ 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/utmpwatcher.c b/policycoreutils/restorecond/utmpwatcher.c
|
|
index f182c22..feddb5a 100644
|
|
--- a/policycoreutils/restorecond/utmpwatcher.c
|
|
+++ b/policycoreutils/restorecond/utmpwatcher.c
|
|
@@ -72,8 +72,8 @@ unsigned int utmpwatcher_handle(int inotify_fd, int wd)
|
|
if (utmp_wd == -1)
|
|
exitApp("Error watching utmp file.");
|
|
|
|
+ changed = strings_list_diff(prev_utmp_ptr, utmp_ptr);
|
|
if (prev_utmp_ptr) {
|
|
- changed = strings_list_diff(prev_utmp_ptr, utmp_ptr);
|
|
strings_list_free(prev_utmp_ptr);
|
|
}
|
|
return changed;
|
|
diff --git a/policycoreutils/restorecond/watch.c b/policycoreutils/restorecond/watch.c
|
|
new file mode 100644
|
|
index 0000000..20a861f
|
|
--- /dev/null
|
|
+++ b/policycoreutils/restorecond/watch.c
|
|
@@ -0,0 +1,270 @@
|
|
+#define _GNU_SOURCE
|
|
+#include <sys/inotify.h>
|
|
+#include <errno.h>
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <string.h>
|
|
+#include <unistd.h>
|
|
+#include <ctype.h>
|
|
+#include <sys/types.h>
|
|
+#include <syslog.h>
|
|
+#include "../setfiles/restore.h"
|
|
+#include <glob.h>
|
|
+#include <libgen.h>
|
|
+#include <sys/stat.h>
|
|
+#include <string.h>
|
|
+#include <stdio.h>
|
|
+#include <fcntl.h>
|
|
+#include <selinux/selinux.h>
|
|
+#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;
|
|
+ asprintf(&ptr, "%s%s", homedir, &buffer[1]);
|
|
+ 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/open_init_pty.8 b/policycoreutils/run_init/open_init_pty.8
|
|
index 540860a..10175dd 100644
|
|
--- a/policycoreutils/run_init/open_init_pty.8
|
|
+++ b/policycoreutils/run_init/open_init_pty.8
|
|
@@ -24,18 +24,18 @@
|
|
.\"
|
|
.TH OPEN_INIT_PTY "8" "January 2005" "Security Enhanced Linux" NSA
|
|
.SH NAME
|
|
-open_init_pty \- run an program under a psuedo terminal
|
|
+open_init_pty \- run an program under a pseudo terminal
|
|
.SH SYNOPSIS
|
|
.B open_init_pty
|
|
\fISCRIPT\fR [[\fIARGS\fR]...]
|
|
.br
|
|
.SH DESCRIPTION
|
|
.PP
|
|
-Run a program under a psuedo terminal. This is used by
|
|
+Run a program under a pseudo terminal. This is used by
|
|
.B run_init
|
|
to run actually run the program after setting up the proper
|
|
-context. This program acquires a new Psuedo terminal, forks a child
|
|
-process that binds to the psueado terminal, and then sits around and
|
|
+context. This program acquires a new Pseudo terminal, forks a child
|
|
+process that binds to the pseudo terminal, and then sits around and
|
|
connects the physical terminal it was invoked upon with the pseudo
|
|
terminal, passing keyboard input into to the child process, and passing the
|
|
output of the child process to the physical terminal.
|
|
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..0c8a085 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/
|
|
-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 48a26c2..4d17385 100644
|
|
--- a/policycoreutils/sandbox/sandbox
|
|
+++ b/policycoreutils/sandbox/sandbox
|
|
@@ -1,5 +1,6 @@
|
|
-#! /usr/bin/python -E
|
|
+#! /usr/bin/python -Es
|
|
# Authors: Dan Walsh <dwalsh@redhat.com>
|
|
+# Authors: Thomas Liu <tliu@fedoraproject.org>
|
|
# 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()
|
|
@@ -226,14 +244,25 @@ kill -TERM $WM_PID 2> /dev/null
|
|
|
|
def usage(self, message = ""):
|
|
error_exit("%s\n%s" % (self.__parser.usage, message))
|
|
-
|
|
+
|
|
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] [-l level ] [-[X|M] [-H homedir] [-T tempdir]] [-I includefile ] [-W windowmanager ] [ -w windowsize ] [[-i file ] ...] [ -t type ] command
|
|
+
|
|
+sandbox [-h] [-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()
|
|
@@ -268,6 +297,10 @@ sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [-
|
|
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])
|
|
@@ -329,44 +374,47 @@ sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [-
|
|
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 not self.__options.level:
|
|
+ cmds.append('-k')
|
|
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 +452,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..3deb4b2 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 <dwalsh@redhat.com>
|
|
+and
|
|
+.I Thomas Liu <tliu@fedoraproject.org>
|
|
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..ee97e10
|
|
--- /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 <tliu@fedoraproject.org>
|
|
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..0b0239c 100644
|
|
--- a/policycoreutils/sandbox/sandboxX.sh
|
|
+++ b/policycoreutils/sandbox/sandboxX.sh
|
|
@@ -1,15 +1,21 @@
|
|
#!/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..c69ceda
|
|
--- /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 <dwalsh@redhat.com>
|
|
+and
|
|
+.I Thomas Liu <tliu@fedoraproject.org>
|
|
diff --git a/policycoreutils/sandbox/seunshare.c b/policycoreutils/sandbox/seunshare.c
|
|
index ec692e7..2718a68 100644
|
|
--- a/policycoreutils/sandbox/seunshare.c
|
|
+++ b/policycoreutils/sandbox/seunshare.c
|
|
@@ -1,27 +1,35 @@
|
|
+/*
|
|
+ * Authors: Dan Walsh <dwalsh@redhat.com>
|
|
+ * Authors: Thomas Liu <tliu@fedoraproject.org>
|
|
+ */
|
|
+
|
|
+#define _GNU_SOURCE
|
|
#include <signal.h>
|
|
#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
#include <sys/wait.h>
|
|
#include <syslog.h>
|
|
#include <sys/mount.h>
|
|
+#include <glob.h>
|
|
#include <pwd.h>
|
|
-#define _GNU_SOURCE
|
|
#include <sched.h>
|
|
+#include <libcgroup.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
+#include <regex.h>
|
|
#include <unistd.h>
|
|
+#include <sys/fsuid.h>
|
|
#include <stdlib.h>
|
|
#include <cap-ng.h>
|
|
#include <getopt.h> /* for getopt_long() form of getopt() */
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
+#include <fcntl.h>
|
|
|
|
#include <selinux/selinux.h>
|
|
#include <selinux/context.h> /* for context-mangling functions */
|
|
-
|
|
-#include <sys/types.h>
|
|
-#include <sys/stat.h>
|
|
-#include <unistd.h>
|
|
+#include <dirent.h>
|
|
|
|
#ifdef USE_NLS
|
|
#include <locale.h> /* for setlocale() */
|
|
@@ -39,29 +47,56 @@
|
|
#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 ] -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 +110,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 SIGHUP 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 +243,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,45 +251,594 @@ 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));
|
|
+
|
|
+ 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) {
|
|
+ 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)))) {
|
|
+ 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}
|
|
};
|
|
|
|
@@ -180,6 +849,12 @@ int main(int argc, char **argv) {
|
|
return -1;
|
|
}
|
|
|
|
+#ifdef USE_NLS
|
|
+ setlocale(LC_ALL, "");
|
|
+ bindtextdomain(PACKAGE, LOCALEDIR);
|
|
+ textdomain(PACKAGE);
|
|
+#endif
|
|
+
|
|
struct passwd *pwd=getpwuid(uid);
|
|
if (!pwd) {
|
|
perror(_("getpwduid failed"));
|
|
@@ -187,34 +862,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,76 +900,84 @@ 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())
|
|
- return -1;
|
|
-
|
|
- if (unshare(CLONE_NEWNS) < 0) {
|
|
- perror(_("Failed to unshare"));
|
|
+ if (execcon && is_selinux_enabled() != 1) {
|
|
+ fprintf(stderr, _("Error: execution context specified, but SELinux is not enabled\n"));
|
|
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 (set_signal_handles()) 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);
|
|
+
|
|
+ /* 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);
|
|
|
|
- if (drop_capabilities(uid)) {
|
|
- perror(_("Failed to drop all capabilities"));
|
|
+ /* 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)
|
|
rc |= setenv("DISPLAY", display, 1);
|
|
rc |= setenv("HOME", pwd->pw_dir, 1);
|
|
@@ -300,22 +985,47 @@ int main(int argc, char **argv) {
|
|
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/Makefile b/policycoreutils/scripts/Makefile
|
|
index 53b65b6..cc75a96 100644
|
|
--- a/policycoreutils/scripts/Makefile
|
|
+++ b/policycoreutils/scripts/Makefile
|
|
@@ -14,6 +14,7 @@ install: all
|
|
install -m 755 genhomedircon $(SBINDIR)
|
|
-mkdir -p $(MANDIR)/man8
|
|
install -m 644 fixfiles.8 $(MANDIR)/man8/
|
|
+ install -m 644 genhomedircon.8 $(MANDIR)/man8/
|
|
install -m 644 chcat.8 $(MANDIR)/man8/
|
|
|
|
clean:
|
|
diff --git a/policycoreutils/scripts/chcat b/policycoreutils/scripts/chcat
|
|
index 4038a99..9efcb22 100755
|
|
--- a/policycoreutils/scripts/chcat
|
|
+++ b/policycoreutils/scripts/chcat
|
|
@@ -1,4 +1,4 @@
|
|
-#! /usr/bin/python -E
|
|
+#! /usr/bin/python -Es
|
|
# Copyright (C) 2005 Red Hat
|
|
# see file 'COPYING' for use and warranty information
|
|
#
|
|
diff --git a/policycoreutils/scripts/chcat.8 b/policycoreutils/scripts/chcat.8
|
|
index 3f9efba..7c6d75a 100644
|
|
--- a/policycoreutils/scripts/chcat.8
|
|
+++ b/policycoreutils/scripts/chcat.8
|
|
@@ -51,5 +51,5 @@ When operating on files this script wraps the chcon command.
|
|
.SH "FILES"
|
|
/etc/selinux/{SELINUXTYPE}/setrans.conf
|
|
.br
|
|
-/etc/selinux/{SELINUXTYPE}/seuser
|
|
+/etc/selinux/{SELINUXTYPE}/seusers
|
|
|
|
diff --git a/policycoreutils/scripts/fixfiles b/policycoreutils/scripts/fixfiles
|
|
index ae519fc..8e47d94 100755
|
|
--- a/policycoreutils/scripts/fixfiles
|
|
+++ b/policycoreutils/scripts/fixfiles
|
|
@@ -21,6 +21,44 @@
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
#
|
|
+# Get all mounted rw file systems that support seclabel
|
|
+#
|
|
+get_labeled_mounts() {
|
|
+# /dev is not listed in the mountab
|
|
+FS="`mount | egrep -v '\((|.*,)bind(,.*|)\)' | awk '/\(rw/{print $3}';` /dev"
|
|
+for i in $FS; do
|
|
+ grep --silent "$i ".*seclabel /proc/self/mounts && echo $i
|
|
+done
|
|
+}
|
|
+exclude_dirs_from_relabelling() {
|
|
+ exclude_from_relabelling=
|
|
+ if [ -e /etc/selinux/fixfiles_exclude_dirs ]
|
|
+ then
|
|
+ while read i
|
|
+ do
|
|
+ # skip blank line and comment
|
|
+ # skip not absolute path
|
|
+ # skip not directory
|
|
+ [ -z "${i}" ] && continue
|
|
+ [[ "${i}" =~ "^[[:blank:]]*#" ]] && continue
|
|
+ [[ ! "${i}" =~ ^/.* ]] && continue
|
|
+ [[ ! -d "${i}" ]] && continue
|
|
+ exclude_from_relabelling="$exclude_from_relabelling -e $i"
|
|
+ logit "skipping the directory $i from relabelling"
|
|
+ done < /etc/selinux/fixfiles_exclude_dirs
|
|
+ fi
|
|
+ echo "$exclude_from_relabelling"
|
|
+}
|
|
+exclude_dirs() {
|
|
+ exclude=
|
|
+ for i in /var/lib/BackupPC /home /tmp /dev; do
|
|
+ [ -e $i ] && exclude="$exclude -e $i";
|
|
+ done
|
|
+ exclude="$exclude `exclude_dirs_from_relabelling`"
|
|
+ echo "$exclude"
|
|
+}
|
|
+
|
|
+#
|
|
# Set global Variables
|
|
#
|
|
fullFlag=0
|
|
@@ -35,9 +73,7 @@ SYSLOGFLAG="-l"
|
|
LOGGER=/usr/sbin/logger
|
|
SETFILES=/sbin/setfiles
|
|
RESTORECON=/sbin/restorecon
|
|
-FILESYSTEMSRW=`mount | grep -v "context=" | egrep -v '\((|.*,)bind(,.*|)\)' | awk '/(ext[234]| ext4dev | gfs2 | xfs | jfs | btrfs ).*\(rw/{print $3}';`
|
|
-FILESYSTEMSRO=`mount | grep -v "context=" | egrep -v '\((|.*,)bind(,.*|)\)' | awk '/(ext[234]| ext4dev | gfs2 | xfs | jfs | btrfs ).*\(ro/{print $3}';`
|
|
-FILESYSTEMS="$FILESYSTEMSRW $FILESYSTEMSRO"
|
|
+FILESYSTEMS=`get_labeled_mounts`
|
|
SELINUXTYPE="targeted"
|
|
if [ -e /etc/selinux/config ]; then
|
|
. /etc/selinux/config
|
|
@@ -87,23 +123,10 @@ if [ -f ${PREFC} -a -x /usr/bin/diff ]; then
|
|
esac; \
|
|
fi; \
|
|
done | \
|
|
- while read pattern ; do sh -c "find $pattern \
|
|
- ! \( -fstype ext2 -o -fstype ext3 -o -fstype ext4 -o -fstype ext4dev -o -fstype gfs2 -o -fstype jfs -o -fstype xfs -o -fstype btrfs \) -prune -o \
|
|
- \( -wholename /home -o -wholename /root -o -wholename /tmp -wholename /dev \) -prune -o -print0"; \
|
|
- done 2> /dev/null | \
|
|
- ${RESTORECON} $* -0 -f -
|
|
+ ${RESTORECON} -f - -R -p `exclude_dirs`; \
|
|
rm -f ${TEMPFILE} ${PREFCTEMPFILE}
|
|
fi
|
|
}
|
|
-#
|
|
-# Log all Read Only file systems
|
|
-#
|
|
-LogReadOnly() {
|
|
-if [ ! -z "$FILESYSTEMSRO" ]; then
|
|
- logit "Warning: Skipping the following R/O filesystems:"
|
|
- logit "$FILESYSTEMSRO"
|
|
-fi
|
|
-}
|
|
|
|
rpmlist() {
|
|
rpm -q --qf '[%{FILESTATES} %{FILENAMES}\n]' "$1" | grep '^0 ' | cut -f2- -d ' '
|
|
@@ -121,33 +144,45 @@ if [ ! -z "$PREFC" ]; then
|
|
fi
|
|
if [ ! -z "$RPMFILES" ]; then
|
|
for i in `echo "$RPMFILES" | sed 's/,/ /g'`; do
|
|
- rpmlist $i | ${RESTORECON} ${FORCEFLAG} $* -R -i -f - 2>&1 >> $LOGFILE
|
|
+ rpmlist $i | ${RESTORECON} ${FORCEFLAG} $* -R -i -f - 2>&1 | cat >> $LOGFILE
|
|
done
|
|
exit $?
|
|
fi
|
|
if [ ! -z "$FILEPATH" ]; then
|
|
- if [ -x /usr/bin/find ]; then
|
|
- /usr/bin/find "$FILEPATH" \
|
|
- ! \( -fstype ext2 -o -fstype ext3 -o -fstype ext4 -o -fstype ext4dev -o -fstype gfs2 -o -fstype jfs -o -fstype xfs -o -fstype btrfs \) -prune -o -print0 | \
|
|
- ${RESTORECON} ${FORCEFLAG} $* -0 -f - 2>&1 >> $LOGFILE
|
|
- else
|
|
- ${RESTORECON} ${FORCEFLAG} -R $* $FILEPATH 2>&1 >> $LOGFILE
|
|
- fi
|
|
+ ${RESTORECON} ${FORCEFLAG} -R $* $FILEPATH 2>&1 | cat >> $LOGFILE
|
|
return
|
|
fi
|
|
[ -x /usr/sbin/genhomedircon ] && /usr/sbin/genhomedircon
|
|
-LogReadOnly
|
|
-${SETFILES} -q ${SYSLOGFLAG} ${FORCEFLAG} $* ${FC} ${FILESYSTEMSRW} 2>&1 >> $LOGFILE
|
|
-rm -rf /tmp/gconfd-* /tmp/pulse-* /tmp/orbit-*
|
|
+#
|
|
+exclude_dirs="`exclude_dirs_from_relabelling`"
|
|
+if [ -n "${exclude_dirs}" ]
|
|
+then
|
|
+ TEMPFCFILE=`mktemp ${FC}.XXXXXXXXXX`
|
|
+ test -z "$TEMPFCFILE" && exit
|
|
+ /bin/cp -p ${FC} ${TEMPFCFILE} &>/dev/null || exit
|
|
+ exclude_dirs=${exclude_dirs//-e/}
|
|
+ for p in ${exclude_dirs}
|
|
+ do
|
|
+ p="${p%/}"
|
|
+ p1="${p}(/.*)? -- <<none>>"
|
|
+ echo "${p1}" >> $TEMPFCFILE
|
|
+ logit "skipping the directory ${p} from relabelling"
|
|
+ done
|
|
+FC=$TEMPFCFILE
|
|
+fi
|
|
+${SETFILES} -q ${SYSLOGFLAG} ${FORCEFLAG} $* ${FC} ${FILESYSTEMS} 2>&1 | cat >> $LOGFILE
|
|
+rm -rf /tmp/gconfd-* /tmp/pulse-* /tmp/orbit-* $TEMPFCFILE
|
|
+find /tmp \( -context "*:file_t*" -o -context "*:unlabeled_t*" \) \( -type s -o -type p \) -delete
|
|
find /tmp \( -context "*:file_t*" -o -context "*:unlabeled_t*" \) -exec chcon -t tmp_t {} \;
|
|
find /var/tmp \( -context "*:file_t*" -o -context "*:unlabeled_t*" \) -exec chcon -t tmp_t {} \;
|
|
+find /var/run \( -context "*:file_t*" -o -context "*:unlabeled_t*" \) -exec chcon -t var_run_t {} \;
|
|
+[ -e /var/lib/debug ] && find /var/lib/debug \( -context "*:file_t*" -o -context "*:unlabeled_t*" \) -exec chcon -t lib_t {} \;
|
|
exit $?
|
|
}
|
|
|
|
fullrelabel() {
|
|
logit "Cleaning out /tmp"
|
|
- find /tmp/ -mindepth 1 -print0 | xargs -0 /bin/rm -f
|
|
- LogReadOnly
|
|
+ find /tmp/ -mindepth 1 -delete
|
|
restore
|
|
}
|
|
|
|
diff --git a/policycoreutils/scripts/fixfiles.8 b/policycoreutils/scripts/fixfiles.8
|
|
index dfe8aa9..0b4cbaa 100644
|
|
--- a/policycoreutils/scripts/fixfiles.8
|
|
+++ b/policycoreutils/scripts/fixfiles.8
|
|
@@ -29,6 +29,8 @@ new policy, or just check whether the file contexts are all
|
|
as you expect. By default it will relabel all mounted ext2, ext3, xfs and
|
|
jfs file systems as long as they do not have a security context mount
|
|
option. You can use the -R flag to use rpmpackages as an alternative.
|
|
+The file /etc/selinux/fixfiles_exclude_dirs can contain a list of directories
|
|
+excluded from relabelling.
|
|
.P
|
|
.B fixfiles onboot
|
|
will setup the machine to relabel on the next reboot.
|
|
diff --git a/policycoreutils/scripts/genhomedircon.8 b/policycoreutils/scripts/genhomedircon.8
|
|
new file mode 100644
|
|
index 0000000..6331660
|
|
--- /dev/null
|
|
+++ b/policycoreutils/scripts/genhomedircon.8
|
|
@@ -0,0 +1,37 @@
|
|
+.\" Hey, Emacs! This is an -*- nroff -*- source file.
|
|
+.\" Copyright (c) 2010 Dan Walsh <dwalsh@redhat.com>
|
|
+.\"
|
|
+.\" This is free documentation; 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.
|
|
+.\"
|
|
+.\" The GNU General Public License's references to "object code"
|
|
+.\" and "executables" are to be interpreted as the output of any
|
|
+.\" document formatting or typesetting system, including
|
|
+.\" intermediate and printed output.
|
|
+.\"
|
|
+.\" This manual 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 manual; if not, write to the Free
|
|
+.\" Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
|
|
+.\" USA.
|
|
+.\"
|
|
+.\"
|
|
+.TH GENHOMEDIRCON "8" "May 2010" "Security Enhanced Linux" "SELinux"
|
|
+.SH NAME
|
|
+genhomedircon \- generate SELinux file context configuration entries for user home directories
|
|
+.SH SYNOPSIS
|
|
+.B genhomedircon
|
|
+is a script that executes semodule to rebuild policy and create the
|
|
+labels for HOMEDIRS based on home directories returned by the getpw calls.
|
|
+
|
|
+This functionality is enabled via the usepasswd flag in /etc/selinux/semanage.conf.
|
|
+
|
|
+.SH AUTHOR
|
|
+This manual page was written by
|
|
+.I Dan Walsh <dwalsh@redhat.com>
|
|
diff --git a/policycoreutils/semanage/default_encoding/Makefile b/policycoreutils/semanage/default_encoding/Makefile
|
|
new file mode 100644
|
|
index 0000000..176b11f
|
|
--- /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..c3cdd4e
|
|
--- /dev/null
|
|
+++ b/policycoreutils/semanage/default_encoding/default_encoding.c
|
|
@@ -0,0 +1,59 @@
|
|
+/*
|
|
+ * Authors:
|
|
+ * John Dennis <jdennis@redhat.com>
|
|
+ *
|
|
+ * 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 <Python.h>
|
|
+
|
|
+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 <jdennis@redhat.com>
|
|
+#
|
|
+# 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 ffaca5b..bc989bf 100644
|
|
--- a/policycoreutils/semanage/semanage
|
|
+++ b/policycoreutils/semanage/semanage
|
|
@@ -1,4 +1,4 @@
|
|
-#! /usr/bin/python -E
|
|
+#! /usr/bin/python -Es
|
|
# Copyright (C) 2005, 2006, 2007 Red Hat
|
|
# see file 'COPYING' for use and warranty information
|
|
#
|
|
@@ -20,6 +20,7 @@
|
|
# 02111-1307 USA
|
|
#
|
|
#
|
|
+import policycoreutils.default_encoding_utf8
|
|
import sys, getopt, re
|
|
import seobject
|
|
import selinux
|
|
@@ -32,27 +33,35 @@ gettext.textdomain(PROGNAME)
|
|
try:
|
|
gettext.install(PROGNAME,
|
|
localedir="/usr/share/locale",
|
|
- unicode=False,
|
|
+ unicode=True,
|
|
codeset = 'utf-8')
|
|
except IOError:
|
|
import __builtin__
|
|
__builtin__.__dict__['_'] = unicode
|
|
|
|
if __name__ == '__main__':
|
|
-
|
|
+ action = False
|
|
+ manageditems=[ "boolean", "login", "user", "port", "interface", "node", "fcontext"]
|
|
+ def set_action(option):
|
|
+ global action
|
|
+ if action:
|
|
+ raise ValueError(_("%s bad option") % option)
|
|
+ action = True
|
|
+
|
|
def usage(message = ""):
|
|
text = _("""
|
|
semanage [ -S store ] -i [ input_file | - ]
|
|
-
|
|
-semanage {boolean|login|user|port|interface|node|fcontext} -{l|D} [-n]
|
|
-semanage login -{a|d|m} [-sr] login_name | %groupname
|
|
-semanage user -{a|d|m} [-LrRP] selinux_name
|
|
-semanage port -{a|d|m} [-tr] [ -p proto ] port | port_range
|
|
-semanage interface -{a|d|m} [-tr] interface_spec
|
|
-semanage node -{a|d|m} [-tr] [ -p protocol ] [-M netmask] addr
|
|
-semanage fcontext -{a|d|m} [-frst] file_spec
|
|
+semanage [ -S store ] -o [ output_file | - ]
|
|
+
|
|
+semanage login -{a|d|m|l|D|E} [-nrs] login_name | %groupname
|
|
+semanage user -{a|d|m|l|D|E} [-LnrRP] selinux_name
|
|
+semanage port -{a|d|m|l|D|E} [-nrt] [ -p proto ] port | port_range
|
|
+semanage interface -{a|d|m|l|D|E} [-nrt] interface_spec
|
|
+semanage module -{a|d|m} [--enable|--disable] module
|
|
+semanage node -{a|d|m|l|D|E} [-nrt] [ -p protocol ] [-M netmask] addr
|
|
+semanage fcontext -{a|d|m|l|D|E} [-efnrst] file_spec
|
|
semanage boolean -{d|m} [--on|--off|-1|-0] -F boolean | boolean_file
|
|
-semanage permissive -{d|a} type
|
|
+semanage permissive -{d|a|l} [-n] type
|
|
semanage dontaudit [ on | off ]
|
|
|
|
Primary Options:
|
|
@@ -61,7 +70,9 @@ Primary Options:
|
|
-d, --delete Delete a OBJECT record NAME
|
|
-m, --modify Modify a OBJECT record NAME
|
|
-i, --input Input multiple semange commands in a transaction
|
|
+ -o, --output Output current customizations as semange commands
|
|
-l, --list List the OBJECTS
|
|
+ -E, --extract extract customizable commands
|
|
-C, --locallist List OBJECTS local customizations
|
|
-D, --deleteall Remove all OBJECTS local customizations
|
|
|
|
@@ -84,12 +95,15 @@ Object-specific Options (see above):
|
|
-F, --file Treat target as an input file for command, change multiple settings
|
|
-p, --proto Port protocol (tcp or udp) or internet protocol version of node (ipv4 or ipv6)
|
|
-M, --mask Netmask
|
|
+ -e, --equal Substitue source path for dest path when labeling
|
|
-P, --prefix Prefix for home directory labeling
|
|
-L, --level Default SELinux Level (MLS/MCS Systems only)
|
|
-R, --roles SELinux Roles (ex: "sysadm_r staff_r")
|
|
-s, --seuser SELinux User Name
|
|
-t, --type SELinux Type for the object
|
|
-r, --range MLS/MCS Security Range (MLS/MCS Systems only)
|
|
+ --enable Enable a module
|
|
+ --disable Disable a module
|
|
""")
|
|
raise ValueError("%s\n%s" % (text, message))
|
|
|
|
@@ -101,22 +115,25 @@ Object-specific Options (see above):
|
|
|
|
def get_options():
|
|
valid_option={}
|
|
- valid_everyone=[ '-a', '--add', '-d', '--delete', '-m', '--modify', '-l', '--list', '-h', '--help', '-n', '--noheading', '-C', '--locallist', '-D', '--deleteall', '-S', '--store' ]
|
|
+ valid_everyone=[ '-a', '--add', '-d', '--delete', '-m', '--modify', '-l', '--list', '-h', '--help', '-n', '--noheading', '-S', '--store' ]
|
|
+ valid_local=[ '-E', '--extract', '-C', '--locallist', '-D', '--deleteall']
|
|
valid_option["login"] = []
|
|
- valid_option["login"] += valid_everyone + [ '-s', '--seuser', '-r', '--range']
|
|
+ valid_option["login"] += valid_everyone + valid_local + [ '-s', '--seuser', '-r', '--range']
|
|
valid_option["user"] = []
|
|
- valid_option["user"] += valid_everyone + [ '-L', '--level', '-r', '--range', '-R', '--roles', '-P', '--prefix' ]
|
|
+ valid_option["user"] += valid_everyone + valid_local + [ '-L', '--level', '-r', '--range', '-R', '--roles', '-P', '--prefix' ]
|
|
valid_option["port"] = []
|
|
- valid_option["port"] += valid_everyone + [ '-t', '--type', '-r', '--range', '-p', '--proto' ]
|
|
+ valid_option["port"] += valid_everyone + valid_local + [ '-t', '--type', '-r', '--range', '-p', '--proto' ]
|
|
valid_option["interface"] = []
|
|
- valid_option["interface"] += valid_everyone + [ '-t', '--type', '-r', '--range']
|
|
+ valid_option["interface"] += valid_everyone + valid_local + [ '-t', '--type', '-r', '--range']
|
|
valid_option["node"] = []
|
|
- valid_option["node"] += valid_everyone + [ '-M', '--mask', '-t', '--type', '-r', '--range', '-p', '--protocol']
|
|
+ valid_option["node"] += valid_everyone + valid_local + [ '-M', '--mask', '-t', '--type', '-r', '--range', '-p', '--protocol']
|
|
+ valid_option["module"] = []
|
|
+ valid_option["module"] += valid_everyone + [ '--enable', '--disable']
|
|
valid_option["fcontext"] = []
|
|
- valid_option["fcontext"] += valid_everyone + [ '-f', '--ftype', '-s', '--seuser', '-t', '--type', '-r', '--range']
|
|
+ valid_option["fcontext"] += valid_everyone + valid_local + [ '-e', '--equal', '-f', '--ftype', '-s', '--seuser', '-t', '--type', '-r', '--range']
|
|
valid_option["dontaudit"] = [ '-S', '--store' ]
|
|
valid_option["boolean"] = []
|
|
- valid_option["boolean"] += valid_everyone + [ '--on', "--off", "-1", "-0", "-F", "--file"]
|
|
+ valid_option["boolean"] += valid_everyone + valid_local + [ '--on', "--off", "-1", "-0", "-F", "--file"]
|
|
valid_option["permissive"] = []
|
|
valid_option["permissive"] += [ '-a', '--add', '-d', '--delete', '-l', '--list', '-h', '--help', '-n', '--noheading', '-D', '--deleteall' ]
|
|
return valid_option
|
|
@@ -168,6 +185,8 @@ Object-specific Options (see above):
|
|
return ret
|
|
|
|
def process_args(argv):
|
|
+ global action
|
|
+ action = False
|
|
serange = ""
|
|
port = ""
|
|
proto = ""
|
|
@@ -184,11 +203,17 @@ Object-specific Options (see above):
|
|
modify = False
|
|
delete = False
|
|
deleteall = False
|
|
+ enable = False
|
|
+ extract = False
|
|
+ disable = False
|
|
list = False
|
|
locallist = False
|
|
use_file = False
|
|
store = ""
|
|
+ equal=""
|
|
|
|
+ if len(argv) == 0:
|
|
+ return
|
|
object = argv[0]
|
|
option_dict=get_options()
|
|
if object not in option_dict.keys():
|
|
@@ -196,58 +221,84 @@ Object-specific Options (see above):
|
|
|
|
args = argv[1:]
|
|
|
|
- gopts, cmds = getopt.getopt(args,
|
|
- '01adf:i:lhmnp:s:FCDR:L:r:t:P:S:M:',
|
|
- ['add',
|
|
- 'delete',
|
|
- 'deleteall',
|
|
- 'ftype=',
|
|
- 'file',
|
|
- 'help',
|
|
- 'input=',
|
|
- 'list',
|
|
- 'modify',
|
|
- 'noheading',
|
|
- 'localist',
|
|
- 'off',
|
|
- 'on',
|
|
- 'proto=',
|
|
- 'seuser=',
|
|
- 'store=',
|
|
- 'range=',
|
|
- 'locallist=',
|
|
- 'level=',
|
|
- 'roles=',
|
|
- 'type=',
|
|
- 'prefix=',
|
|
- 'mask='
|
|
- ])
|
|
+ try:
|
|
+ gopts, cmds = getopt.getopt(args,
|
|
+ '01adEe:f:i:lhmnp:s:FCDR:L:r:t:P:S:M:',
|
|
+ ['add',
|
|
+ 'delete',
|
|
+ 'deleteall',
|
|
+ 'equal=',
|
|
+ 'enable',
|
|
+ 'extract',
|
|
+ 'disable',
|
|
+ 'ftype=',
|
|
+ 'file',
|
|
+ 'help',
|
|
+ 'input=',
|
|
+ 'list',
|
|
+ 'modify',
|
|
+ 'noheading',
|
|
+ 'localist',
|
|
+ 'off',
|
|
+ 'on',
|
|
+ 'proto=',
|
|
+ 'seuser=',
|
|
+ 'store=',
|
|
+ 'range=',
|
|
+ 'locallist=',
|
|
+ 'level=',
|
|
+ 'roles=',
|
|
+ 'type=',
|
|
+ 'prefix=',
|
|
+ 'mask='
|
|
+ ])
|
|
+ except getopt.error, error:
|
|
+ usage(_("Options Error %s ") % error.msg)
|
|
+
|
|
for o, a in gopts:
|
|
if o not in option_dict[object]:
|
|
sys.stderr.write(_("%s not valid for %s objects\n") % ( o, object) );
|
|
+
|
|
+ return
|
|
|
|
for o,a in gopts:
|
|
if o == "-a" or o == "--add":
|
|
- if modify or delete:
|
|
- raise ValueError(_("%s bad option") % o)
|
|
+ set_action(o)
|
|
add = True
|
|
|
|
if o == "-d" or o == "--delete":
|
|
- if modify or add:
|
|
- raise ValueError(_("%s bad option") % o)
|
|
+ set_action(o)
|
|
delete = True
|
|
+
|
|
if o == "-D" or o == "--deleteall":
|
|
- if modify:
|
|
- raise ValueError(_("%s bad option") % o)
|
|
+ set_action(o)
|
|
deleteall = True
|
|
+
|
|
+ if o == "-E" or o == "--extract":
|
|
+ set_action(o)
|
|
+ extract = True
|
|
if o == "-f" or o == "--ftype":
|
|
ftype=a
|
|
|
|
+ if o == "-e" or o == "--equal":
|
|
+ equal = a
|
|
+
|
|
+ if o == "--enable":
|
|
+ if disable:
|
|
+ raise ValueError(_("You can't disable and enable at the same time"))
|
|
+
|
|
+ enable = True
|
|
+
|
|
+ if o == "--disable":
|
|
+ if enable:
|
|
+ raise ValueError(_("You can't disable and enable at the same time"))
|
|
+ disable = True
|
|
+
|
|
if o == "-F" or o == "--file":
|
|
use_file = True
|
|
|
|
if o == "-h" or o == "--help":
|
|
- raise ValueError(_("%s bad option") % o)
|
|
+ raise usage()
|
|
|
|
if o == "-n" or o == "--noheading":
|
|
heading = False
|
|
@@ -256,8 +307,7 @@ Object-specific Options (see above):
|
|
locallist = True
|
|
|
|
if o == "-m"or o == "--modify":
|
|
- if delete or add:
|
|
- raise ValueError(_("%s bad option") % o)
|
|
+ set_action(o)
|
|
modify = True
|
|
|
|
if o == "-S" or o == '--store':
|
|
@@ -292,8 +342,10 @@ 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)
|
|
@@ -315,6 +367,11 @@ Object-specific Options (see above):
|
|
|
|
if object == "boolean":
|
|
OBJECT = seobject.booleanRecords(store)
|
|
+ if use_file:
|
|
+ modify=True
|
|
+
|
|
+ if object == "module":
|
|
+ OBJECT = seobject.moduleRecords(store)
|
|
|
|
if object == "permissive":
|
|
OBJECT = seobject.permissiveRecords(store)
|
|
@@ -330,65 +387,97 @@ Object-specific Options (see above):
|
|
OBJECT.deleteall()
|
|
return
|
|
|
|
+ if extract:
|
|
+ for i in OBJECT.customized():
|
|
+ print "%s %s" % (object, str(i))
|
|
+ return
|
|
+
|
|
if len(cmds) != 1:
|
|
- raise ValueError(_("%s bad option") % o)
|
|
+ raise ValueError(_("bad option"))
|
|
|
|
target = cmds[0]
|
|
|
|
-
|
|
if object == "dontaudit":
|
|
- OBJECT = seobject.dontauditClass(store)
|
|
- OBJECT.toggle(target)
|
|
- return
|
|
+ OBJECT = seobject.dontauditClass(store)
|
|
+ OBJECT.toggle(target)
|
|
+ return
|
|
|
|
if add:
|
|
if object == "login":
|
|
OBJECT.add(target, seuser, serange)
|
|
+ return
|
|
|
|
if object == "user":
|
|
OBJECT.add(target, roles.split(), selevel, serange, prefix)
|
|
+ return
|
|
|
|
if object == "port":
|
|
OBJECT.add(target, proto, serange, setype)
|
|
+ return
|
|
|
|
if object == "interface":
|
|
OBJECT.add(target, serange, setype)
|
|
+ return
|
|
+
|
|
+ if object == "module":
|
|
+ OBJECT.add(target)
|
|
+ return
|
|
|
|
if object == "node":
|
|
OBJECT.add(target, mask, proto, serange, setype)
|
|
+ return
|
|
|
|
if object == "fcontext":
|
|
- OBJECT.add(target, setype, ftype, serange, seuser)
|
|
+ if equal == "":
|
|
+ OBJECT.add(target, setype, ftype, serange, seuser)
|
|
+ else:
|
|
+ OBJECT.add_equal(target, equal)
|
|
+ return
|
|
if object == "permissive":
|
|
OBJECT.add(target)
|
|
+ return
|
|
|
|
- return
|
|
-
|
|
if modify:
|
|
if object == "boolean":
|
|
OBJECT.modify(target, value, use_file)
|
|
+ return
|
|
|
|
if object == "login":
|
|
OBJECT.modify(target, seuser, serange)
|
|
+ return
|
|
|
|
if object == "user":
|
|
rlist = roles.split()
|
|
OBJECT.modify(target, rlist, selevel, serange, prefix)
|
|
+ return
|
|
+
|
|
+ if object == "module":
|
|
+ if enable:
|
|
+ OBJECT.enable(target)
|
|
+ elif disable:
|
|
+ OBJECT.disable(target)
|
|
+ else:
|
|
+ OBJECT.modify(target)
|
|
+ return
|
|
|
|
if object == "port":
|
|
OBJECT.modify(target, proto, serange, setype)
|
|
+ return
|
|
|
|
if object == "interface":
|
|
OBJECT.modify(target, serange, setype)
|
|
+ return
|
|
|
|
if object == "node":
|
|
OBJECT.modify(target, mask, proto, serange, setype)
|
|
+ return
|
|
|
|
if object == "fcontext":
|
|
- OBJECT.modify(target, setype, ftype, serange, seuser)
|
|
-
|
|
- return
|
|
-
|
|
+ if equal == "":
|
|
+ OBJECT.modify(target, setype, ftype, serange, seuser)
|
|
+ else:
|
|
+ OBJECT.modify_equal(target, equal)
|
|
+ return
|
|
if delete:
|
|
if object == "port":
|
|
OBJECT.delete(target, proto)
|
|
@@ -401,50 +490,65 @@ Object-specific Options (see above):
|
|
|
|
else:
|
|
OBJECT.delete(target)
|
|
-
|
|
return
|
|
-
|
|
- raise ValueError(_("Invalid command") % " ".join(argv))
|
|
+ raise ValueError(_("Invalid command: semanage %s") % " ".join(argv))
|
|
|
|
#
|
|
#
|
|
#
|
|
try:
|
|
+ output = None
|
|
input = None
|
|
store = ""
|
|
|
|
if len(sys.argv) < 3:
|
|
usage(_("Requires 2 or more arguments"))
|
|
|
|
- gopts, cmds = getopt.getopt(sys.argv[1:],
|
|
- '01adf:i:lhmnp:s:FCDR:L:r:t:T:P:S:',
|
|
- ['add',
|
|
- 'delete',
|
|
- 'deleteall',
|
|
- 'ftype=',
|
|
- 'file',
|
|
- 'help',
|
|
- 'input=',
|
|
- 'list',
|
|
- 'modify',
|
|
- 'noheading',
|
|
- 'localist',
|
|
- 'off',
|
|
- 'on',
|
|
- '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
|
|
if o == "-i" or o == '--input':
|
|
input = a
|
|
+ if o == "-o" or o == '--output':
|
|
+ output = a
|
|
+
|
|
+ if output != None:
|
|
+ if output != "-":
|
|
+ sys.stdout = open(output, 'w')
|
|
+ for i in manageditems:
|
|
+ print "%s -D" % i
|
|
+ process_args([i, "-E"])
|
|
+ sys.exit(0)
|
|
|
|
if input != None:
|
|
if input == "-":
|
|
@@ -459,11 +563,11 @@ 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:
|
|
errorExit(_("Invalid value %s") % error.args[0])
|
|
except IOError, error:
|
|
errorExit(error.args[1])
|
|
+ except OSError, error:
|
|
+ errorExit(error.args[1])
|
|
diff --git a/policycoreutils/semanage/semanage.8 b/policycoreutils/semanage/semanage.8
|
|
index 70d1a20..fb6a79b 100644
|
|
--- a/policycoreutils/semanage/semanage.8
|
|
+++ b/policycoreutils/semanage/semanage.8
|
|
@@ -1,29 +1,69 @@
|
|
-.TH "semanage" "8" "2005111103" "" ""
|
|
+.TH "semanage" "8" "20100223" "" ""
|
|
.SH "NAME"
|
|
semanage \- SELinux Policy Management tool
|
|
|
|
.SH "SYNOPSIS"
|
|
-.B semanage {boolean|login|user|port|interface|node|fcontext} \-{l|D} [\-n] [\-S store]
|
|
+Output local customizations
|
|
.br
|
|
-.B semanage boolean \-{d|m} [\-\-on|\-\-off|\-1|\-0] -F boolean | boolean_file
|
|
+.B semanage [ -S store ] -o [ output_file | - ]
|
|
+
|
|
+Input local customizations
|
|
+.br
|
|
+.B semanage [ -S store ] -i [ input_file | - ]
|
|
+
|
|
+Manage booleans. Booleans allow the administrator to modify the confinement of
|
|
+processes based on his configuration.
|
|
+.br
|
|
+.B semanage boolean [\-S store] \-{d|m|l|n|D} \-[\-on|\-off|\1|0] -F boolean | boolean_file
|
|
+
|
|
+Manage SELinux confined users (Roles and levels for an SELinux user)
|
|
+.br
|
|
+.B semanage user [\-S store] \-{a|d|m|l|n|D} [\-LrRP] selinux_name
|
|
+
|
|
+Manage login mappings between linux users and SELinux confined users.
|
|
+.br
|
|
+.B semanage login [\-S store] \-{a|d|m|l|n|D} [\-sr] login_name | %groupname
|
|
+
|
|
+Manage policy modules.
|
|
+.br
|
|
+.B semanage module [\-S store] \-{a|d|l} [-m [--enable | --disable] ] module_name
|
|
+
|
|
+Manage network port type definitions
|
|
.br
|
|
-.B semanage login \-{a|d|m} [\-sr] login_name | %groupname
|
|
+.B semanage port [\-S store] \-{a|d|m|l|n|D} [\-tr] [\-p proto] port | port_range
|
|
.br
|
|
-.B semanage user \-{a|d|m} [\-LrRP] selinux_name
|
|
+
|
|
+Manage network interface type definitions
|
|
+.br
|
|
+.B semanage interface [\-S store] \-{a|d|m|l|n|D} [\-tr] interface_spec
|
|
+
|
|
+Manage network node type definitions
|
|
.br
|
|
-.B semanage port \-{a|d|m} [\-tr] [\-p proto] port | port_range
|
|
+.B semanage node [\-S store] -{a|d|m|l|n|D} [-tr] [ -p protocol ] [-M netmask] address
|
|
.br
|
|
-.B semanage interface \-{a|d|m} [\-tr] interface_spec
|
|
+
|
|
+Manage file context mapping definitions
|
|
+.br
|
|
+.B semanage fcontext [\-S store] \-{a|d|m|l|n|D} [\-frst] file_spec
|
|
.br
|
|
-.B semanage node -{a|d|m} [-tr] [ -p protocol ] [-M netmask] address
|
|
+.B semanage fcontext [\-S store] \-{a|d|m|l|n|D} \-e replacement target
|
|
.br
|
|
-.B semanage fcontext \-{a|d|m} [\-frst] file_spec
|
|
+
|
|
+Manage processes type enforcement mode
|
|
.br
|
|
-.B semanage permissive \-{a|d} type
|
|
+.B semanage permissive [\-S store] \-{a|d|l|n|D} type
|
|
.br
|
|
-.B semanage dontaudit [ on | off ]
|
|
+
|
|
+Disable/Enable dontaudit rules in policy
|
|
+.br
|
|
+.B semanage dontaudit [\-S store] [ on | off ]
|
|
.P
|
|
|
|
+Execute multiple commands within a single transaction.
|
|
+.br
|
|
+.B semanage [\-S store] \-i command-file
|
|
+.br
|
|
+
|
|
.SH "DESCRIPTION"
|
|
semanage is used to configure certain elements of
|
|
SELinux policy without requiring modification to or recompilation
|
|
@@ -52,6 +92,22 @@ Delete a OBJECT record NAME
|
|
.I \-D, \-\-deleteall
|
|
Remove all OBJECTS local customizations
|
|
.TP
|
|
+.I \-\-disable
|
|
+Disable a policy module, requires -m option
|
|
+
|
|
+Currently modules only.
|
|
+.TP
|
|
+.I \-\-enable
|
|
+Enable a disabled policy module, requires -m option
|
|
+
|
|
+Currently modules only.
|
|
+.TP
|
|
+.I \-e, \-\-equal
|
|
+Substitute target path with sourcepath when generating default label. This is used with
|
|
+fcontext. Requires source and target path arguments. The context
|
|
+labeling for the target subtree is made equivalent to that
|
|
+defined for the source.
|
|
+.TP
|
|
.I \-f, \-\-ftype
|
|
File Type. This is used with fcontext.
|
|
Requires a file type as shown in the mode field by ls, e.g. use -d to match only directories or -- to match only regular files.
|
|
@@ -60,6 +116,7 @@ Requires a file type as shown in the mode field by ls, e.g. use -d to match only
|
|
Set multiple records from the input file. When used with the \-l \-\-list, it will output the current settings to stdout in the proper format.
|
|
|
|
Currently booleans only.
|
|
+
|
|
.TP
|
|
.I \-h, \-\-help
|
|
display this message
|
|
@@ -76,6 +133,9 @@ Default SELinux Level for SELinux use, s0 Default. (MLS/MCS Systems only)
|
|
.I \-m, \-\-modify
|
|
Modify a OBJECT record NAME
|
|
.TP
|
|
+.I \-M, \-\-mask
|
|
+Network Mask
|
|
+.TP
|
|
.I \-n, \-\-noheading
|
|
Do not print heading when listing OBJECTS.
|
|
.TP
|
|
@@ -99,26 +159,67 @@ Select and alternate SELinux store to manage
|
|
.TP
|
|
.I \-t, \-\-type
|
|
SELinux Type for the object
|
|
+.TP
|
|
+.I \-i, \-\-input
|
|
+Take a set of commands from a specified file and load them in a single
|
|
+transaction.
|
|
|
|
.SH EXAMPLE
|
|
.nf
|
|
-# View SELinux user mappings
|
|
-$ semanage user -l
|
|
-# Allow joe to login as staff_u
|
|
-$ semanage login -a -s staff_u joe
|
|
-# Allow the group clerks to login as user_u
|
|
-$ semanage login -a -s user_u %clerks
|
|
-# Add file-context for everything under /web (used by restorecon)
|
|
-$ semanage fcontext -a -t httpd_sys_content_t "/web(/.*)?"
|
|
-# Allow Apache to listen on port 81
|
|
-$ semanage port -a -t http_port_t -p tcp 81
|
|
-# Change apache to a permissive domain
|
|
-$ semanage permissive -a httpd_t
|
|
-# Turn off dontaudit rules
|
|
-$ semanage dontaudit off
|
|
+.B SELinux user
|
|
+List SELinux users
|
|
+# semanage user -l
|
|
+
|
|
+.B SELinux login
|
|
+Change joe to login as staff_u
|
|
+# semanage login -a -s staff_u joe
|
|
+Change the group clerks to login as user_u
|
|
+# semanage login -a -s user_u %clerks
|
|
+
|
|
+.B File contexts
|
|
+.i remember to run restorecon after you set the file context
|
|
+Add file-context for everything under /web
|
|
+# semanage fcontext -a -t httpd_sys_content_t "/web(/.*)?"
|
|
+# restorecon -R -v /web
|
|
+
|
|
+Substitute /home1 with /home when setting file context
|
|
+# semanage fcontext -a -e /home /home1
|
|
+# restorecon -R -v /home1
|
|
+
|
|
+For home directories under top level directory, for example /disk6/home,
|
|
+execute the following commands.
|
|
+# semanage fcontext -a -t home_root_t "/disk6"
|
|
+# semanage fcontext -a -e /home /disk6/home
|
|
+# restorecon -R -v /disk6
|
|
+
|
|
+.B Port contexts
|
|
+Allow Apache to listen on tcp port 81
|
|
+# semanage port -a -t http_port_t -p tcp 81
|
|
+
|
|
+.B Change apache to a permissive domain
|
|
+# semanage permissive -a httpd_t
|
|
+
|
|
+.B Turn off dontaudit rules
|
|
+# semanage dontaudit off
|
|
+
|
|
+.B Managing multiple machines
|
|
+Multiple machines that need the same customizations.
|
|
+Extract customizations off first machine, copy them
|
|
+to second and import them.
|
|
+
|
|
+# semanage -o /tmp/local.selinux
|
|
+# scp /tmp/local.selinux secondmachine:/tmp
|
|
+# ssh secondmachine
|
|
+# semanage -i /tmp/local.selinux
|
|
+
|
|
+If these customizations include file context, you need to apply the
|
|
+context using restorecon.
|
|
+
|
|
.fi
|
|
|
|
.SH "AUTHOR"
|
|
-This man page was written by Daniel Walsh <dwalsh@redhat.com> and
|
|
-Russell Coker <rcoker@redhat.com>.
|
|
+This man page was written by Daniel Walsh <dwalsh@redhat.com>
|
|
+.br
|
|
+and Russell Coker <rcoker@redhat.com>.
|
|
+.br
|
|
Examples by Thomas Bleher <ThomasBleher@gmx.de>.
|
|
diff --git a/policycoreutils/semanage/seobject.py b/policycoreutils/semanage/seobject.py
|
|
index b7d257b..4462c9e 100644
|
|
--- a/policycoreutils/semanage/seobject.py
|
|
+++ b/policycoreutils/semanage/seobject.py
|
|
@@ -25,51 +25,17 @@ import pwd, grp, string, selinux, tempfile, os, re, sys, stat
|
|
from semanage import *;
|
|
PROGNAME = "policycoreutils"
|
|
import sepolgen.module as module
|
|
+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 syslog
|
|
|
|
-handle = None
|
|
-
|
|
-def get_handle(store):
|
|
- global handle
|
|
- global is_mls_enabled
|
|
-
|
|
- handle = semanage_handle_create()
|
|
- if not handle:
|
|
- raise ValueError(_("Could not create semanage handle"))
|
|
-
|
|
- if store != "":
|
|
- semanage_select_store(handle, store, SEMANAGE_CON_DIRECT);
|
|
-
|
|
- if not semanage_is_managed(handle):
|
|
- semanage_handle_destroy(handle)
|
|
- raise ValueError(_("SELinux policy is not managed or store cannot be accessed."))
|
|
-
|
|
- rc = semanage_access_check(handle)
|
|
- if rc < SEMANAGE_CAN_READ:
|
|
- semanage_handle_destroy(handle)
|
|
- raise ValueError(_("Cannot read policy store."))
|
|
-
|
|
- rc = semanage_connect(handle)
|
|
- if rc < 0:
|
|
- semanage_handle_destroy(handle)
|
|
- raise ValueError(_("Could not establish semanage connection"))
|
|
-
|
|
- is_mls_enabled = semanage_mls_enabled(handle)
|
|
- if is_mls_enabled < 0:
|
|
- semanage_handle_destroy(handle)
|
|
- raise ValueError(_("Could not test MLS enabled status"))
|
|
+import gettext
|
|
+translation=gettext.translation(PROGNAME, localedir = "/usr/share/locale", fallback=True)
|
|
+_=translation.ugettext
|
|
|
|
- return handle
|
|
+import syslog
|
|
|
|
file_types = {}
|
|
file_types[""] = SEMANAGE_FCONTEXT_ALL;
|
|
@@ -194,45 +160,148 @@ def untranslate(trans, prepend = 1):
|
|
return trans
|
|
else:
|
|
return raw
|
|
-
|
|
+
|
|
class semanageRecords:
|
|
- def __init__(self, store):
|
|
+ transaction = False
|
|
+ handle = None
|
|
+ store = None
|
|
+
|
|
+ def __init__(self, store):
|
|
global handle
|
|
|
|
- if handle != None:
|
|
- self.sh = handle
|
|
- else:
|
|
- self.sh = get_handle(store)
|
|
- self.transaction = False
|
|
+ self.sh = self.get_handle(store)
|
|
+
|
|
+ def get_handle(self, store):
|
|
+ global is_mls_enabled
|
|
+
|
|
+ if semanageRecords.handle:
|
|
+ return semanageRecords.handle
|
|
+
|
|
+ handle = semanage_handle_create()
|
|
+ if not handle:
|
|
+ raise ValueError(_("Could not create semanage handle"))
|
|
+
|
|
+ if not semanageRecords.transaction and store != "":
|
|
+ semanage_select_store(handle, store, SEMANAGE_CON_DIRECT);
|
|
+ semanageRecords.store = store
|
|
+
|
|
+ if not semanage_is_managed(handle):
|
|
+ semanage_handle_destroy(handle)
|
|
+ raise ValueError(_("SELinux policy is not managed or store cannot be accessed."))
|
|
+
|
|
+ rc = semanage_access_check(handle)
|
|
+ if rc < SEMANAGE_CAN_READ:
|
|
+ semanage_handle_destroy(handle)
|
|
+ raise ValueError(_("Cannot read policy store."))
|
|
+
|
|
+ rc = semanage_connect(handle)
|
|
+ if rc < 0:
|
|
+ semanage_handle_destroy(handle)
|
|
+ raise ValueError(_("Could not establish semanage connection"))
|
|
+
|
|
+ is_mls_enabled = semanage_mls_enabled(handle)
|
|
+ if is_mls_enabled < 0:
|
|
+ semanage_handle_destroy(handle)
|
|
+ raise ValueError(_("Could not test MLS enabled status"))
|
|
+
|
|
+ semanageRecords.handle = handle
|
|
+ return semanageRecords.handle
|
|
|
|
def deleteall(self):
|
|
raise ValueError(_("Not yet implemented"))
|
|
|
|
def start(self):
|
|
- if self.transaction:
|
|
+ if semanageRecords.transaction:
|
|
raise ValueError(_("Semanage transaction already in progress"))
|
|
self.begin()
|
|
- self.transaction = True
|
|
-
|
|
+ semanageRecords.transaction = True
|
|
def begin(self):
|
|
- if self.transaction:
|
|
+ if semanageRecords.transaction:
|
|
return
|
|
rc = semanage_begin_transaction(self.sh)
|
|
if rc < 0:
|
|
raise ValueError(_("Could not start semanage transaction"))
|
|
+ def customized(self):
|
|
+ raise ValueError(_("Not yet implemented"))
|
|
+
|
|
def commit(self):
|
|
- if self.transaction:
|
|
+ if semanageRecords.transaction:
|
|
return
|
|
rc = semanage_commit(self.sh)
|
|
if rc < 0:
|
|
raise ValueError(_("Could not commit semanage transaction"))
|
|
|
|
def finish(self):
|
|
- if not self.transaction:
|
|
+ if not semanageRecords.transaction:
|
|
raise ValueError(_("Semanage transaction not in progress"))
|
|
- self.transaction = False
|
|
+ semanageRecords.transaction = False
|
|
self.commit()
|
|
|
|
+class moduleRecords(semanageRecords):
|
|
+ def __init__(self, store):
|
|
+ semanageRecords.__init__(self, store)
|
|
+
|
|
+ def get_all(self):
|
|
+ l = []
|
|
+ (rc, mlist, number) = semanage_module_list(self.sh)
|
|
+ if rc < 0:
|
|
+ raise ValueError(_("Could not list SELinux modules"))
|
|
+
|
|
+ for i in range(number):
|
|
+ mod = semanage_module_list_nth(mlist, i)
|
|
+ l.append((semanage_module_get_name(mod), semanage_module_get_version(mod), semanage_module_get_enabled(mod)))
|
|
+ return l
|
|
+
|
|
+ def list(self, heading = 1, locallist = 0):
|
|
+ if heading:
|
|
+ print "\n%-25s%-10s\n" % (_("Modules Name"), _("Version"))
|
|
+ for t in self.get_all():
|
|
+ if t[2] == 0:
|
|
+ disabled = _("Disabled")
|
|
+ else:
|
|
+ disabled = ""
|
|
+ print "%-25s%-10s%s" % (t[0], t[1], disabled)
|
|
+
|
|
+ def add(self, file):
|
|
+ rc = semanage_module_install_file(self.sh, file);
|
|
+ if rc >= 0:
|
|
+ self.commit()
|
|
+
|
|
+ def disable(self, module):
|
|
+ need_commit = False
|
|
+ for m in module.split():
|
|
+ rc = semanage_module_disable(self.sh, m)
|
|
+ if rc < 0 and rc != -3:
|
|
+ raise ValueError(_("Could not disable module %s (remove failed)") % m)
|
|
+ if rc != -3:
|
|
+ need_commit = True
|
|
+ if need_commit:
|
|
+ self.commit()
|
|
+
|
|
+ def enable(self, module):
|
|
+ need_commit = False
|
|
+ for m in module.split():
|
|
+ rc = semanage_module_enable(self.sh, m)
|
|
+ if rc < 0 and rc != -3:
|
|
+ raise ValueError(_("Could not enable module %s (remove failed)") % m)
|
|
+ if rc != -3:
|
|
+ need_commit = True
|
|
+ if need_commit:
|
|
+ self.commit()
|
|
+
|
|
+ def modify(self, file):
|
|
+ rc = semanage_module_update_file(self.sh, file);
|
|
+ if rc >= 0:
|
|
+ self.commit()
|
|
+
|
|
+ def delete(self, module):
|
|
+ for m in module.split():
|
|
+ rc = semanage_module_remove(self.sh, m)
|
|
+ if rc < 0 and rc != -2:
|
|
+ raise ValueError(_("Could not remove module %s (remove failed)") % m)
|
|
+
|
|
+ self.commit()
|
|
+
|
|
class dontauditClass(semanageRecords):
|
|
def __init__(self, store):
|
|
semanageRecords.__init__(self, store)
|
|
@@ -259,14 +328,23 @@ 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):
|
|
- if heading:
|
|
- print "\n%-25s\n" % (_("Permissive Types"))
|
|
- for t in self.get_all():
|
|
- print t
|
|
+ import setools
|
|
+ all = map(lambda y: y["name"], filter(lambda x: x["permissive"], setools.seinfo(setools.TYPE)))
|
|
|
|
+ if heading:
|
|
+ print "\n%-25s\n" % (_("Builtin Permissive Types"))
|
|
+ customized = self.get_all()
|
|
+ for t in all:
|
|
+ if t not in customized:
|
|
+ print t
|
|
+ if heading:
|
|
+ print "\n%-25s\n" % (_("Customized Permissive Types"))
|
|
+ for t in customized:
|
|
+ print t
|
|
|
|
def add(self, type):
|
|
import glob
|
|
@@ -343,7 +421,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:])
|
|
@@ -475,6 +555,16 @@ class loginRecords(semanageRecords):
|
|
|
|
mylog.log(1, "delete SELinux user mapping", name);
|
|
|
|
+ def deleteall(self):
|
|
+ (rc, ulist) = semanage_seuser_list_local(self.sh)
|
|
+ if rc < 0:
|
|
+ raise ValueError(_("Could not list login mappings"))
|
|
+
|
|
+ self.begin()
|
|
+ for u in ulist:
|
|
+ self.__delete(semanage_seuser_get_name(u))
|
|
+ self.commit()
|
|
+
|
|
def get_all(self, locallist = 0):
|
|
ddict = {}
|
|
if locallist:
|
|
@@ -489,6 +579,15 @@ class loginRecords(semanageRecords):
|
|
ddict[name] = (semanage_seuser_get_sename(u), semanage_seuser_get_mlsrange(u))
|
|
return ddict
|
|
|
|
+ def customized(self):
|
|
+ l = []
|
|
+ ddict = self.get_all(True)
|
|
+ keys = ddict.keys()
|
|
+ keys.sort()
|
|
+ for k in keys:
|
|
+ l.append("-a -s %s -r '%s' %s" % (ddict[k][0], ddict[k][1], k))
|
|
+ return l
|
|
+
|
|
def list(self,heading = 1, locallist = 0):
|
|
ddict = self.get_all(locallist)
|
|
keys = ddict.keys()
|
|
@@ -531,7 +630,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:
|
|
@@ -682,6 +782,16 @@ class seluserRecords(semanageRecords):
|
|
|
|
mylog.log(1,"delete SELinux user record", name)
|
|
|
|
+ def deleteall(self):
|
|
+ (rc, ulist) = semanage_user_list_local(self.sh)
|
|
+ if rc < 0:
|
|
+ raise ValueError(_("Could not list login mappings"))
|
|
+
|
|
+ self.begin()
|
|
+ for u in ulist:
|
|
+ self.__delete(semanage_user_get_name(u))
|
|
+ self.commit()
|
|
+
|
|
def get_all(self, locallist = 0):
|
|
ddict = {}
|
|
if locallist:
|
|
@@ -702,6 +812,15 @@ class seluserRecords(semanageRecords):
|
|
|
|
return ddict
|
|
|
|
+ def customized(self):
|
|
+ l = []
|
|
+ ddict = self.get_all(True)
|
|
+ keys = ddict.keys()
|
|
+ keys.sort()
|
|
+ for k in keys:
|
|
+ l.append("-a -r %s -R '%s' %s" % (ddict[k][2], ddict[k][3], k))
|
|
+ return l
|
|
+
|
|
def list(self, heading = 1, locallist = 0):
|
|
ddict = self.get_all(locallist)
|
|
keys = ddict.keys()
|
|
@@ -740,12 +859,16 @@ class portRecords(semanageRecords):
|
|
low = int(ports[0])
|
|
high = int(ports[1])
|
|
|
|
+ if high > 65536:
|
|
+ raise ValueError(_("Invalid Port"))
|
|
+
|
|
(rc, k) = semanage_port_key_create(self.sh, low, high, proto_d)
|
|
if rc < 0:
|
|
raise ValueError(_("Could not create a key for %s/%s") % (proto, port))
|
|
return ( k, proto_d, low, high )
|
|
|
|
def __add(self, port, proto, serange, type):
|
|
+
|
|
if is_mls_enabled == 1:
|
|
if serange == "":
|
|
serange = "s0"
|
|
@@ -808,6 +931,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"))
|
|
@@ -942,6 +1066,18 @@ class portRecords(semanageRecords):
|
|
ddict[(ctype,proto_str)].append("%d-%d" % (low, high))
|
|
return ddict
|
|
|
|
+ def customized(self):
|
|
+ l = []
|
|
+ ddict = self.get_all(True)
|
|
+ keys = ddict.keys()
|
|
+ keys.sort()
|
|
+ for k in keys:
|
|
+ if k[0] == k[1]:
|
|
+ l.append("-a -t %s -p %s %s" % (ddict[k][0], k[2], k[0]))
|
|
+ else:
|
|
+ l.append("-a -t %s -p %s %s-%s" % (ddict[k][0], k[2], k[0], k[1]))
|
|
+ return l
|
|
+
|
|
def list(self, heading = 1, locallist = 0):
|
|
if heading:
|
|
print "%-30s %-8s %s\n" % (_("SELinux Port Type"), _("Proto"), _("Port Number"))
|
|
@@ -958,21 +1094,36 @@ class portRecords(semanageRecords):
|
|
class nodeRecords(semanageRecords):
|
|
def __init__(self, store = ""):
|
|
semanageRecords.__init__(self,store)
|
|
+ self.protocol = ["ipv4", "ipv6"]
|
|
+
|
|
+ def validate(self, addr, mask, protocol):
|
|
+ newaddr=addr
|
|
+ newmask=mask
|
|
+ newprotocol=""
|
|
|
|
- def __add(self, addr, mask, proto, serange, ctype):
|
|
if addr == "":
|
|
raise ValueError(_("Node Address is required"))
|
|
|
|
- if mask == "":
|
|
- raise ValueError(_("Node Netmask is required"))
|
|
-
|
|
- if proto == "ipv4":
|
|
- proto = 0
|
|
- elif proto == "ipv6":
|
|
- proto = 1
|
|
- else:
|
|
+ # verify valid comination
|
|
+ if len(mask) == 0 or mask[0] == "/":
|
|
+ i = IP(addr + mask)
|
|
+ newaddr = i.strNormal(0)
|
|
+ newmask = str(i.netmask())
|
|
+ if newmask == "0.0.0.0" and i.version() == 6:
|
|
+ newmask = "::"
|
|
+
|
|
+ protocol = "ipv%d" % i.version()
|
|
+
|
|
+ try:
|
|
+ newprotocol = self.protocol.index(protocol)
|
|
+ except:
|
|
raise ValueError(_("Unknown or missing protocol"))
|
|
|
|
+ return newaddr, newmask, newprotocol
|
|
+
|
|
+ def __add(self, addr, mask, proto, serange, ctype):
|
|
+
|
|
+ addr, mask, proto = self.validate(addr, mask, proto)
|
|
|
|
if is_mls_enabled == 1:
|
|
if serange == "":
|
|
@@ -991,11 +1142,13 @@ 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:
|
|
raise ValueError(_("Could not create addr for %s") % addr)
|
|
+ semanage_node_set_proto(node, proto)
|
|
|
|
rc = semanage_node_set_addr(self.sh, node, proto, addr)
|
|
(rc, con) = semanage_context_create(self.sh)
|
|
@@ -1005,8 +1158,7 @@ class nodeRecords(semanageRecords):
|
|
rc = semanage_node_set_mask(self.sh, node, proto, mask)
|
|
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)
|
|
@@ -1042,18 +1194,8 @@ class nodeRecords(semanageRecords):
|
|
self.commit()
|
|
|
|
def __modify(self, addr, mask, proto, serange, setype):
|
|
- if addr == "":
|
|
- raise ValueError(_("Node Address is required"))
|
|
-
|
|
- if mask == "":
|
|
- raise ValueError(_("Node Netmask is required"))
|
|
- if proto == "ipv4":
|
|
- proto = 0
|
|
- elif proto == "ipv6":
|
|
- proto = 1
|
|
- else:
|
|
- raise ValueError(_("Unknown or missing protocol"))
|
|
|
|
+ addr, mask, proto = self.validate(addr, mask, proto)
|
|
|
|
if serange == "" and setype == "":
|
|
raise ValueError(_("Requires setype or serange"))
|
|
@@ -1068,12 +1210,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 != "":
|
|
@@ -1092,18 +1233,8 @@ class nodeRecords(semanageRecords):
|
|
self.commit()
|
|
|
|
def __delete(self, addr, mask, proto):
|
|
- if addr == "":
|
|
- raise ValueError(_("Node Address is required"))
|
|
-
|
|
- if mask == "":
|
|
- raise ValueError(_("Node Netmask is required"))
|
|
|
|
- if proto == "ipv4":
|
|
- proto = 0
|
|
- elif proto == "ipv6":
|
|
- proto = 1
|
|
- else:
|
|
- raise ValueError(_("Unknown or missing protocol"))
|
|
+ addr, mask, proto = self.validate(addr, mask, proto)
|
|
|
|
(rc, k) = semanage_node_key_create(self.sh, addr, mask, proto)
|
|
if rc < 0:
|
|
@@ -1132,6 +1263,16 @@ class nodeRecords(semanageRecords):
|
|
self.__delete(addr, mask, proto)
|
|
self.commit()
|
|
|
|
+ def deleteall(self):
|
|
+ (rc, nlist) = semanage_node_list_local(self.sh)
|
|
+ if rc < 0:
|
|
+ raise ValueError(_("Could not deleteall node mappings"))
|
|
+
|
|
+ self.begin()
|
|
+ for node in nlist:
|
|
+ self.__delete(semanage_node_get_addr(self.sh, node)[1], semanage_node_get_mask(self.sh, node)[1], self.protocol[semanage_node_get_proto(node)])
|
|
+ self.commit()
|
|
+
|
|
def get_all(self, locallist = 0):
|
|
ddict = {}
|
|
if locallist :
|
|
@@ -1145,15 +1286,20 @@ class nodeRecords(semanageRecords):
|
|
con = semanage_node_get_con(node)
|
|
addr = semanage_node_get_addr(self.sh, node)
|
|
mask = semanage_node_get_mask(self.sh, node)
|
|
- proto = semanage_node_get_proto(node)
|
|
- if proto == 0:
|
|
- proto = "ipv4"
|
|
- elif proto == 1:
|
|
- proto = "ipv6"
|
|
+ proto = self.protocol[semanage_node_get_proto(node)]
|
|
ddict[(addr[1], mask[1], proto)] = (semanage_context_get_user(con), semanage_context_get_role(con), semanage_context_get_type(con), semanage_context_get_mls(con))
|
|
|
|
return ddict
|
|
|
|
+ def customized(self):
|
|
+ l = []
|
|
+ ddict = self.get_all(True)
|
|
+ keys = ddict.keys()
|
|
+ keys.sort()
|
|
+ for k in keys:
|
|
+ l.append("-a -M %s -p %s -t %s %s" % (k[1], k[2],ddict[k][2], k[0]))
|
|
+ return l
|
|
+
|
|
def list(self, heading = 1, locallist = 0):
|
|
if heading:
|
|
print "%-18s %-18s %-5s %-5s\n" % ("IP Address", "Netmask", "Protocol", "Context")
|
|
@@ -1193,7 +1339,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:
|
|
@@ -1307,6 +1454,16 @@ class interfaceRecords(semanageRecords):
|
|
self.__delete(interface)
|
|
self.commit()
|
|
|
|
+ def deleteall(self):
|
|
+ (rc, ulist) = semanage_iface_list_local(self.sh)
|
|
+ if rc < 0:
|
|
+ raise ValueError(_("Could not delete all interface mappings"))
|
|
+
|
|
+ self.begin()
|
|
+ for i in ulist:
|
|
+ self.__delete(semanage_iface_get_name(i))
|
|
+ self.commit()
|
|
+
|
|
def get_all(self, locallist = 0):
|
|
ddict = {}
|
|
if locallist:
|
|
@@ -1322,6 +1479,15 @@ class interfaceRecords(semanageRecords):
|
|
|
|
return ddict
|
|
|
|
+ def customized(self):
|
|
+ l = []
|
|
+ ddict = self.get_all(True)
|
|
+ keys = ddict.keys()
|
|
+ keys.sort()
|
|
+ for k in keys:
|
|
+ l.append("-a -t %s %s" % (ddict[k][2], k))
|
|
+ return l
|
|
+
|
|
def list(self, heading = 1, locallist = 0):
|
|
if heading:
|
|
print "%-30s %s\n" % (_("SELinux Interface"), _("Context"))
|
|
@@ -1338,6 +1504,48 @@ class interfaceRecords(semanageRecords):
|
|
class fcontextRecords(semanageRecords):
|
|
def __init__(self, store = ""):
|
|
semanageRecords.__init__(self, store)
|
|
+ self.equiv = {}
|
|
+ self.equal_ind = False
|
|
+ try:
|
|
+ fd = open(selinux.selinux_file_context_subs_path(), "r")
|
|
+ for i in fd.readlines():
|
|
+ src, dst = i.split()
|
|
+ self.equiv[src] = dst
|
|
+ fd.close()
|
|
+ except IOError:
|
|
+ pass
|
|
+
|
|
+ def commit(self):
|
|
+ if self.equal_ind:
|
|
+ subs_file = selinux.selinux_file_context_subs_path()
|
|
+ tmpfile = "%s.tmp" % subs_file
|
|
+ fd = open(tmpfile, "w")
|
|
+ for src in self.equiv.keys():
|
|
+ fd.write("%s %s\n" % (src, self.equiv[src]))
|
|
+ fd.close()
|
|
+ try:
|
|
+ os.chmod(tmpfile, os.stat(subs_file)[stat.ST_MODE])
|
|
+ except:
|
|
+ pass
|
|
+ os.rename(tmpfile,subs_file)
|
|
+ self.equal_ind = False
|
|
+ semanageRecords.commit(self)
|
|
+
|
|
+ def add_equal(self, src, dst):
|
|
+ self.begin()
|
|
+ if src in self.equiv.keys():
|
|
+ raise ValueError(_("Equivalence class for %s already exists") % src)
|
|
+ self.equiv[src] = dst
|
|
+ self.equal_ind = True
|
|
+ self.commit()
|
|
+
|
|
+ def modify_equal(self, src, dst):
|
|
+ self.begin()
|
|
+ if src not in self.equiv.keys():
|
|
+ raise ValueError(_("Equivalence class for %s does not exists") % src)
|
|
+ self.equiv[src] = dst
|
|
+ self.equal_ind = True
|
|
+ self.commit()
|
|
|
|
def createcon(self, target, seuser = "system_u"):
|
|
(rc, con) = semanage_context_create(self.sh)
|
|
@@ -1364,6 +1572,8 @@ class fcontextRecords(semanageRecords):
|
|
def validate(self, target):
|
|
if target == "" or target.find("\n") >= 0:
|
|
raise ValueError(_("Invalid file specification"))
|
|
+ if target.find(" ") != -1:
|
|
+ raise ValueError(_("File specification can not include spaces"))
|
|
|
|
def __add(self, target, type, ftype = "", serange = "", seuser = "system_u"):
|
|
self.validate(target)
|
|
@@ -1388,7 +1598,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:
|
|
@@ -1504,9 +1715,16 @@ class fcontextRecords(semanageRecords):
|
|
raise ValueError(_("Could not delete the file context %s") % target)
|
|
semanage_fcontext_key_free(k)
|
|
|
|
+ self.equiv = {}
|
|
+ self.equal_ind = True
|
|
self.commit()
|
|
|
|
def __delete(self, target, ftype):
|
|
+ if target in self.equiv.keys():
|
|
+ self.equiv.pop(target)
|
|
+ self.equal_ind = True
|
|
+ return
|
|
+
|
|
(rc,k) = semanage_fcontext_key_create(self.sh, target, file_types[ftype])
|
|
if rc < 0:
|
|
raise ValueError(_("Could not create a key for %s") % target)
|
|
@@ -1561,12 +1779,22 @@ class fcontextRecords(semanageRecords):
|
|
|
|
return ddict
|
|
|
|
+ def customized(self):
|
|
+ l = []
|
|
+ fcon_dict = self.get_all(True)
|
|
+ keys = fcon_dict.keys()
|
|
+ keys.sort()
|
|
+ for k in keys:
|
|
+ if fcon_dict[k]:
|
|
+ l.append("-a -f '%s' -t %s '%s'" % (k[1], fcon_dict[k][2], k[0]))
|
|
+ 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:
|
|
@@ -1575,6 +1803,12 @@ class fcontextRecords(semanageRecords):
|
|
print "%-50s %-18s %s:%s:%s " % (k[0], k[1], fcon_dict[k][0], fcon_dict[k][1],fcon_dict[k][2])
|
|
else:
|
|
print "%-50s %-18s <<None>>" % (k[0], k[1])
|
|
+ if len(self.equiv.keys()) > 0:
|
|
+ if heading:
|
|
+ print _("\nSELinux fcontext Equivalence \n")
|
|
+
|
|
+ for src in self.equiv.keys():
|
|
+ print "%s = %s" % (src, self.equiv[src])
|
|
|
|
class booleanRecords(semanageRecords):
|
|
def __init__(self, store = ""):
|
|
@@ -1587,6 +1821,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:
|
|
@@ -1606,9 +1852,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)
|
|
@@ -1691,8 +1938,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
|
|
@@ -1706,6 +1957,16 @@ class booleanRecords(semanageRecords):
|
|
else:
|
|
return _("unknown")
|
|
|
|
+ def customized(self):
|
|
+ l = []
|
|
+ ddict = self.get_all(True)
|
|
+ keys = ddict.keys()
|
|
+ keys.sort()
|
|
+ for k in keys:
|
|
+ if ddict[k]:
|
|
+ l.append("-%s %s" % (ddict[k][2], k))
|
|
+ return l
|
|
+
|
|
def list(self, heading = True, locallist = False, use_file = False):
|
|
on_off = (_("off"), _("on"))
|
|
if use_file:
|
|
diff --git a/policycoreutils/semodule/semodule.c b/policycoreutils/semodule/semodule.c
|
|
index 059f629..81d6a3c 100644
|
|
--- a/policycoreutils/semodule/semodule.c
|
|
+++ b/policycoreutils/semodule/semodule.c
|
|
@@ -162,6 +162,7 @@ static void parse_command_line(int argc, char **argv)
|
|
{"noreload", 0, NULL, 'n'},
|
|
{"build", 0, NULL, 'B'},
|
|
{"disable_dontaudit", 0, NULL, 'D'},
|
|
+ {"path", required_argument, NULL, 'p'},
|
|
{NULL, 0, NULL, 0}
|
|
};
|
|
int i;
|
|
@@ -170,7 +171,7 @@ static void parse_command_line(int argc, char **argv)
|
|
no_reload = 0;
|
|
create_store = 0;
|
|
while ((i =
|
|
- getopt_long(argc, argv, "s:b:hi:lvqe:d:r:u:RnBD", opts,
|
|
+ getopt_long(argc, argv, "p:s:b:hi:lvqe:d:r:u:RnBD", opts,
|
|
NULL)) != -1) {
|
|
switch (i) {
|
|
case 'b':
|
|
@@ -198,6 +199,9 @@ static void parse_command_line(int argc, char **argv)
|
|
case 'r':
|
|
set_mode(REMOVE_M, optarg);
|
|
break;
|
|
+ case 'p':
|
|
+ semanage_set_root(optarg);
|
|
+ break;
|
|
case 'u':
|
|
set_mode(UPGRADE_M, optarg);
|
|
break;
|
|
diff --git a/policycoreutils/semodule_expand/semodule_expand.8 b/policycoreutils/semodule_expand/semodule_expand.8
|
|
index 22ad3be..35df2ed 100644
|
|
--- a/policycoreutils/semodule_expand/semodule_expand.8
|
|
+++ b/policycoreutils/semodule_expand/semodule_expand.8
|
|
@@ -3,7 +3,7 @@
|
|
semodule_expand \- Expand a SELinux policy module package.
|
|
|
|
.SH SYNOPSIS
|
|
-.B semodule_expand [-V -c [version]] basemodpkg outputfile
|
|
+.B semodule_expand [-V ] [ -a ] [ -c [version]] basemodpkg outputfile
|
|
.br
|
|
.SH DESCRIPTION
|
|
.PP
|
|
@@ -22,6 +22,9 @@ show version
|
|
.TP
|
|
.B \-c [version]
|
|
policy version to create
|
|
+.TP
|
|
+.B \-a
|
|
+Check assertions. This will cause the policy to check all neverallow rules.
|
|
|
|
.SH SEE ALSO
|
|
.B checkmodule(8), semodule_package(8), semodule(8), semodule_link(8)
|
|
diff --git a/policycoreutils/semodule_package/semodule_package.8 b/policycoreutils/semodule_package/semodule_package.8
|
|
index fb41480..29c9eb2 100644
|
|
--- a/policycoreutils/semodule_package/semodule_package.8
|
|
+++ b/policycoreutils/semodule_package/semodule_package.8
|
|
@@ -45,7 +45,6 @@ netfilter context file to be included in the package.
|
|
|
|
.SH SEE ALSO
|
|
.B checkmodule(8), semodule(8)
|
|
-(8),
|
|
.SH AUTHORS
|
|
.nf
|
|
This manual page was written by Dan Walsh <dwalsh@redhat.com>.
|
|
diff --git a/policycoreutils/sepolgen-ifgen/Makefile b/policycoreutils/sepolgen-ifgen/Makefile
|
|
new file mode 100644
|
|
index 0000000..211580d
|
|
--- /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 ?= -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..8f5c8e0
|
|
--- /dev/null
|
|
+++ b/policycoreutils/sepolgen-ifgen/sepolgen-ifgen-attr-helper.c
|
|
@@ -0,0 +1,233 @@
|
|
+/* Authors: Frank Mayer <mayerf@tresys.com>
|
|
+ * and Karl MacMillan <kmacmillan@tresys.com>
|
|
+ *
|
|
+ * 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 <sepol/policydb/policydb.h>
|
|
+#include <sepol/policydb/avtab.h>
|
|
+#include <sepol/policydb/util.h>
|
|
+
|
|
+#include <stdio.h>
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
+#include <fcntl.h>
|
|
+#include <sys/mman.h>
|
|
+#include <unistd.h>
|
|
+
|
|
+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 b649d8f..38416d8 100644
|
|
--- a/policycoreutils/setfiles/restore.c
|
|
+++ b/policycoreutils/setfiles/restore.c
|
|
@@ -1,4 +1,5 @@
|
|
#include "restore.h"
|
|
+#include <glob.h>
|
|
|
|
#define SKIP -2
|
|
#define ERR -1
|
|
@@ -31,7 +32,6 @@ struct edir {
|
|
|
|
|
|
static file_spec_t *fl_head;
|
|
-static int exclude(const char *file);
|
|
static int filespec_add(ino_t ino, const security_context_t con, const char *file);
|
|
static int only_changed_user(const char *a, const char *b);
|
|
struct restore_opts *r_opts = NULL;
|
|
@@ -53,7 +53,6 @@ void remove_exclude(const char *directory)
|
|
}
|
|
}
|
|
return;
|
|
-
|
|
}
|
|
|
|
void restore_init(struct restore_opts *opts)
|
|
@@ -300,8 +299,14 @@ static int process_one(char *name, int recurse_this_path)
|
|
int rc = 0;
|
|
const char *namelist[2] = {name, NULL};
|
|
dev_t dev_num = 0;
|
|
- FTS *fts_handle;
|
|
- FTSENT *ftsent;
|
|
+ FTS *fts_handle = NULL;
|
|
+ FTSENT *ftsent = NULL;
|
|
+
|
|
+ if (r_opts == NULL){
|
|
+ fprintf(stderr,
|
|
+ "Must call initialize first!");
|
|
+ goto err;
|
|
+ }
|
|
|
|
fts_handle = fts_open((char **)namelist, r_opts->fts_flags, NULL);
|
|
if (fts_handle == NULL) {
|
|
@@ -357,11 +362,34 @@ err:
|
|
goto out;
|
|
}
|
|
|
|
+int process_glob(char *name, int recurse) {
|
|
+ glob_t globbuf;
|
|
+ size_t i = 0;
|
|
+ int errors = 0;
|
|
+ memset(&globbuf, 0, sizeof(globbuf));
|
|
+ globbuf.gl_offs = 0;
|
|
+ if (glob(name,
|
|
+ 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;
|
|
+ errors |= process_one_realpath(globbuf.gl_pathv[i], recurse) < 0;
|
|
+ }
|
|
+ globfree(&globbuf);
|
|
+ }
|
|
+ else
|
|
+ errors |= process_one_realpath(name, recurse) < 0;
|
|
+ return errors;
|
|
+}
|
|
+
|
|
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,
|
|
@@ -372,8 +400,9 @@ 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;
|
|
fprintf(stderr, "%s: lstat(%s) failed: %s\n",
|
|
r_opts->progname, name, strerror(errno));
|
|
return -1;
|
|
@@ -409,7 +438,7 @@ int process_one_realpath(char *name, int recurse)
|
|
}
|
|
}
|
|
|
|
-static int exclude(const char *file)
|
|
+int exclude(const char *file)
|
|
{
|
|
int i = 0;
|
|
for (i = 0; i < excludeCtr; i++) {
|
|
@@ -537,7 +566,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);
|
|
@@ -550,7 +579,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);
|
|
@@ -602,5 +631,67 @@ static int filespec_add(ino_t ino, const security_context_t con, const char *fil
|
|
return -1;
|
|
}
|
|
|
|
+#include <sys/utsname.h>
|
|
+/*
|
|
+ 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 03b82e8..8b50ff8 100644
|
|
--- a/policycoreutils/setfiles/restore.h
|
|
+++ b/policycoreutils/setfiles/restore.h
|
|
@@ -27,6 +27,7 @@ struct restore_opts {
|
|
int hard_links;
|
|
int verbose;
|
|
int logging;
|
|
+ int ignore_enoent;
|
|
char *rootpath;
|
|
int rootpathlen;
|
|
char *progname;
|
|
@@ -44,7 +45,10 @@ struct restore_opts {
|
|
void restore_init(struct restore_opts *opts);
|
|
void restore_finish();
|
|
int add_exclude(const char *directory);
|
|
+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/restorecon.8 b/policycoreutils/setfiles/restorecon.8
|
|
index 1eb6a43..c8ea4bb 100644
|
|
--- a/policycoreutils/setfiles/restorecon.8
|
|
+++ b/policycoreutils/setfiles/restorecon.8
|
|
@@ -4,10 +4,10 @@ restorecon \- restore file(s) default SELinux security contexts.
|
|
|
|
.SH "SYNOPSIS"
|
|
.B restorecon
|
|
-.I [\-o outfilename ] [\-R] [\-n] [\-v] [\-e directory ] pathname...
|
|
+.I [\-o outfilename ] [\-R] [\-n] [\-p] [\-v] [\-e directory ] pathname...
|
|
.P
|
|
.B restorecon
|
|
-.I \-f infilename [\-o outfilename ] [\-e directory ] [\-R] [\-n] [\-v] [\-F]
|
|
+.I \-f infilename [\-o outfilename ] [\-e directory ] [\-R] [\-n] [\-p] [\-v] [\-F]
|
|
|
|
.SH "DESCRIPTION"
|
|
This manual page describes the
|
|
@@ -40,6 +40,9 @@ don't change any file labels.
|
|
.TP
|
|
.B \-o outfilename
|
|
save list of files with incorrect context in outfilename.
|
|
+.TP
|
|
+.B \-p
|
|
+show progress by printing * every 1000 files.
|
|
.TP
|
|
.B \-v
|
|
show changes in file labels.
|
|
diff --git a/policycoreutils/setfiles/setfiles.8 b/policycoreutils/setfiles/setfiles.8
|
|
index ac68b94..7f700ca 100644
|
|
--- a/policycoreutils/setfiles/setfiles.8
|
|
+++ b/policycoreutils/setfiles/setfiles.8
|
|
@@ -10,7 +10,7 @@ This manual page describes the
|
|
.BR setfiles
|
|
program.
|
|
.P
|
|
-This program is primarily used to initialise the security context
|
|
+This program is primarily used to initialize the security context
|
|
database (extended attributes) on one or more filesystems. This
|
|
program is initially run as part of the SE Linux installation process.
|
|
.P
|
|
@@ -31,6 +31,9 @@ log changes in file labels to syslog.
|
|
.TP
|
|
.B \-n
|
|
don't change any file labels.
|
|
+.TP
|
|
+.B \-p
|
|
+show progress by printing * every 1000 files.
|
|
.TP
|
|
.B \-q
|
|
suppress non-error output.
|
|
diff --git a/policycoreutils/setfiles/setfiles.c b/policycoreutils/setfiles/setfiles.c
|
|
index 8f4f663..b0a7e09 100644
|
|
--- a/policycoreutils/setfiles/setfiles.c
|
|
+++ b/policycoreutils/setfiles/setfiles.c
|
|
@@ -5,7 +5,6 @@
|
|
#include <ctype.h>
|
|
#include <regex.h>
|
|
#include <sys/vfs.h>
|
|
-#include <sys/utsname.h>
|
|
#define __USE_XOPEN_EXTENDED 1 /* nftw */
|
|
#include <libgen.h>
|
|
#ifdef USE_AUDIT
|
|
@@ -25,7 +24,6 @@ static char *policyfile = NULL;
|
|
static int warn_no_match = 0;
|
|
static int null_terminated = 0;
|
|
static int errors;
|
|
-static int ignore_enoent;
|
|
static struct restore_opts r_opts;
|
|
|
|
#define STAT_BLOCK_SIZE 1
|
|
@@ -44,13 +42,13 @@ void usage(const char *const name)
|
|
{
|
|
if (iamrestorecon) {
|
|
fprintf(stderr,
|
|
- "usage: %s [-iFnrRv0] [-e excludedir ] [-o filename ] [-f filename | pathname... ]\n",
|
|
+ "usage: %s [-iFnprRv0] [-e excludedir ] [-o filename ] [-f filename | pathname... ]\n",
|
|
name);
|
|
} else {
|
|
fprintf(stderr,
|
|
"usage: %s [-dnpqvW] [-o filename] [-r alt_root_path ] spec_file pathname...\n"
|
|
"usage: %s -c policyfile spec_file\n"
|
|
- "usage: %s -s [-dnqvW] [-o filename ] spec_file\n", name, name,
|
|
+ "usage: %s -s [-dnpqvW] [-o filename ] spec_file\n", name, name,
|
|
name);
|
|
}
|
|
exit(1);
|
|
@@ -138,69 +136,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;
|
|
@@ -335,7 +270,7 @@ int main(int argc, char **argv)
|
|
r_opts.debug = 1;
|
|
break;
|
|
case 'i':
|
|
- ignore_enoent = 1;
|
|
+ r_opts.ignore_enoent = 1;
|
|
break;
|
|
case 'l':
|
|
r_opts.logging = 1;
|
|
@@ -371,7 +306,7 @@ int main(int argc, char **argv)
|
|
break;
|
|
}
|
|
if (optind + 1 >= argc) {
|
|
- fprintf(stderr, "usage: %s -r r_opts.rootpath\n",
|
|
+ fprintf(stderr, "usage: %s -r rootpath\n",
|
|
argv[0]);
|
|
exit(1);
|
|
}
|
|
@@ -475,7 +410,7 @@ int main(int argc, char **argv)
|
|
buf[len - 1] = 0;
|
|
if (!strcmp(buf, "/"))
|
|
mass_relabel = 1;
|
|
- errors |= process_one_realpath(buf, recurse) < 0;
|
|
+ errors |= process_glob(buf, recurse) < 0;
|
|
}
|
|
if (strcmp(input_filename, "-") != 0)
|
|
fclose(f);
|
|
@@ -483,7 +418,8 @@ int main(int argc, char **argv)
|
|
for (i = optind; i < argc; i++) {
|
|
if (!strcmp(argv[i], "/"))
|
|
mass_relabel = 1;
|
|
- errors |= process_one_realpath(argv[i], recurse) < 0;
|
|
+
|
|
+ errors |= process_glob(argv[i], recurse) < 0;
|
|
}
|
|
}
|
|
|
|
diff --git a/policycoreutils/setsebool/setsebool.8 b/policycoreutils/setsebool/setsebool.8
|
|
index 4b13387..2b66bad 100644
|
|
--- a/policycoreutils/setsebool/setsebool.8
|
|
+++ b/policycoreutils/setsebool/setsebool.8
|
|
@@ -16,7 +16,7 @@ affected; the boot-time default settings
|
|
are not changed.
|
|
|
|
If the -P option is given, all pending values are written to
|
|
-the policy file on disk. So they will be persistant across reboots.
|
|
+the policy file on disk. So they will be persistent across reboots.
|
|
|
|
.SH AUTHOR
|
|
This manual page was written by Dan Walsh <dwalsh@redhat.com>.
|
|
diff --git a/policycoreutils/setsebool/setsebool.c b/policycoreutils/setsebool/setsebool.c
|
|
index dc037dd..d6c041b 100644
|
|
--- a/policycoreutils/setsebool/setsebool.c
|
|
+++ b/policycoreutils/setsebool/setsebool.c
|
|
@@ -82,8 +82,13 @@ static int selinux_set_boolean_list(size_t boolcnt,
|
|
if (errno == ENOENT)
|
|
fprintf(stderr, "Could not change active booleans: "
|
|
"Invalid boolean\n");
|
|
- else if (errno)
|
|
- perror("Could not change active booleans");
|
|
+ else if (errno) {
|
|
+ if (getuid() == 0) {
|
|
+ perror("Could not change active booleans");
|
|
+ } else {
|
|
+ perror("Could not change active booleans. Please try as root");
|
|
+ }
|
|
+ }
|
|
|
|
return -1;
|
|
}
|
|
@@ -115,8 +120,13 @@ static int semanage_set_boolean_list(size_t boolcnt,
|
|
goto err;
|
|
|
|
} else if (managed == 0) {
|
|
- fprintf(stderr,
|
|
- "Cannot set persistent booleans without managed policy.\n");
|
|
+ if (getuid() == 0) {
|
|
+ fprintf(stderr,
|
|
+ "Cannot set persistent booleans without managed policy.\n");
|
|
+ } else {
|
|
+ fprintf(stderr,
|
|
+ "Cannot set persistent booleans, please try as root.\n");
|
|
+ }
|
|
goto err;
|
|
}
|
|
|