[PATCH 91/92] policycoreutils: sandbox: FIXME update sandbox

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


This patch looks good to me. acked.

I agree that this is almost an entire rewrite.  Most of the sandbox code
that went into upstream has changed drastically.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAk44GakACgkQrlYvE4MpobObAQCZAay+sKzeKgObo07Qf6RIyxcG
UT4Anitd00nZGNMjInNOsDfhxH8iAbER
=yn41
-----END PGP SIGNATURE-----
>From 3a904ea749b3d02bf039ddf0d622626a0a7ed3e7 Mon Sep 17 00:00:00 2001
From: Eric Paris <eparis@xxxxxxxxxx>
Date: Wed, 20 Jul 2011 14:54:47 -0400
Subject: [PATCH 91/92] policycoreutils: sandbox: FIXME update sandbox

Should I break this down more?  Can I make a better commit message?

This patch updates the sandbox code to be in line with Fedora.  The
current code is way behind and this is almost a rewrite.  Sandbox has
new features, configuration, and all sorts of new changes.

NOT-Signed-off-by: Eric Paris <eparis@xxxxxxxxxx>
---
 policycoreutils/sandbox/Makefile       |   12 +-
 policycoreutils/sandbox/sandbox        |  112 +++--
 policycoreutils/sandbox/sandbox.8      |   29 +-
 policycoreutils/sandbox/sandbox.conf   |    7 +
 policycoreutils/sandbox/sandbox.conf.5 |   40 ++
 policycoreutils/sandbox/sandbox.init   |   26 +-
 policycoreutils/sandbox/sandboxX.sh    |   16 +-
 policycoreutils/sandbox/seunshare.8    |   43 ++
 policycoreutils/sandbox/seunshare.c    |  936 ++++++++++++++++++++++++++++----
 policycoreutils/sandbox/start          |    9 +
 10 files changed, 1052 insertions(+), 178 deletions(-)
 create mode 100644 policycoreutils/sandbox/sandbox.conf
 create mode 100644 policycoreutils/sandbox/sandbox.conf.5
 create mode 100644 policycoreutils/sandbox/seunshare.8
 create mode 100644 policycoreutils/sandbox/start

diff --git a/policycoreutils/sandbox/Makefile b/policycoreutils/sandbox/Makefile
index ff0ee7c..0c8a085 100644
--- a/policycoreutils/sandbox/Makefile
+++ b/policycoreutils/sandbox/Makefile
@@ -7,10 +7,10 @@ SBINDIR ?= $(PREFIX)/sbin
 MANDIR ?= $(PREFIX)/share/man
 LOCALEDIR ?= /usr/share/locale
 SHAREDIR ?= $(PREFIX)/share/sandbox
-override CFLAGS += $(LDFLAGS) -I$(PREFIX)/include -DPACKAGE="\"policycoreutils\""
-LDLIBS += -lselinux -lcap-ng 
+override CFLAGS += $(LDFLAGS) -I$(PREFIX)/include -DPACKAGE="\"policycoreutils\"" -Wall -Werror -Wextra
+LDLIBS += -lcgroup -lselinux -lcap-ng 
 
-all: sandbox seunshare sandboxX.sh 
+all: sandbox seunshare sandboxX.sh start
 
 seunshare: seunshare.o $(EXTRA_OBJS)
 	$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
@@ -20,14 +20,18 @@ install: all
 	install -m 755 sandbox $(BINDIR)
 	-mkdir -p $(MANDIR)/man8
 	install -m 644 sandbox.8 $(MANDIR)/man8/
+	install -m 644 seunshare.8 $(MANDIR)/man8/
+	-mkdir -p $(MANDIR)/man5
+	install -m 644 sandbox.conf.5 $(MANDIR)/man5/
 	-mkdir -p $(SBINDIR)
 	install -m 4755 seunshare $(SBINDIR)/
 	-mkdir -p $(SHAREDIR)
 	install -m 755 sandboxX.sh $(SHAREDIR)
+	install -m 755 start $(SHAREDIR)
 	-mkdir -p $(INITDIR)
 	install -m 755 sandbox.init $(INITDIR)/sandbox
 	-mkdir -p $(SYSCONFDIR)
-	install -m 644 sandbox.config $(SYSCONFDIR)/sandbox
+	install -m 644 sandbox.conf $(SYSCONFDIR)/sandbox
 
 test:
 	@python test_sandbox.py -v
diff --git a/policycoreutils/sandbox/sandbox b/policycoreutils/sandbox/sandbox
index 0b89e9a..85a1fce 100644
--- a/policycoreutils/sandbox/sandbox
+++ b/policycoreutils/sandbox/sandbox
@@ -1,5 +1,6 @@
 #! /usr/bin/python -Es
 # Authors: Dan Walsh <dwalsh@xxxxxxxxxx>
+# Authors: Thomas Liu <tliu@xxxxxxxxxxxxxxxxx>
 # Authors: Josh Cogliati
 #
 # Copyright (C) 2009,2010  Red Hat
@@ -19,15 +20,17 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 #
 
-import os, sys, socket, random, fcntl, shutil, re, subprocess
+import os, stat, sys, socket, random, fcntl, shutil, re, subprocess
 import selinux
 import signal
 from tempfile import mkdtemp
 import pwd
+import commands 
+import setools
 
 PROGNAME = "policycoreutils"
-HOMEDIR=pwd.getpwuid(os.getuid()).pw_dir
-
+SEUNSHARE = "/usr/sbin/seunshare"
+SANDBOXSH = "/usr/share/sandbox/sandboxX.sh"
 import gettext
 gettext.bindtextdomain(PROGNAME, "/usr/share/locale")
 gettext.textdomain(PROGNAME)
@@ -41,6 +44,7 @@ except IOError:
        import __builtin__
        __builtin__.__dict__['_'] = unicode
 
+DEFAULT_WINDOWSIZE = "1000x700"
 DEFAULT_TYPE = "sandbox_t"
 DEFAULT_X_TYPE = "sandbox_x_t"
 SAVE_FILES = {}
@@ -63,15 +67,15 @@ def error_exit(msg):
     sys.stderr.flush()
     sys.exit(1)
 
-def copyfile(file, dir, dest):
+def copyfile(file, srcdir, dest):
        import re
-       if file.startswith(dir):
+       if file.startswith(srcdir):
               dname = os.path.dirname(file)
               bname = os.path.basename(file)
-              if dname == dir:
+              if dname == srcdir:
                      dest = dest + "/" + bname
               else:
-                     newdir = re.sub(dir, dest, dname)
+                     newdir = re.sub(srcdir, dest, dname)
                      if not os.path.exists(newdir):
                             os.makedirs(newdir)
                      dest = newdir + "/" + bname
@@ -81,9 +85,10 @@ def copyfile(file, dir, dest):
                             shutil.copytree(file, dest)
                      else:
                             shutil.copy2(file, dest)
+
               except shutil.Error, elist:
-                     for e in elist:
-                            sys.stderr.write(e[1])
+                     for e in elist.message:
+                            sys.stderr.write(e[2])
                      
               SAVE_FILES[file] = (dest, os.path.getmtime(dest))
 
@@ -161,10 +166,10 @@ class Sandbox:
                   if not self.__options.homedir or not self.__options.tmpdir:
                          self.usage(_("Homedir and tempdir required for level mounts"))
 
-           if not os.path.exists("/usr/sbin/seunshare"):
+           if not os.path.exists(SEUNSHARE):
                   raise ValueError(_("""
-/usr/sbin/seunshare is required for the action you want to perform.  
-"""))
+%s is required for the action you want to perform.  
+""") % SEUNSHARE)
 
     def __mount_callback(self, option, opt, value, parser):
            self.__mount = True
@@ -172,6 +177,15 @@ class Sandbox:
     def __x_callback(self, option, opt, value, parser):
            self.__mount = True
            setattr(parser.values, option.dest, True)
+           if not os.path.exists(SEUNSHARE):
+                  raise ValueError(_("""
+%s is required for the action you want to perform.  
+""") % SEUNSHARE)
+
+           if not os.path.exists(SANDBOXSH):
+                  raise ValueError(_("""
+%s is required for the action you want to perform.  
+""") % SANDBOXSH)
 
     def __validdir(self, option, opt, value, parser):
            if not os.path.isdir(value):
@@ -194,6 +208,8 @@ class Sandbox:
                          self.__include(option, opt, i[:-1], parser)
                   except IOError, e:
                          sys.stderr.write(str(e))
+                  except TypeError, e:
+                         sys.stderr.write(str(e))
            fd.close()
 
     def __copyfiles(self):
@@ -212,13 +228,15 @@ class Sandbox:
 /etc/gdm/Xsession
 """)
            else:
