Navigation Logo 21.4  Customizing Slave Interpreters Navigation Logo

 

 

The plugin relies on a Tcl command, interp, that creates a new interpreter that can be used to execute tclets. This command is introduced above in Executing Scripts from within Scripts. However, as shown there, interp only creates fully powered slave interpreters. What the plugin needs to do is create a slave which has only powers consistent with some security policy.

In fact, the command set of any slave has some properties that are different than the master interpreter created by tclsh, wish, or the plugin. The commands are divided into two categories: hidden and exposed. Hidden commands are not executable from the slave but are executable from any master of the slave. Exposed commands are executable either place as described above in Executing Scripts from within Scripts. No command may be both hidden and exposed. If CI is a slave and Script contains a Tcl script, then

CI eval $Script
can be executed from the master provided Script makes use of only exposed and alias commands. If there are any hidden commands in Script, they will not be executed. Hidden commands must be invoked from the master this way:
CI invokehidden ?-global? HIDDEN_COMMAND_NAME ARGUMENTS

Suppose the open command is hidden in a slave named S. Here is an example interactive session in the master which opens a file named sample and puts its contents into a variable, Contents, of the slave.

 
% CI invokehidden open sample 
file5 
% CI eval {set Contents [read file5]; close file5} 
%

Exercise 21.4a

The following script will fail. Fix it.
set F [CI invokehidden open sample]
CI eval "set Contents [read $F]; close $F"
Your solution will not produce anything useful, but it will give you some insight into using invokehidden and eval.

Solution

As the last exercise helps to show, the invokehidden action is not particularly useful taken in isolation. Its value comes when it is used with another unusual property of slaves. This property permits the creation of some procedures which appear to be commands in a slave but are actually procedures in a master. These procedures are called aliases. An alias may have the same name in a slave as a hidden command of that slave.

As an example of the usefulness of these features, suppose you want a version of the open command in a slave that refuses to open pipes and insists on opening files in one particular directory which cannot be chosen by scripts in the slave. You would accomplish this by hiding the slave's real open command and creating an alias which enforces the rules before using the hidden open command to actually open the file in the slave.

The next section discusses the -safe switch to the interp command which creates a Safe Base Tcl interpreter. This section continues with explanations of how to hide commands and create aliases.

Two interpreter object commands are provided for hiding and exposing members of the an interpreter's command set.

interpreter_object hide interpreter_object COMMAND_NAME
hides the named command in interpreter_object's command set.
interpreter_object expose interpreter_object COMMAND_NAME
exposes the named command interpreter_object's command set.

There are two substeps to creating an alias:

  1. Implement a procedure p in the master which uses the slave's hidden commands under suitable restrictions.

  2. Create an alias a in the slave for p.

As an example, these two steps can permit a to open a file through p which checks to make sure the file being opened is acceptable for the current security policy. The procedure p runs in the master as it checks for authorization but it uses the hidden open procedure in the slave interpreter. Because it uses the slave's open command, the file is opened in the slave and not its master.

The first substep in this method involves executing a hidden command h of a slave S from a procedure p in the master. Here is an example.

proc limited_open {Intrp Path Name {Mode r}} { 
   $Intrp invokehidden open [file join $Path [file tail $Name]] $Mode 
}
This procedure will either open a file in the interpreter $Intrp and return its file identifier or generate an error. The directory in which the file must appear is determined by $Path. It may seem silly to separate the directory from the file name this way when both are parameters to limited_open. The reason it is not silly is that the alias which causes limited_open to execute will accept only arguments for the last two parameters. The first two arguments expected by limited_open will be fixed at the time the alias is created.

The second substep involves creating an alias in a slave for a procedure in a master. To make a procedure p in the master available in the slave S under the name a, use the alias action this way.

S alias a p ?CON1 CON2 ... CONn?
This will cause p to be invoked whenever a command line executed by the slave uses the command name a. For example, suppose the following command is executed in the slave.
a ARG1 ARG2 ... ARGm
The effect will be to execute the following in the master.
p ?SUBST_ARGS? SUBST_CON1 ... SUBST_CONn?  ?SUBST_ARG1 ... SUBST_ARGm

