All of lore.kernel.org
 help / color / mirror / Atom feed
* Updated sandbox patch.
@ 2010-04-01 19:20 Daniel J Walsh
  0 siblings, 0 replies; 10+ messages in thread
From: Daniel J Walsh @ 2010-04-01 19:20 UTC (permalink / raw)
  To: Joshua Brindle, SELinux

[-- Attachment #1: Type: text/plain, Size: 1 bytes --]



[-- Attachment #2: sandbox.patch --]
[-- Type: text/plain, Size: 33611 bytes --]

diff --git a/policycoreutils/Makefile b/policycoreutils/Makefile
index 538302b..b5b97bc 100644
--- a/policycoreutils/Makefile
+++ b/policycoreutils/Makefile
@@ -1,4 +1,4 @@
-SUBDIRS = setfiles semanage load_policy newrole run_init secon audit2allow audit2why scripts sestatus semodule_package semodule semodule_link semodule_expand semodule_deps setsebool po
+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 
 
 INOTIFYH = $(shell ls /usr/include/sys/inotify.h 2>/dev/null)
 
diff --git a/policycoreutils/sandbox/Makefile b/policycoreutils/sandbox/Makefile
new file mode 100644
index 0000000..ff0ee7c
--- /dev/null
+++ b/policycoreutils/sandbox/Makefile
@@ -0,0 +1,41 @@
+# Installation directories.
+PREFIX ?= ${DESTDIR}/usr
+INITDIR ?= ${DESTDIR}/etc/rc.d/init.d/
+SYSCONFDIR ?= ${DESTDIR}/etc/sysconfig
+BINDIR ?= $(PREFIX)/bin
+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 
+
+all: sandbox seunshare sandboxX.sh 
+
+seunshare: seunshare.o $(EXTRA_OBJS)
+	$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+install: all
+	-mkdir -p $(BINDIR)
+	install -m 755 sandbox $(BINDIR)
+	-mkdir -p $(MANDIR)/man8
+	install -m 644 sandbox.8 $(MANDIR)/man8/
+	-mkdir -p $(SBINDIR)
+	install -m 4755 seunshare $(SBINDIR)/
+	-mkdir -p $(SHAREDIR)
+	install -m 755 sandboxX.sh $(SHAREDIR)
+	-mkdir -p $(INITDIR)
+	install -m 755 sandbox.init $(INITDIR)/sandbox
+	-mkdir -p $(SYSCONFDIR)
+	install -m 644 sandbox.config $(SYSCONFDIR)/sandbox
+
+test:
+	@python test_sandbox.py -v
+
+clean:
+	-rm -f seunshare *.o *~
+
+indent:
+	../../scripts/Lindent $(wildcard *.[ch])
+
+relabel:
diff --git a/policycoreutils/sandbox/sandbox b/policycoreutils/sandbox/sandbox
new file mode 100644
index 0000000..051fa39
--- /dev/null
+++ b/policycoreutils/sandbox/sandbox
@@ -0,0 +1,420 @@
+#! /usr/bin/python -E
+# Authors: Dan Walsh <dwalsh@redhat.com>
+# Authors: Josh Cogliati
+#
+# Copyright (C) 2009,2010  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; version 2 only
+#
+# 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
+#
+
+import os, sys, socket, random, fcntl, shutil, re, subprocess
+import selinux
+import signal
+from tempfile import mkdtemp
+import pwd
+
+PROGNAME = "policycoreutils"
+HOMEDIR=pwd.getpwuid(os.getuid()).pw_dir
+
+import gettext
+gettext.bindtextdomain(PROGNAME, "/usr/share/locale")
+gettext.textdomain(PROGNAME)
+
+try:
+       gettext.install(PROGNAME,
+                       localedir = "/usr/share/locale",
+                       unicode=False,
+                       codeset = 'utf-8')
+except IOError:
+       import __builtin__
+       __builtin__.__dict__['_'] = unicode
+
+DEFAULT_TYPE = "sandbox_t"
+DEFAULT_X_TYPE = "sandbox_x_t"
+X_FILES = {}
+
+random.seed(None)
+
+def sighandler(signum, frame):
+    signal.signal(signum,  signal.SIG_IGN)
+    os.kill(0, signum)
+    raise KeyboardInterrupt
+
+def setup_sighandlers():
+    signal.signal(signal.SIGHUP,  sighandler)
+    signal.signal(signal.SIGQUIT, sighandler)
+    signal.signal(signal.SIGTERM, sighandler)
+
+def error_exit(msg):
+    sys.stderr.write("%s: " % sys.argv[0])
+    sys.stderr.write("%s\n" % msg)
+    sys.stderr.flush()
+    sys.exit(1)
+
+def copyfile(file, dir, dest):
+       import re
+       if file.startswith(dir):
+              dname = os.path.dirname(file)
+              bname = os.path.basename(file)
+              if dname == dir:
+                     dest = dest + "/" + bname
+              else:
+                     newdir = re.sub(dir, dest, dname)
+                     if not os.path.exists(newdir):
+                            os.makedirs(newdir)
+                     dest = newdir + "/" + bname
+
+              if os.path.isdir(file):
+                     shutil.copytree(file, dest)
+              else:
+                     shutil.copy2(file, dest)
+              X_FILES[file] = (dest, os.path.getmtime(dest))
+
+def savefile(new, orig, X_ind):
+       copy = False
+       if(X_ind):
+              import gtk
+              dlg = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO,
+                                      gtk.BUTTONS_YES_NO,
+                                      _("Do you want to save changes to '%s' (Y/N): ") % orig)
+              dlg.set_title(_("Sandbox Message"))
+              dlg.set_position(gtk.WIN_POS_MOUSE)
+              dlg.show_all()
+              rc = dlg.run()
+              dlg.destroy()
+              if rc == gtk.RESPONSE_YES:
+                     copy = True
+       else:
+              ans = raw_input(_("Do you want to save changes to '%s' (y/N): ") % orig)
+              if(re.match(_("[yY]"),ans)):
+                     copy = True
+       if(copy):
+              shutil.copy2(new,orig)
+
+def reserve(level):
+    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+    sock.bind("\0%s" % level)
+    fcntl.fcntl(sock.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
+
+def gen_mcs():
+       while True:
+              i1 = random.randrange(0, 1024)
+              i2 = random.randrange(0, 1024)
+              if i1 == i2:
+                     continue
+              if i1 > i2:
+                     tmp = i1
+                     i1 = i2
+                     i2 = tmp
+                     level = "s0:c%d,c%d" % (i1, i2)
+              level = "s0:c%d,c%d" % (i1, i2)
+              try:
+                     reserve(level)
+              except socket.error:
+                     continue
+              break
+       return level
+
+def fullpath(cmd):
+       for i in [ "/", "./", "../" ]:
+              if cmd.startswith(i):
+                     return cmd
+       for i in  os.environ["PATH"].split(':'):
+              f = "%s/%s" % (i, cmd)
+              if os.access(f, os.X_OK):
+                     return f
+       return cmd
+
+class Sandbox:
+    VERSION = "sandbox .1"
+    SYSLOG = "/var/log/messages"
+
+    def __init__(self):
+        self.__options = None
+        self.__cmds = None
+        self.__init_files = []
+        self.__paths = []
+        self.__mount = False
+        self.__level = None
+        self.__homedir = None
+        self.__tmpdir = None
+
+    def __validate_mount(self):
+           if self.__options.level:
+                  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"):
+                  raise ValueError("""
+/usr/sbin/seunshare required for sandbox -M, to install you need to execute 
+#yum install /usr/sbin/seunshare
+""")
+           homedir=pwd.getpwuid(os.getuid()).pw_dir
+           fd = open("/proc/self/mountinfo", "r")
+           recs = fd.readlines()
+           fd.close()
+           for i in recs:
+                  x = i.split() 
+                  if x[3] == x[4] and homedir.startswith(x[3]+"/"):
+                         return
+           raise ValueError(_("""
+'%s' is required to be a shared mount point for this tool to run.  
+'%s' can be added to the HOMEDIR variable in /etc/sysconfig/sandbox
+ along with a reboot will fix the problem.
+""" % ((os.path.dirname(homedir)), os.path.dirname(homedir))))
+        
+    def __mount_callback(self, option, opt, value, parser):
+           self.__mount = True
+
+    def __x_callback(self, option, opt, value, parser):
+           self.__mount = True
+           setattr(parser.values, option.dest, True)
+
+    def __validdir(self, option, opt, value, parser):
+           if not os.path.isdir(value):
+                  raise IOError("Directory "+value+" not found")
+           self.__mount = True
+
+    def __include(self, option, opt, value, parser):
+           rp = os.path.realpath(os.path.expanduser(value))
+           if not os.path.exists(rp):
+                  raise IOError(value+" not found")
+
+           if rp not in self.__init_files:
+                  self.__init_files.append(rp)
+
+    def __includefile(self, option, opt, value, parser):
+           fd = open(value, "r")
+           for i in fd.readlines():
+                  rp = os.path.realpath(os.path.expanduser(i[:-1]))
+                  if rp not in self.__init_files and os.path.exists(rp):
+                         self.__init_files.append(rp)
+           fd.close()
+
+    def __copyfiles(self):
+           files = self.__init_files + self.__paths
+           homedir=pwd.getpwuid(os.getuid()).pw_dir
+           for f in files:
+                  copyfile(f, homedir, self.__homedir)
+                  copyfile(f, "/tmp", self.__tmpdir)
+
+    def __setup_sandboxrc(self, wm = "/usr/bin/matchbox-window-manager -use_titlebar no"):
+           execfile =self.__homedir + "/.sandboxrc"
+           fd = open(execfile, "w+") 
+           if self.__options.session:
+                  fd.write("""#!/bin/sh
+#TITLE: /etc/gdm/Xsession
+/etc/gdm/Xsession
+""")
+           else:
+                  command = " ".join(self.__paths)
+                  fd.write("""#! /bin/sh
+#TITLE: %s
+/usr/bin/test -r ~/.xmodmap && /usr/bin/xmodmap ~/.xmodmap
+%s &
+WM_PID=$!
+%s
+kill -TERM $WM_PID  2> /dev/null
+""" % (command, wm, command))
+           fd.close()
+           os.chmod(execfile, 0700)
+
+    def usage(self, message = ""):
+           error_exit("%s\n%s" % (self.__parser.usage, message))
+
+    def __parse_options(self):
+        from optparse import OptionParser
+        usage = _("""
+sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [-W windowmanager ] [[-i file ] ...] [ -t type ] command
+
+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()
+        parser.add_option("-i", "--include", 
+                          action="callback", callback=self.__include, 
+                          type="string",
+                          help="include file in sandbox")
+        parser.add_option("-I", "--includefile",  action="callback", callback=self.__includefile,
+                          type="string",
+                          help="include contents of file in sandbox")
+        parser.add_option("-t", "--type", dest="setype", action="store", default=DEFAULT_TYPE,
+                          help="Run sandbox with SELinux type")
+        parser.add_option("-M", "--mount", 
+                          action="callback", callback=self.__mount_callback, 
+                          help="Mount new home and tmp Dir")
+
+        parser.add_option("-S", "--session", action="store_true",  dest="session", 
+                          default=False,  help="Run complete desktop session within sandbox")
+        parser.add_option("-X", dest="X_ind", 
+                          action="callback", callback=self.__x_callback, 
+                          default=False,  help="Run X sandbox")
+
+        parser.add_option("-H", "--homedir", 
+                          action="callback", callback=self.__validdir,
+                          type="string",
+                          dest="homedir",  
+                          help="Alternate homedir to use for mounting")
+
+        parser.add_option("-T", "--tmpdir", dest="tmpdir",  
+                          type="string",
+                          action="callback", callback=self.__validdir,
+                          help="Alternate tempdir to use for mounting")
+
+        parser.add_option("-W", "--windowmanager", dest="wm",  
+                          type="string",
+                          default="/usr/bin/matchbox-window-manager -use_titlebar no",
+                          help="Alternate window maanger")
+
+        parser.add_option("-l", "--level", dest="level", 
+                          help="MCS/MLS Level for the sandbox")
+
+        self.__parser=parser
+
+        self.__options, cmds = parser.parse_args()
+
+        if self.__options.X_ind:
+               if DEFAULT_TYPE == self.__options.setype:
+                     self.__options.setype = DEFAULT_X_TYPE
+
+        if self.__mount:
+               self.__validate_mount()
+
+        if self.__options.session:
+               if self.__options.setype in (DEFAULT_TYPE, DEFAULT_X_TYPE):
+                      self.__options.setype = selinux.getcon()[1].split(":")[2]
+               if not self.__options.homedir or not self.__options.tmpdir:
+                      self.usage(_("Homedir and tempdir required for session"))
+               if len(cmds) > 0:
+                      self.usage(_("Commands not allowed in a session"))
+        else:
+               if len(cmds) == 0:
+                      self.usage(_("Command required"))
+               cmds[0] = fullpath(cmds[0])
+               self.__cmds = cmds
+
+        for f in cmds:
+               rp = os.path.realpath(f)
+               if os.path.exists(rp):
+                      self.__paths.append(rp)
+               else:
+                      self.__paths.append(f)
+                  
+    def __gen_context(self):
+           if self.__options.level:
+                  level = self.__options.level
+           else:
+                  level = gen_mcs()
+
+           con = selinux.getcon()[1].split(":")
+           self.__execcon = "%s:%s:%s:%s" % (con[0], con[1], self.__options.setype, level)
+           self.__filecon = "%s:%s:%s:%s" % (con[0], "object_r", 
+                                             "%s_file_t" % self.__options.setype[:-2], 
+                                             level)
+    def __setup_dir(self):
+           if self.__options.level or self.__options.session:
+                  return
+           sandboxdir = HOMEDIR + "/.sandbox"
+           if not os.path.exists(sandboxdir):
+                  os.mkdir(sandboxdir)
+
+           import warnings 
+           warnings.simplefilter("ignore")
+           if self.__options.homedir:
+                  chcon =  ("/usr/bin/chcon -R %s %s" % (self.__filecon, self.__options.homedir)).split()
+                  rc = os.spawnvp(os.P_WAIT, chcon[0], chcon)
+                  self.__homedir = self.__options.homedir
+           else:
+                  selinux.setfscreatecon(self.__filecon)
+                  self.__homedir = mkdtemp(dir=sandboxdir, prefix=".sandbox")
+
+           if self.__options.tmpdir:
+                  chcon =  ("/usr/bin/chcon -R %s %s" % (self.__filecon, self.__options.tmpdir)).split()
+                  rc = os.spawnvp(os.P_WAIT, chcon[0], chcon)
+                  self.__tmpdir = self.__options.homedir
+           else:
+                  selinux.setfscreatecon(self.__filecon)
+                  self.__tmpdir = mkdtemp(dir="/tmp", prefix=".sandbox")
+           warnings.resetwarnings()
+           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 %s -h %s -- %s /usr/share/sandbox/sandboxX.sh" % (self.__tmpdir, self.__homedir, self.__execcon)).split()
+                         rc = os.spawnvp(os.P_WAIT, cmds[0], cmds)
+                         return rc
+
+                  if self.__mount:
+                         cmds =  ("/usr/sbin/seunshare -t %s -h %s -- %s " % (self.__tmpdir, self.__homedir, self.__execcon)).split()+self.__paths
+                         rc = os.spawnvp(os.P_WAIT, cmds[0], cmds)
+                         return rc
+
+                  selinux.setexeccon(self.__execcon)
+                  rc = os.spawnvp(os.P_WAIT, self.__cmds[0], self.__cmds)
+                  selinux.setexeccon(None)
+                  return rc
+
+           finally:
+                  for i in self.__paths:
+                         if i not in X_FILES:
+                                continue
+                         (dest, mtime) = X_FILES[i]
+                         if os.path.getmtime(dest) > mtime:
+                                savefile(dest, i, X_ind)
+
+                  if self.__homedir and not self.__options.homedir: 
+                         shutil.rmtree(self.__homedir)
+                  if self.__tmpdir and not self.__options.tmpdir:
+                         shutil.rmtree(self.__tmpdir)
+    def main(self):
+        try:
+               self.__parse_options()
+               self.__gen_context()
+               self.__setup_dir()
+               return self.__execute()
+        except KeyboardInterrupt:
+            sys.exit(0)
+
+
+if __name__ == '__main__':
+    setup_sighandlers()
+    if selinux.is_selinux_enabled() != 1:
+        error_exit("Requires an SELinux enabled system")
+    
+    try:
+           sandbox = Sandbox()
+           rc = sandbox.main()
+    except OSError, error:
+           error_exit(error.args[1])
+    except ValueError, error:
+           error_exit(error.args[0])
+    except KeyError, error:
+           error_exit(_("Invalid value %s") % error.args[0])
+    except IOError, error:
+           error_exit(error)
+    except KeyboardInterrupt:
+           rc = 0
+           
+    sys.exit(rc)
diff --git a/policycoreutils/sandbox/sandbox.8 b/policycoreutils/sandbox/sandbox.8
new file mode 100644
index 0000000..ffaae49
--- /dev/null
+++ b/policycoreutils/sandbox/sandbox.8
@@ -0,0 +1,56 @@
+.TH SANDBOX "8" "May 2009" "chcat" "User Commands"
+.SH NAME
+sandbox \- Run cmd under an SELinux sandbox
+.SH SYNOPSIS
+.B sandbox
+[-l level ] [[-M | -X]  -H homedir -T tmpdir ] [-I includefile ] [ -W windowmanager ] [[-i file ]...] [ -t type ] cmd
+.br
+.SH DESCRIPTION
+.PP
+Run the 
+.I cmd 
+application within a tightly confined SELinux domain.  The default sandbox domain only allows applications the ability to read and write stdin, stdout and any other file descriptors handed to it. It is not allowed to open any other files.  The -M option will mount an alternate homedir and tmpdir to be used by the sandbox.
+
+If you have the 
+.I policycoreutils-sandbox 
+package installed, you can use the -X option and the -M option.
+.B sandbox -X
+allows you to run sandboxed X applications.  These applications will start up their own X Server and create a temporary homedir and /tmp.  The default policy does not allow any capabilities or network access.  It also prevents all access to the users other processes and files.  Any file specified on the command line will be copied into the sandbox.
+
+If directories are specified with -H or -T the directory will have its context modified with chcon(1) unless a level is specified with -l.  If the MLS/MCS security level is specified, the directories need to have a matching label.
+.PP
+.TP
+\fB\-H\ homedir
+Use alternate homedir to mount.  Defaults to temporary. Requires -X or -M.
+.TP
+\fB\-i file\fR
+Copy this file into the temporary sandbox appriate. Command can be repeated.
+.TP
+\fB\-I inputfile\fR Copy all files listed in inputfile into the
+appropriate temporary sandbox direcories.
+.TP
+\fB\-l\fR
+Specify the MLS/MCS Security Level to run the sandbox in.  Defaults to random.
+.TP
+\fB\-M\fR
+Create a Sandbox with temporary files for $HOME and /tmp, defaults to sandbox_t
+.TP
+\fB\-t type\fR
+Use alternate sandbox type, defaults to sandbox_t or sandbox_x_t for -X.
+.TP
+\fB\-T\ tmpdir
+Use alternate tempdir to mount.  Defaults to temporary. Requires -X or -M.
+.TP
+\fB\-W windowmanager\fR
+Select alternative window manager to run within 
+.B sandbox -X.
+Default to /usr/bin/matchbox-window-manager.
+.TP
+\fB\-X\fR 
+Create an X based Sandbox for gui apps, temporary files for
+$HOME and /tmp, seconday Xserver, defaults to sandbox_x_t
+.PP
+.SH "SEE ALSO"
+.TP
+runcon(1)
+.PP
diff --git a/policycoreutils/sandbox/sandbox.config b/policycoreutils/sandbox/sandbox.config
new file mode 100644
index 0000000..f9f059a
--- /dev/null
+++ b/policycoreutils/sandbox/sandbox.config
@@ -0,0 +1,2 @@
+# Space separate list of homedirs
+HOMEDIRS="/home"
diff --git a/policycoreutils/sandbox/sandbox.init b/policycoreutils/sandbox/sandbox.init
new file mode 100644
index 0000000..30d0861
--- /dev/null
+++ b/policycoreutils/sandbox/sandbox.init
@@ -0,0 +1,67 @@
+#!/bin/bash
+## BEGIN INIT INFO
+# Provides: sandbox
+# Default-Start: 3 4 5
+# Default-Stop: 0 1 2 3 4 6
+# Required-Start:
+#              
+## END INIT INFO
+# sandbox:        Set up / mountpoint to be shared, /var/tmp, /tmp, /home/sandbox unshared
+#
+# chkconfig: 345 1 99
+#
+# Description: sandbox is using pam_namespace to share the /var/tmp, /tmp and 
+#              /home/sandbox accounts.  This script will setup the / mount 
+#              point as shared and all of the subdirectories just these 
+#              directories as unshared.
+#
+
+# Source function library.
+. /etc/init.d/functions
+
+HOMEDIRS="/home"
+
+. /etc/sysconfig/sandbox
+
+LOCKFILE=/var/lock/subsys/sandbox
+
+base=${0##*/}
+
+case "$1" in
+    start)
+	[ -f "$LOCKFILE" ] && exit 0
+
+	touch $LOCKFILE
+	mount --make-rshared /
+	mount --rbind /tmp /tmp
+	mount --rbind /var/tmp /var/tmp
+	mount --make-private /tmp
+	mount --make-private /var/tmp
+	for h in $HOMEDIRS; do
+	    mount --rbind $h $h 
+	    mount --make-private $h
+	done
+
+	RETVAL=$?
+	exit $RETVAL
+	;;
+
+    status)
+	if [ -f "$LOCKFILE" ]; then 
+	    echo "$base is running"
+	else
+	    echo "$base is stopped"
+	fi
+	exit 0
+	;;
+
+    stop)
+	rm -f $LOCKFILE
+	exit 0
+	;;
+
+    *)
+	echo $"Usage: $0 {start|stop}"
+	exit 3
+	;;
+esac
diff --git a/policycoreutils/sandbox/sandboxX.sh b/policycoreutils/sandbox/sandboxX.sh
new file mode 100644
index 0000000..ed318d0
--- /dev/null
+++ b/policycoreutils/sandbox/sandboxX.sh
@@ -0,0 +1,15 @@
+#!/bin/bash 
+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 }'`
+trap "exit 0" HUP
+
+(/usr/bin/Xephyr -title "$TITLE" -terminate -screen $SCREENSIZE -displayfd 5 5>&1 2>/dev/null) | while read D; do 
+    export DISPLAY=:$D
+    python -c 'import gtk, os; os.system("%s/.sandboxrc" % os.environ["HOME"])'
+    export EXITCODE=$?
+    kill -HUP 0
+    break
+done
+exit 0
diff --git a/policycoreutils/sandbox/seunshare.c b/policycoreutils/sandbox/seunshare.c
new file mode 100644
index 0000000..ddf6bf1
--- /dev/null
+++ b/policycoreutils/sandbox/seunshare.c
@@ -0,0 +1,265 @@
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <syslog.h>
+#include <sys/mount.h>
+#include <pwd.h>
+#define _GNU_SOURCE
+#include <sched.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.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 <selinux/selinux.h>
+#include <selinux/context.h>	/* for context-mangling functions */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+/**
+ * This function will drop all capabilities 
+ * Returns zero on success, non-zero otherwise
+ */
+static int drop_capabilities(uid_t uid)
+{
+	capng_clear(CAPNG_SELECT_BOTH);
+
+	if (capng_lock() < 0) 
+		return -1;
+	/* Change uid */
+	if (setresuid(uid, uid, uid)) {
+		fprintf(stderr, "Error changing uid, aborting.\n");
+		return -1;
+	}
+	return capng_apply(CAPNG_SELECT_BOTH);
+}
+
+#define DEFAULT_PATH "/usr/bin:/bin"
+#define TRUE 1
+#define FALSE 0
+
+/**
+ * Take care of any signal setup
+ */
+static int set_signal_handles(void)
+{
+	sigset_t empty;
+
+	/* Empty the signal mask in case someone is blocking a signal */
+	if (sigemptyset(&empty)) {
+		fprintf(stderr, "Unable to obtain empty signal set\n");
+		return -1;
+	}
+
+	(void)sigprocmask(SIG_SETMASK, &empty, NULL);
+
+	/* Terminate on SIGHUP. */
+	if (signal(SIGHUP, SIG_DFL) == SIG_ERR) {
+		perror("Unable to set SIGHUP handler");
+		return -1;
+	}
+
+	return 0;
+}
+#define USAGE_STRING "USAGE: seunshare [ -t tmpdir ] [ -h homedir ] -- CONTEXT executable [args] "
+
+
+
+static int verify_mount(const char *mntdir, struct passwd *pwd) {
+	struct stat sb;
+	if (stat(mntdir, &sb) == -1) {
+		perror("Invalid mount point");
+		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");
+		return -1;
+	}
+	return 0;
+}
+
+/**
+ * This function checks to see if the shell is known in /etc/shells.
+ * If so, it returns 1. On error or illegal shell, it returns 0.
+ */
+static int verify_shell(const char *shell_name)
+{
+	int found = 0;
+	const char *buf;
+
+	if (!(shell_name && shell_name[0]))
+		return found;
+
+	while ((buf = getusershell()) != NULL) {
+		/* ignore comments */
+		if (*buf == '#')
+			continue;
+
+		/* check the shell skipping newline char */
+		if (!strcmp(shell_name, buf)) {
+			found = 1;
+			break;
+		}
+	}
+	endusershell();
+	return found;
+}
+
+int main(int argc, char **argv) {
+	int rc;
+	int status = -1;
+
+	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[] */
+	char *homedir_s = NULL;	/* homedir spec'd by user in argv[] */
+
+	const struct option long_options[] = {
+		{"homedir", 1, 0, 'h'},
+		{"tmpdir", 1, 0, 't'},
+		{NULL, 0, 0, 0}
+	};
+
+	uid_t uid = getuid();
+
+	if (!uid) {
+		fprintf(stderr, "Must not be root");
+		return -1;
+	}
+
+	struct passwd *pwd=getpwuid(uid);
+	if (!pwd) {
+		perror("getpwduid failed");
+		return -1;
+	}
+
+	if (verify_shell(pwd->pw_shell) == 0) {
+		fprintf(stderr, "Error!  Shell is not valid.\n");
+		return -1;
+	}
+
+	while (1) {
+		clflag = getopt_long(argc, argv, "h:t:", long_options,
+				     &flag_index);
+		if (clflag == -1)
+			break;
+
+		switch (clflag) {
+		case 't':
+			tmpdir_s = optarg;
+			if (verify_mount(tmpdir_s, pwd) < 0) return -1;
+			break;
+		case 'h':
+			homedir_s = optarg;
+			if (verify_mount(homedir_s, pwd) < 0) return -1;
+			if (verify_mount(pwd->pw_dir, pwd) < 0) return -1;
+			break;
+		default:
+			fprintf(stderr, "%s\n", USAGE_STRING);
+			return -1;
+		}
+	}
+
+	if (! homedir_s && ! tmpdir_s) {
+		fprintf(stderr, "Error: tmpdir and/or homedir required \n"
+			"%s\n", USAGE_STRING);
+		return -1;
+	}
+
+	if (argc - optind < 2) {
+		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");
+		return -1;
+	}
+
+	if (homedir_s && mount(homedir_s, pwd->pw_dir, NULL, MS_BIND, NULL) < 0) {
+		perror("Failed to mount HOMEDIR");
+		return -1;
+	}
+
+	if (homedir_s && verify_mount(pwd->pw_dir, pwd) < 0) 
+		return -1;
+
+	if (tmpdir_s && mount(tmpdir_s, "/tmp", NULL, MS_BIND, NULL) < 0) {
+		perror("Failed to mount /tmp");
+		return -1;
+	}
+
+	if (tmpdir_s && verify_mount("/tmp", pwd) < 0) 
+		return -1;
+
+	if (drop_capabilities(uid)) {
+		perror("Failed to drop all capabilities");
+		return -1;
+	}
+
+	int child = fork();
+	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 ((rc = clearenv())) {
+			perror("Unable to clear environment");
+			free(display);
+			exit(-1);
+		}
+		
+		if (setexeccon(scontext)) {
+			fprintf(stderr, "Could not set exec context to %s.\n",
+				scontext);
+			free(display);
+			exit(-1);
+		}
+
+		if (display) 
+			rc |= setenv("DISPLAY", display, 1);
+		rc |= setenv("HOME", pwd->pw_dir, 1);
+		rc |= setenv("SHELL", pwd->pw_shell, 1);
+		rc |= setenv("USER", pwd->pw_name, 1);
+		rc |= setenv("LOGNAME", pwd->pw_name, 1);
+		rc |= setenv("PATH", DEFAULT_PATH, 1);
+		
+		if (chdir(pwd->pw_dir)) {
+			perror("Failed to change dir to homedir");
+			exit(-1);
+		}
+		setsid();
+		execv(argv[optind], argv + optind);
+		free(display);
+		perror("execv");
+		exit(-1);
+	} else {
+		waitpid(child, &status, 0);
+	}
+
+	return status;
+}
diff --git a/policycoreutils/sandbox/test_sandbox.py b/policycoreutils/sandbox/test_sandbox.py
new file mode 100644
index 0000000..b3b7f64
--- /dev/null
+++ b/policycoreutils/sandbox/test_sandbox.py
@@ -0,0 +1,98 @@
+import unittest, os, shutil 
+from tempfile import mkdtemp
+from subprocess import Popen, PIPE
+
+class SandboxTests(unittest.TestCase):
+    def assertDenied(self, err):
+        self.assert_('Permission denied' in err,
+                     '"Permission denied" not found in %r' % err)
+    def assertNotFound(self, err):
+        self.assert_('not found' in err,
+                     '"not found" not found in %r' % err)
+
+    def assertFailure(self, status):
+        self.assert_(status != 0,
+                     '"Succeeded when it should have failed')
+
+    def assertSuccess(self, status, err):
+        self.assert_(status == 0,
+                     '"Sandbox should have succeeded for this test %r' %  err)
+
+    def test_simple_success(self):
+        "Verify that we can read file descriptors handed to sandbox"
+        p1 = Popen(['cat', '/etc/passwd'], stdout = PIPE)
+        p2 = Popen(['sandbox', 'grep', 'root'], stdin = p1.stdout, stdout=PIPE)
+        out, err = p2.communicate()
+        self.assert_('root' in out)
+
+    def test_cant_kill(self):
+        "Verify that we cannot send kill signal in the sandbox"
+        pid = os.getpid()
+        p = Popen(['sandbox', 'kill', '-HUP', str(pid)], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        self.assertDenied(err)
+
+    def test_cant_ping(self):
+        "Verify that we can't ping within the sandbox"
+        p = Popen(['sandbox', 'ping', '-c 1 ', '127.0.0.1'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        self.assertDenied(err)
+    
+    def test_cant_mkdir(self):
+        "Verify that we can't mkdir within the sandbox"
+        p = Popen(['sandbox', 'mkdir', '~/test'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        self.assertFailure(p.returncode)
+
+    def test_cant_list_homedir(self):
+        "Verify that we can't list homedir within the sandbox"
+        p = Popen(['sandbox', 'ls', '~'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        self.assertFailure(p.returncode)
+
+    def test_cant_send_mail(self):
+        "Verify that we can't send mail within the sandbox"
+        p = Popen(['sandbox', 'mail'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        self.assertDenied(err)
+    
+    def test_cant_sudo(self):
+        "Verify that we can't run sudo within the sandbox"
+        p = Popen(['sandbox', 'sudo'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        self.assertFailure(p.returncode)
+    
+    def test_mount(self):
+        "Verify that we mount a file system"
+        p = Popen(['sandbox', '-M', 'id'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        self.assertSuccess(p.returncode, err)
+    
+    def test_set_level(self):
+        "Verify that we set level a file system"
+        p = Popen(['sandbox', '-l', 's0', 'id'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        self.assertSuccess(p.returncode, err)
+    
+    def test_homedir(self):
+        "Verify that we set homedir a file system"
+        homedir = mkdtemp(dir=".", prefix=".sandbox_test")
+        p = Popen(['sandbox', '-H', homedir, '-M', 'id'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        shutil.rmtree(homedir)
+        self.assertSuccess(p.returncode, err)
+    
+    def test_tmpdir(self):
+        "Verify that we set tmpdir a file system"
+        tmpdir = mkdtemp(dir="/tmp", prefix=".sandbox_test")
+        p = Popen(['sandbox', '-T', tmpdir, '-M', 'id'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        shutil.rmtree(tmpdir)
+        self.assertSuccess(p.returncode, err)
+    
+if __name__ == "__main__":
+    import selinux
+    if selinux.security_getenforce() == 1:
+        unittest.main()
+    else:
+        print "SELinux must be in enforcing mode for this test"

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: Updated sandbox patch.
@ 2010-05-18 18:58 Steve Lawrence
  2010-05-19 17:31 ` Daniel J Walsh
  2010-05-19 19:59 ` Daniel J Walsh
  0 siblings, 2 replies; 10+ messages in thread
From: Steve Lawrence @ 2010-05-18 18:58 UTC (permalink / raw)
  To: Daniel J Walsh; +Cc: SELinux

Chad asked me to take a look at this patch. My comments are inline.

- Steve

> diff --git a/policycoreutils/Makefile b/policycoreutils/Makefile
> index 538302b..b5b97bc 100644
> --- a/policycoreutils/Makefile
> +++ b/policycoreutils/Makefile
> @@ -1,4 +1,4 @@
> -SUBDIRS = setfiles semanage load_policy newrole run_init secon audit2allow audit2why scripts sestatus semodule_package semodule semodule_link semodule_expand semodule_deps setsebool po
> +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 
>  
>  INOTIFYH = $(shell ls /usr/include/sys/inotify.h 2>/dev/null)
>  
> diff --git a/policycoreutils/sandbox/Makefile b/policycoreutils/sandbox/Makefile
> new file mode 100644
> index 0000000..ff0ee7c
> --- /dev/null
> +++ b/policycoreutils/sandbox/Makefile
> @@ -0,0 +1,41 @@
> +# Installation directories.
> +PREFIX ?= ${DESTDIR}/usr
> +INITDIR ?= ${DESTDIR}/etc/rc.d/init.d/
> +SYSCONFDIR ?= ${DESTDIR}/etc/sysconfig
> +BINDIR ?= $(PREFIX)/bin
> +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 
> +
> +all: sandbox seunshare sandboxX.sh 
> +
> +seunshare: seunshare.o $(EXTRA_OBJS)
> +	$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
> +
> +install: all
> +	-mkdir -p $(BINDIR)
> +	install -m 755 sandbox $(BINDIR)
> +	-mkdir -p $(MANDIR)/man8
> +	install -m 644 sandbox.8 $(MANDIR)/man8/
> +	-mkdir -p $(SBINDIR)
> +	install -m 4755 seunshare $(SBINDIR)/
> +	-mkdir -p $(SHAREDIR)
> +	install -m 755 sandboxX.sh $(SHAREDIR)
> +	-mkdir -p $(INITDIR)
> +	install -m 755 sandbox.init $(INITDIR)/sandbox
> +	-mkdir -p $(SYSCONFDIR)
> +	install -m 644 sandbox.config $(SYSCONFDIR)/sandbox
> +
> +test:
> +	@python test_sandbox.py -v
> +
> +clean:
> +	-rm -f seunshare *.o *~
> +
> +indent:
> +	../../scripts/Lindent $(wildcard *.[ch])
> +
> +relabel:
> diff --git a/policycoreutils/sandbox/sandbox b/policycoreutils/sandbox/sandbox
> new file mode 100644
> index 0000000..051fa39
> --- /dev/null
> +++ b/policycoreutils/sandbox/sandbox
> @@ -0,0 +1,420 @@
> +#! /usr/bin/python -E
> +# Authors: Dan Walsh <dwalsh@redhat.com>
> +# Authors: Josh Cogliati
> +#
> +# Copyright (C) 2009,2010  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; version 2 only
> +#
> +# 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
> +#
> +
> +import os, sys, socket, random, fcntl, shutil, re, subprocess
> +import selinux
> +import signal
> +from tempfile import mkdtemp
> +import pwd
> +
> +PROGNAME = "policycoreutils"
> +HOMEDIR=pwd.getpwuid(os.getuid()).pw_dir
> +
> +import gettext
> +gettext.bindtextdomain(PROGNAME, "/usr/share/locale")
> +gettext.textdomain(PROGNAME)
> +
> +try:
> +       gettext.install(PROGNAME,
> +                       localedir = "/usr/share/locale",
> +                       unicode=False,
> +                       codeset = 'utf-8')
> +except IOError:
> +       import __builtin__
> +       __builtin__.__dict__['_'] = unicode
> +
> +DEFAULT_TYPE = "sandbox_t"
> +DEFAULT_X_TYPE = "sandbox_x_t"
> +X_FILES = {}

The name X_FILES is a little confusing. It makes me think it is only for
the -X option, but it is for everything. Maybe something like SAVE_FILES
would be more clear?

> +
> +random.seed(None)
> +
> +def sighandler(signum, frame):
> +    signal.signal(signum,  signal.SIG_IGN)
> +    os.kill(0, signum)
> +    raise KeyboardInterrupt
> +
> +def setup_sighandlers():
> +    signal.signal(signal.SIGHUP,  sighandler)
> +    signal.signal(signal.SIGQUIT, sighandler)
> +    signal.signal(signal.SIGTERM, sighandler)
> +
> +def error_exit(msg):
> +    sys.stderr.write("%s: " % sys.argv[0])
> +    sys.stderr.write("%s\n" % msg)
> +    sys.stderr.flush()
> +    sys.exit(1)
> +
> +def copyfile(file, dir, dest):
> +       import re
> +       if file.startswith(dir):
> +              dname = os.path.dirname(file)
> +              bname = os.path.basename(file)
> +              if dname == dir:
> +                     dest = dest + "/" + bname
> +              else:
> +                     newdir = re.sub(dir, dest, dname)
> +                     if not os.path.exists(newdir):
> +                            os.makedirs(newdir)
> +                     dest = newdir + "/" + bname
> +
> +              if os.path.isdir(file):
> +                     shutil.copytree(file, dest)
> +              else:
> +                     shutil.copy2(file, dest)
> +              X_FILES[file] = (dest, os.path.getmtime(dest))
> +
> +def savefile(new, orig, X_ind):
> +       copy = False
> +       if(X_ind):
> +              import gtk
> +              dlg = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO,
> +                                      gtk.BUTTONS_YES_NO,
> +                                      _("Do you want to save changes to '%s' (Y/N): ") % orig)
> +              dlg.set_title(_("Sandbox Message"))
> +              dlg.set_position(gtk.WIN_POS_MOUSE)
> +              dlg.show_all()
> +              rc = dlg.run()
> +              dlg.destroy()
> +              if rc == gtk.RESPONSE_YES:
> +                     copy = True
> +       else:
> +              ans = raw_input(_("Do you want to save changes to '%s' (y/N): ") % orig)
> +              if(re.match(_("[yY]"),ans)):
> +                     copy = True
> +       if(copy):
> +              shutil.copy2(new,orig)
> +
> +def reserve(level):
> +    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
> +    sock.bind("\0%s" % level)
> +    fcntl.fcntl(sock.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
> +
> +def gen_mcs():
> +       while True:
> +              i1 = random.randrange(0, 1024)
> +              i2 = random.randrange(0, 1024)
> +              if i1 == i2:
> +                     continue
> +              if i1 > i2:
> +                     tmp = i1
> +                     i1 = i2
> +                     i2 = tmp
> +                     level = "s0:c%d,c%d" % (i1, i2)

Unnecessary assignment. The above line can be removed.

> +              level = "s0:c%d,c%d" % (i1, i2)
> +              try:
> +                     reserve(level)
> +              except socket.error:
> +                     continue
> +              break
> +       return level
> +
> +def fullpath(cmd):
> +       for i in [ "/", "./", "../" ]:
> +              if cmd.startswith(i):
> +                     return cmd
> +       for i in  os.environ["PATH"].split(':'):
> +              f = "%s/%s" % (i, cmd)
> +              if os.access(f, os.X_OK):
> +                     return f
> +       return cmd
> +
> +class Sandbox:
> +    VERSION = "sandbox .1"
> +    SYSLOG = "/var/log/messages"
> +
> +    def __init__(self):
> +        self.__options = None
> +        self.__cmds = None
> +        self.__init_files = []
> +        self.__paths = []
> +        self.__mount = False
> +        self.__level = None
> +        self.__homedir = None
> +        self.__tmpdir = None
> +
> +    def __validate_mount(self):
> +           if self.__options.level:
> +                  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"):
> +                  raise ValueError("""
> +/usr/sbin/seunshare required for sandbox -M, to install you need to execute 
> +#yum install /usr/sbin/seunshare
> +""")

There are other cases where seunshare is required (e.g. -X, -H or -T). I
would add those to the error message, or maybe just have a more generic
message like "/usr/sbin/seunshare is required for the actions you want
to perform."

> Also, seunshare isn't specific to rpm based distros, I wouldn't mention yum here.
> 
> +           homedir=pwd.getpwuid(os.getuid()).pw_dir
> +           fd = open("/proc/self/mountinfo", "r")
> +           recs = fd.readlines()
> +           fd.close()
> +           for i in recs:
> +                  x = i.split() 
> +                  if x[3] == x[4] and homedir.startswith(x[3]+"/"):
> +                         return
> +           raise ValueError(_("""
> +'%s' is required to be a shared mount point for this tool to run.  
> +'%s' can be added to the HOMEDIR variable in /etc/sysconfig/sandbox
> + along with a reboot will fix the problem.
> +""" % ((os.path.dirname(homedir)), os.path.dirname(homedir))))
> +        
> +    def __mount_callback(self, option, opt, value, parser):
> +           self.__mount = True
> +
> +    def __x_callback(self, option, opt, value, parser):
> +           self.__mount = True
> +           setattr(parser.values, option.dest, True)
> +
> +    def __validdir(self, option, opt, value, parser):
> +           if not os.path.isdir(value):
> +                  raise IOError("Directory "+value+" not found")
> +           self.__mount = True

Since your using the callback __validdir for the homedir and tmpdir
options, the options.dest needs to be set manually via setattr.
Otherwise the -H and -T options are ignored. The following should fix
it:

setattr(parser.values, option.dest, value)

> +
> +    def __include(self, option, opt, value, parser):
> +           rp = os.path.realpath(os.path.expanduser(value))
> +           if not os.path.exists(rp):
> +                  raise IOError(value+" not found")
> +
> +           if rp not in self.__init_files:
> +                  self.__init_files.append(rp)
> +
> +    def __includefile(self, option, opt, value, parser):
> +           fd = open(value, "r")
> +           for i in fd.readlines():
> +                  rp = os.path.realpath(os.path.expanduser(i[:-1]))
> +                  if rp not in self.__init_files and os.path.exists(rp):
> +                         self.__init_files.append(rp)

This should probably throw an error like __include if rp doesn't exist.
In fact, how about just reuse __include, e.g:

for i in fd.readlines():
    self.__include(option, opt, i[:-1], parser)

> +           fd.close()
> +
> +    def __copyfiles(self):
> +           files = self.__init_files + self.__paths
> +           homedir=pwd.getpwuid(os.getuid()).pw_dir
> +           for f in files:
> +                  copyfile(f, homedir, self.__homedir)
> +                  copyfile(f, "/tmp", self.__tmpdir)
> +
> +    def __setup_sandboxrc(self, wm = "/usr/bin/matchbox-window-manager -use_titlebar no"):
> +           execfile =self.__homedir + "/.sandboxrc"
> +           fd = open(execfile, "w+") 
> +           if self.__options.session:
> +                  fd.write("""#!/bin/sh
> +#TITLE: /etc/gdm/Xsession
> +/etc/gdm/Xsession
> +""")
> +           else:
> +                  command = " ".join(self.__paths)
> +                  fd.write("""#! /bin/sh
> +#TITLE: %s
> +/usr/bin/test -r ~/.xmodmap && /usr/bin/xmodmap ~/.xmodmap
> +%s &
> +WM_PID=$!
> +%s
> +kill -TERM $WM_PID  2> /dev/null
> +""" % (command, wm, command))
> +           fd.close()
> +           os.chmod(execfile, 0700)
> +
> +    def usage(self, message = ""):
> +           error_exit("%s\n%s" % (self.__parser.usage, message))
> +
> +    def __parse_options(self):
> +        from optparse import OptionParser
> +        usage = _("""
> +sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [-W windowmanager ] [[-i file ] ...] [ -t type ] command
> +
> +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()
> +        parser.add_option("-i", "--include", 
> +                          action="callback", callback=self.__include, 
> +                          type="string",
> +                          help="include file in sandbox")
> +        parser.add_option("-I", "--includefile",  action="callback", callback=self.__includefile,
> +                          type="string",
> +                          help="include contents of file in sandbox")

This description is a little confusing, what about something like "read
list of files to include in sandbox from INCLUDEFILE"?

> +        parser.add_option("-t", "--type", dest="setype", action="store", default=DEFAULT_TYPE,
> +                          help="Run sandbox with SELinux type")

Lowercase "run" to have consistent capitalization across help text

> +        parser.add_option("-M", "--mount", 
> +                          action="callback", callback=self.__mount_callback, 
> +                          help="Mount new home and tmp Dir")
> +

How about "mount new home and temporary directories"

> +        parser.add_option("-S", "--session", action="store_true",  dest="session", 
> +                          default=False,  help="Run complete desktop session within sandbox")

Lower case "run" for consistent capitalization

> +        parser.add_option("-X", dest="X_ind", 
> +                          action="callback", callback=self.__x_callback, 
> +                          default=False,  help="Run X sandbox")

Ditto for "run"

> +
> +        parser.add_option("-H", "--homedir", 
> +                          action="callback", callback=self.__validdir,
> +                          type="string",
> +                          dest="homedir",  
> +                          help="Alternate homedir to use for mounting")

Ditto for "alternate"

> +
> +        parser.add_option("-T", "--tmpdir", dest="tmpdir",  
> +                          type="string",
> +                          action="callback", callback=self.__validdir,
> +                          help="Alternate tempdir to use for mounting")

Ditto for "alternate"

> +
> +        parser.add_option("-W", "--windowmanager", dest="wm",  
> +                          type="string",
> +                          default="/usr/bin/matchbox-window-manager -use_titlebar no",
> +                          help="Alternate window maanger")

Ditto for "alternate"

> +
> +        parser.add_option("-l", "--level", dest="level", 
> +                          help="MCS/MLS Level for the sandbox")
> +
> +        self.__parser=parser
> +
> +        self.__options, cmds = parser.parse_args()
> +
> +        if self.__options.X_ind:
> +               if DEFAULT_TYPE == self.__options.setype:
> +                     self.__options.setype = DEFAULT_X_TYPE

There's a corner case here where if the user explicitly sets setype to
DEFAULT_TYPE (e.g. sandbox -X -t sandbox_t) then the sandbox_t type will
be ignored and it will use DEFAULT_X_TYPE (sandbox_x_t) instead. It
seems that if the user specifies --type, that should be used no matter
what.

> +
> +        if self.__mount:
> +               self.__validate_mount()
> +
> +        if self.__options.session:
> +               if self.__options.setype in (DEFAULT_TYPE, DEFAULT_X_TYPE):
> +                      self.__options.setype = selinux.getcon()[1].split(":")[2]

There is a similar corner case here as above. If the user explicitly
specifies the type as sandbox_t or sandbox_x_t with and also specifies
the -S option, then the type would be ignored and the result of getcon()
will be used.

> +               if not self.__options.homedir or not self.__options.tmpdir:
> +                      self.usage(_("Homedir and tempdir required for session"))
> +               if len(cmds) > 0:
> +                      self.usage(_("Commands not allowed in a session"))
> +        else:
> +               if len(cmds) == 0:
> +                      self.usage(_("Command required"))
> +               cmds[0] = fullpath(cmds[0])
> +               self.__cmds = cmds
> +
> +        for f in cmds:
> +               rp = os.path.realpath(f)
> +               if os.path.exists(rp):
> +                      self.__paths.append(rp)
> +               else:
> +                      self.__paths.append(f)
> +                  
> +    def __gen_context(self):
> +           if self.__options.level:
> +                  level = self.__options.level
> +           else:
> +                  level = gen_mcs()
> +
> +           con = selinux.getcon()[1].split(":")
> +           self.__execcon = "%s:%s:%s:%s" % (con[0], con[1], self.__options.setype, level)
> +           self.__filecon = "%s:%s:%s:%s" % (con[0], "object_r", 
> +                                             "%s_file_t" % self.__options.setype[:-2], 
> +                                             level)

This assumes setype is of the form foo_t, is this a safe assumption?

> +    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)
> +
> +           import warnings 
> +           warnings.simplefilter("ignore")

What warnings are being ignored? Can those be fixed?

> +           if self.__options.homedir:
> +                  chcon =  ("/usr/bin/chcon -R %s %s" % (self.__filecon, self.__options.homedir)).split()

This will fail if homedir has spaces. Why not just use spawnl? The
number of parameters is always fixed.

> +                  rc = os.spawnvp(os.P_WAIT, chcon[0], chcon)

This doesn't check return code if chcon fails.

> +                  self.__homedir = self.__options.homedir
> +           else:
> +                  selinux.setfscreatecon(self.__filecon)
> +                  self.__homedir = mkdtemp(dir=sandboxdir, prefix=".sandbox")
> +
> +           if self.__options.tmpdir:
> +                  chcon =  ("/usr/bin/chcon -R %s %s" % (self.__filecon, self.__options.tmpdir)).split()
> +                  rc = os.spawnvp(os.P_WAIT, chcon[0], chcon)

Same as previous two comments

> +                  self.__tmpdir = self.__options.homedir
> +           else:
> +                  selinux.setfscreatecon(self.__filecon)
> +                  self.__tmpdir = mkdtemp(dir="/tmp", prefix=".sandbox")
> +           warnings.resetwarnings()
> +           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 %s -h %s -- %s /usr/share/sandbox/sandboxX.sh" % (self.__tmpdir, self.__homedir, self.__execcon)).split()

Ditto

> +                         rc = os.spawnvp(os.P_WAIT, cmds[0], cmds)
> +                         return rc
> +
> +                  if self.__mount:
> +                         cmds =  ("/usr/sbin/seunshare -t %s -h %s -- %s " % (self.__tmpdir, self.__homedir, self.__execcon)).split()+self.__paths

Ditto

> +                         rc = os.spawnvp(os.P_WAIT, cmds[0], cmds)
> +                         return rc
> +
> +                  selinux.setexeccon(self.__execcon)
> +                  rc = os.spawnvp(os.P_WAIT, self.__cmds[0], self.__cmds)
> +                  selinux.setexeccon(None)
> +                  return rc
> +
> +           finally:
> +                  for i in self.__paths:
> +                         if i not in X_FILES:
> +                                continue
> +                         (dest, mtime) = X_FILES[i]
> +                         if os.path.getmtime(dest) > mtime:
> +                                savefile(dest, i, X_ind)

This should be savefile(dest, i, self.__options.X_ind)

> +
> +                  if self.__homedir and not self.__options.homedir: 
> +                         shutil.rmtree(self.__homedir)
> +                  if self.__tmpdir and not self.__options.tmpdir:
> +                         shutil.rmtree(self.__tmpdir)
> +    def main(self):
> +        try:
> +               self.__parse_options()
> +               self.__gen_context()
> +               self.__setup_dir()
> +               return self.__execute()
> +        except KeyboardInterrupt:
> +            sys.exit(0)
> +
> +
> +if __name__ == '__main__':
> +    setup_sighandlers()
> +    if selinux.is_selinux_enabled() != 1:
> +        error_exit("Requires an SELinux enabled system")
> +    
> +    try:
> +           sandbox = Sandbox()
> +           rc = sandbox.main()
> +    except OSError, error:
> +           error_exit(error.args[1])
> +    except ValueError, error:
> +           error_exit(error.args[0])
> +    except KeyError, error:
> +           error_exit(_("Invalid value %s") % error.args[0])
> +    except IOError, error:
> +           error_exit(error)
> +    except KeyboardInterrupt:
> +           rc = 0
> +           
> +    sys.exit(rc)
> diff --git a/policycoreutils/sandbox/sandbox.8 b/policycoreutils/sandbox/sandbox.8
> new file mode 100644
> index 0000000..ffaae49
> --- /dev/null
> +++ b/policycoreutils/sandbox/sandbox.8
> @@ -0,0 +1,56 @@
> This needs to be updated
> +.TH SANDBOX "8" "May 2009" "chcat" "User Commands"
> +.SH NAME
> +sandbox \- Run cmd under an SELinux sandbox
> +.SH SYNOPSIS
> +.B sandbox
> +sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [-W windowmanager ] [[-i file ] ...] [ -t type ] -S

The usage is missing the 'cmd' option. Also, the -S option isn't
described anywhere. Adding the usage description from sandbox.py ("Run
complete desktop session within sandbox") should be fine.

> +.br
> +.SH DESCRIPTION
> +.PP
> +Run the 
> +.I cmd 
> +application within a tightly confined SELinux domain.  The default sandbox domain only allows applications the ability to read and write stdin, stdout and any other file descriptors handed to it. It is not allowed to open any other files.  The -M option will mount an alternate homedir and tmpdir to be used by the sandbox.
> +
> +If you have the 
> +.I policycoreutils-sandbox 
> +package installed, you can use the -X option and the -M option.
> +.B sandbox -X
> +allows you to run sandboxed X applications.  These applications will start up their own X Server and create a temporary homedir and /tmp.  The default policy does not allow any capabilities or network access.  It also prevents all access to the users other processes and files.  Any file specified on the command line will be copied into the sandbox.
> +
> +If directories are specified with -H or -T the directory will have its context modified with chcon(1) unless a level is specified with -l.  If the MLS/MCS security level is specified, the directories need to have a matching label.
> +.PP
> +.TP
> +\fB\-H\ homedir
> +Use alternate homedir to mount.  Defaults to temporary. Requires -X or -M.
> +.TP
> +\fB\-i file\fR
> +Copy this file into the temporary sandbox appriate. Command can be repeated.

appropriate is spelled wrong

> +.TP
> +\fB\-I inputfile\fR Copy all files listed in inputfile into the
> +appropriate temporary sandbox direcories.

directories is spelled wrong

> +.TP
> +\fB\-l\fR
> +Specify the MLS/MCS Security Level to run the sandbox in.  Defaults to random.
> +.TP
> +\fB\-M\fR
> +Create a Sandbox with temporary files for $HOME and /tmp, defaults to sandbox_t
> +.TP
> +\fB\-t type\fR
> +Use alternate sandbox type, defaults to sandbox_t or sandbox_x_t for -X.
> +.TP
> +\fB\-T\ tmpdir
> +Use alternate tempdir to mount.  Defaults to temporary. Requires -X or -M.

Should this be "Defaults to /tmp."?

> +.TP
> +\fB\-W windowmanager\fR
> +Select alternative window manager to run within 
> +.B sandbox -X.
> +Default to /usr/bin/matchbox-window-manager.
> +.TP
> +\fB\-X\fR 
> +Create an X based Sandbox for gui apps, temporary files for
> +$HOME and /tmp, seconday Xserver, defaults to sandbox_x_t
> +.PP
> +.SH "SEE ALSO"
> +.TP
> +runcon(1)
> +.PP
> diff --git a/policycoreutils/sandbox/sandbox.config b/policycoreutils/sandbox/sandbox.config
> new file mode 100644
> index 0000000..f9f059a
> --- /dev/null
> +++ b/policycoreutils/sandbox/sandbox.config
> @@ -0,0 +1,2 @@
> +# Space separate list of homedirs
> +HOMEDIRS="/home"
> diff --git a/policycoreutils/sandbox/sandbox.init b/policycoreutils/sandbox/sandbox.init
> new file mode 100644
> index 0000000..30d0861
> --- /dev/null
> +++ b/policycoreutils/sandbox/sandbox.init
> @@ -0,0 +1,67 @@
> +#!/bin/bash
> +## BEGIN INIT INFO
> +# Provides: sandbox
> +# Default-Start: 3 4 5
> +# Default-Stop: 0 1 2 3 4 6
> +# Required-Start:
> +#              
> +## END INIT INFO
> +# sandbox:        Set up / mountpoint to be shared, /var/tmp, /tmp, /home/sandbox unshared
> +#
> +# chkconfig: 345 1 99
> +#
> +# Description: sandbox is using pam_namespace to share the /var/tmp, /tmp and 
> +#              /home/sandbox accounts.  This script will setup the / mount 
> +#              point as shared and all of the subdirectories just these 
> +#              directories as unshared.

The last sentence doesn't make sense. Is this what you meant?

"This script will setup the / mount point as shared and each of these
directories as unshared."

> +#
> +
> +# Source function library.
> +. /etc/init.d/functions
> +
> +HOMEDIRS="/home"
> +
> +. /etc/sysconfig/sandbox
> +
> +LOCKFILE=/var/lock/subsys/sandbox
> +
> +base=${0##*/}
> +
> +case "$1" in
> +    start)
> +	[ -f "$LOCKFILE" ] && exit 0
> +
> +	touch $LOCKFILE
> +	mount --make-rshared /
> +	mount --rbind /tmp /tmp
> +	mount --rbind /var/tmp /var/tmp
> +	mount --make-private /tmp
> +	mount --make-private /var/tmp
> +	for h in $HOMEDIRS; do
> +	    mount --rbind $h $h 
> +	    mount --make-private $h
> +	done
> +
> +	RETVAL=$?
> +	exit $RETVAL

RETVAL is meaningless here. It only captures the result of the last
mount command. I would make this should check the results of each mount
and exit on failure.

> +	;;
> +
> +    status)
> +	if [ -f "$LOCKFILE" ]; then 
> +	    echo "$base is running"
> +	else
> +	    echo "$base is stopped"
> +	fi
> +	exit 0
> +	;;
> +
> +    stop)
> +	rm -f $LOCKFILE
> +	exit 0
> +	;;
> +
> +    *)
> +	echo $"Usage: $0 {start|stop}"

