#!/usr/bin/python -E
import os, sys, getopt, socket, random, fcntl, shutil
import selinux

PROGNAME = "policycoreutils"

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"

random.seed(None)

def error_exit(msg):
    sys.stderr.write("%s: " % sys.argv[0])
    sys.stderr.write("%s\n" % msg)
    sys.stderr.flush()
    sys.exit(1)

def reserve(mcs):
    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    sock.bind("\0%s" % mcs)
    fcntl.fcntl(sock.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)

def gen_context(setype):
    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
        mcs = "s0:c%d,c%d" % (i1, i2)
        reserve(mcs)
        try:
            reserve(mcs)
        except:
            continue
        break
    con = selinux.getcon()[1].split(":")

    execcon = "%s:%s:%s:%s" % (con[0], con[1], setype, mcs)
    
    filecon = "%s:%s:%s:%s" % (con[0], 
                               "object_r", 
                               "%s_file_t" % setype[:-2], 
                               mcs)
    return execcon, filecon

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)
                     os.makedirs(newdir)
                     dest = newdir + "/" + bname

              if os.path.isdir(file):
                     shutil.copytree(file, dest)
              else:
                     shutil.copy2(file, dest)

def copyfiles(newhomedir, newtmpdir, files):
       import pwd
       homedir=pwd.getpwuid(os.getuid()).pw_dir
       for f in files:
              copyfile(f,homedir, newhomedir)
              copyfile(f,"/tmp", newtmpdir)

if __name__ == '__main__':
    if selinux.is_selinux_enabled() != 1:
        error_exit("Requires an SELinux enabled system")
        
    init_files = []

    def usage(message = ""):
        text = _("""
sandbox [-h] [-I includefile ] [[-i file ] ...] [ -t type ] command
""")
        error_exit("%s\n%s" % (message, text))

    setype = DEFAULT_TYPE
    X_ind = False
    try:
           gopts, cmds = getopt.getopt(sys.argv[1:], "i:ht:XI:", 
                                       ["help",
                                        "include=", 
                                        "includefile=", 
                                        "type="
                                        ])
           for o, a in gopts:
                  if o == "-t" or o == "--type":
                         setype = a
                         
                  if o == "-i" or o == "--include":
                         rp = os.path.realpath(a)
                         if rp not in init_files:
                                init_files.append(rp)
                         
                  if o == "-I" or o == "--includefile":
                         fd = open(a, "r")
                         for i in fd.read().split("\n"):
                                if os.path.exists(i):
                                       rp = os.path.realpath(i)
                                       if rp not in init_files:
                                              init_files.append(rp)
                                       
                         fd.close
                         
                  if o == "-X":
                         if DEFAULT_TYPE == setype:
                                setype = DEFAULT_X_TYPE
                         X_ind = True

                  if o == "-h" or o == "--help":
                         usage(_("Usage"));
            
           if len(cmds) == 0:
                  usage(_("Command required"))

           execcon, filecon = gen_context(setype)
           rc = -1

           if cmds[0][0] != "/" and cmds[0][:2] != "./" and cmds[0][:3] != "../":
                  for i in  os.environ["PATH"].split(':'):
                         f = "%s/%s" % (i, cmds[0])
                         if os.access(f, os.X_OK):
                                cmds[0] = f
                                break

           try:
                  newhomedir = None
                  newtmpdir = None
                  if X_ind:
                         if not os.path.exists("/usr/sbin/seunshare"):
                                raise ValueError("""/usr/sbin/seunshare required for sandbox -X, to install you need to execute 
#yum install /usr/sbin/seunshare""")
                         import warnings
                         warnings.simplefilter("ignore")
                         newhomedir = os.tempnam(".", ".sandbox%s")
                         os.mkdir(newhomedir)
                         selinux.setfilecon(newhomedir, filecon) 
                         newtmpdir = os.tempnam("/tmp", ".sandbox")
                         os.mkdir(newtmpdir)
                         selinux.setfilecon(newtmpdir, filecon)
                         warnings.resetwarnings()
                         paths = []
                         for i in cmds:
                                f = os.path.realpath(i)
                                if os.path.exists(f):
                                       paths.append(f)
                                else:
                                       paths.append(i)
                                       
                         copyfiles(newhomedir, newtmpdir, init_files + paths)
                         execfile = newhomedir + "/.sandboxrc"
                         fd = open(execfile, "w+")
                         fd.write("""#! /bin/sh
%s
""" % " ".join(paths))
                         fd.close()
                         os.chmod(execfile, 0700)
                         
                         cmds =  ("/usr/sbin/seunshare -t %s -h %s -- %s /usr/share/sandbox/sandboxX.sh" % (newtmpdir, newhomedir, execcon)).split()
                         rc = os.spawnvp(os.P_WAIT, cmds[0], cmds)
                  else:
                         selinux.setexeccon(execcon)
                         rc = os.spawnvp(os.P_WAIT, cmds[0], cmds)
                         selinux.setexeccon(None)
           finally:
                  if X_ind:
                         if newhomedir:
                                shutil.rmtree(newhomedir)
                         if newtmpdir:
                                shutil.rmtree(newtmpdir)
                  
    except getopt.GetoptError, error:
           usage(_("Options Error %s ") % error.msg)
    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.args[1])
        
    sys.exit(rc)

