Safe Interps

A page to discuss the usefulness of safe interps, and things related (such as what to ensure about your extension to make it 'safe' (see also the safe command).

I'm starting this page with a very informative post from comp.lang.tcl by Jacob Levy. Feel free to add more!

29Sep02 Jacob Levy:

The motivation of this discussion is whether adding a SafeInit C procedure to Itcl is justified based on the grounds of security evaluation. I think this discussion applies equally to any C extension. I also explain below why it is always safe to add any Tcl-only extension to a safe slave interpreter.

To start with, anything available in a safe slave interpreter (when it is first created) is safe for that interpreter. Note that I said "available", hidden functions (ones whose name is given in a call to "interp hide") are not available in the safe interpreter. All dangerous stuff like "exec" and "source" is hidden by "interp create -safe". After that, if the user does not add native code packages or "interp expose" or "interp alias", then the interpreter stays safe. The interpreter stays safe even if you do these things, but carefully. The master interpreter is able to relax security to a degree, under its control, by making more functionality available in the slave safe interpreter through aliases.

In general, a native code package should not violate this rule, so a slave safe interpreter should be as safe with the functionality of this package as it is without it. For more relaxed security, use security policies first introduced with the Tcl plugin. Note that by design, if the package is written in 100% Tcl code it is safe because all Tcl primitives that are unsafe are not available in the safe slave interpreter and thus it matters nought if the code tries to use them; the call will just fail.

So the question to be asked by a security review of Itcl is: is Itcl safe enough so that it can be added to a safe interpreter and not compromise the base security policy provided by "interp create -safe". If not, you should write some other mechanism that only allows adding Itcl to the safe slave under control of an explicit security policy. Of course as Itcl provides functionality implemented by native code, that mechanism is going to look awfully much like the SafeInit, but it will be some other API accessible only under control of the master interpreter.

For an example of a safe native code package, my very own e4Graph [L1 ] package has a SafeInit function that makes the package available in a safe interpreter and only does not make the "tgraph::open" function available. The reasoning is that "tgraph::open" would allow the safe interpreter to open new storages, use arbitrary on disk files, etc. Clearly a security violation. Instead, storages are explicitly shared with the slave safe interpreter by its master through "tgraph::transfer". The rest of the functionality *is* safe because the safe interpreter can not obtain access to storages that it wasn't given access to, only to storages that have explicitly been shared with it by its master. Yes, the safe slave interpreter could do arbitrary damage to the storages that the master chose to share, but that's a calculated risk that the master interpreter decided to take. I consider this risk not to be serious, on the same order as the risk taken by the master when "interp transfer"ing Tcl I/O channels into a slave safe interpreter. The end conclusion is that the safe slave interpreter is as safe with as it is without the provided functionality.

On the other hand, if you want to give a safe slave interpreter access to sockets, the risks are more serious. In that case we need to implement a way to protect resources outside of Tcl on the local machine from serious damage, ideally making them inaccessible to the untrusted code. So for that you could use the "outside" security policy that Laurent Demailly and I wrote for the Tcl plugin. This policy protects the local machine against programs running on other machines, but allows the interpreter to communicate with those programs by opening sockets on ports on those machines. The slave safe interpreter cannot open sockets on ports serviced by the local machine, so it cannot connect e.g. to your smtpd to send hate mail from your machine. But it can connect to nearly anything off of your machine. This is a calculated risk that the master interpreter chose to take: while it protects your local machine from damage, it does not protect your reputation from damage.

As another example, Expect should not have a SafeInit. That is because it provides commands like "spawn" which can be used to execute any command provided by the OS. If the SafeInit hid that command from the safe slave interpreter, perhaps it'd be OK, but "spawn" is so central to the functioning of Expect scripts that removing it makes the package nearly unusable.

Finally, does extending a safe interpreter with an explicit security policy make it unsafe to add any Tcl-only extension to that interpreter? I think not, because security policies are (or should be!) written so that the commands they make available are safe (or low risk) under all circumstances. Since Tcl procedures only combine existing commands, any security policy should be safe with any Tcl-only extension. If this is too complicated, don't worry: just remember that it's OK to use any pure Tcl code in a safe interpreter, whether or not that interpreter has been extended with a security policy.

RHS To the question of does extending a safe interpreter with an explicit security policy make it unsafe to add any Tcl-only extension to that interpreter, I think the answer has to be yes. If the interp is truly safe to start with, then any combination of commands available in that interp cannot make it unsafe. Since a pureTcl extension is only a combination of Tcl commands, then it cannot make the interp unsafe.

Johannes13 Some extensions don't know about the implications of having SafeInit and just call the Init there. Some examples are: Thread package (fixed in 2.6.7, can give safe interp access to a trusted interp [L2 ]), mysqltcl (allows connecting to any mysql server from your machine, bad if you allow root@localhost with no password access). If you allow anybody to execute arbitrary code in your safe interp please check if all extensions written in C does not violate your policy.

Should add more about security policies and how to use them to extend safe slave interpreters. Maybe I'll even get motivated to extricate this functionality from the Tcl plug-in and make it useful on its own. Jacob Levy

Another thing that would be useful to describe, not sure whether here or on its own page, is how to write C extensions that work with multiple interpreters. Jacob Levy

29Sep02 Jacob Levy How do you load Tcl code into a safe interpreter? The "source" command is not available, right? So how do you force the interpreter to read the Tcl code?

The answer is simple: Use "interp invokehidden":

 % set slave [interp create -safe]
 % $slave eval {source foo.tcl}
 ==> invalid command name "source"
 % $slave invokehidden source foo.tcl

So, the "source" command cannot be invoked from code evaluated in the safe interpreter, but it *can* be invoked by the master interpreter, using "invokehidden".

The above only works when the whole extension is contained within one Tcl file. The general solution is provided by the safe command written by Laurent Demailly.

30Sep02 Jacob Levy Two more items.

First, Bob Techentin asks about the relative merits of using interp::safeCreate versus using a raw safe interpreter returned by interp create -safe.

The answer is that nilly willy you're going to want to source some stuff into a safe interpreter, open some scratch files etc. So, either you come up with your own idiosyncratic solution, or use safe::interpCreate to do it in a semi standard way.

In other words, [interp create -safe] gives you a raw interpreter without any aliases. That's of course also what safe::interpCreate does internally. And then it adds some aliases that were very very carefully designed to be safe (nearly as safe as the base safe interpreter that [interp create -safe] returns. The Safe Base is therefore simply one way to extend the raw safe interpreter, and has been carefully reviewed and used extensively, so it's believed to be safe.

If you were to roll your own, you could easily introduce some of the bugs that the war tested Safe Base has already avoided.

Second, Cameron Laird asks what criteria to use for evaluating whether a native code extension is safe.

The answer is that when you add a SafeInit procedure, you're saying that the native code in this extension does not add *any* unsafe capabilities to the interpreter. Defining a SafeInit procedure for an extension is a guarantee of absolute safety, you're saying this code is as safe as the raw safe interpreter, because the extension can now be loaded into any safe interpreter into which other code has already been injected. Thus, not only must this extension be in and of itself safe, it must also be safe in conjunction with any other code that may already be present in a safe interpreter.

As a general rule, if it writes some files, opens sockets, opens windows on the display through which it can talk to the luser, or executes any external commands, I would be suspicious of its safety. Such an extension should NOT have a SafeInit procedure. The explanation in the "SAFE INTERPRETERS" section of the interp man page is a good place to start.

Resource exhaustion attacks

AMG: It's a shame [willHalt] (from Halting Problem) cannot exist. It would be useful for ensuring that arbitrary scripts executed in safe interps do not block the execution of the remainder of the program.

Imagine a web site for experimenting with the Tcl language. It has a form for submitting scripts and getting back the results as calculated by the web server. This would let people play around with Tcl without downloading and installing it themselves, plus the form would incorporate documentation, tutorials, detailed explanations accompanying error results, and so on. The server software would be single-threaded TclHttpd, and the user-submitted scripts would run in safe interpreters.

Can you guess what script will be submitted first? while {1} {}.

My point is that safe interpreters (as created by [interp create -safe]) don't protect against wasting CPU time. I'm curious how to fix this problem. Would it be sufficient to hide [for] and [while]? Those are the only two "safe" commands I see that are capable of unbounded looping. I'd also have to wrap or hide [after] and probably also [vwait].

Thankfully infinite recursions aren't a problem, or I'd have to also outlaw [proc] and even [eval].

 % interp create -safe child
 % interp eval child {proc recurse {} {recurse}; recurse}
 too many nested evaluations (infinite loop?)
 % interp eval child {set x {eval $x}; eval $x}
 too many nested evaluations (infinite loop?)
 % interp eval child {vwait forever}
 can't wait for variable "forever":  would wait forever
 % interp eval child {while {1} {}}

Not so safe after all?

KBK 2004-08-13 - The original idea of safe interpreters was support for browser applets, where resource exhaustion attacks weren't much of an issue (the user, after all, can always close the page). Needless to say, server-side scripting has to be rather more careful, and Tcl 8.5 makes while and for (and hashtable attacks, and several other things) a good bit safer. It's still wise, however, to launch servlets in separate processes, because (a) we don't have a memory-limiting framework (that's a good bit harder than limiting CPU time), and (b) you really want them run chroot() and so on - even in a safe interp. Multiple layers of protection, and all that. The spec for resource limiting is over at [L3 ].