The usage string is missing 'status'

> +	exit 3
> +	;;
> +esac
> diff --git a/policycoreutils/sandbox/sandboxX.sh b/policycoreutils/sandbox/sandboxX.sh
> new file mode 100644
> index 0000000..ed318d0
> --- /dev/null
> +++ b/policycoreutils/sandbox/sandboxX.sh
> @@ -0,0 +1,15 @@
> +#!/bin/bash 
> +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 }'`
> +trap "exit 0" HUP
> +
> +(/usr/bin/Xephyr -title "$TITLE" -terminate -screen $SCREENSIZE -displayfd 5 5>&1 2>/dev/null) | while read D; do 
> +    export DISPLAY=:$D
> +    python -c 'import gtk, os; os.system("%s/.sandboxrc" % os.environ["HOME"])'
> +    export EXITCODE=$?
> +    kill -HUP 0
> +    break
> +done
> +exit 0
> diff --git a/policycoreutils/sandbox/seunshare.c b/policycoreutils/sandbox/seunshare.c
> new file mode 100644
> index 0000000..ddf6bf1
> --- /dev/null
> +++ b/policycoreutils/sandbox/seunshare.c
> 
> @@ -0,0 +1,265 @@
> +#include <signal.h>
> +#include <sys/types.h>
> +#include <sys/wait.h>
> +#include <syslog.h>
> +#include <sys/mount.h>
> +#include <pwd.h>
> +#define _GNU_SOURCE
> +#include <sched.h>
> +#include <string.h>
> +#include <stdio.h>
> +#include <unistd.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 <selinux/selinux.h>
> +#include <selinux/context.h>	/* for context-mangling functions */
> +
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <unistd.h>
> +
> +/**
> + * This function will drop all capabilities 
> + * Returns zero on success, non-zero otherwise
> + */
> +static int drop_capabilities(uid_t uid)
> +{
> +	capng_clear(CAPNG_SELECT_BOTH);
> +
> +	if (capng_lock() < 0) 
> +		return -1;
> +	/* Change uid */
> +	if (setresuid(uid, uid, uid)) {
> +		fprintf(stderr, "Error changing uid, aborting.\n");
> +		return -1;
> +	}
> +	return capng_apply(CAPNG_SELECT_BOTH);
> +}
> +
> +#define DEFAULT_PATH "/usr/bin:/bin"
> +#define TRUE 1
> +#define FALSE 0

TRUE/FALSE is never used anywhere. They can be removed.

> +
> +/**
> + * Take care of any signal setup
> + */
> +static int set_signal_handles(void)
> +{
> +	sigset_t empty;
> +
> +	/* Empty the signal mask in case someone is blocking a signal */
> +	if (sigemptyset(&empty)) {
> +		fprintf(stderr, "Unable to obtain empty signal set\n");
> +		return -1;
> +	}
> +
> +	(void)sigprocmask(SIG_SETMASK, &empty, NULL);
> +
> +	/* Terminate on SIGHUP. */
> +	if (signal(SIGHUP, SIG_DFL) == SIG_ERR) {
> +		perror("Unable to set SIGHUP handler");
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +#define USAGE_STRING "USAGE: seunshare [ -t tmpdir ] [ -h homedir ] -- CONTEXT executable [args] "

This is kindof a weird place to define the usage string. Maybe move this
up near the other defines or down near main() where it is used.

> +
> +
> +
> +static int verify_mount(const char *mntdir, struct passwd *pwd) {
> +	struct stat sb;
> +	if (stat(mntdir, &sb) == -1) {
> +		perror("Invalid mount point");
> +		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");
> +		return -1;
> +	}
> +	return 0;
> +}
> +
> +/**
> + * This function checks to see if the shell is known in /etc/shells.
> + * If so, it returns 1. On error or illegal shell, it returns 0.
> + */

Why does this return 1 on success and 0 on failure? This is the opposite
behavior from the other functions.

> +static int verify_shell(const char *shell_name)
> +{
> +	int found = 0;
> +	const char *buf;
> +
> +	if (!(shell_name && shell_name[0]))
> +		return found;
> +
> +	while ((buf = getusershell()) != NULL) {
> +		/* ignore comments */
> +		if (*buf == '#')
> +			continue;
> +
> +		/* check the shell skipping newline char */
> +		if (!strcmp(shell_name, buf)) {
> +			found = 1;
> +			break;
> +		}
> +	}
> +	endusershell();
> +	return found;
> +}
> +
> +int main(int argc, char **argv) {
> +	int rc;
> +	int status = -1;
> +
> +	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[] */
> +	char *homedir_s = NULL;	/* homedir spec'd by user in argv[] */
> +
> +	const struct option long_options[] = {
> +		{"homedir", 1, 0, 'h'},
> +		{"tmpdir", 1, 0, 't'},
> +		{NULL, 0, 0, 0}
> +	};
> +
> +	uid_t uid = getuid();
> +
> +	if (!uid) {
> +		fprintf(stderr, "Must not be root");

Why restrict this to non-root users only?

> +		return -1;
> +	}
> +
> +	struct passwd *pwd=getpwuid(uid);
> +	if (!pwd) {
> +		perror("getpwduid failed");
> +		return -1;
> +	}
> +
> +	if (verify_shell(pwd->pw_shell) == 0) {
> +		fprintf(stderr, "Error!  Shell is not valid.\n");
> +		return -1;
> +	}
> +
> +	while (1) {
> +		clflag = getopt_long(argc, argv, "h:t:", long_options,
> +				     &flag_index);
> +		if (clflag == -1)
> +			break;
> +
> +		switch (clflag) {
> +		case 't':
> +			tmpdir_s = optarg;
> +			if (verify_mount(tmpdir_s, pwd) < 0) return -1;
> +			break;
> +		case 'h':
> +			homedir_s = optarg;
> +			if (verify_mount(homedir_s, pwd) < 0) return -1;
> +			if (verify_mount(pwd->pw_dir, pwd) < 0) return -1;
> +			break;
> +		default:
> +			fprintf(stderr, "%s\n", USAGE_STRING);
> +			return -1;
> +		}
> +	}
> +
> +	if (! homedir_s && ! tmpdir_s) {
> +		fprintf(stderr, "Error: tmpdir and/or homedir required \n"
> +			"%s\n", USAGE_STRING);
> +		return -1;
> +	}
> +
> +	if (argc - optind < 2) {
> +		fprintf(stderr, "Error: executable required \n"

Should be "Error: context and executable required"

> +			"%s\n", USAGE_STRING);
> +		return -1;
> +	}
> +
> +	scontext = argv[optind++];
> +	
> +	if (set_signal_handles())
> +		return -1;
> +
> +        if (unshare(CLONE_NEWNS) < 0) {
> +		perror("Failed to unshare");
> +		return -1;
> +	}
> +
> +	if (homedir_s && mount(homedir_s, pwd->pw_dir, NULL, MS_BIND, NULL) < 0) {
> +		perror("Failed to mount HOMEDIR");
> +		return -1;
> +	}
> +
> +	if (homedir_s && verify_mount(pwd->pw_dir, pwd) < 0) 
> +		return -1;
> +
> +	if (tmpdir_s && mount(tmpdir_s, "/tmp", NULL, MS_BIND, NULL) < 0) {
> +		perror("Failed to mount /tmp");
> +		return -1;
> +	}
> +
> +	if (tmpdir_s && verify_mount("/tmp", pwd) < 0) 
> +		return -1;
> +
> +	if (drop_capabilities(uid)) {
> +		perror("Failed to drop all capabilities");
> +		return -1;
> +	}
> +
> +	int child = fork();

The error case of fork isn't checked, maybe it's not necessary, but it
would be nice to display an error message instead of silently failing.

> +	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 ((rc = clearenv())) {
> +			perror("Unable to clear environment");
> +			free(display);
> +			exit(-1);
> +		}
> +		
> +		if (setexeccon(scontext)) {
> +			fprintf(stderr, "Could not set exec context to %s.\n",
> +				scontext);
> +			free(display);
> +			exit(-1);
> +		}
> +
> +		if (display) 
> +			rc |= setenv("DISPLAY", display, 1);
> +		rc |= setenv("HOME", pwd->pw_dir, 1);
> +		rc |= setenv("SHELL", pwd->pw_shell, 1);
> +		rc |= setenv("USER", pwd->pw_name, 1);
> +		rc |= setenv("LOGNAME", pwd->pw_name, 1);
> +		rc |= setenv("PATH", DEFAULT_PATH, 1);
> +	
> +		if (chdir(pwd->pw_dir)) {
> +			perror("Failed to change dir to homedir");
> +			exit(-1);
> +		}
> +		setsid();
> +		execv(argv[optind], argv + optind);
> +		free(display);
> +		perror("execv");
> +		exit(-1);
> +	} else {
> +		waitpid(child, &status, 0);
> +	}
> +
> +	return status;
> +}
> diff --git a/policycoreutils/sandbox/test_sandbox.py b/policycoreutils/sandbox/test_sandbox.py
> new file mode 100644
> index 0000000..b3b7f64
> --- /dev/null
> +++ b/policycoreutils/sandbox/test_sandbox.py
> @@ -0,0 +1,98 @@
> +import unittest, os, shutil 
> +from tempfile import mkdtemp
> +from subprocess import Popen, PIPE
> +
> +class SandboxTests(unittest.TestCase):
> +    def assertDenied(self, err):
> +        self.assert_('Permission denied' in err,
> +                     '"Permission denied" not found in %r' % err)
> +    def assertNotFound(self, err):
> +        self.assert_('not found' in err,
> +                     '"not found" not found in %r' % err)
> +
> +    def assertFailure(self, status):
> +        self.assert_(status != 0,
> +                     '"Succeeded when it should have failed')
> +
> +    def assertSuccess(self, status, err):
> +        self.assert_(status == 0,
> +                     '"Sandbox should have succeeded for this test %r' %  err)
> +
> +    def test_simple_success(self):
> +        "Verify that we can read file descriptors handed to sandbox"
> +        p1 = Popen(['cat', '/etc/passwd'], stdout = PIPE)
> +        p2 = Popen(['sandbox', 'grep', 'root'], stdin = p1.stdout, stdout=PIPE)
> +        out, err = p2.communicate()
> +        self.assert_('root' in out)
> +
> +    def test_cant_kill(self):
> +        "Verify that we cannot send kill signal in the sandbox"
> +        pid = os.getpid()
> +        p = Popen(['sandbox', 'kill', '-HUP', str(pid)], stdout=PIPE, stderr=PIPE)
> +        out, err = p.communicate()
> +        self.assertDenied(err)
> +
> +    def test_cant_ping(self):
> +        "Verify that we can't ping within the sandbox"
> +        p = Popen(['sandbox', 'ping', '-c 1 ', '127.0.0.1'], stdout=PIPE, stderr=PIPE)
> +        out, err = p.communicate()
> +        self.assertDenied(err)
> +    
> +    def test_cant_mkdir(self):
> +        "Verify that we can't mkdir within the sandbox"
> +        p = Popen(['sandbox', 'mkdir', '~/test'], stdout=PIPE, stderr=PIPE)
> +        out, err = p.communicate()
> +        self.assertFailure(p.returncode)
> +
> +    def test_cant_list_homedir(self):
> +        "Verify that we can't list homedir within the sandbox"
> +        p = Popen(['sandbox', 'ls', '~'], stdout=PIPE, stderr=PIPE)
> +        out, err = p.communicate()
> +        self.assertFailure(p.returncode)
> +
> +    def test_cant_send_mail(self):
> +        "Verify that we can't send mail within the sandbox"
> +        p = Popen(['sandbox', 'mail'], stdout=PIPE, stderr=PIPE)
> +        out, err = p.communicate()
> +        self.assertDenied(err)
> +    
> +    def test_cant_sudo(self):
> +        "Verify that we can't run sudo within the sandbox"
> +        p = Popen(['sandbox', 'sudo'], stdout=PIPE, stderr=PIPE)
> +        out, err = p.communicate()
> +        self.assertFailure(p.returncode)
> +    
> +    def test_mount(self):
> +        "Verify that we mount a file system"
> +        p = Popen(['sandbox', '-M', 'id'], stdout=PIPE, stderr=PIPE)
> +        out, err = p.communicate()
> +        self.assertSuccess(p.returncode, err)
> +    
> +    def test_set_level(self):
> +        "Verify that we set level a file system"
> +        p = Popen(['sandbox', '-l', 's0', 'id'], stdout=PIPE, stderr=PIPE)
> +        out, err = p.communicate()
> +        self.assertSuccess(p.returncode, err)
> +    
> +    def test_homedir(self):
> +        "Verify that we set homedir a file system"
> +        homedir = mkdtemp(dir=".", prefix=".sandbox_test")
> +        p = Popen(['sandbox', '-H', homedir, '-M', 'id'], stdout=PIPE, stderr=PIPE)
> +        out, err = p.communicate()
> +        shutil.rmtree(homedir)
> +        self.assertSuccess(p.returncode, err)
> +    
> +    def test_tmpdir(self):
> +        "Verify that we set tmpdir a file system"
> +        tmpdir = mkdtemp(dir="/tmp", prefix=".sandbox_test")
> +        p = Popen(['sandbox', '-T', tmpdir, '-M', 'id'], stdout=PIPE, stderr=PIPE)
> +        out, err = p.communicate()
> +        shutil.rmtree(tmpdir)
> +        self.assertSuccess(p.returncode, err)
> +    
> +if __name__ == "__main__":
> +    import selinux
> +    if selinux.security_getenforce() == 1:
> +        unittest.main()
> +    else:
> +        print "SELinux must be in enforcing mode for this test"


--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Updated sandbox patch.
  2010-05-18 18:58 Updated sandbox patch Steve Lawrence
@ 2010-05-19 17:31 ` Daniel J Walsh
  2010-05-19 19:59 ` Daniel J Walsh
  1 sibling, 0 replies; 10+ messages in thread
From: Daniel J Walsh @ 2010-05-19 17:31 UTC (permalink / raw)
  To: slawrence; +Cc: SELinux

[-- Attachment #1: Type: text/plain, Size: 889 bytes --]

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Update patch

Changed X_FILES to SAVE_FILES
Removed Level line
Fixed missing seunshare line
Fixed __validdir function
Make __includefiles use __include
Fixed Descriptions
Fixed setype selection
Did not fix "_t" assumption.
Removed warnings ignore.
Changed chcon command to be internal.
Switched to useing Popen rather then spawnvp
Fixed up man pages
Fixed init script description and usage statement
Fixed all of your comments on seunshare.c except running as root.  I
think there is a vulnerability in dropping capabilities that makes
allowing this to be run as root problematic.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.14 (GNU/Linux)
Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org/

iEYEARECAAYFAkv0IIkACgkQrlYvE4MpobPNnQCdGXyaG4YUYoueZb+dG2IjjSiL
8zMAn1bws/c4ub26T12X4ru/+I9nXR/7
=WE7M
-----END PGP SIGNATURE-----

[-- Attachment #2: policycoreutils-sandbox.patch --]
[-- Type: text/plain, Size: 35697 bytes --]

diff --git a/policycoreutils/Makefile b/policycoreutils/Makefile
index 538302b..86ed03f 100644
--- a/policycoreutils/Makefile
+++ b/policycoreutils/Makefile
@@ -1,4 +1,4 @@
-SUBDIRS = setfiles semanage load_policy newrole run_init secon audit2allow audit2why scripts sestatus semodule_package semodule semodule_link semodule_expand semodule_deps setsebool po
+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
 
 INOTIFYH = $(shell ls /usr/include/sys/inotify.h 2>/dev/null)
 
diff --git a/policycoreutils/sandbox/Makefile b/policycoreutils/sandbox/Makefile
new file mode 100644
index 0000000..ff0ee7c
--- /dev/null
+++ b/policycoreutils/sandbox/Makefile
@@ -0,0 +1,41 @@
+# Installation directories.
+PREFIX ?= ${DESTDIR}/usr
+INITDIR ?= ${DESTDIR}/etc/rc.d/init.d/
+SYSCONFDIR ?= ${DESTDIR}/etc/sysconfig
+BINDIR ?= $(PREFIX)/bin
+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 
+
+all: sandbox seunshare sandboxX.sh 
+
+seunshare: seunshare.o $(EXTRA_OBJS)
+	$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+install: all
+	-mkdir -p $(BINDIR)
+	install -m 755 sandbox $(BINDIR)
+	-mkdir -p $(MANDIR)/man8
+	install -m 644 sandbox.8 $(MANDIR)/man8/
+	-mkdir -p $(SBINDIR)
+	install -m 4755 seunshare $(SBINDIR)/
+	-mkdir -p $(SHAREDIR)
+	install -m 755 sandboxX.sh $(SHAREDIR)
+	-mkdir -p $(INITDIR)
+	install -m 755 sandbox.init $(INITDIR)/sandbox
+	-mkdir -p $(SYSCONFDIR)
+	install -m 644 sandbox.config $(SYSCONFDIR)/sandbox
+
+test:
+	@python test_sandbox.py -v
+
+clean:
+	-rm -f seunshare *.o *~
+
+indent:
+	../../scripts/Lindent $(wildcard *.[ch])
+
+relabel:
diff --git a/policycoreutils/sandbox/sandbox b/policycoreutils/sandbox/sandbox
new file mode 100644
index 0000000..0124f6c
--- /dev/null
+++ b/policycoreutils/sandbox/sandbox
@@ -0,0 +1,430 @@
+#! /usr/bin/python -E
+# Authors: Dan Walsh <dwalsh@redhat.com>
+# Authors: Josh Cogliati
+#
+# Copyright (C) 2009,2010  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; version 2 only
+#
+# 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
+#
+
+import os, sys, socket, random, fcntl, shutil, re, subprocess
+import selinux
+import signal
+from tempfile import mkdtemp
+import pwd
+import commands 
+
+PROGNAME = "policycoreutils"
+HOMEDIR=pwd.getpwuid(os.getuid()).pw_dir
+
+import gettext
+gettext.bindtextdomain(PROGNAME, "/usr/share/locale")
+gettext.textdomain(PROGNAME)
+
+try:
+       gettext.install(PROGNAME,
+                       localedir = "/usr/share/locale",
+                       unicode=False,
+                       codeset = 'utf-8')
+except IOError:
+       import __builtin__
+       __builtin__.__dict__['_'] = unicode
+
+DEFAULT_TYPE = "sandbox_t"
+DEFAULT_X_TYPE = "sandbox_x_t"
+SAVE_FILES = {}
+
+random.seed(None)
+
+def sighandler(signum, frame):
+    signal.signal(signum,  signal.SIG_IGN)
+    os.kill(0, signum)
+    raise KeyboardInterrupt
+
+def setup_sighandlers():
+    signal.signal(signal.SIGHUP,  sighandler)
+    signal.signal(signal.SIGQUIT, sighandler)
+    signal.signal(signal.SIGTERM, sighandler)
+
+def error_exit(msg):
+    sys.stderr.write("%s: " % sys.argv[0])
+    sys.stderr.write("%s\n" % msg)
+    sys.stderr.flush()
+    sys.exit(1)
+
+def chcon(path, context, recursive=False):
+    """ Restore SELinux context on a given path """
+    mode = os.lstat(path)[stat.ST_MODE]
+    lsetfilecon(path, context)
+    if recursive:
+           os.path.walk(path, lambda arg, dirname, fnames:
+                               map(chcon, [os.path.join(dirname, fname)
+                                                for fname in fnames]), context)
+def copyfile(file, dir, dest):
+       import re
+       if file.startswith(dir):
+              dname = os.path.dirname(file)
+              bname = os.path.basename(file)
+              if dname == dir:
+                     dest = dest + "/" + bname
+              else:
+                     newdir = re.sub(dir, dest, dname)
+                     if not os.path.exists(newdir):
+                            os.makedirs(newdir)
+                     dest = newdir + "/" + bname
+
+              try:
+                     if os.path.isdir(file):
+                            shutil.copytree(file, dest)
+                     else:
+                            shutil.copy2(file, dest)
+              except shutil.Error, elist:
+                     for e in elist:
+                            sys.stderr.write(e[1])
+                     
+              SAVE_FILES[file] = (dest, os.path.getmtime(dest))
+
+def savefile(new, orig, X_ind):
+       copy = False
+       if(X_ind):
+              import gtk
+              dlg = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO,
+                                      gtk.BUTTONS_YES_NO,
+                                      _("Do you want to save changes to '%s' (Y/N): ") % orig)
+              dlg.set_title(_("Sandbox Message"))
+              dlg.set_position(gtk.WIN_POS_MOUSE)
+              dlg.show_all()
+              rc = dlg.run()
+              dlg.destroy()
+              if rc == gtk.RESPONSE_YES:
+                     copy = True
+       else:
+              ans = raw_input(_("Do you want to save changes to '%s' (y/N): ") % orig)
+              if(re.match(_("[yY]"),ans)):
+                     copy = True
+       if(copy):
+              shutil.copy2(new,orig)
+
+def reserve(level):
+    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+    sock.bind("\0%s" % level)
+    fcntl.fcntl(sock.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
+
+def gen_mcs():
+       while True:
+              i1 = random.randrange(0, 1024)
+              i2 = random.randrange(0, 1024)
+              if i1 == i2:
+                     continue
+              if i1 > i2:
+                     tmp = i1
+                     i1 = i2
+                     i2 = tmp
+              level = "s0:c%d,c%d" % (i1, i2)
+              try:
+                     reserve(level)
+              except socket.error:
+                     continue
+              break
+       return level
+
+def fullpath(cmd):
+       for i in [ "/", "./", "../" ]:
+              if cmd.startswith(i):
+                     return cmd
+       for i in  os.environ["PATH"].split(':'):
+              f = "%s/%s" % (i, cmd)
+              if os.access(f, os.X_OK):
+                     return f
+       return cmd
+
+class Sandbox:
+    VERSION = "sandbox .1"
+    SYSLOG = "/var/log/messages"
+
+    def __init__(self):
+        self.setype = DEFAULT_TYPE
+        self.__options = None
+        self.__cmds = None
+        self.__init_files = []
+        self.__paths = []
+        self.__mount = False
+        self.__level = None
+        self.__homedir = None
+        self.__tmpdir = None
+
+    def __validate_mount(self):
+           if self.__options.level:
+                  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"):
+                  raise ValueError(_("""
+/usr/sbin/seunshare is required for the action you want to perform.  
+Install seunshare by executing:
+
+# yum install /usr/sbin/seunshare
+
+"""))
+
+    def __mount_callback(self, option, opt, value, parser):
+           self.__mount = True
+
+    def __x_callback(self, option, opt, value, parser):
+           self.__mount = True
+           setattr(parser.values, option.dest, True)
+
+    def __validdir(self, option, opt, value, parser):
+           if not os.path.isdir(value):
+                  raise IOError("Directory "+value+" not found")
+           setattr(parser.values, option.dest, value)
+           self.__mount = True
+
+    def __include(self, option, opt, value, parser):
+           rp = os.path.realpath(os.path.expanduser(value))
+           if not os.path.exists(rp):
+                  raise IOError(value+" not found")
+
+           if rp not in self.__init_files:
+                  self.__init_files.append(rp)
+
+    def __includefile(self, option, opt, value, parser):
+           fd = open(value, "r")
+           for i in fd.readlines():
+                  try:
+                         self.__include(option, opt, i[:-1], parser)
+                  except IOError, e:
+                         sys.stderr.write(e)
+           fd.close()
+
+    def __copyfiles(self):
+           files = self.__init_files + self.__paths
+           homedir=pwd.getpwuid(os.getuid()).pw_dir
+           for f in files:
+                  copyfile(f, homedir, self.__homedir)
+                  copyfile(f, "/tmp", self.__tmpdir)
+
+    def __setup_sandboxrc(self, wm = "/usr/bin/matchbox-window-manager -use_titlebar no"):
+           execfile =self.__homedir + "/.sandboxrc"
+           fd = open(execfile, "w+") 
+           if self.__options.session:
+                  fd.write("""#!/bin/sh
+#TITLE: /etc/gdm/Xsession
+/etc/gdm/Xsession
+""")
+           else:
+                  command = " ".join(self.__paths)
+                  fd.write("""#! /bin/sh
+#TITLE: %s
+/usr/bin/test -r ~/.xmodmap && /usr/bin/xmodmap ~/.xmodmap
+%s &
+WM_PID=$!
+%s
+kill -TERM $WM_PID  2> /dev/null
+""" % (command, wm, command))
+           fd.close()
+           os.chmod(execfile, 0700)
+
+    def usage(self, message = ""):
+           error_exit("%s\n%s" % (self.__parser.usage, message))
+
+    def __parse_options(self):
+        from optparse import OptionParser
+        usage = _("""
+sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [-W windowmanager ] [[-i file ] ...] [ -t type ] command
+
+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()
+        parser.add_option("-i", "--include", 
+                          action="callback", callback=self.__include, 
+                          type="string",
+                          help=_("include file in sandbox"))
+        parser.add_option("-I", "--includefile",  action="callback", callback=self.__includefile,
+                          type="string",
+                          help=_("read list of files to include in sandbox from INCLUDEFILE"))
+        parser.add_option("-t", "--type", dest="setype", action="store", default=None,
+                          help=_("run sandbox with SELinux type"))
+        parser.add_option("-M", "--mount", 
+                          action="callback", callback=self.__mount_callback, 
+                          help=_("mount new home and/or tmp directory"))
+
+        parser.add_option("-S", "--session", action="store_true",  dest="session", 
+                          default=False,  help=_("run complete desktop session within sandbox"))
+
+        parser.add_option("-X", dest="X_ind", 
+                          action="callback", callback=self.__x_callback, 
+                          default=False,  help=_("run X application within a sandbox"))
+
+        parser.add_option("-H", "--homedir", 
+                          action="callback", callback=self.__validdir,
+                          type="string",
+                          dest="homedir",  
+                          help=_("alternate home directory to use for mounting"))
+
+        parser.add_option("-T", "--tmpdir", dest="tmpdir",  
+                          type="string",
+                          action="callback", callback=self.__validdir,
+                          help=_("alternate /tmp directory to use for mounting"))
+
+        parser.add_option("-W", "--windowmanager", dest="wm",  
+                          type="string",
+                          default="/usr/bin/matchbox-window-manager -use_titlebar no",
+                          help=_("alternate window manager"))
+
+        parser.add_option("-l", "--level", dest="level", 
+                          help=_("MCS/MLS level for the sandbox"))
+
+        self.__parser=parser
+
+        self.__options, cmds = parser.parse_args()
+
+        if self.__options.X_ind:
+               self.setype = DEFAULT_X_TYPE
+        
+        if self.__options.setype:
+               self.setype = self.__options.setype
+
+        if self.__mount:
+               self.__validate_mount()
+
+        if self.__options.session:
+               if not self.__options.setype:
+                      self.setype = selinux.getcon()[1].split(":")[2]
+               if not self.__options.homedir or not self.__options.tmpdir:
+                      self.usage(_("You must specify a Homedir and tempdir when setting up a session sandbox"))
+               if len(cmds) > 0:
+                      self.usage(_("Commands are not allowed in a session sandbox"))
+               self.__options.X_ind = True
+               self.__homedir = self.__options.homedir
+               self.__tmpdir = self.__options.tmpdir
+        else:
+               if len(cmds) == 0:
+                      self.usage(_("Command required"))
+               cmds[0] = fullpath(cmds[0])
+               if not os.access(cmds[0], os.X_OK):
+                      self.usage(_("%s is not an executable") % cmds[0]  )
+                      
+               self.__cmds = cmds
+
+        for f in cmds:
+               rp = os.path.realpath(f)
+               if os.path.exists(rp):
+                      self.__paths.append(rp)
+               else:
+                      self.__paths.append(f)
+                  
+    def __gen_context(self):
+           if self.__options.level:
+                  level = self.__options.level
+           else:
+                  level = gen_mcs()
+
+           con = selinux.getcon()[1].split(":")
+           self.__execcon = "%s:%s:%s:%s" % (con[0], con[1], self.setype, level)
+           self.__filecon = "%s:%s:%s:%s" % (con[0], "object_r", 
+                                             "%s_file_t" % self.setype[:-2], 
+                                             level)
+    def __setup_dir(self):
+           if self.__options.level or self.__options.session:
+                  return
+           sandboxdir = HOMEDIR + "/.sandbox"
+           if not os.path.exists(sandboxdir):
+                  os.mkdir(sandboxdir)
+
+           if self.__options.homedir:
+                  chcon(self.__options.homedir, self.__filecon, True)
+                  self.__homedir = self.__options.homedir
+           else:
+                  selinux.setfscreatecon(self.__filecon)
+                  self.__homedir = mkdtemp(dir=sandboxdir, prefix=".sandbox")
+
+           if self.__options.tmpdir:
+                  chcon(self.__options.homedir, self.__filecon, True)
+                  self.__tmpdir = self.__options.tmpdir
+           else:
+                  selinux.setfscreatecon(self.__filecon)
+                  self.__tmpdir = mkdtemp(dir="/tmp", prefix=".sandbox")
+           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 "%s" -h "%s" -- %s /usr/share/sandbox/sandboxX.sh' % (self.__tmpdir, self.__homedir, self.__execcon)).split()
+                         rc = subprocess.Popen(cmds).wait()
+                         return rc
+
+                  if self.__mount:
+                         cmds =  ('/usr/sbin/seunshare -t "%s" -h "%s" -- %s ' % (self.__tmpdir, self.__homedir, self.__execcon)).split()+self.__paths
+                         rc = subprocess.Popen(cmds).wait()
+                         return rc
+
+                  selinux.setexeccon(self.__execcon)
+                  rc = subprocess.Popen(self.__cmds).wait()
+                  selinux.setexeccon(None)
+                  return rc
+
+           finally:
+                  for i in self.__paths:
+                         if i not in SAVE_FILES:
+                                continue
+                         (dest, mtime) = SAVE_FILES[i]
+                         if os.path.getmtime(dest) > mtime:
+                                savefile(dest, i, self.__options.X_ind)
+
+                  if self.__homedir and not self.__options.homedir: 
+                         shutil.rmtree(self.__homedir)
+                  if self.__tmpdir and not self.__options.tmpdir:
+                         shutil.rmtree(self.__tmpdir)
+    def main(self):
+        try:
+               self.__parse_options()
+               self.__gen_context()
+               self.__setup_dir()
+               return self.__execute()
+        except KeyboardInterrupt:
+            sys.exit(0)
+
+
+if __name__ == '__main__':
+    setup_sighandlers()
+    if selinux.is_selinux_enabled() != 1:
+        error_exit("Requires an SELinux enabled system")
+    
+    try:
+           sandbox = Sandbox()
+           rc = sandbox.main()
+    except OSError, error:
+           error_exit(error.args[1])
+    except ValueError, error:
+           error_exit(error.args[0])
+    except KeyError, error:
+           error_exit(_("Invalid value %s") % error.args[0])
+    except IOError, error:
+           error_exit(error)
+    except KeyboardInterrupt:
+           rc = 0
+           
+    sys.exit(rc)
diff --git a/policycoreutils/sandbox/sandbox.8 b/policycoreutils/sandbox/sandbox.8
new file mode 100644
index 0000000..1479364
--- /dev/null
+++ b/policycoreutils/sandbox/sandbox.8
@@ -0,0 +1,57 @@
+.TH SANDBOX "8" "May 2009" "chcat" "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
+.br
+.SH DESCRIPTION
+.PP
+Run the 
+.I cmd 
+application within a tightly confined SELinux domain.  The default sandbox domain only allows applications the ability to read and write stdin, stdout and any other file descriptors handed to it. It is not allowed to open any other files.  The -M option will mount an alternate homedir and tmpdir to be used by the sandbox.
+
+If you have the 
+.I policycoreutils-sandbox 
+package installed, you can use the -X option and the -M option.
+.B sandbox -X
+allows you to run X applications within a sandbox.  These applications will start up their own X Server and create a temporary home directory and /tmp.  The default SELinux policy does not allow any capabilities or network access.  It also prevents all access to the users other processes and files.  Files specified on the command that are in the home directory or /tmp will be copied into the sandbox directories.
+
+If directories are specified with -H or -T the directory will have its context modified with chcon(1) unless a level is specified with -l.  If the MLS/MCS security level is specified, the user is responsible to set the correct labels.
+.PP
+.TP
+\fB\-H\ homedir
+Use alternate homedir to mount over your home directory.  Defaults to temporary. Requires -X or -M.
+.TP
+\fB\-i file\fR
+Copy this file into the appropriate temporary sandbox directory. Command can be repeated.
+.TP
+\fB\-I inputfile\fR Copy all files listed in inputfile into the
+appropriate temporary sandbox directories.
+.TP
+\fB\-l\fR
+Specify the MLS/MCS Security Level to run the sandbox with.  Defaults to random.
+.TP
+\fB\-M\fR
+Create a Sandbox with temporary files for $HOME and /tmp.
+.TP
+\fB\-t type\fR
+Use alternate sandbox type, defaults to sandbox_t or sandbox_x_t for -X.
+.TP
+\fB\-T\ tmpdir
+Use alternate tempory directory to mount on /tmp.  Defaults to tmpfs. Requires -X or -M.
+.TP
+\fB\-W windowmanager\fR
+Select alternative window manager to run within 
+.B sandbox -X.
+Default to /usr/bin/matchbox-window-manager.
+.TP
+\fB\-X\fR 
+Create an X based Sandbox for gui apps, temporary files for
+$HOME and /tmp, secondary Xserver, defaults to sandbox_x_t
+.PP
+.SH "SEE ALSO"
+.TP
+runcon(1)
+.PP
diff --git a/policycoreutils/sandbox/sandbox.config b/policycoreutils/sandbox/sandbox.config
new file mode 100644
index 0000000..f9f059a
--- /dev/null
+++ b/policycoreutils/sandbox/sandbox.config
@@ -0,0 +1,2 @@
+# Space separate list of homedirs
+HOMEDIRS="/home"
diff --git a/policycoreutils/sandbox/sandbox.init b/policycoreutils/sandbox/sandbox.init
new file mode 100644
index 0000000..44867d1
--- /dev/null
+++ b/policycoreutils/sandbox/sandbox.init
@@ -0,0 +1,74 @@
+#!/bin/bash
+## BEGIN INIT INFO
+# Provides: sandbox
+# Default-Start: 3 4 5
+# Default-Stop: 0 1 2 3 4 6
+# Required-Start:
+#              
+## END INIT INFO
+# sandbox:        Set up / mountpoint to be shared, /var/tmp, /tmp, /home/sandbox unshared
+#
+# 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.
+#
+
+# Source function library.
+. /etc/init.d/functions
+
+HOMEDIRS="/home"
+
+. /etc/sysconfig/sandbox
+
+LOCKFILE=/var/lock/subsys/sandbox
+
+base=${0##*/}
+
+case "$1" in
+    restart)
+    start)
+	[ -f "$LOCKFILE" ] && exit 0
+
+	touch $LOCKFILE
+	mount --make-rshared /
+	mount --rbind /tmp /tmp
+	mount --rbind /var/tmp /var/tmp
+	mount --make-private /tmp
+	mount --make-private /var/tmp
+	for h in $HOMEDIRS; do
+	    mount --rbind $h $h 
+	    mount --make-private $h
+	done
+
+	exit $?
+	;;
+
+    status)
+	if [ -f "$LOCKFILE" ]; then 
+	    echo "$base is running"
+	else
+	    echo "$base is stopped"
+	fi
+	exit 0
+	;;
+
+    stop)
+	rm -f $LOCKFILE
+	exit 0
+	;;
+
+    *)
+	echo $"Usage: $0 {start|stop|status|restart}"
+	exit 3
+	;;
+esac
diff --git a/policycoreutils/sandbox/sandboxX.sh b/policycoreutils/sandbox/sandboxX.sh
new file mode 100644
index 0000000..ed318d0
--- /dev/null
+++ b/policycoreutils/sandbox/sandboxX.sh
@@ -0,0 +1,15 @@
+#!/bin/bash 
+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 }'`
+trap "exit 0" HUP
+
+(/usr/bin/Xephyr -title "$TITLE" -terminate -screen $SCREENSIZE -displayfd 5 5>&1 2>/dev/null) | while read D; do 
+    export DISPLAY=:$D
+    python -c 'import gtk, os; os.system("%s/.sandboxrc" % os.environ["HOME"])'
+    export EXITCODE=$?
+    kill -HUP 0
+    break
+done
+exit 0
diff --git a/policycoreutils/sandbox/seunshare.c b/policycoreutils/sandbox/seunshare.c
new file mode 100644
index 0000000..848f787
--- /dev/null
+++ b/policycoreutils/sandbox/seunshare.c
@@ -0,0 +1,304 @@
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <syslog.h>
+#include <sys/mount.h>
+#include <pwd.h>
+#define _GNU_SOURCE
+#include <sched.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.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 <selinux/selinux.h>
+#include <selinux/context.h>	/* for context-mangling functions */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#ifdef USE_NLS
+#include <locale.h>		/* for setlocale() */
+#include <libintl.h>		/* for gettext() */
+#define _(msgid) gettext (msgid)
+#else
+#define _(msgid) (msgid)
+#endif
+
+/**
+ * This function will drop all capabilities 
+ * Returns zero on success, non-zero otherwise
+ */
+static int drop_capabilities(uid_t uid)
+{
+	capng_clear(CAPNG_SELECT_BOTH);
+
+	if (capng_lock() < 0) 
+		return -1;
+	/* Change uid */
+	if (setresuid(uid, uid, uid)) {
+		fprintf(stderr, _("Error changing uid, aborting.\n"));
+		return -1;
+	}
+	return capng_apply(CAPNG_SELECT_BOTH);
+}
+
+#define DEFAULT_PATH "/usr/bin:/bin"
+static	int verbose = 0;
+
+/**
+ * Take care of any signal setup
+ */
+static int set_signal_handles(void)
+{
+	sigset_t empty;
+
+	/* Empty the signal mask in case someone is blocking a signal */
+	if (sigemptyset(&empty)) {
+		fprintf(stderr, "Unable to obtain empty signal set\n");
+		return -1;
+	}
+
+	(void)sigprocmask(SIG_SETMASK, &empty, NULL);
+
+	/* Terminate on SIGHUP. */
+	if (signal(SIGHUP, SIG_DFL) == SIG_ERR) {
+		perror("Unable to set SIGHUP handler");
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * 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.
+ */
+static int verify_mount(const char *mntdir, struct passwd *pwd) {
+	struct stat sb;
+	if (stat(mntdir, &sb) == -1) {
+		fprintf(stderr, _("Invalid mount point %s: %s\n"), mntdir, 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"));
+		return -1;
+	}
+	return 0;
+}
+
+/**
+ * This function checks to see if the shell is known in /etc/shells.
+ * If so, it returns 0. On error or illegal shell, it returns -1.
+ */
+static int verify_shell(const char *shell_name)
+{
+	int rc = -1;
+	const char *buf;
+
+	if (!(shell_name && shell_name[0]))
+		return rc;
+
+	while ((buf = getusershell()) != NULL) {
+		/* ignore comments */
+		if (*buf == '#')
+			continue;
+
+		/* check the shell skipping newline char */
+		if (!strcmp(shell_name, buf)) {
+			rc = 1;
+			break;
+		}
+	}
+	endusershell();
+	return rc;
+}
+
+static int seunshare_mount(const char *src, const char *dst, struct passwd *pwd) {
+	if (verbose)
+		printf("Mount %s on %s\n", src, dst);
+	if (mount(dst, dst,  NULL, MS_BIND, 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, NULL) < 0) {
+		fprintf(stderr, _("Failed to make %s private: %s\n"), dst, strerror(errno));
+		return -1;
+	}
+
+	if (mount(src, dst, NULL, MS_BIND, NULL) < 0) {
+		fprintf(stderr, _("Failed to mount %s on %s: %s\n"), src, dst, strerror(errno));
+		return -1;
+	}
+
+	if (verify_mount(dst, pwd) < 0) 
+		return -1;
+}
+
+#define USAGE_STRING _("USAGE: seunshare [ -v ] [ -t tmpdir ] [ -h homedir ] -- CONTEXT executable [args] ")
+
+int main(int argc, char **argv) {
+	int rc;
+	int status = -1;
+
+	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[] */
+	char *homedir_s = NULL;	/* homedir spec'd by user in argv[] */
+
+	const struct option long_options[] = {
+		{"homedir", 1, 0, 'h'},
+		{"tmpdir", 1, 0, 't'},
+		{"verbose", 1, 0, 'v'},
+		{NULL, 0, 0, 0}
+	};
+
+	uid_t uid = getuid();
+
+	if (!uid) {
+		fprintf(stderr, _("Must not be root"));
+		return -1;
+	}
+
+	struct passwd *pwd=getpwuid(uid);
+	if (!pwd) {
+		perror(_("getpwduid failed"));
+		return -1;
+	}
+
+	if (verify_shell(pwd->pw_shell) < 0) {
+		fprintf(stderr, _("Error!  Shell is not valid.\n"));
+		return -1;
+	}
+
+	while (1) {
+		clflag = getopt_long(argc, argv, "h:t:", long_options,
+				     &flag_index);
+		if (clflag == -1)
+			break;
+
+		switch (clflag) {
+		case 't':
+			tmpdir_s = optarg;
+			if (verify_mount(tmpdir_s, pwd) < 0) return -1;
+			break;
+		case 'h':
+			homedir_s = optarg;
+			if (verify_mount(homedir_s, pwd) < 0) return -1;
+			if (verify_mount(pwd->pw_dir, pwd) < 0) return -1;
+			break;
+		case 'v':
+			verbose = 1;
+			break;
+		default:
+			fprintf(stderr, "%s\n", USAGE_STRING);
+			return -1;
+		}
+	}
+
+	if (! homedir_s && ! tmpdir_s) {
+		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);
+		return -1;
+	}
+
+	scontext = argv[optind++];
+	
+	if (set_signal_handles())
+		return -1;
+
+        if (unshare(CLONE_NEWNS) < 0) {
+		perror(_("Failed to unshare"));
+		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 (drop_capabilities(uid)) {
+		perror(_("Failed to drop all capabilities"));
+		return -1;
+	}
+
+	int child = fork();
+	if (child == -1) {
+		perror(_("Unable to fork"));
+		return -1;
+	}
+
+	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 ((rc = clearenv())) {
+			perror(_("Unable to clear environment"));
+			free(display);
+			exit(-1);
+		}
+		
+		if (setexeccon(scontext)) {
+			fprintf(stderr, _("Could not set exec context to %s.\n"),
+				scontext);
+			free(display);
+			exit(-1);
+		}
+
+		if (display) 
+			rc |= setenv("DISPLAY", display, 1);
+		rc |= setenv("HOME", pwd->pw_dir, 1);
+		rc |= setenv("SHELL", pwd->pw_shell, 1);
+		rc |= setenv("USER", pwd->pw_name, 1);
+		rc |= setenv("LOGNAME", pwd->pw_name, 1);
+		rc |= setenv("PATH", DEFAULT_PATH, 1);
+		
+		if (chdir(pwd->pw_dir)) {
+			perror(_("Failed to change dir to homedir"));
+			exit(-1);
+		}
+		setsid();
+		execv(argv[optind], argv + optind);
+		free(display);
+		perror("execv");
+		exit(-1);
+	} else {
+		waitpid(child, &status, 0);
+	}
+
+	return status;
+}
diff --git a/policycoreutils/sandbox/test_sandbox.py b/policycoreutils/sandbox/test_sandbox.py
new file mode 100644
index 0000000..b3b7f64
--- /dev/null
+++ b/policycoreutils/sandbox/test_sandbox.py
@@ -0,0 +1,98 @@
+import unittest, os, shutil 
+from tempfile import mkdtemp
+from subprocess import Popen, PIPE
+
+class SandboxTests(unittest.TestCase):
+    def assertDenied(self, err):
+        self.assert_('Permission denied' in err,
+                     '"Permission denied" not found in %r' % err)
+    def assertNotFound(self, err):
+        self.assert_('not found' in err,
+                     '"not found" not found in %r' % err)
+
+    def assertFailure(self, status):
+        self.assert_(status != 0,
+                     '"Succeeded when it should have failed')
+
+    def assertSuccess(self, status, err):
+        self.assert_(status == 0,
+                     '"Sandbox should have succeeded for this test %r' %  err)
+
+    def test_simple_success(self):
+        "Verify that we can read file descriptors handed to sandbox"
+        p1 = Popen(['cat', '/etc/passwd'], stdout = PIPE)
+        p2 = Popen(['sandbox', 'grep', 'root'], stdin = p1.stdout, stdout=PIPE)
+        out, err = p2.communicate()
+        self.assert_('root' in out)
+
+    def test_cant_kill(self):
+        "Verify that we cannot send kill signal in the sandbox"
+        pid = os.getpid()
+        p = Popen(['sandbox', 'kill', '-HUP', str(pid)], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        self.assertDenied(err)
+
+    def test_cant_ping(self):
+        "Verify that we can't ping within the sandbox"
+        p = Popen(['sandbox', 'ping', '-c 1 ', '127.0.0.1'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        self.assertDenied(err)
+    
+    def test_cant_mkdir(self):
+        "Verify that we can't mkdir within the sandbox"
+        p = Popen(['sandbox', 'mkdir', '~/test'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        self.assertFailure(p.returncode)
+
+    def test_cant_list_homedir(self):
+        "Verify that we can't list homedir within the sandbox"
+        p = Popen(['sandbox', 'ls', '~'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        self.assertFailure(p.returncode)
+
+    def test_cant_send_mail(self):
+        "Verify that we can't send mail within the sandbox"
+        p = Popen(['sandbox', 'mail'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        self.assertDenied(err)
+    
+    def test_cant_sudo(self):
+        "Verify that we can't run sudo within the sandbox"
+        p = Popen(['sandbox', 'sudo'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        self.assertFailure(p.returncode)
+    
+    def test_mount(self):
+        "Verify that we mount a file system"
+        p = Popen(['sandbox', '-M', 'id'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        self.assertSuccess(p.returncode, err)
+    
+    def test_set_level(self):
+        "Verify that we set level a file system"
+        p = Popen(['sandbox', '-l', 's0', 'id'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        self.assertSuccess(p.returncode, err)
+    
+    def test_homedir(self):
+        "Verify that we set homedir a file system"
+        homedir = mkdtemp(dir=".", prefix=".sandbox_test")
+        p = Popen(['sandbox', '-H', homedir, '-M', 'id'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        shutil.rmtree(homedir)
+        self.assertSuccess(p.returncode, err)
+    
+    def test_tmpdir(self):
+        "Verify that we set tmpdir a file system"
+        tmpdir = mkdtemp(dir="/tmp", prefix=".sandbox_test")
+        p = Popen(['sandbox', '-T', tmpdir, '-M', 'id'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        shutil.rmtree(tmpdir)
+        self.assertSuccess(p.returncode, err)
+    
+if __name__ == "__main__":
+    import selinux
+    if selinux.security_getenforce() == 1:
+        unittest.main()
+    else:
+        print "SELinux must be in enforcing mode for this test"

[-- Attachment #3: policycoreutils-sandbox.patch.sig --]
[-- Type: application/pgp-signature, Size: 72 bytes --]

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: Updated sandbox patch.
  2010-05-18 18:58 Updated sandbox patch Steve Lawrence
  2010-05-19 17:31 ` Daniel J Walsh
@ 2010-05-19 19:59 ` Daniel J Walsh
  2010-05-26 20:06   ` Steve Lawrence
  2010-06-10 21:04   ` Chad Sellers
  1 sibling, 2 replies; 10+ messages in thread
From: Daniel J Walsh @ 2010-05-19 19:59 UTC (permalink / raw)
  To: slawrence; +Cc: SELinux

[-- Attachment #1: Type: text/plain, Size: 352 bytes --]

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Fixed patch that handles Spaces in homedir.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.14 (GNU/Linux)
Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org/

iEYEARECAAYFAkv0QyAACgkQrlYvE4MpobNBXQCgmUu92HsN5PiksOTZoGxSp0W+
1noAoKoCujFPLHduJ9BP3hrveeXvGKXO
=iqC+
-----END PGP SIGNATURE-----

[-- Attachment #2: policycoreutils-sandbox.patch --]
[-- Type: text/plain, Size: 35699 bytes --]

diff --git a/policycoreutils/Makefile b/policycoreutils/Makefile
index 538302b..86ed03f 100644
--- a/policycoreutils/Makefile
+++ b/policycoreutils/Makefile
@@ -1,4 +1,4 @@
-SUBDIRS = setfiles semanage load_policy newrole run_init secon audit2allow audit2why scripts sestatus semodule_package semodule semodule_link semodule_expand semodule_deps setsebool po
+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
 
 INOTIFYH = $(shell ls /usr/include/sys/inotify.h 2>/dev/null)
 
diff --git a/policycoreutils/sandbox/Makefile b/policycoreutils/sandbox/Makefile
new file mode 100644
index 0000000..ff0ee7c
--- /dev/null
+++ b/policycoreutils/sandbox/Makefile
@@ -0,0 +1,41 @@
+# Installation directories.
+PREFIX ?= ${DESTDIR}/usr
+INITDIR ?= ${DESTDIR}/etc/rc.d/init.d/
+SYSCONFDIR ?= ${DESTDIR}/etc/sysconfig
+BINDIR ?= $(PREFIX)/bin
+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 
+
+all: sandbox seunshare sandboxX.sh 
+
+seunshare: seunshare.o $(EXTRA_OBJS)
+	$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+install: all
+	-mkdir -p $(BINDIR)
+	install -m 755 sandbox $(BINDIR)
+	-mkdir -p $(MANDIR)/man8
+	install -m 644 sandbox.8 $(MANDIR)/man8/
+	-mkdir -p $(SBINDIR)
+	install -m 4755 seunshare $(SBINDIR)/
+	-mkdir -p $(SHAREDIR)
+	install -m 755 sandboxX.sh $(SHAREDIR)
+	-mkdir -p $(INITDIR)
+	install -m 755 sandbox.init $(INITDIR)/sandbox
+	-mkdir -p $(SYSCONFDIR)
+	install -m 644 sandbox.config $(SYSCONFDIR)/sandbox
+
+test:
+	@python test_sandbox.py -v
+
+clean:
+	-rm -f seunshare *.o *~
+
+indent:
+	../../scripts/Lindent $(wildcard *.[ch])
+
+relabel:
diff --git a/policycoreutils/sandbox/sandbox b/policycoreutils/sandbox/sandbox
new file mode 100644
index 0000000..bc7992b
--- /dev/null
+++ b/policycoreutils/sandbox/sandbox
@@ -0,0 +1,430 @@
+#! /usr/bin/python -E
+# Authors: Dan Walsh <dwalsh@redhat.com>
+# Authors: Josh Cogliati
+#
+# Copyright (C) 2009,2010  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; version 2 only
+#
+# 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
+#
+
+import os, sys, socket, random, fcntl, shutil, re, subprocess
+import selinux
+import signal
+from tempfile import mkdtemp
+import pwd
+import commands 
+
+PROGNAME = "policycoreutils"
+HOMEDIR=pwd.getpwuid(os.getuid()).pw_dir
+
+import gettext
+gettext.bindtextdomain(PROGNAME, "/usr/share/locale")
+gettext.textdomain(PROGNAME)
+
+try:
+       gettext.install(PROGNAME,
+                       localedir = "/usr/share/locale",
+                       unicode=False,
+                       codeset = 'utf-8')
+except IOError:
+       import __builtin__
+       __builtin__.__dict__['_'] = unicode
+
+DEFAULT_TYPE = "sandbox_t"
+DEFAULT_X_TYPE = "sandbox_x_t"
+SAVE_FILES = {}
+
+random.seed(None)
+
+def sighandler(signum, frame):
+    signal.signal(signum,  signal.SIG_IGN)
+    os.kill(0, signum)
+    raise KeyboardInterrupt
+
+def setup_sighandlers():
+    signal.signal(signal.SIGHUP,  sighandler)
+    signal.signal(signal.SIGQUIT, sighandler)
+    signal.signal(signal.SIGTERM, sighandler)
+
+def error_exit(msg):
+    sys.stderr.write("%s: " % sys.argv[0])
+    sys.stderr.write("%s\n" % msg)
+    sys.stderr.flush()
+    sys.exit(1)
+
+def chcon(path, context, recursive=False):
+    """ Restore SELinux context on a given path """
+    mode = os.lstat(path)[stat.ST_MODE]
+    lsetfilecon(path, context)
+    if recursive:
+           os.path.walk(path, lambda arg, dirname, fnames:
+                               map(chcon, [os.path.join(dirname, fname)
+                                                for fname in fnames]), context)
+def copyfile(file, dir, dest):
+       import re
+       if file.startswith(dir):
+              dname = os.path.dirname(file)
+              bname = os.path.basename(file)
+              if dname == dir:
+                     dest = dest + "/" + bname
+              else:
+                     newdir = re.sub(dir, dest, dname)
+                     if not os.path.exists(newdir):
+                            os.makedirs(newdir)
+                     dest = newdir + "/" + bname
+
+              try:
+                     if os.path.isdir(file):
+                            shutil.copytree(file, dest)
+                     else:
+                            shutil.copy2(file, dest)
+              except shutil.Error, elist:
+                     for e in elist:
+                            sys.stderr.write(e[1])
+                     
+              SAVE_FILES[file] = (dest, os.path.getmtime(dest))
+
+def savefile(new, orig, X_ind):
+       copy = False
+       if(X_ind):
+              import gtk
+              dlg = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO,
+                                      gtk.BUTTONS_YES_NO,
+                                      _("Do you want to save changes to '%s' (Y/N): ") % orig)
+              dlg.set_title(_("Sandbox Message"))
+              dlg.set_position(gtk.WIN_POS_MOUSE)
+              dlg.show_all()
+              rc = dlg.run()
+              dlg.destroy()
+              if rc == gtk.RESPONSE_YES:
+                     copy = True
+       else:
+              ans = raw_input(_("Do you want to save changes to '%s' (y/N): ") % orig)
+              if(re.match(_("[yY]"),ans)):
+                     copy = True
+       if(copy):
+              shutil.copy2(new,orig)
+
+def reserve(level):
+    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+    sock.bind("\0%s" % level)
+    fcntl.fcntl(sock.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
+
+def gen_mcs():
+       while True:
+              i1 = random.randrange(0, 1024)
+              i2 = random.randrange(0, 1024)
+              if i1 == i2:
+                     continue
+              if i1 > i2:
+                     tmp = i1
+                     i1 = i2
+                     i2 = tmp
+              level = "s0:c%d,c%d" % (i1, i2)
+              try:
+                     reserve(level)
+              except socket.error:
+                     continue
+              break
+       return level
+
+def fullpath(cmd):
+       for i in [ "/", "./", "../" ]:
+              if cmd.startswith(i):
+                     return cmd
+       for i in  os.environ["PATH"].split(':'):
+              f = "%s/%s" % (i, cmd)
+              if os.access(f, os.X_OK):
+                     return f
+       return cmd
+
+class Sandbox:
+    VERSION = "sandbox .1"
+    SYSLOG = "/var/log/messages"
+
+    def __init__(self):
+        self.setype = DEFAULT_TYPE
+        self.__options = None
+        self.__cmds = None
+        self.__init_files = []
+        self.__paths = []
+        self.__mount = False
+        self.__level = None
+        self.__homedir = None
+        self.__tmpdir = None
+
+    def __validate_mount(self):
+           if self.__options.level:
+                  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"):
+                  raise ValueError(_("""
+/usr/sbin/seunshare is required for the action you want to perform.  
+Install seunshare by executing:
+
+# yum install /usr/sbin/seunshare
+
+"""))
+
+    def __mount_callback(self, option, opt, value, parser):
+           self.__mount = True
+
+    def __x_callback(self, option, opt, value, parser):
+           self.__mount = True
+           setattr(parser.values, option.dest, True)
+
+    def __validdir(self, option, opt, value, parser):
+           if not os.path.isdir(value):
+                  raise IOError("Directory "+value+" not found")
+           setattr(parser.values, option.dest, value)
+           self.__mount = True
+
+    def __include(self, option, opt, value, parser):
+           rp = os.path.realpath(os.path.expanduser(value))
+           if not os.path.exists(rp):
+                  raise IOError(value+" not found")
+
+           if rp not in self.__init_files:
+                  self.__init_files.append(rp)
+
+    def __includefile(self, option, opt, value, parser):
+           fd = open(value, "r")
+           for i in fd.readlines():
+                  try:
+                         self.__include(option, opt, i[:-1], parser)
+                  except IOError, e:
+                         sys.stderr.write(e)
+           fd.close()
+
+    def __copyfiles(self):
+           files = self.__init_files + self.__paths
+           homedir=pwd.getpwuid(os.getuid()).pw_dir
+           for f in files:
+                  copyfile(f, homedir, self.__homedir)
+                  copyfile(f, "/tmp", self.__tmpdir)
+
+    def __setup_sandboxrc(self, wm = "/usr/bin/matchbox-window-manager -use_titlebar no"):
+           execfile =self.__homedir + "/.sandboxrc"
+           fd = open(execfile, "w+") 
+           if self.__options.session:
+                  fd.write("""#!/bin/sh
+#TITLE: /etc/gdm/Xsession
+/etc/gdm/Xsession
+""")
+           else:
+                  command = " ".join(self.__paths)
+                  fd.write("""#! /bin/sh
+#TITLE: %s
+/usr/bin/test -r ~/.xmodmap && /usr/bin/xmodmap ~/.xmodmap
+%s &
+WM_PID=$!
+%s
+kill -TERM $WM_PID  2> /dev/null
+""" % (command, wm, command))
+           fd.close()
+           os.chmod(execfile, 0700)
+
+    def usage(self, message = ""):
+           error_exit("%s\n%s" % (self.__parser.usage, message))
+
+    def __parse_options(self):
+        from optparse import OptionParser
+        usage = _("""
+sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [-W windowmanager ] [[-i file ] ...] [ -t type ] command
+
+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()
+        parser.add_option("-i", "--include", 
+                          action="callback", callback=self.__include, 
+                          type="string",
+                          help=_("include file in sandbox"))
+        parser.add_option("-I", "--includefile",  action="callback", callback=self.__includefile,
+                          type="string",
+                          help=_("read list of files to include in sandbox from INCLUDEFILE"))
+        parser.add_option("-t", "--type", dest="setype", action="store", default=None,
+                          help=_("run sandbox with SELinux type"))
+        parser.add_option("-M", "--mount", 
+                          action="callback", callback=self.__mount_callback, 
+                          help=_("mount new home and/or tmp directory"))
+
+        parser.add_option("-S", "--session", action="store_true",  dest="session", 
+                          default=False,  help=_("run complete desktop session within sandbox"))
+
+        parser.add_option("-X", dest="X_ind", 
+                          action="callback", callback=self.__x_callback, 
+                          default=False,  help=_("run X application within a sandbox"))
+
+        parser.add_option("-H", "--homedir", 
+                          action="callback", callback=self.__validdir,
+                          type="string",
+                          dest="homedir",  
+                          help=_("alternate home directory to use for mounting"))
+
+        parser.add_option("-T", "--tmpdir", dest="tmpdir",  
+                          type="string",
+                          action="callback", callback=self.__validdir,
+                          help=_("alternate /tmp directory to use for mounting"))
+
+        parser.add_option("-W", "--windowmanager", dest="wm",  
+                          type="string",
+                          default="/usr/bin/matchbox-window-manager -use_titlebar no",
+                          help=_("alternate window manager"))
+
+        parser.add_option("-l", "--level", dest="level", 
+                          help=_("MCS/MLS level for the sandbox"))
+
+        self.__parser=parser
+
+        self.__options, cmds = parser.parse_args()
+
+        if self.__options.X_ind:
+               self.setype = DEFAULT_X_TYPE
+        
+        if self.__options.setype:
+               self.setype = self.__options.setype
+
+        if self.__mount:
+               self.__validate_mount()
+
+        if self.__options.session:
+               if not self.__options.setype:
+                      self.setype = selinux.getcon()[1].split(":")[2]
+               if not self.__options.homedir or not self.__options.tmpdir:
+                      self.usage(_("You must specify a Homedir and tempdir when setting up a session sandbox"))
+               if len(cmds) > 0:
+                      self.usage(_("Commands are not allowed in a session sandbox"))
+               self.__options.X_ind = True
+               self.__homedir = self.__options.homedir
+               self.__tmpdir = self.__options.tmpdir
+        else:
+               if len(cmds) == 0:
+                      self.usage(_("Command required"))
+               cmds[0] = fullpath(cmds[0])
+               if not os.access(cmds[0], os.X_OK):
+                      self.usage(_("%s is not an executable") % cmds[0]  )
+                      
+               self.__cmds = cmds
+
+        for f in cmds:
+               rp = os.path.realpath(f)
+               if os.path.exists(rp):
+                      self.__paths.append(rp)
+               else:
+                      self.__paths.append(f)
+                  
+    def __gen_context(self):
+           if self.__options.level:
+                  level = self.__options.level
+           else:
+                  level = gen_mcs()
+
+           con = selinux.getcon()[1].split(":")
+           self.__execcon = "%s:%s:%s:%s" % (con[0], con[1], self.setype, level)
+           self.__filecon = "%s:%s:%s:%s" % (con[0], "object_r", 
+                                             "%s_file_t" % self.setype[:-2], 
+                                             level)
+    def __setup_dir(self):
+           if self.__options.level or self.__options.session:
+                  return
+           sandboxdir = HOMEDIR + "/.sandbox"
+           if not os.path.exists(sandboxdir):
+                  os.mkdir(sandboxdir)
+
+           if self.__options.homedir:
+                  chcon(self.__options.homedir, self.__filecon, True)
+                  self.__homedir = self.__options.homedir
+           else:
+                  selinux.setfscreatecon(self.__filecon)
+                  self.__homedir = mkdtemp(dir=sandboxdir, prefix=".sandbox")
+
+           if self.__options.tmpdir:
+                  chcon(self.__options.homedir, self.__filecon, True)
+                  self.__tmpdir = self.__options.tmpdir
+           else:
+                  selinux.setfscreatecon(self.__filecon)
+                  self.__tmpdir = mkdtemp(dir="/tmp", prefix=".sandbox")
+           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
+
+                  if self.__mount:
+                         cmds =  [ '/usr/sbin/seunshare', "-t", self.__tmpdir, "-h", self.__homedir, "--", self.__execcon ] + self.__paths
+                         rc = subprocess.Popen(cmds).wait()
+                         return rc
+
+                  selinux.setexeccon(self.__execcon)
+                  rc = subprocess.Popen(self.__cmds).wait()
+                  selinux.setexeccon(None)
+                  return rc
+
+           finally:
+                  for i in self.__paths:
+                         if i not in SAVE_FILES:
+                                continue
+                         (dest, mtime) = SAVE_FILES[i]
+                         if os.path.getmtime(dest) > mtime:
+                                savefile(dest, i, self.__options.X_ind)
+
+                  if self.__homedir and not self.__options.homedir: 
+                         shutil.rmtree(self.__homedir)
+                  if self.__tmpdir and not self.__options.tmpdir:
+                         shutil.rmtree(self.__tmpdir)
+    def main(self):
+        try:
+               self.__parse_options()
+               self.__gen_context()
+               self.__setup_dir()
+               return self.__execute()
+        except KeyboardInterrupt:
+            sys.exit(0)
+
+
+if __name__ == '__main__':
+    setup_sighandlers()
+    if selinux.is_selinux_enabled() != 1:
+        error_exit("Requires an SELinux enabled system")
+    
+    try:
+           sandbox = Sandbox()
+           rc = sandbox.main()
+    except OSError, error:
+           error_exit(error.args[1])
+    except ValueError, error:
+           error_exit(error.args[0])
+    except KeyError, error:
+           error_exit(_("Invalid value %s") % error.args[0])
+    except IOError, error:
+           error_exit(error)
+    except KeyboardInterrupt:
+           rc = 0
+           
+    sys.exit(rc)
diff --git a/policycoreutils/sandbox/sandbox.8 b/policycoreutils/sandbox/sandbox.8
new file mode 100644
index 0000000..1479364
--- /dev/null
+++ b/policycoreutils/sandbox/sandbox.8
@@ -0,0 +1,57 @@
+.TH SANDBOX "8" "May 2009" "chcat" "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
+.br
+.SH DESCRIPTION
+.PP
+Run the 
+.I cmd 
+application within a tightly confined SELinux domain.  The default sandbox domain only allows applications the ability to read and write stdin, stdout and any other file descriptors handed to it. It is not allowed to open any other files.  The -M option will mount an alternate homedir and tmpdir to be used by the sandbox.
+
+If you have the 
+.I policycoreutils-sandbox 
+package installed, you can use the -X option and the -M option.
+.B sandbox -X
+allows you to run X applications within a sandbox.  These applications will start up their own X Server and create a temporary home directory and /tmp.  The default SELinux policy does not allow any capabilities or network access.  It also prevents all access to the users other processes and files.  Files specified on the command that are in the home directory or /tmp will be copied into the sandbox directories.
+
+If directories are specified with -H or -T the directory will have its context modified with chcon(1) unless a level is specified with -l.  If the MLS/MCS security level is specified, the user is responsible to set the correct labels.
+.PP
+.TP
+\fB\-H\ homedir
+Use alternate homedir to mount over your home directory.  Defaults to temporary. Requires -X or -M.
+.TP
+\fB\-i file\fR
+Copy this file into the appropriate temporary sandbox directory. Command can be repeated.
+.TP
+\fB\-I inputfile\fR Copy all files listed in inputfile into the
+appropriate temporary sandbox directories.
+.TP
+\fB\-l\fR
+Specify the MLS/MCS Security Level to run the sandbox with.  Defaults to random.
+.TP
+\fB\-M\fR
+Create a Sandbox with temporary files for $HOME and /tmp.
+.TP
+\fB\-t type\fR
+Use alternate sandbox type, defaults to sandbox_t or sandbox_x_t for -X.
+.TP
+\fB\-T\ tmpdir
+Use alternate tempory directory to mount on /tmp.  Defaults to tmpfs. Requires -X or -M.
+.TP
+\fB\-W windowmanager\fR
+Select alternative window manager to run within 
+.B sandbox -X.
+Default to /usr/bin/matchbox-window-manager.
+.TP
+\fB\-X\fR 
+Create an X based Sandbox for gui apps, temporary files for
+$HOME and /tmp, secondary Xserver, defaults to sandbox_x_t
+.PP
+.SH "SEE ALSO"
+.TP
+runcon(1)
+.PP
diff --git a/policycoreutils/sandbox/sandbox.config b/policycoreutils/sandbox/sandbox.config
new file mode 100644
index 0000000..f9f059a
--- /dev/null
+++ b/policycoreutils/sandbox/sandbox.config
@@ -0,0 +1,2 @@
+# Space separate list of homedirs
+HOMEDIRS="/home"
diff --git a/policycoreutils/sandbox/sandbox.init b/policycoreutils/sandbox/sandbox.init
new file mode 100644
index 0000000..44867d1
--- /dev/null
+++ b/policycoreutils/sandbox/sandbox.init
@@ -0,0 +1,74 @@
+#!/bin/bash
+## BEGIN INIT INFO
+# Provides: sandbox
+# Default-Start: 3 4 5
+# Default-Stop: 0 1 2 3 4 6
+# Required-Start:
+#              
+## END INIT INFO
+# sandbox:        Set up / mountpoint to be shared, /var/tmp, /tmp, /home/sandbox unshared
+#
+# 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.
+#
+
+# Source function library.
+. /etc/init.d/functions
+
+HOMEDIRS="/home"
+
+. /etc/sysconfig/sandbox
+
+LOCKFILE=/var/lock/subsys/sandbox
+
+base=${0##*/}
+
+case "$1" in
+    restart)
+    start)
+	[ -f "$LOCKFILE" ] && exit 0
+
+	touch $LOCKFILE
+	mount --make-rshared /
+	mount --rbind /tmp /tmp
+	mount --rbind /var/tmp /var/tmp
+	mount --make-private /tmp
+	mount --make-private /var/tmp
+	for h in $HOMEDIRS; do
+	    mount --rbind $h $h 
+	    mount --make-private $h
+	done
+
+	exit $?
+	;;
+
+    status)
+	if [ -f "$LOCKFILE" ]; then 
+	    echo "$base is running"
+	else
+	    echo "$base is stopped"
+	fi
+	exit 0
+	;;
+
+    stop)
+	rm -f $LOCKFILE
+	exit 0
+	;;
+
+    *)
+	echo $"Usage: $0 {start|stop|status|restart}"
+	exit 3
+	;;
+esac
diff --git a/policycoreutils/sandbox/sandboxX.sh b/policycoreutils/sandbox/sandboxX.sh
new file mode 100644
index 0000000..8338203
--- /dev/null
+++ b/policycoreutils/sandbox/sandboxX.sh
@@ -0,0 +1,15 @@
+#!/bin/bash 
+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 }'`
+trap "exit 0" HUP
+
+(/usr/bin/Xephyr -title "$TITLE" -terminate -screen $SCREENSIZE -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"])'
+    export EXITCODE=$?
+    kill -HUP 0
+    break
+done
+exit 0
diff --git a/policycoreutils/sandbox/seunshare.c b/policycoreutils/sandbox/seunshare.c
new file mode 100644
index 0000000..848f787
--- /dev/null
+++ b/policycoreutils/sandbox/seunshare.c
@@ -0,0 +1,304 @@
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <syslog.h>
+#include <sys/mount.h>
+#include <pwd.h>
+#define _GNU_SOURCE
+#include <sched.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.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 <selinux/selinux.h>
+#include <selinux/context.h>	/* for context-mangling functions */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#ifdef USE_NLS
+#include <locale.h>		/* for setlocale() */
+#include <libintl.h>		/* for gettext() */
+#define _(msgid) gettext (msgid)
+#else
+#define _(msgid) (msgid)
+#endif
+
+/**
+ * This function will drop all capabilities 
+ * Returns zero on success, non-zero otherwise
+ */
+static int drop_capabilities(uid_t uid)
+{
+	capng_clear(CAPNG_SELECT_BOTH);
+
+	if (capng_lock() < 0) 
+		return -1;
+	/* Change uid */
+	if (setresuid(uid, uid, uid)) {
+		fprintf(stderr, _("Error changing uid, aborting.\n"));
+		return -1;
+	}
+	return capng_apply(CAPNG_SELECT_BOTH);
+}
+
+#define DEFAULT_PATH "/usr/bin:/bin"
+static	int verbose = 0;
+
+/**
+ * Take care of any signal setup
+ */
+static int set_signal_handles(void)
+{
+	sigset_t empty;
+
+	/* Empty the signal mask in case someone is blocking a signal */
+	if (sigemptyset(&empty)) {
+		fprintf(stderr, "Unable to obtain empty signal set\n");
+		return -1;
+	}
+
+	(void)sigprocmask(SIG_SETMASK, &empty, NULL);
+
+	/* Terminate on SIGHUP. */
+	if (signal(SIGHUP, SIG_DFL) == SIG_ERR) {
+		perror("Unable to set SIGHUP handler");
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * 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.
+ */
+static int verify_mount(const char *mntdir, struct passwd *pwd) {
+	struct stat sb;
+	if (stat(mntdir, &sb) == -1) {
+		fprintf(stderr, _("Invalid mount point %s: %s\n"), mntdir, 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"));
+		return -1;
+	}
+	return 0;
+}
+
+/**
+ * This function checks to see if the shell is known in /etc/shells.
+ * If so, it returns 0. On error or illegal shell, it returns -1.
+ */
+static int verify_shell(const char *shell_name)
+{
+	int rc = -1;
+	const char *buf;
+
+	if (!(shell_name && shell_name[0]))
+		return rc;
+
+	while ((buf = getusershell()) != NULL) {
+		/* ignore comments */
+		if (*buf == '#')
+			continue;
+
+		/* check the shell skipping newline char */
+		if (!strcmp(shell_name, buf)) {
+			rc = 1;
+			break;
+		}
+	}
+	endusershell();
+	return rc;
+}
+
+static int seunshare_mount(const char *src, const char *dst, struct passwd *pwd) {
+	if (verbose)
+		printf("Mount %s on %s\n", src, dst);
+	if (mount(dst, dst,  NULL, MS_BIND, 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, NULL) < 0) {
+		fprintf(stderr, _("Failed to make %s private: %s\n"), dst, strerror(errno));
+		return -1;
+	}
+
+	if (mount(src, dst, NULL, MS_BIND, NULL) < 0) {
+		fprintf(stderr, _("Failed to mount %s on %s: %s\n"), src, dst, strerror(errno));
+		return -1;
+	}
+
+	if (verify_mount(dst, pwd) < 0) 
+		return -1;
+}
+
+#define USAGE_STRING _("USAGE: seunshare [ -v ] [ -t tmpdir ] [ -h homedir ] -- CONTEXT executable [args] ")
+
+int main(int argc, char **argv) {
+	int rc;
+	int status = -1;
+
+	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[] */
+	char *homedir_s = NULL;	/* homedir spec'd by user in argv[] */
+
+	const struct option long_options[] = {
+		{"homedir", 1, 0, 'h'},
+		{"tmpdir", 1, 0, 't'},
+		{"verbose", 1, 0, 'v'},
+		{NULL, 0, 0, 0}
+	};
+
+	uid_t uid = getuid();
+
+	if (!uid) {
+		fprintf(stderr, _("Must not be root"));
+		return -1;
+	}
+
+	struct passwd *pwd=getpwuid(uid);
+	if (!pwd) {
+		perror(_("getpwduid failed"));
+		return -1;
+	}
+
+	if (verify_shell(pwd->pw_shell) < 0) {
+		fprintf(stderr, _("Error!  Shell is not valid.\n"));
+		return -1;
+	}
+
+	while (1) {
+		clflag = getopt_long(argc, argv, "h:t:", long_options,
+				     &flag_index);
+		if (clflag == -1)
+			break;
+
+		switch (clflag) {
+		case 't':
+			tmpdir_s = optarg;
+			if (verify_mount(tmpdir_s, pwd) < 0) return -1;
+			break;
+		case 'h':
+			homedir_s = optarg;
+			if (verify_mount(homedir_s, pwd) < 0) return -1;
+			if (verify_mount(pwd->pw_dir, pwd) < 0) return -1;
+			break;
+		case 'v':
+			verbose = 1;
+			break;
+		default:
+			fprintf(stderr, "%s\n", USAGE_STRING);
+			return -1;
+		}
+	}
+
+	if (! homedir_s && ! tmpdir_s) {
+		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);
+		return -1;
+	}
+
+	scontext = argv[optind++];
+	
+	if (set_signal_handles())
+		return -1;
+
+        if (unshare(CLONE_NEWNS) < 0) {
+		perror(_("Failed to unshare"));
+		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 (drop_capabilities(uid)) {
+		perror(_("Failed to drop all capabilities"));
+		return -1;
+	}
+
+	int child = fork();
+	if (child == -1) {
+		perror(_("Unable to fork"));
+		return -1;
+	}
+
+	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 ((rc = clearenv())) {
+			perror(_("Unable to clear environment"));
+			free(display);
+			exit(-1);
+		}
+		
+		if (setexeccon(scontext)) {
+			fprintf(stderr, _("Could not set exec context to %s.\n"),
+				scontext);
+			free(display);
+			exit(-1);
+		}
+
+		if (display) 
+			rc |= setenv("DISPLAY", display, 1);
+		rc |= setenv("HOME", pwd->pw_dir, 1);
+		rc |= setenv("SHELL", pwd->pw_shell, 1);
+		rc |= setenv("USER", pwd->pw_name, 1);
+		rc |= setenv("LOGNAME", pwd->pw_name, 1);
+		rc |= setenv("PATH", DEFAULT_PATH, 1);
+		
+		if (chdir(pwd->pw_dir)) {
+			perror(_("Failed to change dir to homedir"));
+			exit(-1);
+		}
+		setsid();
+		execv(argv[optind], argv + optind);
+		free(display);
+		perror("execv");
+		exit(-1);
+	} else {
+		waitpid(child, &status, 0);
+	}
+
+	return status;
+}
diff --git a/policycoreutils/sandbox/test_sandbox.py b/policycoreutils/sandbox/test_sandbox.py
new file mode 100644
index 0000000..b3b7f64
--- /dev/null
+++ b/policycoreutils/sandbox/test_sandbox.py
@@ -0,0 +1,98 @@
+import unittest, os, shutil 
+from tempfile import mkdtemp
+from subprocess import Popen, PIPE
+
+class SandboxTests(unittest.TestCase):
+    def assertDenied(self, err):
+        self.assert_('Permission denied' in err,
+                     '"Permission denied" not found in %r' % err)
+    def assertNotFound(self, err):
+        self.assert_('not found' in err,
+                     '"not found" not found in %r' % err)
+
+    def assertFailure(self, status):
+        self.assert_(status != 0,
+                     '"Succeeded when it should have failed')
+
+    def assertSuccess(self, status, err):
+        self.assert_(status == 0,
+                     '"Sandbox should have succeeded for this test %r' %  err)
+
+    def test_simple_success(self):
+        "Verify that we can read file descriptors handed to sandbox"
+        p1 = Popen(['cat', '/etc/passwd'], stdout = PIPE)
+        p2 = Popen(['sandbox', 'grep', 'root'], stdin = p1.stdout, stdout=PIPE)
+        out, err = p2.communicate()
+        self.assert_('root' in out)
+
+    def test_cant_kill(self):
+        "Verify that we cannot send kill signal in the sandbox"
+        pid = os.getpid()
+        p = Popen(['sandbox', 'kill', '-HUP', str(pid)], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        self.assertDenied(err)
+
+    def test_cant_ping(self):
+        "Verify that we can't ping within the sandbox"
+        p = Popen(['sandbox', 'ping', '-c 1 ', '127.0.0.1'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        self.assertDenied(err)
+    
+    def test_cant_mkdir(self):
+        "Verify that we can't mkdir within the sandbox"
+        p = Popen(['sandbox', 'mkdir', '~/test'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        self.assertFailure(p.returncode)
+
+    def test_cant_list_homedir(self):
+        "Verify that we can't list homedir within the sandbox"
+        p = Popen(['sandbox', 'ls', '~'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        self.assertFailure(p.returncode)
+
+    def test_cant_send_mail(self):
+        "Verify that we can't send mail within the sandbox"
+        p = Popen(['sandbox', 'mail'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        self.assertDenied(err)
+    
+    def test_cant_sudo(self):
+        "Verify that we can't run sudo within the sandbox"
+        p = Popen(['sandbox', 'sudo'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        self.assertFailure(p.returncode)
+    
+    def test_mount(self):
+        "Verify that we mount a file system"
+        p = Popen(['sandbox', '-M', 'id'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        self.assertSuccess(p.returncode, err)
+    
+    def test_set_level(self):
+        "Verify that we set level a file system"
+        p = Popen(['sandbox', '-l', 's0', 'id'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        self.assertSuccess(p.returncode, err)
+    
+    def test_homedir(self):
+        "Verify that we set homedir a file system"
+        homedir = mkdtemp(dir=".", prefix=".sandbox_test")
+        p = Popen(['sandbox', '-H', homedir, '-M', 'id'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        shutil.rmtree(homedir)
+        self.assertSuccess(p.returncode, err)
+    
+    def test_tmpdir(self):
+        "Verify that we set tmpdir a file system"
+        tmpdir = mkdtemp(dir="/tmp", prefix=".sandbox_test")
+        p = Popen(['sandbox', '-T', tmpdir, '-M', 'id'], stdout=PIPE, stderr=PIPE)
+        out, err = p.communicate()
+        shutil.rmtree(tmpdir)
+        self.assertSuccess(p.returncode, err)
+    
+if __name__ == "__main__":
+    import selinux
+    if selinux.security_getenforce() == 1:
+        unittest.main()
+    else:
+        print "SELinux must be in enforcing mode for this test"

[-- Attachment #3: policycoreutils-sandbox.patch.sig --]
[-- Type: application/pgp-signature, Size: 72 bytes --]

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: Updated sandbox patch.
  2010-05-19 19:59 ` Daniel J Walsh
@ 2010-05-26 20:06   ` Steve Lawrence
  2010-05-27 12:57     ` Daniel J Walsh
  2010-06-10 21:04   ` Chad Sellers
  1 sibling, 1 reply; 10+ messages in thread
From: Steve Lawrence @ 2010-05-26 20:06 UTC (permalink / raw)
  To: Daniel J Walsh; +Cc: SELinux

On Wed, 2010-05-19 at 15:59 -0400, Daniel J Walsh wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> 
> Fixed patch that handles Spaces in homedir.
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v2.0.14 (GNU/Linux)
> Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org/
> 
> iEYEARECAAYFAkv0QyAACgkQrlYvE4MpobNBXQCgmUu92HsN5PiksOTZoGxSp0W+
> 1noAoKoCujFPLHduJ9BP3hrveeXvGKXO
> =iqC+
> -----END PGP SIGNATURE-----


The following patch makes a few updates the the sandbox patch, though I
have a question:

Is the sandbox.init script needed anymore? It looks like seunshare was
changed to now bind mount and make private the necessary directories.
The only thing that seems missing is making root rshared. Also, if the
init script is obsolete, do the mounts also need the MS_REC flag for
recursive bind/private like they are mounted in the init script? e.g.

mount(dst, dst, NULL, (MS_BIND | MS_REC), NULL)
mount(dst, dst, NULL, (MS_PRIVATE | MS_REC), NULL)


Changes the following patch makes:

sandbox.py
- Removes unused 'import commands'
- Fixes the chcon function, and replaces the deprecated os.path.walk
  with os.walk. I think this way is a bit easier to read too.
- Removes the 'yum install seunshare' message. This tool is not specific
  to RPM based distros.
- Remove try/except around -I include to be consistent with the -i
  option. If we can't include a file, then this should bail, no matter
  if it's being included via -i or -I.
- Fix homedir/tmpdir typo in chcon call

sandbox.init (maybe obsoleted?)
- Fix restart so it stops and starts
- unmount the bind mounts when stopped
- Abort with failure if any mounts fail

seunshare.c
- Define the mount flag MS_PRIVATE if it isn't already. The flag is only
  defined in the latest glibc but has been in the kernel since 2005.
- Simplify an if-statment. Also, I'm not sure the purpose of the
  strncmmp in that conditional, so maybe I've oversimplified. It seems
  like maybe an error should be thrown if tmpdir_s == pw_dir or
  homedir_s == "/tmp", but maybe I'm missing something.

---
 policycoreutils/sandbox/sandbox      |   27 +++++---------
 policycoreutils/sandbox/sandbox.init |   65 +++++++++++++++++++++++++---------
 policycoreutils/sandbox/seunshare.c  |   21 +++++------
 3 files changed, 67 insertions(+), 46 deletions(-)

diff --git a/policycoreutils/sandbox/sandbox b/policycoreutils/sandbox/sandbox
index bc7992b..42ebd6c 100644
--- a/policycoreutils/sandbox/sandbox
+++ b/policycoreutils/sandbox/sandbox
@@ -24,7 +24,6 @@ import selinux
 import signal
 from tempfile import mkdtemp
 import pwd
-import commands 
 
 PROGNAME = "policycoreutils"
 HOMEDIR=pwd.getpwuid(os.getuid()).pw_dir
@@ -64,14 +63,13 @@ def error_exit(msg):
     sys.stderr.flush()
     sys.exit(1)
 
-def chcon(path, context, recursive=False):
+def chcon(path, context):
     """ Restore SELinux context on a given path """
-    mode = os.lstat(path)[stat.ST_MODE]
-    lsetfilecon(path, context)
-    if recursive:
-           os.path.walk(path, lambda arg, dirname, fnames:
-                               map(chcon, [os.path.join(dirname, fname)
-                                                for fname in fnames]), context)
+    selinux.lsetfilecon(path, context)
+    for root, dirs, files in os.walk(path):
+        for name in files + dirs:
+            selinux.lsetfilecon(os.path.join(root,name), context)
+
 def copyfile(file, dir, dest):
        import re
        if file.startswith(dir):
@@ -173,10 +171,6 @@ class Sandbox:
            if not os.path.exists("/usr/sbin/seunshare"):
                   raise ValueError(_("""
 /usr/sbin/seunshare is required for the action you want to perform.  
-Install seunshare by executing:
-
-# yum install /usr/sbin/seunshare
-
 """))
 
     def __mount_callback(self, option, opt, value, parser):
@@ -203,10 +197,7 @@ Install seunshare by executing:
     def __includefile(self, option, opt, value, parser):
            fd = open(value, "r")
            for i in fd.readlines():
-                  try:
-                         self.__include(option, opt, i[:-1], parser)
-                  except IOError, e:
-                         sys.stderr.write(e)
+                  self.__include(option, opt, i[:-1], parser)
            fd.close()
 
     def __copyfiles(self):
@@ -347,14 +338,14 @@ sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [-
                   os.mkdir(sandboxdir)
 
            if self.__options.homedir:
-                  chcon(self.__options.homedir, self.__filecon, True)
+                  chcon(self.__options.homedir, self.__filecon)
                   self.__homedir = self.__options.homedir
            else:
                   selinux.setfscreatecon(self.__filecon)
                   self.__homedir = mkdtemp(dir=sandboxdir, prefix=".sandbox")
 
            if self.__options.tmpdir:
-                  chcon(self.__options.homedir, self.__filecon, True)
+                  chcon(self.__options.tmpdir, self.__filecon)
                   self.__tmpdir = self.__options.tmpdir
            else:
                   selinux.setfscreatecon(self.__filecon)
diff --git a/policycoreutils/sandbox/sandbox.init b/policycoreutils/sandbox/sandbox.init
index 44867d1..d4e99a0 100644
--- a/policycoreutils/sandbox/sandbox.init
+++ b/policycoreutils/sandbox/sandbox.init
@@ -34,37 +34,68 @@ LOCKFILE=/var/lock/subsys/sandbox
 
 base=${0##*/}
 
-case "$1" in
-    restart)
-    start)
-	[ -f "$LOCKFILE" ] && exit 0
+start() {
+	echo -n "Starting sandbox"
+
+	[ -f "$LOCKFILE" ] && return 1
 
 	touch $LOCKFILE
-	mount --make-rshared /
-	mount --rbind /tmp /tmp
-	mount --rbind /var/tmp /var/tmp
-	mount --make-private /tmp
-	mount --make-private /var/tmp
+	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 
-	    mount --make-private $h
+	    mount --rbind $h $h || return $?
+	    mount --make-private $h || return $?
 	done
 
-	exit $?
-	;;
+	return 0
+}
 
-    status)
+stop() {
+	echo -n "Stopping sandbox"
+
+	[ -f "$LOCKFILE" ] || return 1
+
+	rm -f "$LOCKFILE"
+	umount /tmp || return $?
+	umount /var/tmp || return $?
+	for h in $HOMEDIRS; do
+	    umount $h || return $?
+	done
+}
+
+restart() {
+	stop && success || failure
+	echo
+	start && success || failure
+	echo
+}
+
+status() {
 	if [ -f "$LOCKFILE" ]; then 
 	    echo "$base is running"
 	else
 	    echo "$base is stopped"
 	fi
 	exit 0
-	;;
+}
 
+case "$1" in
+    start)
+	start && success || failure
+	echo
+	;;
     stop)
-	rm -f $LOCKFILE
-	exit 0
+	stop && success || failure
+	echo
+	;;
+    restart)
+	restart
+	;;
+    status)
+	status
 	;;
 
     *)
diff --git a/policycoreutils/sandbox/seunshare.c b/policycoreutils/sandbox/seunshare.c
index 848f787..9878e9a 100644
--- a/policycoreutils/sandbox/seunshare.c
+++ b/policycoreutils/sandbox/seunshare.c
@@ -31,6 +31,10 @@
 #define _(msgid) (msgid)
 #endif
 
+#ifndef MS_PRIVATE
+#define MS_PRIVATE 1<<18
+#endif
+
 /**
  * This function will drop all capabilities 
  * Returns zero on success, non-zero otherwise
@@ -230,19 +234,14 @@ int main(int argc, char **argv) {
 		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 (tmpdir_s && seunshare_mount(tmpdir_s, "/tmp", pwd) < 0) {
+		return -1;
 	}
 
+	if (homedir_s && seunshare_mount(homedir_s, pwd->pw_dir, pwd) < 0) {
+		return -1;
+	}
+				
 	if (drop_capabilities(uid)) {
 		perror(_("Failed to drop all capabilities"));
 		return -1;
-- 
1.6.6.1

--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: Updated sandbox patch.
  2010-05-26 20:06   ` Steve Lawrence
@ 2010-05-27 12:57     ` Daniel J Walsh
  2010-06-07 21:53       ` Steve Lawrence
  0 siblings, 1 reply; 10+ messages in thread
From: Daniel J Walsh @ 2010-05-27 12:57 UTC (permalink / raw)
  To: slawrence; +Cc: SELinux

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 05/26/2010 04:06 PM, Steve Lawrence wrote:
> On Wed, 2010-05-19 at 15:59 -0400, Daniel J Walsh wrote:
> Fixed patch that handles Spaces in homedir.

> The following patch makes a few updates the the sandbox patch, though I
> have a question:

> Is the sandbox.init script needed anymore? It looks like seunshare was
> changed to now bind mount and make private the necessary directories.
> The only thing that seems missing is making root rshared. Also, if the
> init script is obsolete, do the mounts also need the MS_REC flag for
> recursive bind/private like they are mounted in the init script? e.g.

The init script is needed for the xguest package/more specifically
pam_namespace, but also needed for
mount --make-rshared /

Whether the init script belongs in policycoreutils is questionable though.


> mount(dst, dst, NULL, (MS_BIND | MS_REC), NULL)
> mount(dst, dst, NULL, (MS_PRIVATE | MS_REC), NULL)

We probably should add these.  Although it is not likely.

> Changes the following patch makes:

> sandbox.py
> - Removes unused 'import commands'
> - Fixes the chcon function, and replaces the deprecated os.path.walk
>   with os.walk. I think this way is a bit easier to read too.

I think chcon should be added to libselinux python bindings and then
leave the recursive flag.  (restorecon is currently in python bindings._

> - Removes the 'yum install seunshare' message. This tool is not specific
>   to RPM based distros.

People are using seunshare without X now that I have added the -M flag.
 So I will move it from the -gui package to the base package with
sandbox and then this should not be necessary.
> - Remove try/except around -I include to be consistent with the -i
>   option. If we can't include a file, then this should bail, no matter
>   if it's being included via -i or -I.

Ok, I was thinking you could list a whole bunch of files in the -I case
and if one does not exist, allow it to continue.  But I don't really care.
> - Fix homedir/tmpdir typo in chcon call

> sandbox.init (maybe obsoleted?)
> - Fix restart so it stops and starts
> - unmount the bind mounts when stopped
I doubt this will work.  Two many locks in /tmp /home
> - Abort with failure if any mounts fail

> seunshare.c
> - Define the mount flag MS_PRIVATE if it isn't already. The flag is only
>   defined in the latest glibc but has been in the kernel since 2005.
> - Simplify an if-statment. Also, I'm not sure the purpose of the
>   strncmmp in that conditional, so maybe I've oversimplified. 
This is wrong.  The problem comes about when you mount within the same
directory.

seunshare -t /home/dwalsh/sanbox/tmp -h /home/dwalsh/sandbox/home   ...

seunshare -t /tmp/sandbox/tmp -h /tmp/sandbox/home

If you do not have the check one of the above will fail.

In the first example if Homedir is mounted first,
/home/dwalsh/sanbox/tmp will no longer exist when seunshare attempts to
mount it on /tmp.

Similarly, if /tmp is mounted first in the second example.
/tmp/sandbox/home will no longer exist.

You have to check to make sure one of the directories is not included in
the other.

It seems
>   like maybe an error should be thrown if tmpdir_s == pw_dir or
>   homedir_s == "/tmp", but maybe I'm missing something.

See above.

I was blowing up because I use

~/sandbox/tmp and ~/sandbox/home for my mountpoints.
> ---
>  policycoreutils/sandbox/sandbox      |   27 +++++---------
>  policycoreutils/sandbox/sandbox.init |   65 +++++++++++++++++++++++++---------
>  policycoreutils/sandbox/seunshare.c  |   21 +++++------
>  3 files changed, 67 insertions(+), 46 deletions(-)

> diff --git a/policycoreutils/sandbox/sandbox b/policycoreutils/sandbox/sandbox
> index bc7992b..42ebd6c 100644
> --- a/policycoreutils/sandbox/sandbox
> +++ b/policycoreutils/sandbox/sandbox
> @@ -24,7 +24,6 @@ import selinux
>  import signal
>  from tempfile import mkdtemp
>  import pwd
> -import commands 

>  PROGNAME = "policycoreutils"
>  HOMEDIR=pwd.getpwuid(os.getuid()).pw_dir
> @@ -64,14 +63,13 @@ def error_exit(msg):
>      sys.stderr.flush()
>      sys.exit(1)

> -def chcon(path, context, recursive=False):
> +def chcon(path, context):
>      """ Restore SELinux context on a given path """
> -    mode = os.lstat(path)[stat.ST_MODE]
> -    lsetfilecon(path, context)
> -    if recursive:
> -           os.path.walk(path, lambda arg, dirname, fnames:
> -                               map(chcon, [os.path.join(dirname, fname)
> -                                                for fname in fnames]), context)
> +    selinux.lsetfilecon(path, context)
> +    for root, dirs, files in os.walk(path):
> +        for name in files + dirs:
> +            selinux.lsetfilecon(os.path.join(root,name), context)
> +
>  def copyfile(file, dir, dest):
>         import re
>         if file.startswith(dir):
> @@ -173,10 +171,6 @@ class Sandbox:
>             if not os.path.exists("/usr/sbin/seunshare"):
>                    raise ValueError(_("""
>  /usr/sbin/seunshare is required for the action you want to perform.  
> -Install seunshare by executing:
> -
> -# yum install /usr/sbin/seunshare
> -
>  """))

>      def __mount_callback(self, option, opt, value, parser):
> @@ -203,10 +197,7 @@ Install seunshare by executing:
>      def __includefile(self, option, opt, value, parser):
>             fd = open(value, "r")
>             for i in fd.readlines():
> -                  try:
> -                         self.__include(option, opt, i[:-1], parser)
> -                  except IOError, e:
> -                         sys.stderr.write(e)
> +                  self.__include(option, opt, i[:-1], parser)
>             fd.close()

>      def __copyfiles(self):
> @@ -347,14 +338,14 @@ sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [-
>                    os.mkdir(sandboxdir)

>             if self.__options.homedir:
> -                  chcon(self.__options.homedir, self.__filecon, True)
> +                  chcon(self.__options.homedir, self.__filecon)
>                    self.__homedir = self.__options.homedir
>             else:
>                    selinux.setfscreatecon(self.__filecon)
>                    self.__homedir = mkdtemp(dir=sandboxdir, prefix=".sandbox")

>             if self.__options.tmpdir:
> -                  chcon(self.__options.homedir, self.__filecon, True)
> +                  chcon(self.__options.tmpdir, self.__filecon)
>                    self.__tmpdir = self.__options.tmpdir
>             else:
>                    selinux.setfscreatecon(self.__filecon)
> diff --git a/policycoreutils/sandbox/sandbox.init b/policycoreutils/sandbox/sandbox.init
> index 44867d1..d4e99a0 100644
> --- a/policycoreutils/sandbox/sandbox.init
> +++ b/policycoreutils/sandbox/sandbox.init
> @@ -34,37 +34,68 @@ LOCKFILE=/var/lock/subsys/sandbox

>  base=${0##*/}

> -case "$1" in
> -    restart)
> -    start)
> -	[ -f "$LOCKFILE" ] && exit 0
> +start() {
> +	echo -n "Starting sandbox"
> +
> +	[ -f "$LOCKFILE" ] && return 1

>  	touch $LOCKFILE
> -	mount --make-rshared /
> -	mount --rbind /tmp /tmp
> -	mount --rbind /var/tmp /var/tmp
> -	mount --make-private /tmp
> -	mount --make-private /var/tmp
> +	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 
> -	    mount --make-private $h
> +	    mount --rbind $h $h || return $?
> +	    mount --make-private $h || return $?
>  	done

> -	exit $?
> -	;;
> +	return 0
> +}

> -    status)
> +stop() {
> +	echo -n "Stopping sandbox"
> +
> +	[ -f "$LOCKFILE" ] || return 1
> +
> +	rm -f "$LOCKFILE"
> +	umount /tmp || return $?
> +	umount /var/tmp || return $?
> +	for h in $HOMEDIRS; do
> +	    umount $h || return $?
> +	done
> +}
> +
> +restart() {
> +	stop && success || failure
> +	echo
> +	start && success || failure
> +	echo
> +}
> +
> +status() {
>  	if [ -f "$LOCKFILE" ]; then 
>  	    echo "$base is running"
>  	else
>  	    echo "$base is stopped"
>  	fi
>  	exit 0
> -	;;
> +}

> +case "$1" in
> +    start)
> +	start && success || failure
> +	echo
> +	;;
>      stop)
> -	rm -f $LOCKFILE
> -	exit 0
> +	stop && success || failure
> +	echo
> +	;;
> +    restart)
> +	restart
> +	;;
> +    status)
> +	status
>  	;;

>      *)
> diff --git a/policycoreutils/sandbox/seunshare.c b/policycoreutils/sandbox/seunshare.c
> index 848f787..9878e9a 100644
> --- a/policycoreutils/sandbox/seunshare.c
> +++ b/policycoreutils/sandbox/seunshare.c
> @@ -31,6 +31,10 @@
>  #define _(msgid) (msgid)
>  #endif

> +#ifndef MS_PRIVATE
> +#define MS_PRIVATE 1<<18
> +#endif
> +
>  /**
>   * This function will drop all capabilities 
>   * Returns zero on success, non-zero otherwise
> @@ -230,19 +234,14 @@ int main(int argc, char **argv) {
>  		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 (tmpdir_s && seunshare_mount(tmpdir_s, "/tmp", pwd) < 0) {
> +		return -1;
>  	}

> +	if (homedir_s && seunshare_mount(homedir_s, pwd->pw_dir, pwd) < 0) {
> +		return -1;
> +	}
> +				
>  	if (drop_capabilities(uid)) {
>  		perror(_("Failed to drop all capabilities"));
>  		return -1;

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.14 (GNU/Linux)
Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org/

iEYEARECAAYFAkv+bDAACgkQrlYvE4MpobNgpwCfTj/C7ehIt8VoG/15eJRxA63S
quUAoMFqOS0xnPf9v+SaMA2DmPFI5cnv
=dR3i
-----END PGP SIGNATURE-----

--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Updated sandbox patch.
  2010-05-27 12:57     ` Daniel J Walsh
@ 2010-06-07 21:53       ` Steve Lawrence
  2010-06-08 12:00         ` Daniel J Walsh
  2010-06-10 21:04         ` Chad Sellers
  0 siblings, 2 replies; 10+ messages in thread
From: Steve Lawrence @ 2010-06-07 21:53 UTC (permalink / raw)
  To: Daniel J Walsh; +Cc: SELinux

On Thu, 2010-05-27 at 08:57 -0400, Daniel J Walsh wrote: 
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> 
> On 05/26/2010 04:06 PM, Steve Lawrence wrote:
> > On Wed, 2010-05-19 at 15:59 -0400, Daniel J Walsh wrote:
> > Fixed patch that handles Spaces in homedir.
> 
> > The following patch makes a few updates the the sandbox patch, though I
> > have a question:
> 
> > Is the sandbox.init script needed anymore? It looks like seunshare was
> > changed to now bind mount and make private the necessary directories.
> > The only thing that seems missing is making root rshared. Also, if the
> > init script is obsolete, do the mounts also need the MS_REC flag for
> > recursive bind/private like they are mounted in the init script? e.g.
> 
> The init script is needed for the xguest package/more specifically
> pam_namespace, but also needed for
> mount --make-rshared /
> 
> Whether the init script belongs in policycoreutils is questionable though.
> 
> 
> > mount(dst, dst, NULL, (MS_BIND | MS_REC), NULL)
> > mount(dst, dst, NULL, (MS_PRIVATE | MS_REC), NULL)
> 
> We probably should add these.  Although it is not likely.
> 
> > Changes the following patch makes:
> 
> > sandbox.py
> > - Removes unused 'import commands'
> > - Fixes the chcon function, and replaces the deprecated os.path.walk
> >   with os.walk. I think this way is a bit easier to read too.
> 
> I think chcon should be added to libselinux python bindings and then
> leave the recursive flag.  (restorecon is currently in python bindings._
> 
> > - Removes the 'yum install seunshare' message. This tool is not specific
> >   to RPM based distros.
> 
> People are using seunshare without X now that I have added the -M flag.
>  So I will move it from the -gui package to the base package with
> sandbox and then this should not be necessary.
> > - Remove try/except around -I include to be consistent with the -i
> >   option. If we can't include a file, then this should bail, no matter
> >   if it's being included via -i or -I.
> 
> Ok, I was thinking you could list a whole bunch of files in the -I case
> and if one does not exist, allow it to continue.  But I don't really care.
> > - Fix homedir/tmpdir typo in chcon call
> 
> > sandbox.init (maybe obsoleted?)
> > - Fix restart so it stops and starts
> > - unmount the bind mounts when stopped
> I doubt this will work.  Two many locks in /tmp /home
> > - Abort with failure if any mounts fail
> 
> > seunshare.c
> > - Define the mount flag MS_PRIVATE if it isn't already. The flag is only
> >   defined in the latest glibc but has been in the kernel since 2005.
> > - Simplify an if-statment. Also, I'm not sure the purpose of the
> >   strncmmp in that conditional, so maybe I've oversimplified. 
> This is wrong.  The problem comes about when you mount within the same
> directory.
> 
> seunshare -t /home/dwalsh/sanbox/tmp -h /home/dwalsh/sandbox/home   ...
> 
> seunshare -t /tmp/sandbox/tmp -h /tmp/sandbox/home
> 
> If you do not have the check one of the above will fail.
> 
> In the first example if Homedir is mounted first,
> /home/dwalsh/sanbox/tmp will no longer exist when seunshare attempts to
> mount it on /tmp.
> 
> Similarly, if /tmp is mounted first in the second example.
> /tmp/sandbox/home will no longer exist.
> 
> You have to check to make sure one of the directories is not included in
> the other.
> 
> It seems
> >   like maybe an error should be thrown if tmpdir_s == pw_dir or
> >   homedir_s == "/tmp", but maybe I'm missing something.
> 
> See above.
> 
> I was blowing up because I use
> 
> ~/sandbox/tmp and ~/sandbox/home for my mountpoints.

<snip>

Below is an updated patch that makes a few changes the the latest
Sandbox Patch [1]. This requires the chcon patch [2].

Changes this patch makes:

sandbox.py
- Remove unused 'import commands'
- Uses new chcon method in libselinux [2]
- Removes the 'yum install seunshare' message
- Converts an IOError to a string for printing a warning if a file  
  listed in -I does not exist

sandbox.init
- Print the standard Starting/Stoping messages with the appropriate
  OK/FAIL
- Abort with failure if any mounts fail

seunshare.c
- Add the MS_REC flag during mounts to perform recursive mounts
- Define the mount flags MS_PRIVATE and MS_REC if they aren't already. 
  The flags are only defined in the latest glibc but have been in the 
  kernel since 2005.
- Calls realpath(3) on tmpdir_s and homedir_s. If relative paths are   
  used, it wouldn't correctly detect that tmpdir is inside homedir and 
  change the mount order. This fixes that.

[1] http://marc.info/?l=selinux&m=127429948731841&w=2
[2] http://marc.info/?l=selinux&m=127594712200878&w=2

--- 
policycoreutils/sandbox/sandbox      |   19 ++----------
 policycoreutils/sandbox/sandbox.init |   52 +++++++++++++++++++++++----------
 policycoreutils/sandbox/seunshare.c  |   27 ++++++++++++++---
 3 files changed, 61 insertions(+), 37 deletions(-)

diff --git a/policycoreutils/sandbox/sandbox b/policycoreutils/sandbox/sandbox
index bc7992b..48a26c2 100644
--- a/policycoreutils/sandbox/sandbox
+++ b/policycoreutils/sandbox/sandbox
@@ -24,7 +24,6 @@ import selinux
 import signal
 from tempfile import mkdtemp
 import pwd
-import commands 
 
 PROGNAME = "policycoreutils"
 HOMEDIR=pwd.getpwuid(os.getuid()).pw_dir
@@ -64,14 +63,6 @@ def error_exit(msg):
     sys.stderr.flush()
     sys.exit(1)
 
-def chcon(path, context, recursive=False):
-    """ Restore SELinux context on a given path """
-    mode = os.lstat(path)[stat.ST_MODE]
-    lsetfilecon(path, context)
-    if recursive:
-           os.path.walk(path, lambda arg, dirname, fnames:
-                               map(chcon, [os.path.join(dirname, fname)
-                                                for fname in fnames]), context)
 def copyfile(file, dir, dest):
        import re
        if file.startswith(dir):
@@ -173,10 +164,6 @@ class Sandbox:
            if not os.path.exists("/usr/sbin/seunshare"):
                   raise ValueError(_("""
 /usr/sbin/seunshare is required for the action you want to perform.  
-Install seunshare by executing:
-
-# yum install /usr/sbin/seunshare
-
 """))
 
     def __mount_callback(self, option, opt, value, parser):
@@ -206,7 +193,7 @@ Install seunshare by executing:
                   try:
                          self.__include(option, opt, i[:-1], parser)
                   except IOError, e:
-                         sys.stderr.write(e)
+                         sys.stderr.write(str(e))
            fd.close()
 
     def __copyfiles(self):
@@ -347,14 +334,14 @@ sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [-
                   os.mkdir(sandboxdir)
 
            if self.__options.homedir:
-                  chcon(self.__options.homedir, self.__filecon, True)
+                  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")
 
            if self.__options.tmpdir:
-                  chcon(self.__options.homedir, self.__filecon, True)
+                  selinux.chcon(self.__options.tmpdir, self.__filecon, recursive=True)
                   self.__tmpdir = self.__options.tmpdir
            else:
                   selinux.setfscreatecon(self.__filecon)
diff --git a/policycoreutils/sandbox/sandbox.init b/policycoreutils/sandbox/sandbox.init
index 44867d1..ff8b3ef 100644
--- a/policycoreutils/sandbox/sandbox.init
+++ b/policycoreutils/sandbox/sandbox.init
@@ -34,37 +34,57 @@ LOCKFILE=/var/lock/subsys/sandbox
 
 base=${0##*/}
 
-case "$1" in
-    restart)
-    start)
-	[ -f "$LOCKFILE" ] && exit 0
+start() {
+	echo -n "Starting sandbox"
+
+	[ -f "$LOCKFILE" ] && return 1
 
 	touch $LOCKFILE
-	mount --make-rshared /
-	mount --rbind /tmp /tmp
-	mount --rbind /var/tmp /var/tmp
-	mount --make-private /tmp
-	mount --make-private /var/tmp
+	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 
-	    mount --make-private $h
+	    mount --rbind $h $h || return $?
+	    mount --make-private $h || return $?
 	done
 
-	exit $?
-	;;
+	return 0
+}
 
-    status)
+stop() {
+	echo -n "Stopping sandbox"
+
+	[ -f "$LOCKFILE" ] || return 1
+}
+
+status() {
 	if [ -f "$LOCKFILE" ]; then 
 	    echo "$base is running"
 	else
 	    echo "$base is stopped"
 	fi
 	exit 0
+}
+
+case "$1" in
+    restart)
+	start && success || failure
+	;;
+
+    start)
+	start && success || failure
+	echo
 	;;
 
     stop)