-                  command = " ".join(self.__paths)
+                  command = self.__paths[0] + " "
+                  for p in self.__paths[1:]:
+                         command += "'%s' " % p
                   fd.write("""#! /bin/sh
 #TITLE: %s
 /usr/bin/test -r ~/.xmodmap && /usr/bin/xmodmap ~/.xmodmap
 %s &
 WM_PID=$!
-%s
+dbus-launch --exit-with-session %s
 kill -TERM $WM_PID  2> /dev/null
 """ % (command, wm, command))
            fd.close()
@@ -229,11 +247,22 @@ kill -TERM $WM_PID  2> /dev/null
 
     def __parse_options(self):
         from optparse import OptionParser
+        types = ""
+        try:
+               types = _("""
+Policy defines the following types for use with the -t: 
+\t%s
+""") % "\n\t".join(setools.seinfo(setools.ATTRIBUTE, "sandbox_type")[0]['types'])
+        except RuntimeError:
+               pass
+
         usage = _("""
-sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [-W windowmanager ] [[-i file ] ...] [ -t type ] command
+sandbox [-h] [-l level ] [-[X|M] [-H homedir] [-T tempdir]] [-I includefile ] [-W windowmanager ] [ -w windowsize ] [[-i file ] ...] [ -t type ] command
+
+sandbox [-h] [-l level ] [-[X|M] [-H homedir] [-T tempdir]] [-I includefile ] [-W windowmanager ] [ -w windowsize ] [[-i file ] ...] [ -t type ] -S
+%s
+""") % types
 
-sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [-W windowmanager ] [[-i file ] ...] [ -t type ] -S
-""")
         
         parser = OptionParser(version=self.VERSION, usage=usage)
         parser.disable_interspersed_args()
@@ -268,6 +297,10 @@ sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [-
                           action="callback", callback=self.__validdir,
                           help=_("alternate /tmp directory to use for mounting"))
 
+        parser.add_option("-w", "--windowsize", dest="windowsize",
+                          type="string", default=DEFAULT_WINDOWSIZE,
+                          help="size of the sandbox window")		
+
         parser.add_option("-W", "--windowmanager", dest="wm",  
                           type="string",
                           default="/usr/bin/matchbox-window-manager -use_titlebar no",
@@ -276,13 +309,21 @@ sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [-
         parser.add_option("-l", "--level", dest="level", 
                           help=_("MCS/MLS level for the sandbox"))
 
+        parser.add_option("-c", "--cgroups",
+                         action="store_true", dest="usecgroup", default=False,
+                         help="Use cgroups to limit this sandbox.")
+
+        parser.add_option("-C", "--capabilities",
+                         action="store_true", dest="usecaps", default=False,
+                         help="Allow apps requiring capabilities to run within the sandbox.")
+
         self.__parser=parser
 
         self.__options, cmds = parser.parse_args()
 
         if self.__options.X_ind:
                self.setype = DEFAULT_X_TYPE
-        
+               self.dpi=commands.getoutput("xrdb -query | grep dpi  | /bin/cut -f 2")
         if self.__options.setype:
                self.setype = self.__options.setype
 
@@ -300,6 +341,10 @@ sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [-
                self.__homedir = self.__options.homedir
                self.__tmpdir = self.__options.tmpdir
         else:
+               if self.__options.level:
+                      self.__homedir = self.__options.homedir
+                      self.__tmpdir = self.__options.tmpdir
+
                if len(cmds) == 0:
                       self.usage(_("Command required"))
                cmds[0] = fullpath(cmds[0])
@@ -329,28 +374,35 @@ sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [-
     def __setup_dir(self):
            if self.__options.level or self.__options.session:
                   return
-           sandboxdir = HOMEDIR + "/.sandbox"
-           if not os.path.exists(sandboxdir):
-                  os.mkdir(sandboxdir)
 
            if self.__options.homedir:
                   selinux.chcon(self.__options.homedir, self.__filecon, recursive=True)
                   self.__homedir = self.__options.homedir
            else:
                   selinux.setfscreatecon(self.__filecon)
-                  self.__homedir = mkdtemp(dir=sandboxdir, prefix=".sandbox")
+                  self.__homedir = mkdtemp(dir="/tmp", prefix=".sandbox_home_")
 
            if self.__options.tmpdir:
                   selinux.chcon(self.__options.tmpdir, self.__filecon, recursive=True)
                   self.__tmpdir = self.__options.tmpdir
            else:
                   selinux.setfscreatecon(self.__filecon)
-                  self.__tmpdir = mkdtemp(dir="/tmp", prefix=".sandbox")
+                  self.__tmpdir = mkdtemp(dir="/tmp", prefix=".sandbox_tmp_")
            selinux.setfscreatecon(None)
            self.__copyfiles()
 
     def __execute(self):
            try:
+                  cmds = [ SEUNSHARE,  "-Z", self.__execcon ]
+                  if self.__options.usecgroup:
+                         cmds.append('-c')
+                  if self.__options.usecaps:
+                         cmds.append('-C')
+                  if not self.__options.level:
+                         cmds.append('-k')
+                  if self.__mount:
+                         cmds +=  [ "-t", self.__tmpdir, "-h", self.__homedir ]
+
                   if self.__options.X_ind:
                          xmodmapfile = self.__homedir + "/.xmodmap"
                          xd = open(xmodmapfile,"w")
@@ -359,14 +411,10 @@ sandbox [-h] [-[X|M] [-l level ] [-H homedir] [-T tempdir]] [-I includefile ] [-
 
                          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
+                                cmds += [ "--", SANDBOXSH, self.__options.windowsize, self.dpi ]
+                         else:
+                                cmds += [ "--" ] + self.__paths
+                         return subprocess.Popen(cmds).wait()
 
                   selinux.setexeccon(self.__execcon)
                   rc = subprocess.Popen(self.__cmds).wait()
@@ -404,7 +452,7 @@ if __name__ == '__main__':
            sandbox = Sandbox()
            rc = sandbox.main()
     except OSError, error:
-           error_exit(error.args[1])
+           error_exit(error)
     except ValueError, error:
            error_exit(error.args[0])
     except KeyError, error:
diff --git a/policycoreutils/sandbox/sandbox.8 b/policycoreutils/sandbox/sandbox.8
index 1479364..3deb4b2 100644
--- a/policycoreutils/sandbox/sandbox.8
+++ b/policycoreutils/sandbox/sandbox.8
@@ -1,10 +1,13 @@
-.TH SANDBOX "8" "May 2009" "chcat" "User Commands"
+.TH SANDBOX "8" "May 2010" "sandbox" "User Commands"
 .SH NAME
 sandbox \- Run cmd under an SELinux sandbox
 .SH SYNOPSIS
 .B sandbox
-[-l level ] [[-M | -X]  -H homedir -T tempdir ] [-I includefile ] [ -W windowmanager ] [[-i file ]...] [ -t type ] cmd
-[-l level ] [[-M | -X]  -H homedir -T tempdir ] [-I includefile ] [ -W windowmanager ] [[-i file ]...] [ -t type ] -S
+[-C] [-c] [-l level ] [[-M | -X]  -H homedir -T tempdir ] [-I includefile ] [ -W windowmanager ] [ -w windowsize ] [[-i file ]...] [ -t type ] cmd
+
+.br
+.B sandbox
+[-C] [-c] [-l level ] [[-M | -X]  -H homedir -T tempdir ] [-I includefile ] [ -W windowmanager ] [ -w windowsize ] [[-i file ]...] [ -t type ] -S
 .br
 .SH DESCRIPTION
 .PP
@@ -42,6 +45,12 @@ Use alternate sandbox type, defaults to sandbox_t or sandbox_x_t for -X.
 \fB\-T\ tmpdir
 Use alternate tempory directory to mount on /tmp.  Defaults to tmpfs. Requires -X or -M.
 .TP
+\fB\-S
+Run a full desktop session, Requires level, and home and tmpdir.
+.TP
+\fB\-w windowsize\fR
+Specifies the windowsize when creating an X based Sandbox. The default windowsize is 1000x700. 
+.TP
 \fB\-W windowmanager\fR
 Select alternative window manager to run within 
 .B sandbox -X.
@@ -50,8 +59,20 @@ Default to /usr/bin/matchbox-window-manager.
 \fB\-X\fR 
 Create an X based Sandbox for gui apps, temporary files for
 $HOME and /tmp, secondary Xserver, defaults to sandbox_x_t
+.TP
+\fB\-c\fR
+Use control groups to control this copy of sandbox.  Specify parameters in /etc/sysconfig/sandbox.  Max memory usage and cpu usage are to be specified in percent.  You can specify which CPUs to use by numbering them 0,1,2... etc.
+.TP
+\fB\-C\fR
+Use capabilities within the sandbox.  By default applications executed within the sandbox will not be allowed to use capabilities (setuid apps), with the -C flag, you can use programs requiring capabilities.
 .PP
 .SH "SEE ALSO"
 .TP
-runcon(1)
+runcon(1), seunshare(8), selinux(8)
 .PP
+
+.SH AUTHOR
+This manual page was written by 
+.I Dan Walsh <dwalsh@xxxxxxxxxx>
+and
+.I Thomas Liu <tliu@xxxxxxxxxxxxxxxxx>
diff --git a/policycoreutils/sandbox/sandbox.conf b/policycoreutils/sandbox/sandbox.conf
new file mode 100644
index 0000000..7c35808
--- /dev/null
+++ b/policycoreutils/sandbox/sandbox.conf
@@ -0,0 +1,7 @@
+# Space separate list of homedirs
+HOMEDIRS="/home"
+# Control group configuration
+NAME=sandbox
+CPUAFFINITY=ALL
+MEMUSAGE=80%
+CPUUSAGE=80%
diff --git a/policycoreutils/sandbox/sandbox.conf.5 b/policycoreutils/sandbox/sandbox.conf.5
new file mode 100644
index 0000000..ee97e10
--- /dev/null
+++ b/policycoreutils/sandbox/sandbox.conf.5
@@ -0,0 +1,40 @@
+.TH sandbox.conf "5" "June 2010" "sandbox.conf" "Linux System Administration"
+.SH NAME
+sandbox.conf \- user config file for the SELinux sandbox 
+.SH DESCRIPTION
+.PP
+When running sandbox with the -C argument, it will be confined using control groups and a system administrator can specify how the sandbox is confined.
+
+.PP
+Everything after "#" is ignored, as are empty lines.  All arguments should be separated by and equals sign ("=").
+
+.PP
+These keywords are allowed.
+
+.RS
+.TP
+.B NAME
+The name of the sandbox control group.  Default is "sandbox".
+
+.TP
+.B CPUAFFINITY
+Which cpus to assign sandbox to.  The default is ALL, but users can specify a comma-separated list with dashes ("-") to represent ranges.  Ex: 0-2,5
+
+.TP
+.B MEMUSAGE
+How much memory to allow sandbox to use.  The default is 80%.  Users can specify either a percentage or a value in the form of a number followed by one of the suffixes K, M, G to denote kilobytes, megabytes or gigabytes respectively.  Ex: 50% or 100M
+
+.TP
+.B CPUUSAGE
+Percentage of cpu sandbox should be allowed to use.  The default is 80%.  Specify a value followed by a percent sign ("%"). Ex: 50%
+
+
+
+.SH "SEE ALSO"
+.TP
+sandbox(8)
+.PP
+
+.SH AUTHOR
+This manual page was written by 
+.I Thomas Liu <tliu@xxxxxxxxxxxxxxxxx>
diff --git a/policycoreutils/sandbox/sandbox.init b/policycoreutils/sandbox/sandbox.init
index ff8b3ef..66aadfd 100644
--- a/policycoreutils/sandbox/sandbox.init
+++ b/policycoreutils/sandbox/sandbox.init
@@ -10,17 +10,12 @@
 #
 # chkconfig: 345 1 99
 #
-# Description: sandbox and other apps that want to use pam_namespace 
-#              on /var/tmp, /tmp and home directories, requires this script
-#              to be run at boot time.
-#              This script sets up the / mount point and all of its 
-#              subdirectories as shared. The script sets up
-#              /tmp, /var/tmp, /home and any homedirs listed in 
-#              /etc/sysconfig/sandbox and all of their subdirectories 
-#              as unshared.
-#              All processes that use pam_namespace will see 
-#              modifications to the global mountspace, except for the
-#              unshared directories.
+# description: sandbox, xguest and other apps that want to use pam_namespace \
+#              require this script be run at boot.  This service script does \
+#              not actually run any service but sets up: \
+#              /var/tmp, /tmp and home directories to be used by these tools.\
+#              If you do not use sandbox, xguest or pam_namespace you can turn \
+#              this service off.\
 #
 
 # Source function library.
@@ -41,15 +36,6 @@ start() {
 
 	touch $LOCKFILE
 	mount --make-rshared / || return $? 
-	mount --rbind /tmp /tmp || return $?
-	mount --rbind /var/tmp /var/tmp || return $?
-	mount --make-private /tmp || return $?
-	mount --make-private /var/tmp || return $?
-	for h in $HOMEDIRS; do
-	    mount --rbind $h $h || return $?
-	    mount --make-private $h || return $?
-	done
-
 	return 0
 }
 
diff --git a/policycoreutils/sandbox/sandboxX.sh b/policycoreutils/sandbox/sandboxX.sh
index 8338203..0b0239c 100644
--- a/policycoreutils/sandbox/sandboxX.sh
+++ b/policycoreutils/sandbox/sandboxX.sh
@@ -1,15 +1,21 @@
 #!/bin/bash 
+trap "" TERM
 context=`id -Z | secon -t -l -P`
 export TITLE="Sandbox $context -- `grep ^#TITLE: ~/.sandboxrc | /usr/bin/cut -b8-80`"
-export SCREENSIZE="1000x700"
-#export SCREENSIZE=`xdpyinfo | awk  '/dimensions/ {  print $2 }'`
+[ -z $1 ] && export SCREENSIZE="1000x700" || export SCREENSIZE="$1" 
+[ -z $2 ] && export DPI="96" || export DPI="$2" 
 trap "exit 0" HUP
 
-(/usr/bin/Xephyr -title "$TITLE" -terminate -screen $SCREENSIZE -displayfd 5 5>&1 2>/dev/null) | while read D; do 
+(/usr/bin/Xephyr -title "$TITLE" -terminate -screen $SCREENSIZE -dpi $DPI -displayfd 5 5>&1 2>/dev/null) | while read D; do 
     export DISPLAY=:$D
-    python -c 'import gtk, os, commands; commands.getstatusoutput("%s/.sandboxrc" % os.environ["HOME"])'
+    cat > ~/seremote << __EOF
+#!/bin/sh
+DISPLAY=$DISPLAY "\$@"
+__EOF
+    chmod +x ~/seremote
+    /usr/share/sandbox/start $HOME/.sandboxrc
     export EXITCODE=$?
-    kill -HUP 0
+    kill -TERM 0
     break
 done
 exit 0
diff --git a/policycoreutils/sandbox/seunshare.8 b/policycoreutils/sandbox/seunshare.8
new file mode 100644
index 0000000..c69ceda
--- /dev/null
+++ b/policycoreutils/sandbox/seunshare.8
@@ -0,0 +1,43 @@
+.TH SEUNSHARE "8" "May 2010" "seunshare" "User Commands"
+.SH NAME
+seunshare \- Run cmd with alternate homedir, tmpdir and/or SELinux context
+.SH SYNOPSIS
+.B seunshare
+[-v] [-c] [-C] [-k] [ -t tmpdir ] [ -h homedir ] [ -Z context ] -- executable [args]
+.br
+.SH DESCRIPTION
+.PP
+Run the 
+.I executable
+within the specified context, using the alternate home directory and /tmp directory.  The seunshare command unshares from the default namespace, then mounts the specified homedir and tmpdir over the default homedir and /tmp. Finally it tells the kernel to execute the application under the specified SELinux context.
+
+.TP
+\fB\-h homedir\fR
+Alternate homedir to be used by the application.  Homedir must be owned by the user.
+.TP
+\fB\-t\ tmpdir
+Use alternate tempory directory to mount on /tmp.  tmpdir must be owned by the user.
+.TP
+\fB\-c --cgroups\fR
+Use cgroups to control this copy of seunshare.  Specify parameters in /etc/sysconfig/sandbox.  Max memory usage and cpu usage are to be specified in percent.  You can specify which CPUs to use by numbering them 0,1,2... etc.
+.TP
+\fB\-C --capabilities\fR
+Allow apps executed within the namespace to use capabilities.  Default is no capabilities.
+.TP
+\fB\-k --kill\fR
+Kill all processes with matching MCS level.
+.TP
+\fB\-Z\ context
+Use alternate SELinux context while runing the executable.
+.TP
+\fB\-v\fR
+Verbose output
+.SH "SEE ALSO"
+.TP
+runcon(1), sandbox(8), selinux(8)	
+.PP
+.SH AUTHOR
+This manual page was written by 
+.I Dan Walsh <dwalsh@xxxxxxxxxx>
+and
+.I Thomas Liu <tliu@xxxxxxxxxxxxxxxxx>
diff --git a/policycoreutils/sandbox/seunshare.c b/policycoreutils/sandbox/seunshare.c
index ec692e7..d1a1e47 100644
--- a/policycoreutils/sandbox/seunshare.c
+++ b/policycoreutils/sandbox/seunshare.c
@@ -1,27 +1,35 @@
+/*
+ * Authors: Dan Walsh <dwalsh@xxxxxxxxxx>
+ * Authors: Thomas Liu <tliu@xxxxxxxxxxxxxxxxx>
+ */
+
+#define _GNU_SOURCE
 #include <signal.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <sys/wait.h>
 #include <syslog.h>
 #include <sys/mount.h>
+#include <glob.h>
 #include <pwd.h>
-#define _GNU_SOURCE
 #include <sched.h>
+#include <libcgroup.h>
 #include <string.h>
 #include <stdio.h>
+#include <regex.h>
 #include <unistd.h>
+#include <sys/fsuid.h>
 #include <stdlib.h>
 #include <cap-ng.h>
 #include <getopt.h>		/* for getopt_long() form of getopt() */
 #include <limits.h>
 #include <stdlib.h>
 #include <errno.h>
+#include <fcntl.h>
 
 #include <selinux/selinux.h>
 #include <selinux/context.h>	/* for context-mangling functions */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
+#include <dirent.h>
 
 #ifdef USE_NLS
 #include <locale.h>		/* for setlocale() */
@@ -39,29 +47,56 @@
 #define MS_PRIVATE 1<<18
 #endif
 
+#ifndef PACKAGE
+#define PACKAGE "policycoreutils"	/* the name of this package lang translation */
+#endif
+
+#define BUF_SIZE 1024
+#define DEFAULT_PATH "/usr/bin:/bin"
+
+#define USAGE_STRING _("USAGE: seunshare [ -v ] [ -c ] -C -t tmpdir -h homedir [-Z context] -- executable [args]")
+
+static int verbose = 0;
+static int child = 0;
+
+static capng_select_t cap_set = CAPNG_SELECT_BOTH;
+
 /**
- * This function will drop all capabilities 
- * Returns zero on success, non-zero otherwise
+ * This function will drop all capabilities.
  */
-static int drop_capabilities(uid_t uid)
+static int drop_caps()
 {
-	capng_clear(CAPNG_SELECT_BOTH);
-
-	if (capng_lock() < 0) 
+	if (capng_have_capabilities(cap_set) == CAPNG_NONE)
+		return 0;
+	capng_clear(cap_set);
+	if (capng_lock() == -1 || capng_apply(cap_set) == -1) {
+		fprintf(stderr, _("Failed to drop all capabilities\n"));
 		return -1;
-	/* Change uid */
-	if (setresuid(uid, uid, uid)) {
-		fprintf(stderr, _("Error changing uid, aborting.\n"));
+	}
+	return 0;
+}
+
+/**
+ * This function will drop all privileges.
+ */
+static int drop_privs(uid_t uid)
+{
+	if (drop_caps() == -1 || setresuid(uid, uid, uid) == -1) {
+		fprintf(stderr, _("Failed to drop privileges\n"));
 		return -1;
 	}
-	return capng_apply(CAPNG_SELECT_BOTH);
+	return 0;
 }
 
-#define DEFAULT_PATH "/usr/bin:/bin"
-static	int verbose = 0;
+/**
+ * If the user sends a siginto to seunshare, kill the child's session
+ */
+void handler(int sig) {
+	if (child > 0) kill(-child,sig);
+}
 
 /**
- * Take care of any signal setup
+ * Take care of any signal setup.
  */
 static int set_signal_handles(void)
 {
@@ -75,32 +110,117 @@ static int set_signal_handles(void)
 
 	(void)sigprocmask(SIG_SETMASK, &empty, NULL);
 
-	/* Terminate on SIGHUP. */
+	/* Terminate on SIGHUP */
 	if (signal(SIGHUP, SIG_DFL) == SIG_ERR) {
 		perror("Unable to set SIGHUP handler");
 		return -1;
 	}
 
+	if (signal(SIGINT, handler) == SIG_ERR) {
+		perror("Unable to set SIGHUP handler");
+		return -1;
+	}
+
 	return 0;
 }
 
+#define status_to_retval(status,retval) do { \
+	if ((status) == -1) \
+		retval = -1; \
+	else if (WIFEXITED((status))) \
+		retval = WEXITSTATUS((status)); \
+	else if (WIFSIGNALED((status))) \
+		retval = 128 + WTERMSIG((status)); \
+	else \
+		retval = -1; \
+	} while(0)
+
 /**
- * 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.
+ * Spawn external command using system() with dropped privileges.
+ * TODO: avoid system() and use exec*() instead
  */
-static int verify_mount(const char *mntdir, struct passwd *pwd) {
+static int spawn_command(const char *cmd, uid_t uid){
+	int child;
+	int status = -1;
+
+	if (verbose > 1)
+		printf("spawn_command: %s\n", cmd);
+
+	child = fork();
+	if (child == -1) {
+		perror(_("Unable to fork"));
+		return status;
+	}
+
+	if (child == 0) {
+		if (drop_privs(uid) != 0) exit(-1);
+
+		status = system(cmd);
+		status_to_retval(status, status);
+		exit(status);
+	}
+
+	waitpid(child, &status, 0);
+	status_to_retval(status, status);
+	return status;
+}
+
+/**
+ * Check file/directory ownership, struct stat * must be passed to the
+ * functions.
+ */
+static int check_owner_uid(uid_t uid, const char *file, struct stat *st) {
+	if (S_ISLNK(st->st_mode)) {
+		fprintf(stderr, _("Error: %s must not be a symbolic link\n"), file);
+		return -1;
+	}
+	if (st->st_uid != uid) {
+		fprintf(stderr, _("Error: %s not owned by UID %d\n"), file, uid);
+		return -1;
+	}
+	return 0;
+}
+
+static int check_owner_gid(gid_t gid, const char *file, struct stat *st) {
+	if (S_ISLNK(st->st_mode)) {
+		fprintf(stderr, _("Error: %s must not be a symbolic link\n"), file);
+		return -1;
+	}
+	if (st->st_gid != gid) {
+		fprintf(stderr, _("Error: %s not owned by GID %d\n"), file, gid);
+		return -1;
+	}
+	return 0;
+}
+
+#define equal_stats(one,two) \
+	((one)->st_dev == (two)->st_dev && (one)->st_ino == (two)->st_ino && \
+	 (one)->st_uid == (two)->st_uid && (one)->st_gid == (two)->st_gid && \
+	 (one)->st_mode == (two)->st_mode)
+
+/**
+ * Sanity check specified directory.  Store stat info for future comparison, or
+ * compare with previously saved info to detect replaced directories.
+ * Note: This function does not perform owner checks.
+ */
+static int verify_directory(const char *dir, struct stat *st_in, struct stat *st_out) {
 	struct stat sb;
-	if (stat(mntdir, &sb) == -1) {
-		fprintf(stderr, _("Invalid mount point %s: %s\n"), mntdir, strerror(errno));
+
+	if (st_out == NULL) st_out = &sb;
+	
+	if (lstat(dir, st_out) == -1) {
+		fprintf(stderr, _("Failed to stat %s: %s\n"), dir, strerror(errno));
 		return -1;
 	}
-	if (sb.st_uid != pwd->pw_uid) {
-		errno = EPERM;
-		syslog(LOG_AUTHPRIV | LOG_ALERT, "%s attempted to mount an invalid directory, %s", pwd->pw_name, mntdir);
-		perror(_("Invalid mount point, reporting to administrator"));
+	if (! S_ISDIR(st_out->st_mode)) {
+		fprintf(stderr, _("Error: %s is not a directory: %s\n"), dir, strerror(errno));
 		return -1;
 	}
+	if (st_in && !equal_stats(st_in, st_out)) {
+		fprintf(stderr, _("Error: %s was replaced by a different directory\n"), dir);
+		return -1;
+	}
+
 	return 0;
 }
 
@@ -123,7 +243,7 @@ static int verify_shell(const char *shell_name)
 
 		/* check the shell skipping newline char */
 		if (!strcmp(shell_name, buf)) {
-			rc = 1;
+			rc = 0;
 			break;
 		}
 	}
@@ -131,45 +251,594 @@ static int verify_shell(const char *shell_name)
 	return rc;
 }
 
-static int seunshare_mount(const char *src, const char *dst, struct passwd *pwd) {
+/**
+ * Mount directory and check that we mounted the right directory.
+ */
+static int seunshare_mount(const char *src, const char *dst, struct stat *src_st)
+{
+	int flags = MS_REC;
+	int is_tmp = 0;
+
 	if (verbose)
-		printf("Mount %s on %s\n", src, dst);
-	if (mount(dst, dst,  NULL, MS_BIND | MS_REC, NULL) < 0) {
+		printf(_("Mounting %s on %s\n"), src, dst);
+
+	if (strcmp("/tmp", dst) == 0) {
+		flags = flags | MS_NODEV | MS_NOSUID | MS_NOEXEC;
+		is_tmp = 1;
+	}
+
+	/* mount directory */
+	if (mount(dst, dst,  NULL, MS_BIND | flags, NULL) < 0) {
 		fprintf(stderr, _("Failed to mount %s on %s: %s\n"), dst, dst, strerror(errno));
 		return -1;
 	}
-
-	if (mount(dst, dst, NULL, MS_PRIVATE | MS_REC, NULL) < 0) {
+	if (mount(dst, dst, NULL, MS_PRIVATE | flags, NULL) < 0) {
 		fprintf(stderr, _("Failed to make %s private: %s\n"), dst, strerror(errno));
 		return -1;
 	}
-
-	if (mount(src, dst, NULL, MS_BIND | MS_REC, NULL) < 0) {
+	if (mount(src, dst, NULL, MS_BIND | flags, NULL) < 0) {
 		fprintf(stderr, _("Failed to mount %s on %s: %s\n"), src, dst, strerror(errno));
 		return -1;
 	}
 
-	if (verify_mount(dst, pwd) < 0) 
+	/* verify whether we mounted what we expected to mount */
+	if (verify_directory(dst, src_st, NULL) < 0) return -1;
+
+	/* bind mount /tmp on /var/tmp too */
+	if (is_tmp) {
+		if (verbose)
+			printf(_("Mounting /tmp on /var/tmp\n"));
+
+		if (mount("/var/tmp", "/var/tmp",  NULL, MS_BIND | flags, NULL) < 0) {
+			fprintf(stderr, _("Failed to mount /var/tmp on /var/tmp: %s\n"), strerror(errno));
+			return -1;
+		}
+		if (mount("/var/tmp", "/var/tmp", NULL, MS_PRIVATE | flags, NULL) < 0) {
+			fprintf(stderr, _("Failed to make /var/tmp private: %s\n"), strerror(errno));
+			return -1;
+		}
+		if (mount("/tmp", "/var/tmp",  NULL, MS_BIND | flags, NULL) < 0) {
+			fprintf(stderr, _("Failed to mount /tmp on /var/tmp: %s\n"), strerror(errno));
+			return -1;
+		}
+	}
+
+	return 0;
+
+}
+
+/**
+ * Error logging used by cgroups code.
+ */
+static int sandbox_error(const char *string)
+{
+	fprintf(stderr, string);
+	syslog(LOG_AUTHPRIV | LOG_ALERT, string);
+	exit(-1);
+}
+
+/**
+ * Regular expression match.
+ */
+static int match(const char *string, char *pattern)
+{
+	int status;
+	regex_t re; 
+	if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB) != 0) {
+		return 0;
+	}
+	status = regexec(&re, string, (size_t)0, NULL, 0);
+	regfree(&re);	
+	if (status != 0) {
+		return 0;
+	}
+	return 1;
+}
+
+/**
+ * Apply cgroups settings from the /etc/sysconfig/sandbox config file.
+ */
+static int setup_cgroups()
+{
+	char *cpus = NULL;	/* which CPUs to use */
+	char *cgroupname = NULL;/* name for the cgroup */
+	char *mem = NULL;	/* string for memory amount to pass to cgroup */
+	int64_t memusage = 0;	/* amount of memory to use max (percent) */
+	int cpupercentage = 0;  /* what percentage of cpu to allow usage */
+	FILE* fp;
+	char buf[BUF_SIZE];
+	char *tok = NULL;
+	int rc = -1;
+	char *str = NULL;
+	const char* fname = "/etc/sysconfig/sandbox";	
+	
+	if ((fp = fopen(fname, "rt")) == NULL) {
+		fprintf(stderr, "Error opening sandbox config file.");
+		return rc;
+	}
+	while(fgets(buf, BUF_SIZE, fp) != NULL) {
+		/* Skip comments */
+		if (buf[0] == '#') continue;
+		
+		/* Copy the string, ignoring whitespace */
+		int len = strlen(buf);
+		free(str);
+		str = malloc((len + 1) * sizeof(char));
+		
+		int ind = 0;	
+		int i;
+		for (i = 0; i < len; i++) {
+			char cur = buf[i];
+			if (cur != ' ' && cur != '\t') {
+				str[ind] = cur;
+				ind++;
+			}
+		}
+		str[ind] = '\0';
+		
+		tok = strtok(str, "=\n");
+		if (tok != NULL) {
+			if (!strcmp(tok, "CPUAFFINITY")) {
+				tok = strtok(NULL, "=\n");
+				cpus = strdup(tok);
+				if (!strcmp(cpus, "ALL")) {
+					free(cpus);
+					cpus = NULL;
+				}
+			} else if (!strcmp(tok, "MEMUSAGE")) {
+				tok = strtok(NULL, "=\n");
+				if (match(tok, "^[0-9]+[kKmMgG%]")) {
+					char *ind = strchr(tok, '%');
+					if (ind != NULL) {
+						*ind = '\0';;
+						memusage = atoi(tok);
+					} else {
+						mem = strdup(tok);
+					}
+				} else {
+					fprintf(stderr, "Error parsing config file.");
+					goto err;
+				}
+				
+			} else if (!strcmp(tok, "CPUUSAGE")) {
+				tok = strtok(NULL, "=\n");
+				if (match(tok, "^[0-9]+\%")) {
+					char* ind = strchr(tok, '%');
+					*ind = '\0';
+					cpupercentage = atoi(tok);
+				} else {
+					fprintf(stderr, "Error parsing config file.");
+					goto err;
+				}
+			} else if (!strcmp(tok, "NAME")) {
+				tok = strtok(NULL, "=\n");
+				cgroupname = strdup(tok);
+			} else {
+				continue;
+			}
+		}
+		
+	}
+	if (mem == NULL) {
+		long phypz = sysconf(_SC_PHYS_PAGES);
+		long psize = sysconf(_SC_PAGE_SIZE);
+		memusage = phypz * psize * (float) memusage / 100.0;
+	}
+	
+	cgroup_init();
+
+	int64_t current_runtime = 0;
+	int64_t current_period = 0 ;
+	int64_t current_mem = 0;
+	char *curr_cpu_path = NULL;
+	char *curr_mem_path = NULL;
+	int ret  = cgroup_get_current_controller_path(getpid(), "cpu", &curr_cpu_path);
+	if (ret) {
+		sandbox_error("Error while trying to get current controller path.\n");
+	} else {
+		struct cgroup *curr = cgroup_new_cgroup(curr_cpu_path);
+		cgroup_get_cgroup(curr);
+		cgroup_get_value_int64(cgroup_get_controller(curr, "cpu"), "cpu.rt_runtime_us", &current_runtime);
+		cgroup_get_value_int64(cgroup_get_controller(curr, "cpu"), "cpu.rt_period_us", &current_period);
+	}   
+	
+	ret  = cgroup_get_current_controller_path(getpid(), "memory", &curr_mem_path);
+	if (ret) {
+		sandbox_error("Error while trying to get current controller path.\n");
+	} else {
+		struct cgroup *curr = cgroup_new_cgroup(curr_mem_path);
+		cgroup_get_cgroup(curr);
+		cgroup_get_value_int64(cgroup_get_controller(curr, "memory"), "memory.limit_in_bytes", &current_mem);
+	}   
+	
+	if (((float) cpupercentage)  / 100.0> (float)current_runtime / (float) current_period) {
+		sandbox_error("CPU usage restricted!\n");
+		goto err;
+	}   
+	
+	if (mem == NULL) {	
+		if (memusage > current_mem) {
+			sandbox_error("Attempting to use more memory than allowed!");
+			goto err;
+		}
+	}
+	
+	long nprocs = sysconf(_SC_NPROCESSORS_ONLN);
+	
+	struct sched_param sp; 
+	sp.sched_priority = sched_get_priority_min(SCHED_FIFO);
+	sched_setscheduler(getpid(), SCHED_FIFO, &sp);
+	struct cgroup *sandbox_group = cgroup_new_cgroup(cgroupname);
+	cgroup_add_controller(sandbox_group, "memory");
+	cgroup_add_controller(sandbox_group, "cpu");
+	
+	if (mem == NULL) {
+		if (memusage > 0) {
+			cgroup_set_value_uint64(cgroup_get_controller(sandbox_group, "memory"), "memory.limit_in_bytes", memusage);
+		}	
+	} else {
+		cgroup_set_value_string(cgroup_get_controller(sandbox_group, "memory"), "memory.limit_in_bytes", mem);
+	}
+	if (cpupercentage > 0) {
+		cgroup_set_value_uint64(cgroup_get_controller(sandbox_group, "cpu"), "cpu.rt_runtime_us",
+					(float) cpupercentage / 100.0 * 60000);
+		cgroup_set_value_uint64(cgroup_get_controller(sandbox_group, "cpu"), "cpu.rt_period_us",60000 * nprocs);
+	}
+	if (cpus != NULL) {
+		cgroup_set_value_string(cgroup_get_controller(sandbox_group, "cpu"), "cgroup.procs",cpus);
+	}
+	
+	uint64_t allocated_mem;
+	if (cgroup_get_value_uint64(cgroup_get_controller(sandbox_group, "memory"), "memory.limit_in_bytes", &allocated_mem) > current_mem) {
+		sandbox_error("Attempting to use more memory than allowed!\n");
+		goto err;
+	}
+	
+	rc = cgroup_create_cgroup(sandbox_group, 1);
+	if (rc != 0) {
+		sandbox_error("Failed to create group.  Ensure that cgconfig service is running. \n");
+		goto err;
+	}
+
+	cgroup_attach_task(sandbox_group);
+
+	rc = 0;
+err:
+	fclose(fp);
+	free(str);
+	free(mem);
+	free(cgroupname);
+	free(cpus);
+	return rc;
+}
+
+/* 
+   If path is empy or ends with  "/." or "/.. return -1 else return 0;
+ */
+static int bad_path(const char *path) {
+	const char *ptr;
+	ptr = path;
+	while (*ptr) ptr++;
+	if (ptr == path) return -1; // ptr null
+	ptr--;
+	if (ptr != path && *ptr  == '.') {
+		ptr--;
+		if (*ptr  == '/') return -1; // path ends in /.
+		if (*ptr  == '.') {
+			if (ptr != path) {
+				ptr--;
+				if (*ptr  == '/') return -1; // path ends in /..
+			}
+		}
+	}
+	return 0;
+}
+
+static int rsynccmd(const char * src, const char *dst, char **cmdbuf) 
+{
+	char *buf = NULL;
+	char *newbuf = NULL;
+	glob_t fglob;
+	fglob.gl_offs = 0;
+	int flags = GLOB_PERIOD;
+	unsigned int i = 0;
+	int rc = -1;
+
+	/* match glob for all files in src dir */
+	if (asprintf(&buf, "%s/*", src) == -1) {
+		fprintf(stderr, "Out of memory\n");
 		return -1;
+	}
+
+	if (glob(buf, flags, NULL, &fglob) != 0) {
+		free(buf); buf = NULL;
+		return -1;
+	}
+
+	free(buf); buf = NULL;
+
+	for ( i=0; i < fglob.gl_pathc; i++) {
+		const char *path = fglob.gl_pathv[i];
+
+		if (bad_path(path)) continue;
+
+		if (!buf) {
+			if (asprintf(&newbuf, "\'%s\'", path) == -1) {
+				fprintf(stderr, "Out of memory\n");
+				goto err;
+			}
+		} else {
+			if (asprintf(&newbuf, "%s  \'%s\'", buf, path) == -1) {
+				fprintf(stderr, "Out of memory\n");
+				goto err;
+			}
+		}
+
+		free(buf); buf = newbuf;
+		newbuf = NULL;
+	}
+
+	if (buf) { 
+		if (asprintf(&newbuf, "/usr/bin/rsync -trlHDq %s '%s'", buf, dst) == -1) {
+			fprintf(stderr, "Out of memory\n");
+			goto err;
+		}
+		*cmdbuf=newbuf;
+	}
+	else {
+		*cmdbuf=NULL;
+	}
+	rc = 0;
+
+err:
+	free(buf); buf = NULL;
+	globfree(&fglob);
+	return rc;
 }
 
-#define USAGE_STRING _("USAGE: seunshare [ -v ] [ -t tmpdir ] [ -h homedir ] -- CONTEXT executable [args] ")
+/**
+ * Clean up runtime temporary directory.  Returns 0 if no problem was detected,
+ * >0 if some error was detected, but errors here are treated as non-fatal and
+ * left to tmpwatch to finish incomplete cleanup.
+ */
+static int cleanup_tmpdir(const char *tmpdir, const char *src,
+	struct passwd *pwd, int copy_content)
+{
+	char *cmdbuf = NULL;
+	int rc = 0;
+
+	/* rsync files back */
+	if (copy_content) {
+		if (asprintf(&cmdbuf, "/usr/bin/rsync --exclude=.X11-unix -utrlHDq --delete '%s/' '%s/'", tmpdir, src) == -1) {
+			fprintf(stderr, _("Out of memory\n"));
+			cmdbuf = NULL;
+			rc++;
+		}
+		if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) {
+			fprintf(stderr, _("Failed to copy files from the runtime temporary directory\n"));
+			rc++;
+		}
+		free(cmdbuf); cmdbuf = NULL;
+	}
+
+	/* remove files from the runtime temporary directory */
+	if (asprintf(&cmdbuf, "/bin/rm -r '%s/' 2>/dev/null", tmpdir) == -1) {
+		fprintf(stderr, _("Out of memory\n"));
+		cmdbuf = NULL;
+		rc++;
+	}
+	/* this may fail if there's root-owned file left in the runtime tmpdir */
+	if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) rc++;
+	free(cmdbuf); cmdbuf = NULL;
+
+	/* remove runtime temporary directory */
+	setfsuid(0);
+	if (rmdir(tmpdir) == -1)
+		fprintf(stderr, _("Failed to remove directory %s: %s\n"), tmpdir, strerror(errno));
+	setfsuid(pwd->pw_uid);
+
+	return 0;
+}
+
+/**
+ * seunshare will create a tmpdir in /tmp, with root ownership.  The parent
+ * process waits for it child to exit to attempt to remove the directory.  If
+ * it fails to remove the directory, we will need to rely on tmpreaper/tmpwatch
+ * to clean it up.
+ */
+static char *create_tmpdir(const char *src, struct stat *src_st,
+	struct stat *out_st, struct passwd *pwd, security_context_t execcon)
+{
+	char *tmpdir = NULL;
+	char *cmdbuf = NULL;
+	int fd_t = -1, fd_s = -1;
+	struct stat tmp_st;
+	security_context_t con = NULL;
+
+	/* get selinux context */
+	if (execcon) {
+		setfsuid(pwd->pw_uid);
+		if ((fd_s = open(src, O_RDONLY)) < 0) {
+			fprintf(stderr, _("Failed to open directory %s: %s\n"), src, strerror(errno));
+			goto err;
+		}
+		if (fstat(fd_s, &tmp_st) == -1) {
+			fprintf(stderr, _("Failed to stat directory %s: %s\n"), src, strerror(errno));
+			goto err;
+		}
+		if (!equal_stats(src_st, &tmp_st)) {
+			fprintf(stderr, _("Error: %s was replaced by a different directory\n"), src);
+			goto err;
+		}
+		if (fgetfilecon(fd_s, &con) == -1) {
+			fprintf(stderr, _("Failed to get context of the directory %s: %s\n"), src, strerror(errno));
+			goto err;
+		}
+
+		/* ok to not reach this if there is an error */
+		setfsuid(0);
+	}
+
+	if (asprintf(&tmpdir, "/tmp/.sandbox-%s-XXXXXX", pwd->pw_name) == -1) {
+		fprintf(stderr, _("Out of memory\n"));
+		tmpdir = NULL;
+		goto err;
+	}
+	if (mkdtemp(tmpdir) == NULL) {
+		fprintf(stderr, _("Failed to create temporary directory: %s\n"), strerror(errno));
+		goto err;
+	}
+
+	/* temporary directory must be owned by root:user */
+	if (verify_directory(tmpdir, NULL, out_st) < 0) {
+		goto err;
+	}
+	if (check_owner_uid(0, tmpdir, out_st) < 0) goto err;
+	if (check_owner_gid(getgid(), tmpdir, out_st) < 0) goto err;
+
+	/* change permissions of the temporary directory */
+	if ((fd_t = open(tmpdir, O_RDONLY)) < 0) {
+		fprintf(stderr, _("Failed to open directory %s: %s\n"), tmpdir, strerror(errno));
+		goto err;
+	}
+	if (fstat(fd_t, &tmp_st) == -1) {
+		fprintf(stderr, _("Failed to stat directory %s: %s\n"), tmpdir, strerror(errno));
+		goto err;
+	}
+	if (!equal_stats(out_st, &tmp_st)) {
+		fprintf(stderr, _("Error: %s was replaced by a different directory\n"), tmpdir);
+		goto err;
+	}
+	if (fchmod(fd_t, 01770) == -1) {
+		fprintf(stderr, _("Unable to change mode on %s: %s\n"), tmpdir, strerror(errno));
+		goto err;
+	}
+	/* re-stat again to pick change mode */
+	if (fstat(fd_t, out_st) == -1) {
+		fprintf(stderr, _("Failed to stat directory %s: %s\n"), tmpdir, strerror(errno));
+		goto err;
+	}
+
+	/* copy selinux context */
+	if (execcon) {
+		if (fsetfilecon(fd_t, con) == -1) {	
+			fprintf(stderr, _("Failed to set context of the directory %s: %s\n"), tmpdir, strerror(errno));
+			goto err;
+		}
+	}
+
+	setfsuid(pwd->pw_uid);
+
+	if (rsynccmd(src, tmpdir, &cmdbuf) < 0) {
+		goto err;
+	}
+
+	/* ok to not reach this if there is an error */
+	setfsuid(0);
+
+	if (cmdbuf && spawn_command(cmdbuf, pwd->pw_uid) != 0) {
+		fprintf(stderr, _("Failed to populate runtime temporary directory\n"));
+		cleanup_tmpdir(tmpdir, src, pwd, 0);
+		goto err;
+	}
+
+	goto good;
+err:
+	free(tmpdir); tmpdir = NULL;
+good:
+	free(cmdbuf); cmdbuf = NULL;
+	freecon(con); con = NULL;
+	if (fd_t >= 0) close(fd_t);
+	if (fd_s >= 0) close(fd_s);
+	return tmpdir;
+}
+
+#define PROC_BASE "/proc"
+
+static int
+killall (security_context_t execcon)
+{
+	DIR *dir;
+	security_context_t scon;
+	struct dirent *de;
+	pid_t *pid_table, pid, self;
+	int i;
+	int pids, max_pids;
+	int running = 0;
+	self = getpid();
+	if (!(dir = opendir(PROC_BASE))) {
+		return -1;
+	}
+	max_pids = 256;
+	pid_table = malloc(max_pids * sizeof (pid_t));
+	if (!pid_table) {
+		return -1;
+	}
+	pids = 0;
+	context_t con;
+	con = context_new(execcon);
+	const char *mcs = context_range_get(con);
+	printf("mcs=%s\n", mcs);
+	while ((de = readdir (dir)) != NULL) {
+		if (!(pid = (pid_t)atoi(de->d_name)) || pid == self)
+			continue;
+
+		if (pids == max_pids) {
+			if (!(pid_table = realloc(pid_table, 2*pids*sizeof(pid_t)))) {
+				return -1;
+			}
+			max_pids *= 2;
+		}
+		pid_table[pids++] = pid;
+	}
+
+	(void)closedir(dir);
+
+	for (i = 0; i < pids; i++) {
+		pid_t id = pid_table[i];
+
+		if (getpidcon(id, &scon) == 0) {
+			
+			context_t pidcon = context_new(scon);
+			/* Attempt to kill remaining processes */
+			if (strcmp(context_range_get(pidcon), mcs) == 0)
+				kill(id, SIGKILL);
+
+			context_free(pidcon);
+			freecon(scon);
+		}
+		running++;
+	}
+
+	context_free(con);
+	free(pid_table);
+	return running;
+}
 
 int main(int argc, char **argv) {
-	int rc;
 	int status = -1;
+	security_context_t execcon = NULL;
 
-	security_context_t scontext;
-
-	int flag_index;		/* flag index in argv[] */
 	int clflag;		/* holds codes for command line flags */
-	char *tmpdir_s = NULL;	/* tmpdir spec'd by user in argv[] */
+	int usecgroups = 0;
+	int kill_all = 0;
+
 	char *homedir_s = NULL;	/* homedir spec'd by user in argv[] */
+	char *tmpdir_s = NULL;	/* tmpdir spec'd by user in argv[] */
+	char *tmpdir_r = NULL;	/* tmpdir created by seunshare */
+
+	struct stat st_homedir;
+	struct stat st_tmpdir_s;
+	struct stat st_tmpdir_r;
 
 	const struct option long_options[] = {
 		{"homedir", 1, 0, 'h'},
 		{"tmpdir", 1, 0, 't'},
+		{"kill", 1, 0, 'k'},
 		{"verbose", 1, 0, 'v'},
+		{"cgroups", 1, 0, 'c'},
+		{"context", 1, 0, 'Z'},
+		{"capabilities", 1, 0, 'C'},
 		{NULL, 0, 0, 0}
 	};
 
@@ -180,6 +849,12 @@ int main(int argc, char **argv) {
 		return -1;
 	}
 
+#ifdef USE_NLS
+	setlocale(LC_ALL, "");
+	bindtextdomain(PACKAGE, LOCALEDIR);
+	textdomain(PACKAGE);
+#endif
+
 	struct passwd *pwd=getpwuid(uid);
 	if (!pwd) {
 		perror(_("getpwduid failed"));
@@ -187,34 +862,36 @@ int main(int argc, char **argv) {
 	}
 
 	if (verify_shell(pwd->pw_shell) < 0) {
-		fprintf(stderr, _("Error!  Shell is not valid.\n"));
+		fprintf(stderr, _("Error: User shell is not valid\n"));
 		return -1;
 	}
 
 	while (1) {
-		clflag = getopt_long(argc, argv, "h:t:", long_options,
-				     &flag_index);
+		clflag = getopt_long(argc, argv, "Ccvh:t:Z:", long_options, NULL);
 		if (clflag == -1)
 			break;
 
 		switch (clflag) {
 		case 't':
-			if (!(tmpdir_s = realpath(optarg, NULL))) {
-				fprintf(stderr, _("Invalid mount point %s: %s\n"), optarg, strerror(errno));
-				return -1;
-			}
-			if (verify_mount(tmpdir_s, pwd) < 0) return -1;
+			tmpdir_s = optarg;
+			break;
+		case 'k':
+			kill_all = 1;
 			break;
 		case 'h':
-			if (!(homedir_s = realpath(optarg, NULL))) {
-				fprintf(stderr, _("Invalid mount point %s: %s\n"), optarg, strerror(errno));
-				return -1;
-			}
-			if (verify_mount(homedir_s, pwd) < 0) return -1;
-			if (verify_mount(pwd->pw_dir, pwd) < 0) return -1;
+			homedir_s = optarg;
 			break;
 		case 'v':
-			verbose = 1;
+			verbose++;
+			break;
+		case 'c':
+			usecgroups = 1;
+			break;
+		case 'C':
+			cap_set = CAPNG_SELECT_CAPS;
+			break;
+		case 'Z':
+			execcon = optarg;
 			break;
 		default:
 			fprintf(stderr, "%s\n", USAGE_STRING);
@@ -223,76 +900,84 @@ int main(int argc, char **argv) {
 	}
 
 	if (! homedir_s && ! tmpdir_s) {
-		fprintf(stderr, _("Error: tmpdir and/or homedir required \n"),
-			"%s\n", USAGE_STRING);
+		fprintf(stderr, _("Error: tmpdir and/or homedir required\n %s\n"), USAGE_STRING);
 		return -1;
 	}
 
-	if (argc - optind < 2) {
-		fprintf(stderr, _("Error: context and executable required \n"),
-			"%s\n", USAGE_STRING);
+	if (argc - optind < 1) {
+		fprintf(stderr, _("Error: executable required\n %s\n"), USAGE_STRING);
 		return -1;
 	}
 
-	scontext = argv[optind++];
-	
-	if (set_signal_handles())
-		return -1;
-
-        if (unshare(CLONE_NEWNS) < 0) {
-		perror(_("Failed to unshare"));
+	if (execcon && is_selinux_enabled() != 1) {
+		fprintf(stderr, _("Error: execution context specified, but SELinux is not enabled\n"));
 		return -1;
 	}
 
-	if (homedir_s && tmpdir_s && (strncmp(pwd->pw_dir, tmpdir_s, strlen(pwd->pw_dir)) == 0)) {
-	    if (seunshare_mount(tmpdir_s, "/tmp", pwd) < 0)
-		    return -1;
-	    if (seunshare_mount(homedir_s, pwd->pw_dir, pwd) < 0)
-		    return -1;
-	} else {			
-		if (homedir_s && seunshare_mount(homedir_s, pwd->pw_dir, pwd) < 0)
-				return -1;
+	if (set_signal_handles()) return -1;
 				
-		if (tmpdir_s && seunshare_mount(tmpdir_s, "/tmp", pwd) < 0)
-				return -1;
-	}
+	if (usecgroups && setup_cgroups() < 0) return  -1;
+
+	/* set fsuid to ruid */
+	/* Changing fsuid is usually required when user-specified directory is
+	 * on an NFS mount.  It's also desired to avoid leaking info about
+	 * existence of the files not accessible to the user. */
+	setfsuid(uid);
+
+	/* verify homedir and tmpdir */
+	if (homedir_s && (
+		verify_directory(homedir_s, NULL, &st_homedir) < 0 ||
+		check_owner_uid(uid, homedir_s, &st_homedir))) return -1;
+	if (tmpdir_s && (
+		verify_directory(tmpdir_s, NULL, &st_tmpdir_s) < 0 ||
+		check_owner_uid(uid, tmpdir_s, &st_tmpdir_s))) return -1;
+	setfsuid(0);
 
-	if (drop_capabilities(uid)) {
-		perror(_("Failed to drop all capabilities"));
+	/* create runtime tmpdir */
+	if (tmpdir_s && (tmpdir_r = create_tmpdir(tmpdir_s, &st_tmpdir_s,
+						  &st_tmpdir_r, pwd, execcon)) == NULL) {
+		fprintf(stderr, _("Failed to create runtime temporary directory\n"));
 		return -1;
 	}
 
-	int child = fork();
+	/* spawn child process */
+	child = fork();
 	if (child == -1) {
 		perror(_("Unable to fork"));
-		return -1;
+		goto err;
 	}
 
-	if (!child) {
-		char *display=NULL;
-		/* Construct a new environment */
-		char *d = getenv("DISPLAY");
-		if (d) {
-			display =  strdup(d);
-			if (!display) {
-				perror(_("Out of memory"));
-				exit(-1);
-			}
-		}
+	if (child == 0) {
+		char *display = NULL;
+		int rc = -1;
 
-		if ((rc = clearenv())) {
-			perror(_("Unable to clear environment"));
-			free(display);
-			exit(-1);
+		if (unshare(CLONE_NEWNS) < 0) {
+			perror(_("Failed to unshare"));
+			goto childerr;
 		}
 		
-		if (setexeccon(scontext)) {
-			fprintf(stderr, _("Could not set exec context to %s.\n"),
-				scontext);
-			free(display);
-			exit(-1);
-		}
+		/* assume fsuid==ruid after this point */
+		setfsuid(uid);
+
+		/* mount homedir and tmpdir, in this order */
+		if (homedir_s && seunshare_mount(homedir_s, pwd->pw_dir,
+			&st_homedir) != 0) goto childerr;
+		if (tmpdir_s &&	seunshare_mount(tmpdir_r, "/tmp",
+			&st_tmpdir_r) != 0) goto childerr;
+
+		if (drop_privs(uid) != 0) goto childerr;
 
+		/* construct a new environment */
+		if ((display = getenv("DISPLAY")) != NULL) {
+			if ((display = strdup(display)) == NULL) {
+				perror(_("Out of memory"));
+				goto childerr;
+			}
+		}
+		if ((rc = clearenv()) != 0) {
+			perror(_("Failed to clear environment"));
+			goto childerr;
+		}
 		if (display) 
 			rc |= setenv("DISPLAY", display, 1);
 		rc |= setenv("HOME", pwd->pw_dir, 1);
@@ -300,22 +985,47 @@ int main(int argc, char **argv) {
 		rc |= setenv("USER", pwd->pw_name, 1);
 		rc |= setenv("LOGNAME", pwd->pw_name, 1);
 		rc |= setenv("PATH", DEFAULT_PATH, 1);
+		if (rc != 0) {
+			fprintf(stderr, _("Failed to construct environment\n"));
+			goto childerr;
+		}
+		
+		/* selinux context */
+		if (execcon && setexeccon(execcon) != 0) {
+			fprintf(stderr, _("Could not set exec context to %s.\n"), execcon);
+			goto childerr;
+		}
 		
 		if (chdir(pwd->pw_dir)) {
 			perror(_("Failed to change dir to homedir"));
-			exit(-1);
+			goto childerr;
 		}
+
 		setsid();
+
 		execv(argv[optind], argv + optind);
+		fprintf(stderr, _("Failed to execute command %s: %s\n"), argv[optind], strerror(errno));
+childerr:
 		free(display);
-		perror("execv");
 		exit(-1);
-	} else {
-		waitpid(child, &status, 0);
 	}
 
-	free(tmpdir_s);
-	free(homedir_s);
+	drop_caps();
+
+	/* parent waits for child exit to do the cleanup */
+	waitpid(child, &status, 0);
+	status_to_retval(status, status);
 
+	/* Make sure all child processes exit */
+	kill(-child,SIGTERM);
+
+	if (execcon && kill_all)
+		killall(execcon);
+
+	if (tmpdir_r) cleanup_tmpdir(tmpdir_r, tmpdir_s, pwd, 1);
+
+err:
+	free(tmpdir_r);
 	return status;
 }
+
diff --git a/policycoreutils/sandbox/start b/policycoreutils/sandbox/start
new file mode 100644
index 0000000..52950d7
--- /dev/null
+++ b/policycoreutils/sandbox/start
@@ -0,0 +1,9 @@
+#! /usr/bin/python -Es
+import gtk, commands, sys
+rc = [-1,'']
+try:
+    rc=commands.getstatusoutput(sys.argv[1])
+except:
+    pass
+if rc[0] == 0:
+    print rc[1]
-- 
1.7.6

Attachment: 0091-policycoreutils-sandbox-FIXME-update-sandbox.patch.sig
Description: PGP signature


[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux