Plash: tools for practical least privilege

pola-shell: A shell for interactive use

Differences from Bash: examples

The syntax of pola-shell is similar to Unix shells such as the Bourne shell or Bash. Here are some examples of command invocations using pola-shell:

ls .

Arguments that were implicit before must now be made explicit. With the Bourne shell or Bash you can write `ls' to list the current directory's contents. With pola-shell you must add `.' to grant access to the current directory.

gcc -c foo.c => -o foo.o

Files are passed to the program as read-only by default. Adding the `=>' operator to a command invocation allows you to grant write access to a file. Files that appear to the right of `=>' are passed to the program with write access.

Directories to the left of `=>' will be passed as recursive (or transitive) read-only: files and directories that they contain will also be read-only.

make + => .

If you want to grant access to a file or directory without passing the filename as an argument, you can use the `+' operator. Files that appear to the right of a `+' are attached to the namespace of the process being run, but the filename is not included in the argument list.

The `=>' operator binds more tightly than `+'.

echo "Hello, world!"

The shell distinguishes between filename arguments and plain string arguments so that it can tell which files to grant access to. Arguments beginning with a hyphen (`-') are interpreted as plain strings, but otherwise you must quote arguments to prevent them from being interpreted as filenames.

tar -cvzf { => foo.tar.gz } dir1

If you want to put a read-write file before a file that should only be read-only in the argument list, you can limit the scope of the `=>' operator by enclosing arguments in curly brackets { ... }.

xclock + ~/.Xauthority => /tmp/.X11-unix

You can run X Windows programs if you give them access to ~/.Xauthority, which contains a password generated by the X server, and /tmp/.X11-unix, which contains the socket for connecting to the X server. Programs must be given write access to a socket in order to connect to it.

grep 'pattern' file | less

Pipes work as in conventional shells.


If you want to execute a command in the conventional way, without running the process with a virtualised filesystem, in a chroot jail, etc., you can prefix it with "!!". This can be applied to individual command invocations in a pipeline. The syntax for command invocations is the same whether "!!" is used or not, but when it is used, files listed after the "+" operator are ignored.

cd directory

Changing directory works as before.

Bourne shell features missing from pola-shell

The following features are provided in the Bourne shell and Bash but not in pola-shell.

Installation endowment

A program's installation endowment is the set of files, directories and other objects that it needs and should have access to regardless of the parameters you give to the program. It consists of libraries, configuration files, other executables -- and for interpreted programs, source files. These are files that are in a sense "part of" the program.

On Unix, pola-shell can't tell exactly what the installation endowment of a program is or should be. Unix does not have this information, because programs are usually given access to everything the user can access.

So, pola-shell has a default installation endowment. It grants read-only access to the directories /bin, /lib, /usr and /etc, and also read-write access to the device files /dev/null and /dev/tty.

The default installation endowment is not configurable yet. However, you can change it on a per-program basis by using executable objects.

Enabling access to the X11 Window System

The shell has an option for automatically granting programs access to the X11 Window System. When this is switched on, a command such as:

xpdf foo.pdf

is equivalent to:

xpdf foo.pdf + ~/.Xauthority => /tmp/.X11-unix

This option is switched off by default because X11 is not secure! X servers provide no isolation between the clients connected to them. One client can read keyboard input that goes to other clients, grab the screen's contents, and feed events to other clients, including keypress events. So potentially, an X client can gain the full authority of the user.

The solution to this will be to write a proxy, through which an X client will connect to the X server, which will prevent it from interfering with other X clients.

How to switch on this option (short version):

Either: From the shell, enter:

plash-opts /x=options 'enable_x11' 'on'

Or: To enable it for all shell sessions, you can create a file "~/.plashrc" file containing this (note the semicolon):

plash-opts /x=options 'enable_x11' 'on';

and start pola-shell with the command:

pola-shell --rcfile ~/.plashrc

(In order to make it as predictable as possible, pola-shell doesn't read any options files by default, so you have to specify options files explicitly.)

Job control

As with other shells, you can start a job in the background by putting "&" at the end of the command. Or, having run a job in the foreground, you can suspend it by pressing Control-Z.

The command "fg <N>" puts a job in the foreground. <N> is a job number; it is not prefixed with a "%", unlike in Bash.

The command "bg <N>" resumes a job in the background.

There is no command for listing the currently active jobs yet.

Shell scripts

pola-shell has only rudimentary support for shell scripts. You can get the shell to run a script file on startup with the --rcfile option. The "source file" command will run file as a script.

Each command in the script file must be terminated with a semicolon ";".

pola-shell doesn't accept any leading space in the script. This is a bug.

There is no error handling: if a command exits with a non-zero return code, it doesn't stop the script.

By default, the shell does not read any scripts on startup.


--rcfile file

Executes the given script on startup. Does not switch off interactive mode.

By default, the shell does not read any scripts on startup.

-c command

Execute the given command, and then exit. Disables interactive mode.

Argument lists

arglist1 => arglist2

By default, files and directories are passed as read-only. The "=>" operator lets you pass files and directories with read-write access. Objects to the right of "=>" are passed as read-write slots, so the object doesn't have to exist in advance.

arglist1 + arglist2

Files and directories that appear to the right of the "+" operator are not included in the argument list (the one used in execve()), but they are attached into the file namespace of the process.


Arguments that are not filenames should be quoted, unless they begin with '-'.


You can attach objects to arbitrary points in the file namespace. Here, expr typically evaluates to a file, directory, or executable object. This will include pathname in the argument list.

{ arglist }

You can limit the scope of "+" or "=>" using curly brackets.


IO redirection. You can change the file descriptors that are passed to the process.


cd pathname

Sets the current directory.

fg job-number

Puts the given job in the foreground. (Job numbers are not prefixed with `%', unlike in Bash.)

bg job-number

Puts the given job in the background.

def var = expr

Binds the object reference returned by the expression to a variable.



Returns the object reference that is bound to the variable.

F pathname

Returns the file or directory object at the given path. Will follow symbolic links.

mkfs args...

This expression returns a fabricated directory object containing the files listed in args. The object resides in a server process started by the shell.

args is processed in the same way as argument lists to commands, so read-only access will be given for files that are listed unless "=>" is used, and objects can be attached at points in the directory tree using path=expr.

capcmd command args...

This built-in expression is similar to a normal command invocation, except that it expects the resulting process to return an object reference as a result. The shell passes the process a return continuation argument (return_cont; see the PLASH_CAPS environment variable), which the process invokes with the result.

This expression doesn't wait for the process to exit: the process will typically act as a server and stay running in the background to handle invocations of the object that it returned.

If the process drops the return continuation without invoking it (which will happen if it exits without passing the reference on), the expression results in an error.