-	rm -f $LOCKFILE
-	exit 0
+	stop && success || failure
+	echo
+	;;
+
+    status)
+	status
 	;;
 
     *)
diff --git a/policycoreutils/sandbox/seunshare.c b/policycoreutils/sandbox/seunshare.c
index 848f787..ec692e7 100644
--- a/policycoreutils/sandbox/seunshare.c
+++ b/policycoreutils/sandbox/seunshare.c
@@ -31,6 +31,14 @@
 #define _(msgid) (msgid)
 #endif
 
+#ifndef MS_REC
+#define MS_REC 1<<14
+#endif
+
+#ifndef MS_PRIVATE
+#define MS_PRIVATE 1<<18
+#endif
+
 /**
  * This function will drop all capabilities 
  * Returns zero on success, non-zero otherwise
@@ -126,17 +134,17 @@ static int verify_shell(const char *shell_name)
 static int seunshare_mount(const char *src, const char *dst, struct passwd *pwd) {
 	if (verbose)
 		printf("Mount %s on %s\n", src, dst);
-	if (mount(dst, dst,  NULL, MS_BIND, NULL) < 0) {
+	if (mount(dst, dst,  NULL, MS_BIND | MS_REC, 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, NULL) < 0) {
+	if (mount(dst, dst, NULL, MS_PRIVATE | MS_REC, NULL) < 0) {
 		fprintf(stderr, _("Failed to make %s private: %s\n"), dst, strerror(errno));
 		return -1;
 	}
 
-	if (mount(src, dst, NULL, MS_BIND, NULL) < 0) {
+	if (mount(src, dst, NULL, MS_BIND | MS_REC, NULL) < 0) {
 		fprintf(stderr, _("Failed to mount %s on %s: %s\n"), src, dst, strerror(errno));
 		return -1;
 	}
@@ -191,11 +199,17 @@ int main(int argc, char **argv) {
 
 		switch (clflag) {
 		case 't':
-			tmpdir_s = optarg;
+			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;
 			break;
 		case 'h':
-			homedir_s = optarg;
+			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;
 			break;
@@ -300,5 +314,8 @@ int main(int argc, char **argv) {
 		waitpid(child, &status, 0);
 	}
 
+	free(tmpdir_s);
+	free(homedir_s);
+
 	return status;
 }
-- 
1.6.2.5


--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: Updated sandbox patch.
  2010-06-07 21:53       ` Steve Lawrence
@ 2010-06-08 12:00         ` Daniel J Walsh
  2010-06-10 21:04         ` Chad Sellers
  1 sibling, 0 replies; 10+ messages in thread
From: Daniel J Walsh @ 2010-06-08 12:00 UTC (permalink / raw)
  To: slawrence; +Cc: SELinux

On 06/07/2010 05:53 PM, Steve Lawrence wrote:
> On Thu, 2010-05-27 at 08:57 -0400, Daniel J Walsh wrote:
>> -----BEGIN PGP SIGNED MESSAGE-----
>> Hash: SHA1
>>
>> On 05/26/2010 04:06 PM, Steve Lawrence wrote:
>>> On Wed, 2010-05-19 at 15:59 -0400, Daniel J Walsh wrote:
>>> Fixed patch that handles Spaces in homedir.
>>
>>> The following patch makes a few updates the the sandbox patch, though I
>>> have a question:
>>
>>> Is the sandbox.init script needed anymore? It looks like seunshare was
>>> changed to now bind mount and make private the necessary directories.
>>> The only thing that seems missing is making root rshared. Also, if the
>>> init script is obsolete, do the mounts also need the MS_REC flag for
>>> recursive bind/private like they are mounted in the init script? e.g.
>>
>> The init script is needed for the xguest package/more specifically
>> pam_namespace, but also needed for
>> mount --make-rshared /
>>
>> Whether the init script belongs in policycoreutils is questionable though.
>>
>>
>>> mount(dst, dst, NULL, (MS_BIND | MS_REC), NULL)
>>> mount(dst, dst, NULL, (MS_PRIVATE | MS_REC), NULL)
>>
>> We probably should add these.  Although it is not likely.
>>
>>> Changes the following patch makes:
>>
>>> sandbox.py
>>> - Removes unused 'import commands'
>>> - Fixes the chcon function, and replaces the deprecated os.path.walk
>>>    with os.walk. I think this way is a bit easier to read too.
>>
>> I think chcon should be added to libselinux python bindings and then
>> leave the recursive flag.  (restorecon is currently in python bindings._
>>
>>> - Removes the 'yum install seunshare' message. This tool is not specific
>>>    to RPM based distros.
>>
>> People are using seunshare without X now that I have added the -M flag.
>>   So I will move it from the -gui package to the base package with
>> sandbox and then this should not be necessary.
>>> - Remove try/except around -I include to be consistent with the -i
>>>    option. If we can't include a file, then this should bail, no matter
>>>    if it's being included via -i or -I.
>>
>> Ok, I was thinking you could list a whole bunch of files in the -I case
>> and if one does not exist, allow it to continue.  But I don't really care.
>>> - Fix homedir/tmpdir typo in chcon call
>>
>>> sandbox.init (maybe obsoleted?)
>>> - Fix restart so it stops and starts
>>> - unmount the bind mounts when stopped
>> I doubt this will work.  Two many locks in /tmp /home
>>> - Abort with failure if any mounts fail
>>
>>> seunshare.c
>>> - Define the mount flag MS_PRIVATE if it isn't already. The flag is only
>>>    defined in the latest glibc but has been in the kernel since 2005.
>>> - Simplify an if-statment. Also, I'm not sure the purpose of the
>>>    strncmmp in that conditional, so maybe I've oversimplified.
>> This is wrong.  The problem comes about when you mount within the same
>> directory.
>>
>> seunshare -t /home/dwalsh/sanbox/tmp -h /home/dwalsh/sandbox/home   ...
>>
>> seunshare -t /tmp/sandbox/tmp -h /tmp/sandbox/home
>>
>> If you do not have the check one of the above will fail.
>>
>> In the first example if Homedir is mounted first,
>> /home/dwalsh/sanbox/tmp will no longer exist when seunshare attempts to
>> mount it on /tmp.
>>
>> Similarly, if /tmp is mounted first in the second example.
>> /tmp/sandbox/home will no longer exist.
>>
>> You have to check to make sure one of the directories is not included in
>> the other.
>>
>> It seems
>>>    like maybe an error should be thrown if tmpdir_s == pw_dir or
>>>    homedir_s == "/tmp", but maybe I'm missing something.
>>
>> See above.
>>
>> I was blowing up because I use
>>
>> ~/sandbox/tmp and ~/sandbox/home for my mountpoints.
>
> <snip>
>
> Below is an updated patch that makes a few changes the the latest
> Sandbox Patch [1]. This requires the chcon patch [2].
>
> Changes this patch makes:
>
> sandbox.py
> - Remove unused 'import commands'
> - Uses new chcon method in libselinux [2]
> - Removes the 'yum install seunshare' message
> - Converts an IOError to a string for printing a warning if a file
>    listed in -I does not exist
>
> sandbox.init
> - Print the standard Starting/Stoping messages with the appropriate
>    OK/FAIL
> - Abort with failure if any mounts fail
>
> seunshare.c
> - Add the MS_REC flag during mounts to perform recursive mounts
> - Define the mount flags MS_PRIVATE and MS_REC if they aren't already.
>    The flags are only defined in the latest glibc but have been in the
>    kernel since 2005.
> - Calls realpath(3) on tmpdir_s and homedir_s. If relative paths are
>    used, it wouldn't correctly detect that tmpdir is inside homedir and
>    change the mount order. This fixes that.
>
> [1] http://marc.info/?l=selinux&m=127429948731841&w=2
> [2] http://marc.info/?l=selinux&m=127594712200878&w=2
>
> ---
> policycoreutils/sandbox/sandbox      |   19 ++----------
>   policycoreutils/sandbox/sandbox.init |   52 +++++++++++++++++++++++----------
>   policycoreutils/sandbox/seunshare.c  |   27 ++++++++++++++---
>   3 files changed, 61 insertions(+), 37 deletions(-)
>
> diff --git a/policycoreutils/sandbox/sandbox b/policycoreutils/sandbox/sandbox
> index bc7992b..48a26c2 100644
> --- a/policycoreutils/sandbox/sandbox
> +++ b/policycoreutils/sandbox/sandbox
> @@ -24,7 +24,6 @@ import selinux
>   import signal
>   from tempfile import mkdtemp
>   import pwd
> -import commands
>
>   PROGNAME = "policycoreutils"
>   HOMEDIR=pwd.getpwuid(os.getuid()).pw_dir
> @@ -64,14 +63,6 @@ def error_exit(msg):
>       sys.stderr.flush()
>       sys.exit(1)
>
> -def chcon(path, context, recursive=False):
> -    """ Restore SELinux context on a given path """
> -    mode = os.lstat(path)[stat.ST_MODE]
> -    lsetfilecon(path, context)
> -    if recursive:
> -           os.path.walk(path, lambda arg, dirname, fnames:
> -                               map(chcon, [os.path.join(dirname, fname)
> -                                                for fname in fnames]), context)
>   def copyfile(file, dir, dest):
>          import re
>          if file.startswith(dir):
> @@ -173,10 +164,6 @@ class Sandbox:
>              if not os.path.exists("/usr/sbin/seunshare"):
>                     raise ValueError(_("""
>   /usr/sbin/seunshare is required for the action you want to perform.
> -Install seunshare by executing:
> -
> -# yum install /usr/sbin/seunshare
> -
>   """))
>
>       def __mount_callback(self, option, opt, value, parser):
> @@ -206,7 +193,7 @@ Install seunshare by executing:
>                     try:
>                            self.__include(option, opt, i[:-1], parser)
>                     except IOError, e:
> -                         sys.stderr.write(e)
> +                         sys.stderr.write(str(e))
>              fd.close()
>
>       def __copyfiles(self):
> @@ -347,14 +334,14 @@ sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [-
>                     os.mkdir(sandboxdir)
>
>              if self.__options.homedir:
> -                  chcon(self.__options.homedir, self.__filecon, True)
> +                  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")
>
>              if self.__options.tmpdir:
> -                  chcon(self.__options.homedir, self.__filecon, True)
> +                  selinux.chcon(self.__options.tmpdir, self.__filecon, recursive=True)
>                     self.__tmpdir = self.__options.tmpdir
>              else:
>                     selinux.setfscreatecon(self.__filecon)
> diff --git a/policycoreutils/sandbox/sandbox.init b/policycoreutils/sandbox/sandbox.init
> index 44867d1..ff8b3ef 100644
> --- a/policycoreutils/sandbox/sandbox.init
> +++ b/policycoreutils/sandbox/sandbox.init
> @@ -34,37 +34,57 @@ LOCKFILE=/var/lock/subsys/sandbox
>
>   base=${0##*/}
>
> -case "$1" in
> -    restart)
> -    start)
> -	[ -f "$LOCKFILE" ]&&  exit 0
> +start() {
> +	echo -n "Starting sandbox"
> +
> +	[ -f "$LOCKFILE" ]&&  return 1
>
>   	touch $LOCKFILE
> -	mount --make-rshared /
> -	mount --rbind /tmp /tmp
> -	mount --rbind /var/tmp /var/tmp
> -	mount --make-private /tmp
> -	mount --make-private /var/tmp
> +	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
> -	    mount --make-private $h
> +	    mount --rbind $h $h || return $?
> +	    mount --make-private $h || return $?
>   	done
>
> -	exit $?
> -	;;
> +	return 0
> +}
>
> -    status)
> +stop() {
> +	echo -n "Stopping sandbox"
> +
> +	[ -f "$LOCKFILE" ] || return 1
> +}
> +
> +status() {
>   	if [ -f "$LOCKFILE" ]; then
>   	    echo "$base is running"
>   	else
>   	    echo "$base is stopped"
>   	fi
>   	exit 0
> +}
> +
> +case "$1" in
> +    restart)
> +	start&&  success || failure
> +	;;
> +
> +    start)
> +	start&&  success || failure
> +	echo
>   	;;
>
>       stop)
> -	rm -f $LOCKFILE
> -	exit 0
> +	stop&&  success || failure
> +	echo
> +	;;
> +
> +    status)
> +	status
>   	;;
>
>       *)
> diff --git a/policycoreutils/sandbox/seunshare.c b/policycoreutils/sandbox/seunshare.c
> index 848f787..ec692e7 100644
> --- a/policycoreutils/sandbox/seunshare.c
> +++ b/policycoreutils/sandbox/seunshare.c
> @@ -31,6 +31,14 @@
>   #define _(msgid) (msgid)
>   #endif
>
> +#ifndef MS_REC
> +#define MS_REC 1<<14
> +#endif
> +
> +#ifndef MS_PRIVATE
> +#define MS_PRIVATE 1<<18
> +#endif
> +
>   /**
>    * This function will drop all capabilities
>    * Returns zero on success, non-zero otherwise
> @@ -126,17 +134,17 @@ static int verify_shell(const char *shell_name)
>   static int seunshare_mount(const char *src, const char *dst, struct passwd *pwd) {
>   	if (verbose)
>   		printf("Mount %s on %s\n", src, dst);
> -	if (mount(dst, dst,  NULL, MS_BIND, NULL)<  0) {
> +	if (mount(dst, dst,  NULL, MS_BIND | MS_REC, 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, NULL)<  0) {
> +	if (mount(dst, dst, NULL, MS_PRIVATE | MS_REC, NULL)<  0) {
>   		fprintf(stderr, _("Failed to make %s private: %s\n"), dst, strerror(errno));
>   		return -1;
>   	}
>
> -	if (mount(src, dst, NULL, MS_BIND, NULL)<  0) {
> +	if (mount(src, dst, NULL, MS_BIND | MS_REC, NULL)<  0) {
>   		fprintf(stderr, _("Failed to mount %s on %s: %s\n"), src, dst, strerror(errno));
>   		return -1;
>   	}
> @@ -191,11 +199,17 @@ int main(int argc, char **argv) {
>
>   		switch (clflag) {
>   		case 't':
> -			tmpdir_s = optarg;
> +			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;
>   			break;
>   		case 'h':
> -			homedir_s = optarg;
> +			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;
>   			break;
> @@ -300,5 +314,8 @@ int main(int argc, char **argv) {
>   		waitpid(child,&status, 0);
>   	}
>
> +	free(tmpdir_s);
> +	free(homedir_s);
> +
>   	return status;
>   }

Looks good.  We have a lot of changes coming to sandbox (cgroup 
integration), As soon as you get these checked in,  we will send an 
updated patch.

--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Updated sandbox patch.
  2010-05-19 19:59 ` Daniel J Walsh
  2010-05-26 20:06   ` Steve Lawrence
@ 2010-06-10 21:04   ` Chad Sellers
  1 sibling, 0 replies; 10+ messages in thread
From: Chad Sellers @ 2010-06-10 21:04 UTC (permalink / raw)
  To: Daniel J Walsh, slawrence; +Cc: SELinux

On 5/19/10 3:59 PM, "Daniel J Walsh" <dwalsh@redhat.com> wrote:

> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
> 
> Fixed patch that handles Spaces in homedir.
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v2.0.14 (GNU/Linux)
> Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org/
> 
> iEYEARECAAYFAkv0QyAACgkQrlYvE4MpobNBXQCgmUu92HsN5PiksOTZoGxSp0W+
> 1noAoKoCujFPLHduJ9BP3hrveeXvGKXO
> =iqC+
> -----END PGP SIGNATURE-----

Acked-by: Chad Sellers <csellers@tresys.com>

Merged as of policycoreutils 2.0.83


--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Updated sandbox patch.
  2010-06-07 21:53       ` Steve Lawrence
  2010-06-08 12:00         ` Daniel J Walsh
@ 2010-06-10 21:04         ` Chad Sellers
  1 sibling, 0 replies; 10+ messages in thread
From: Chad Sellers @ 2010-06-10 21:04 UTC (permalink / raw)
  To: slawrence, Daniel J Walsh; +Cc: SELinux

On 6/7/10 5:53 PM, "Steve Lawrence" <slawrence@tresys.com> wrote:

> On Thu, 2010-05-27 at 08:57 -0400, Daniel J Walsh wrote:
>> -----BEGIN PGP SIGNED MESSAGE-----
>> Hash: SHA1
>> 
>> On 05/26/2010 04:06 PM, Steve Lawrence wrote:
>>> On Wed, 2010-05-19 at 15:59 -0400, Daniel J Walsh wrote:
>>> Fixed patch that handles Spaces in homedir.
>> 
>>> The following patch makes a few updates the the sandbox patch, though I
>>> have a question:
>> 
>>> Is the sandbox.init script needed anymore? It looks like seunshare was
>>> changed to now bind mount and make private the necessary directories.
>>> The only thing that seems missing is making root rshared. Also, if the
>>> init script is obsolete, do the mounts also need the MS_REC flag for
>>> recursive bind/private like they are mounted in the init script? e.g.
>> 
>> The init script is needed for the xguest package/more specifically
>> pam_namespace, but also needed for
>> mount --make-rshared /
>> 
>> Whether the init script belongs in policycoreutils is questionable though.
>> 
>> 
>>> mount(dst, dst, NULL, (MS_BIND | MS_REC), NULL)
>>> mount(dst, dst, NULL, (MS_PRIVATE | MS_REC), NULL)
>> 
>> We probably should add these.  Although it is not likely.
>> 
>>> Changes the following patch makes:
>> 
>>> sandbox.py
>>> - Removes unused 'import commands'
>>> - Fixes the chcon function, and replaces the deprecated os.path.walk
>>>   with os.walk. I think this way is a bit easier to read too.
>> 
>> I think chcon should be added to libselinux python bindings and then
>> leave the recursive flag.  (restorecon is currently in python bindings._
>> 
>>> - Removes the 'yum install seunshare' message. This tool is not specific
>>>   to RPM based distros.
>> 
>> People are using seunshare without X now that I have added the -M flag.
>>  So I will move it from the -gui package to the base package with
>> sandbox and then this should not be necessary.
>>> - Remove try/except around -I include to be consistent with the -i
>>>   option. If we can't include a file, then this should bail, no matter
>>>   if it's being included via -i or -I.
>> 
>> Ok, I was thinking you could list a whole bunch of files in the -I case
>> and if one does not exist, allow it to continue.  But I don't really care.
>>> - Fix homedir/tmpdir typo in chcon call
>> 
>>> sandbox.init (maybe obsoleted?)
>>> - Fix restart so it stops and starts
>>> - unmount the bind mounts when stopped
>> I doubt this will work.  Two many locks in /tmp /home
>>> - Abort with failure if any mounts fail
>> 
>>> seunshare.c
>>> - Define the mount flag MS_PRIVATE if it isn't already. The flag is only
>>>   defined in the latest glibc but has been in the kernel since 2005.
>>> - Simplify an if-statment. Also, I'm not sure the purpose of the
>>>   strncmmp in that conditional, so maybe I've oversimplified.
>> This is wrong.  The problem comes about when you mount within the same
>> directory.
>> 
>> seunshare -t /home/dwalsh/sanbox/tmp -h /home/dwalsh/sandbox/home   ...
>> 
>> seunshare -t /tmp/sandbox/tmp -h /tmp/sandbox/home
>> 
>> If you do not have the check one of the above will fail.
>> 
>> In the first example if Homedir is mounted first,
>> /home/dwalsh/sanbox/tmp will no longer exist when seunshare attempts to
>> mount it on /tmp.
>> 
>> Similarly, if /tmp is mounted first in the second example.
>> /tmp/sandbox/home will no longer exist.
>> 
>> You have to check to make sure one of the directories is not included in
>> the other.
>> 
>> It seems
>>>   like maybe an error should be thrown if tmpdir_s == pw_dir or
>>>   homedir_s == "/tmp", but maybe I'm missing something.
>> 
>> See above.
>> 
>> I was blowing up because I use
>> 
>> ~/sandbox/tmp and ~/sandbox/home for my mountpoints.
> 
> <snip>
> 
> Below is an updated patch that makes a few changes the the latest
> Sandbox Patch [1]. This requires the chcon patch [2].
> 
> Changes this patch makes:
> 
> sandbox.py
> - Remove unused 'import commands'
> - Uses new chcon method in libselinux [2]
> - Removes the 'yum install seunshare' message
> - Converts an IOError to a string for printing a warning if a file
>   listed in -I does not exist
> 
> sandbox.init
> - Print the standard Starting/Stoping messages with the appropriate
>   OK/FAIL
> - Abort with failure if any mounts fail
> 
> seunshare.c
> - Add the MS_REC flag during mounts to perform recursive mounts
> - Define the mount flags MS_PRIVATE and MS_REC if they aren't already.
>   The flags are only defined in the latest glibc but have been in the
>   kernel since 2005.
> - Calls realpath(3) on tmpdir_s and homedir_s. If relative paths are
>   used, it wouldn't correctly detect that tmpdir is inside homedir and
>   change the mount order. This fixes that.
> 
> [1] http://marc.info/?l=selinux&m=127429948731841&w=2
> [2] http://marc.info/?l=selinux&m=127594712200878&w=2
> 
> --- 
> policycoreutils/sandbox/sandbox      |   19 ++----------
>  policycoreutils/sandbox/sandbox.init |   52 +++++++++++++++++++++++----------
>  policycoreutils/sandbox/seunshare.c  |   27 ++++++++++++++---
>  3 files changed, 61 insertions(+), 37 deletions(-)
> 
> diff --git a/policycoreutils/sandbox/sandbox b/policycoreutils/sandbox/sandbox
> index bc7992b..48a26c2 100644
> --- a/policycoreutils/sandbox/sandbox
> +++ b/policycoreutils/sandbox/sandbox
> @@ -24,7 +24,6 @@ import selinux
>  import signal
>  from tempfile import mkdtemp
>  import pwd
> -import commands 
>  
>  PROGNAME = "policycoreutils"
>  HOMEDIR=pwd.getpwuid(os.getuid()).pw_dir
> @@ -64,14 +63,6 @@ def error_exit(msg):
>      sys.stderr.flush()
>      sys.exit(1)
>  
> -def chcon(path, context, recursive=False):
> -    """ Restore SELinux context on a given path """
> -    mode = os.lstat(path)[stat.ST_MODE]
> -    lsetfilecon(path, context)
> -    if recursive:
> -           os.path.walk(path, lambda arg, dirname, fnames:
> -                               map(chcon, [os.path.join(dirname, fname)
> -                                                for fname in fnames]),
> context)
>  def copyfile(file, dir, dest):
>         import re
>         if file.startswith(dir):
> @@ -173,10 +164,6 @@ class Sandbox:
>             if not os.path.exists("/usr/sbin/seunshare"):
>                    raise ValueError(_("""
>  /usr/sbin/seunshare is required for the action you want to perform.
> -Install seunshare by executing:
> -
> -# yum install /usr/sbin/seunshare
> -
>  """))
>  
>      def __mount_callback(self, option, opt, value, parser):
> @@ -206,7 +193,7 @@ Install seunshare by executing:
>                    try:
>                           self.__include(option, opt, i[:-1], parser)
>                    except IOError, e:
> -                         sys.stderr.write(e)
> +                         sys.stderr.write(str(e))
>             fd.close()
>  
>      def __copyfiles(self):
> @@ -347,14 +334,14 @@ sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T
> tempdir]] [-I includefile ] [-
>                    os.mkdir(sandboxdir)
>  
>             if self.__options.homedir:
> -                  chcon(self.__options.homedir, self.__filecon, True)
> +                  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")
>  
>             if self.__options.tmpdir:
> -                  chcon(self.__options.homedir, self.__filecon, True)
> +                  selinux.chcon(self.__options.tmpdir, self.__filecon,
> recursive=True)
>                    self.__tmpdir = self.__options.tmpdir
>             else:
>                    selinux.setfscreatecon(self.__filecon)
> diff --git a/policycoreutils/sandbox/sandbox.init
> b/policycoreutils/sandbox/sandbox.init
> index 44867d1..ff8b3ef 100644
> --- a/policycoreutils/sandbox/sandbox.init
> +++ b/policycoreutils/sandbox/sandbox.init
> @@ -34,37 +34,57 @@ LOCKFILE=/var/lock/subsys/sandbox
>  
>  base=${0##*/}
>  
> -case "$1" in
> -    restart)
> -    start)
> - [ -f "$LOCKFILE" ] && exit 0
> +start() {
> + echo -n "Starting sandbox"
> +
> + [ -f "$LOCKFILE" ] && return 1
>  
> touch $LOCKFILE
> - mount --make-rshared /
> - mount --rbind /tmp /tmp
> - mount --rbind /var/tmp /var/tmp
> - mount --make-private /tmp
> - mount --make-private /var/tmp
> + 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
> -     mount --make-private $h
> +     mount --rbind $h $h || return $?
> +     mount --make-private $h || return $?
> done
>  
> - exit $?
> - ;;
> + return 0
> +}
>  
> -    status)
> +stop() {
> + echo -n "Stopping sandbox"
> +
> + [ -f "$LOCKFILE" ] || return 1
> +}
> +
> +status() {
> if [ -f "$LOCKFILE" ]; then
>    echo "$base is running"
> else
>    echo "$base is stopped"
> fi
> exit 0
> +}
> +
> +case "$1" in
> +    restart)
> + start && success || failure
> + ;;
> +
> +    start)
> + start && success || failure
> + echo
> ;;
>  
>      stop)
> - rm -f $LOCKFILE
> - exit 0
> + stop && success || failure
> + echo
> + ;;
> +
> +    status)
> + status
> ;;
>  
>      *)
> diff --git a/policycoreutils/sandbox/seunshare.c
> b/policycoreutils/sandbox/seunshare.c
> index 848f787..ec692e7 100644
> --- a/policycoreutils/sandbox/seunshare.c
> +++ b/policycoreutils/sandbox/seunshare.c
> @@ -31,6 +31,14 @@
>  #define _(msgid) (msgid)
>  #endif
>  
> +#ifndef MS_REC
> +#define MS_REC 1<<14
> +#endif
> +
> +#ifndef MS_PRIVATE
> +#define MS_PRIVATE 1<<18
> +#endif
> +
>  /**
>   * This function will drop all capabilities
>   * Returns zero on success, non-zero otherwise
> @@ -126,17 +134,17 @@ static int verify_shell(const char *shell_name)
>  static int seunshare_mount(const char *src, const char *dst, struct passwd
> *pwd) {
> if (verbose)
> printf("Mount %s on %s\n", src, dst);
> - if (mount(dst, dst,  NULL, MS_BIND, NULL) < 0) {
> + if (mount(dst, dst,  NULL, MS_BIND | MS_REC, 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, NULL) < 0) {
> + if (mount(dst, dst, NULL, MS_PRIVATE | MS_REC, NULL) < 0) {
> fprintf(stderr, _("Failed to make %s private: %s\n"), dst, strerror(errno));
> return -1;
> }
>  
> - if (mount(src, dst, NULL, MS_BIND, NULL) < 0) {
> + if (mount(src, dst, NULL, MS_BIND | MS_REC, NULL) < 0) {
> fprintf(stderr, _("Failed to mount %s on %s: %s\n"), src, dst,
> strerror(errno));
> return -1;
> }
> @@ -191,11 +199,17 @@ int main(int argc, char **argv) {
>  
> switch (clflag) {
> case 't':
> -   tmpdir_s = optarg;
> +   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;
> break;
> case 'h':
> -   homedir_s = optarg;
> +   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;
> break;
> @@ -300,5 +314,8 @@ int main(int argc, char **argv) {
> waitpid(child, &status, 0);
> }
>  
> + free(tmpdir_s);
> + free(homedir_s);
> +
> return status;
>  }

Acked-by: Chad Sellers <csellers@tresys.com>

Merged as of policycoreutils 2.0.83


--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with
the words "unsubscribe selinux" without quotes as the message.

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2010-06-10 21:04 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-05-18 18:58 Updated sandbox patch Steve Lawrence
2010-05-19 17:31 ` Daniel J Walsh
2010-05-19 19:59 ` Daniel J Walsh
2010-05-26 20:06   ` Steve Lawrence
2010-05-27 12:57     ` Daniel J Walsh
2010-06-07 21:53       ` Steve Lawrence
2010-06-08 12:00         ` Daniel J Walsh
2010-06-10 21:04         ` Chad Sellers
2010-06-10 21:04   ` Chad Sellers
  -- strict thread matches above, loose matches on Subject: below --
2010-04-01 19:20 Daniel J Walsh

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.