use rrsync to restrict rsync access
[schsh.git] / schsh
1 #!/usr/bin/python3
2 import logging, logging.handlers
3 import os, sys, shlex, pwd
4 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
5 # Configuration
6 shell = None # set to "/bin/bash" or similar to allow shell access
7 rrsync = "/usr/local/bin/schsh-rrsync" # path to the restricted rsync script - if available, it will be used to further restrict rsync access
8
9 def allowSCP(run, runstr):
10         if len(run) != 3: return False
11         if run[0] != "scp": return False
12         if run[1] not in ("-f", "-t"): return False
13         if run[2].startswith('-'): return False
14         run[0] = "/usr/bin/scp"
15         return True
16
17 def allowRSync(run, runstr):
18         if len(run) < 3: return False
19         if run[0] != "rsync": return False
20         if run[1] != "--server": return False
21         if rrsync is None:
22                 # rrsync is not available, let's hope this is enough protection
23                 run[0] = "/usr/bin/rsync"
24                 return True
25         run[:] = [rrsync, "/", runstr] # allow access to the entire chroot
26         return True
27
28 def allowSFTP(run, runstr):
29         return runstr == "/usr/lib/openssh/sftp-server"
30
31 allowCommands = [allowSCP, allowRSync, allowSFTP]
32
33 # END of Configuration
34 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~#
35 # DO NOT TOUCH ANYTHING BELOW THIS LINE
36
37
38 logger = logging.getLogger("schsh")
39 logger.setLevel(logging.INFO)
40 logger.addHandler(logging.handlers.SysLogHandler(address = '/dev/log',
41                                                 facility = logging.handlers.SysLogHandler.LOG_AUTH))
42
43 def get_username():
44     return pwd.getpwuid(os.getuid()).pw_name
45
46 def log(msg, lvl = logging.INFO):
47         logger.log(lvl, "%s[%d]: <%s> %s" % ("schsh", os.getpid(), get_username(), msg))
48
49 def logquit(msg):
50         log(msg, logging.ERROR)
51         sys.exit(1)
52
53 def commandAllowed(run, runstr):
54         for allowed in allowCommands:
55                 if allowed(run, runstr):
56                         return True
57         return False
58
59 # parse arguments
60 run = []
61 if len(sys.argv) == 1:
62         if shell is None:
63                 print("No shell for you!")
64                 logquit("Shell access not allowed")
65         else:
66                 run = [shell]
67 elif len(sys.argv) == 3 and sys.argv[1] == "-c":
68         # check if the command is allowed, and add path
69         run = shlex.split(sys.argv[2])
70         if commandAllowed(run, sys.argv[2]): # this may change run, but that's okay
71                 log("Running '"+str(run)+"'")
72         else:
73                 print("You are not allowed to run this command.")
74                 logquit("Attempt to run invalid command '"+sys.argv[2]+"'")
75 else:
76         logquit("Invalid arguments for schsh: "+str(sys.argv))
77
78 assert len(run) > 0
79 os.execl("/usr/bin/schroot", "/usr/bin/schroot", "-c", "schsh-"+get_username(), "-d", "/data", "--", *run)