jk: so essentially one should open a pipe to another tclsh running a script that evals stdin in a safe interp (using fileevent), and sends the results to std out, and uses events in the main process to enforce a timeout mechanism for opened process? and also ask the OS to limit the memory the opened process has?

SYStems In case you are trying to learn Tcl/Tk from Book Practical Programming in Tcl and Tk and reached chapter 19 (skipping few chapters in the middle) and started to play with interp and ...

 #You did this
 interp create -safe aisha
 aisha eval [list puts "My name is Aisha"]
 # You will than come across this
 ==> can not find channel named "stdout"
 # Which means you must do this
 interp share {} stdout aisha
 # To be able to do this 
 aish eval [list puts "My name is Aisha"]
 # and get this
 My name is Aisha

Note that for a reason unknow to me till now, if you try in Tkcon (on debian sarge the output to stdout won't appear, but the error message will definitly disappear.

The above also means you should have read page 299 where I/O from safe interpreter is explained

Johannes13 TkCon provides an own implementation of the puts command that redirects all output to stdout/stderr to the visible console. If you send something to the stdout channel by using chan puts or puts in a newly created interp then it will fail. You can change this by "installing" the overwritten tkcon commands by selecting "Interp -> Send tkcon commands" while attached to that interp.

Tk in safe interpreters

Fabricio Rocha - 01-May-2010 - What about Tk in safe interpreters? The documentation about it is somewhat sparse: there is important information in the loadTk page and the Tk_Init page . According to the latter, the following commands are removed when Tk_SafeInit is used for loading Tk into a safe interpreter, for the respective reasons:

bell - Continuous ringing of the bell is a nuisance.
clipboard - A malicious script could replace the contents of the clipboard with the string “rm -r *” and lead to surprises when the contents of the clipboard are pasted.
grab - Grab can be used to block the user from using any other applications.
menu - Menus can be used to cover the entire screen and to steal input from the user.
selection - See clipboard.
send - Send can be used to cause unsafe interpreters to execute commands.
tk - The tk command recreates the send command, which is unsafe.
tkwait - Tkwait can block the containing process forever
toplevel - Toplevels can be used to cover the entire screen and to steal input from the user.
wm - If toplevels are ever allowed, wm can be used to remove decorations, move windows around, etc.

Are these commands just hidden like those of Tcl in safe interpreters? Is it possible to load "normal" Tk in a safe interpreter?

Fabricio Rocha - 17-May-2010 - I got my own hands dirty testing some things for answering these and other questions (using Tcl 8.5.8):

  • tk_messageBox and probably the other dialogs won´t work under SafeBase interps. They are actually displayed, but have their title changed to "_tk__messageBox" and no contents. Trying interp expose with the command causes an error because the command is actually removed from SafeBase interps, and not only hidden. However, it is simple to create an alias from the slave interpreter to the master´s tk_messageBox, and then the dialog shows perfectly. So there are more removed commands than those in the list above. For better security, the suggested alias can point to an "intermediate" procedure which, for example, checks the last time a dialog window was requested by the slave, so a "dialog flood" can be blocked if it reaches a certain limit. I presume that other removed but useful commands (like grab and menu) can be aliased like that as well.
  • I could not load "normal Tk" in a SafeBase interp. I don´t know about tcl/Tk internals, but according to the docs it seems that the SafeBase interps hangs to the "_safeInit" procedure of a library if one is provided (if one is not provided, the library isn´t even loaded).
  • One important thing to notice about SafeBase interps is that 1) if they are initialized without directories passed to their -accessPath option, they "inherit" all the paths which their master can access for loading libraries and extensions; and 2) they convert all these directories (and their subdirectories) to their token-based representation of directories. If your Tcl environment is configured for accessing multiple paths while looking for libraries, this will cause a really big delay when you try a package require Tk or something alike. My Debian system includes the whole /usr/lib in Tcl´s auto_path and this gave me a 35 seconds hang after a package require. So, you´d better tailor and use the -accessPath option whenever possible.

See also