Updated 2014-01-20 20:04:04 by RLE

Sometimes, one needs to run tcl scripts in a secure environment. One of the ways to do this is to use the unix chroot() call to block out all access to the local file system. With tclkit, a static executable which carries a complete runtime with it, this is very easy to do. -jcw
 /*
  *  SafeKit - Running Tclkit inside a secure sandbox.
  *
  *  This wrapper launches tclkit after calling "chroot()" to limit
  *  access to the filesystem to the current subtree.  This approach
  *  guarantees that scripts run within this context cannot access a
  *  file outside this environment, not even through external programs.
  *  
  *  Since chroot can only be done by the superuser, this wrapper program
  *  has to be "setuid root".  On startup, it does a chroot("."), and
  *  then reduces permissions to the original ones.  Then, tclkit is
  *  launched, passing all arguments on to it.
  *
  *  To use this sandbox, you need to place all scripts and data that
  *  are needed in a single directory area, along with a copy of tclkit
  *  and this safekit wrapper.  Nothing else is needed, provided that
  *  tclkit and safekit are both compiled as fully static executables.
  *  If extensions are to be dynamically loaded, you will also need to
  *  create a /lib area with all necessary shared runtime libraries.
  *
  *  For example, to run "myscript.tcl" safely, set up the following:
  *    $ ls -la
  *    total 3334
  *    drwxr-xr-x    2 jcw      users         108 Apr  7 22:44 .
  *    drwxr-xr-x    8 jcw      users         536 Apr  7 22:35 ..
  *    -rw-r--r--    1 jcw      users         126 Apr  7 22:37 myscript.tcl
  *    -rwsr-xr-x    1 root     users      368376 Apr  7 21:57 safekit
  *    -rwxr-xr-x    1 jcw      users     3035383 Apr  6 01:42 tclkit
  *    $ 
  *
  *  The contents of "myscript.tcl" in the following example is:
  *    puts "pwd = pwd"
  *    catch { exec ls } err
  *    puts "exec ls -> [split $err \n]"
  *    puts "glob * -> [glob *]"
  *    cd ..
  *    puts "pwd = pwd"
  *
  *  When run with a normal tclkit, this is the output:
  *    $ ./tclkit myscript.tcl 
  *    pwd = /home/jcw/safexample
  *    exec ls -> myscript.tcl safekit tclkit
  *    glob * -> safekit tclkit myscript.tcl
  *    pwd = /home/jcw
  *    $
  *
  *  When run through the safekit wrapper, it will be different:
  *    $ ./safekit myscript.tcl 
  *    pwd = /
  *    exec ls -> {couldn't execute "ls": no such file or directory}
  *    glob * -> safekit tclkit myscript.tcl
  *    pwd = /
  *    $
  *
  *  As you can see, there is no way out, and things like "ls" are not
  *  accessible.
  *
  *  jcw, 07-04-2002
  */

 #include <unistd.h>
 #include <sys/types.h>

 int main(int argc, char** argv)
 {
   if (chroot(".") != 0)
     return 1;
   if (seteuid(getuid()) != 0)
     return 2;
   argv[0] = "/tclkit";
   if (execv(argv[0], argv) != 0)
     return 3;
   return 0;
 }

SEH -- How portable is this? Isn't the command on BSD "jail" or something like that?

Well, it's a dozen lines of C with 3 system calls. Standard Unix, AFAICT. The *BSD "jail" is a considerably more sophisticated mechanism. This just brings chroot() functionality to tclkit. -jcw

CliC jails are a FreeBSD- and DragonFlyBSD-specific mechanism (DragonFly is a fork of FreeBSD). OpenBSD and NetBSD do not have jail(8). AFAIK, FreeBSD and DragonFlyBSD support chroot as well.

I like the concept, though -- something I never thought about really but is quite straightforward.