Here SUBST_ indicates the substituted version of the argument it prefixes. It is important to note that the CON?? arguments are substituted in the master at the time the alias command is executed and the ARG?? arguments are substituted in the slave at the time a is executed.

As p executes in the master interpreter, over in the slave interpreter the namespace is unchanged from what it was when the command line containing a was executed. When the execution of p is finished, the return string is passed back to the slave as if it had come from a.

Returning to the example in which a slave is given limited power to open files, let the slave be create this way:

% interp create S
S
% S hide open
Assuming DIRECTORY_FOR_S is a variable with the path name of the directory where scripts running in S are permitted to open files and that limited_open is a procedure as described above, the permission is given this way:
% S alias open limited_open S $DIRECTORY_FOR_S
open
After the alias action has executed, open is an alias in S's command set. When it is executed, the procedure which actually executes is limited_open and the first two arguments of limited_open are S and $DIRECTORY_FOR_S. These two arguments are fixed at the time alias open is executed. The value of DIRECTORY_FOR_S comes from the master interpreter at this time, there is no way for the slave to change it. The last two arguments of limited_open are taken from any command line executing open in the slave.

Assuming the value of $DIRECTORY_FOR_S was /users/jazimmer, this command in the slave,

open MyFile
actually causes this command,
limited_open S /users/jazimmer MyFile
to be executed in the master. This is like opening /users/jazimmer/MyFile for reading.

The limitations of open in the slave become more apparent with two more examples. In the slave this command,

set F [open "| pico"]
would attempt to open a file named "| pico" in /users/jazimmer. The normal behavior of opening a pipe named "pico" is suppressed. In the slave this command,
set F [open "/etc/passwd" w]
would attempt to open /users/jazimmer/passwd for writing.

Exercise 21.4b

Implement a procedure create_nonet_interp which creates a slave interpreter that cannot access networks and cannot itself create slaves. The socket command is not the only way to access networks. You will also need to prohibit the execution of other programs through the operating system and the creation of pipes with the open command. Prohibit the former by simply hiding the relevant commands. Prohibit the latter by creating a suitable alias for the open command.

You can test this with a sequence of commands similar to

% create_nonet_interp N
N
% N eval {open "| executable"}
cannot open a pipeline
% N eval { set F [open sample]; puts [read $F]; close $F }
hellow orld
% Safe eval {interp create New}
invalid command name "interp"

Solution

Exercise 21.4c

Skip this exercise unless you have worked through the optional section, A New Command Window above.

Combine ES20.5a and ES21.4b so that the slave interpreter created by ES21.4b is the interpreter which executes in the command window of ES20.5a.

Solution

The alias action can be used to delete aliases. Use this pattern.

interpreter_object alias ALIAS_NAME {}

Deleting the alias does not delete the target command, only the ability to execute it from the subordinate interpreter.

There is another method for customizing a slave which is almost archaic. I think its main usefulness lies in adding stdin, stdout, and stderr back to Safe Base slaves. I/O channels can be transferred to or shared among interpreters as explained in the following table.

interp transfer SOURCE CHANNEL_ID TARGET
causes CHANNEL_ID (the name of an existing I/O channel) to be transferred to TARGET from SOURCE. Use this when files for TARGET are opened in SOURCE and then not needed by SOURCE.

Both SOURCE and TARGET must already exist.

For example, this command line,

interp transfer {} stdout S
will make the stdout channel available for use by commands executing in the S interpreter – provided it was available in the master interpreter before the transfer. The empty string is the name of the current interpreter.

interp share SOURCE CHANNEL_ID TARGET
causes CHANNEL_ID (the name of an existing I/O channel) to be available in the subordinate TARGET interpreter as well as in the SOURCE interpreter. If CHANNEL_ID is later to be closed, it must be closed in both interpreters.

Both SOURCE and TARGET must already exist.

 

 

[Sample TK Application]
Author's Home Page
Navigation Logo [Book's Cover]
Order from Amazon.