summaryrefslogtreecommitdiff
path: root/gnu/usr.bin/perl/ext/Safe/Safe.pm
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/usr.bin/perl/ext/Safe/Safe.pm')
-rw-r--r--gnu/usr.bin/perl/ext/Safe/Safe.pm670
1 files changed, 670 insertions, 0 deletions
diff --git a/gnu/usr.bin/perl/ext/Safe/Safe.pm b/gnu/usr.bin/perl/ext/Safe/Safe.pm
new file mode 100644
index 00000000000..0fafcbe7411
--- /dev/null
+++ b/gnu/usr.bin/perl/ext/Safe/Safe.pm
@@ -0,0 +1,670 @@
+package Safe;
+
+use vars qw($VERSION @ISA @EXPORT_OK);
+
+require Exporter;
+require DynaLoader;
+use Carp;
+$VERSION = "1.00";
+@ISA = qw(Exporter DynaLoader);
+@EXPORT_OK = qw(op_mask ops_to_mask mask_to_ops opcode opname opdesc
+ MAXO emptymask fullmask);
+
+=head1 NAME
+
+Safe - Safe extension module for Perl
+
+=head1 DESCRIPTION
+
+The Safe extension module allows the creation of compartments
+in which perl code can be evaluated. Each compartment has
+
+=over 8
+
+=item a new namespace
+
+The "root" of the namespace (i.e. "main::") is changed to a
+different package and code evaluated in the compartment cannot
+refer to variables outside this namespace, even with run-time
+glob lookups and other tricks. Code which is compiled outside
+the compartment can choose to place variables into (or share
+variables with) the compartment's namespace and only that
+data will be visible to code evaluated in the compartment.
+
+By default, the only variables shared with compartments are the
+"underscore" variables $_ and @_ (and, technically, the much less
+frequently used %_, the _ filehandle and so on). This is because
+otherwise perl operators which default to $_ will not work and neither
+will the assignment of arguments to @_ on subroutine entry.
+
+=item an operator mask
+
+Each compartment has an associated "operator mask". Recall that
+perl code is compiled into an internal format before execution.
+Evaluating perl code (e.g. via "eval" or "do 'file'") causes
+the code to be compiled into an internal format and then,
+provided there was no error in the compilation, executed.
+Code evaulated in a compartment compiles subject to the
+compartment's operator mask. Attempting to evaulate code in a
+compartment which contains a masked operator will cause the
+compilation to fail with an error. The code will not be executed.
+
+By default, the operator mask for a newly created compartment masks
+out all operations which give "access to the system" in some sense.
+This includes masking off operators such as I<system>, I<open>,
+I<chown>, and I<shmget> but does not mask off operators such as
+I<print>, I<sysread> and I<E<lt>HANDLE<gt>>. Those file operators
+are allowed since for the code in the compartment to have access
+to a filehandle, the code outside the compartment must have explicitly
+placed the filehandle variable inside the compartment.
+
+Since it is only at the compilation stage that the operator mask
+applies, controlled access to potentially unsafe operations can
+be achieved by having a handle to a wrapper subroutine (written
+outside the compartment) placed into the compartment. For example,
+
+ $cpt = new Safe;
+ sub wrapper {
+ # vet arguments and perform potentially unsafe operations
+ }
+ $cpt->share('&wrapper');
+
+=back
+
+=head2 Operator masks
+
+An operator mask exists at user-level as a string of bytes of length
+MAXO, each of which is either 0x00 or 0x01. Here, MAXO is the number
+of operators in the current version of perl. The subroutine MAXO()
+(available for export by package Safe) returns the number of operators
+in the current version of perl. Note that, unlike the beta versions of
+the Safe extension, this is a reliable count of the number of
+operators in the currently running perl executable. The presence of a
+0x01 byte at offset B<n> of the string indicates that operator number
+B<n> should be masked (i.e. disallowed). The Safe extension makes
+available routines for converting from operator names to operator
+numbers (and I<vice versa>) and for converting from a list of operator
+names to the corresponding mask (and I<vice versa>).
+
+=head2 Methods in class Safe
+
+To create a new compartment, use
+
+ $cpt = new Safe;
+
+Optional arguments are (NAMESPACE, MASK), where
+
+=over 8
+
+=item NAMESPACE
+
+is the root namespace to use for the compartment (defaults to
+"Safe::Root000000000", auto-incremented for each new compartment); and
+
+=item MASK
+
+is the operator mask to use (defaults to a fairly restrictive set).
+
+=back
+
+The following methods can then be used on the compartment
+object returned by the above constructor. The object argument
+is implicit in each case.
+
+=over 8
+
+=item root (NAMESPACE)
+
+This is a get-or-set method for the compartment's namespace. With the
+NAMESPACE argument present, it sets the root namespace for the
+compartment. With no NAMESPACE argument present, it returns the
+current root namespace of the compartment.
+
+=item mask (MASK)
+
+This is a get-or-set method for the compartment's operator mask.
+With the MASK argument present, it sets the operator mask for the
+compartment. With no MASK argument present, it returns the
+current operator mask of the compartment.
+
+=item trap (OP, ...)
+
+This sets bits in the compartment's operator mask corresponding
+to each operator named in the list of arguments. Each OP can be
+either the name of an operation or its number. See opcode.h or
+opcode.pl in the main perl distribution for a canonical list of
+operator names.
+
+=item untrap (OP, ...)
+
+This resets bits in the compartment's operator mask corresponding
+to each operator named in the list of arguments. Each OP can be
+either the name of an operation or its number. See opcode.h or
+opcode.pl in the main perl distribution for a canonical list of
+operator names.
+
+=item share (VARNAME, ...)
+
+This shares the variable(s) in the argument list with the compartment.
+Each VARNAME must be the B<name> of a variable with a leading type
+identifier included. Examples of legal variable names are '$foo' for
+a scalar, '@foo' for an array, '%foo' for a hash, '&foo' for a
+subroutine and '*foo' for a glob (i.e. all symbol table entries
+associated with "foo", including scalar, array, hash, sub and filehandle).
+
+=item varglob (VARNAME)
+
+This returns a glob for the symbol table entry of VARNAME in the package
+of the compartment. VARNAME must be the B<name> of a variable without
+any leading type marker. For example,
+
+ $cpt = new Safe 'Root';
+ $Root::foo = "Hello world";
+ # Equivalent version which doesn't need to know $cpt's package name:
+ ${$cpt->varglob('foo')} = "Hello world";
+
+
+=item reval (STRING)
+
+This evaluates STRING as perl code inside the compartment. The code
+can only see the compartment's namespace (as returned by the B<root>
+method). Any attempt by code in STRING to use an operator which is
+in the compartment's mask will cause an error (at run-time of the
+main program but at compile-time for the code in STRING). The error
+is of the form "%s trapped by operation mask operation...". If an
+operation is trapped in this way, then the code in STRING will not
+be executed. If such a trapped operation occurs or any other
+compile-time or return error, then $@ is set to the error message,
+just as with an eval(). If there is no error, then the method returns
+the value of the last expression evaluated, or a return statement may
+be used, just as with subroutines and B<eval()>. Note that this
+behaviour differs from the beta distribution of the Safe extension
+where earlier versions of perl made it hard to mimic the return
+behaviour of the eval() command.
+
+=item rdo (FILENAME)
+
+This evaluates the contents of file FILENAME inside the compartment.
+See above documentation on the B<reval> method for further details.
+
+=back
+
+=head2 Subroutines in package Safe
+
+The Safe package contains subroutines for manipulating operator
+names and operator masks. All are available for export by the package.
+The canonical list of operator names is the contents of the array
+op_name defined and initialised in file F<opcode.h> of the Perl
+source distribution.
+
+=over 8
+
+=item ops_to_mask (OP, ...)
+
+This takes a list of operator names and returns an operator mask
+with precisely those operators masked.
+
+=item mask_to_ops (MASK)
+
+This takes an operator mask and returns a list of operator names
+corresponding to those operators which are masked in MASK.
+
+=item opcode (OP, ...)
+
+This takes a list of operator names and returns the corresponding
+list of opcodes (which can then be used as byte offsets into a mask).
+
+=item opname (OP, ...)
+
+This takes a list of opcodes and returns the corresponding list of
+operator names.
+
+=item fullmask
+
+This just returns a mask which has all operators masked.
+It returns the string "\1" x MAXO().
+
+=item emptymask
+
+This just returns a mask which has all operators unmasked.
+It returns the string "\0" x MAXO(). This is useful if you
+want a compartment to make use of the namespace protection
+features but do not want the default restrictive mask.
+
+=item MAXO
+
+This returns the number of operators (and hence the length of an
+operator mask). Note that, unlike the beta distributions of the
+Safe extension, this is derived from a genuine integer variable
+in the perl executable and not from a preprocessor constant.
+This means that the Safe extension is more robust in the presence
+of mismatched versions of the perl executable and the Safe extension.
+
+=item op_mask
+
+This returns the operator mask which is actually in effect at the
+time the invocation to the subroutine is compiled. In general,
+this is probably not terribly useful.
+
+=back
+
+=head2 AUTHOR
+
+Malcolm Beattie, mbeattie@sable.ox.ac.uk.
+
+=cut
+
+my $default_root = 'Root000000000';
+
+my $default_mask;
+
+sub new {
+ my($class, $root, $mask) = @_;
+ my $obj = {};
+ bless $obj, $class;
+ $obj->root(defined($root) ? $root : ("Safe::".$default_root++));
+ $obj->mask(defined($mask) ? $mask : $default_mask);
+ # We must share $_ and @_ with the compartment or else ops such
+ # as split, length and so on won't default to $_ properly, nor
+ # will passing argument to subroutines work (via @_). In fact,
+ # for reasons I don't completely understand, we need to share
+ # the whole glob *_ rather than $_ and @_ separately, otherwise
+ # @_ in non default packages within the compartment don't work.
+ *{$obj->root . "::_"} = *_;
+ return $obj;
+}
+
+sub DESTROY {
+ my($obj) = @_;
+ my $root = $obj->root();
+ if ($root =~ /^Safe::(Root\d+)$/){
+ $root = $1;
+ delete $ {"Safe::"}{"$root\::"};
+ }
+}
+
+sub root {
+ my $obj = shift;
+ if (@_) {
+ $obj->{Root} = $_[0];
+ } else {
+ return $obj->{Root};
+ }
+}
+
+sub mask {
+ my $obj = shift;
+ if (@_) {
+ $obj->{Mask} = verify_mask($_[0]);
+ } else {
+ return $obj->{Mask};
+ }
+}
+
+sub verify_mask {
+ my($mask) = @_;
+ if (length($mask) != MAXO() || $mask !~ /^[\0\1]+$/) {
+ croak("argument is not a mask");
+ }
+ return $mask;
+}
+
+sub trap {
+ my $obj = shift;
+ $obj->setmaskel("\1", @_);
+}
+
+sub untrap {
+ my $obj = shift;
+ $obj->setmaskel("\0", @_);
+}
+
+sub emptymask { "\0" x MAXO() }
+sub fullmask { "\1" x MAXO() }
+
+sub setmaskel {
+ my $obj = shift;
+ my $val = shift;
+ croak("bad value for mask element") unless $val eq "\0" || $val eq "\1";
+ my $maskref = \$obj->{Mask};
+ my ($op, $opcode);
+ foreach $op (@_) {
+ $opcode = ($op =~ /^\d/) ? $op : opcode($op);
+ substr($$maskref, $opcode, 1) = $val;
+ }
+}
+
+sub share {
+ my $obj = shift;
+ my $root = $obj->root();
+ my ($arg);
+ foreach $arg (@_) {
+ my $var;
+ ($var = $arg) =~ s/^(.)//;
+ my $caller = caller;
+ *{$root."::$var"} = ($1 eq '$') ? \${$caller."::$var"}
+ : ($1 eq '@') ? \@{$caller."::$var"}
+ : ($1 eq '%') ? \%{$caller."::$var"}
+ : ($1 eq '*') ? *{$caller."::$var"}
+ : ($1 eq '&') ? \&{$caller."::$var"}
+ : croak(qq(No such variable type for "$1$var"));
+ }
+}
+
+sub varglob {
+ my ($obj, $var) = @_;
+ return *{$obj->root()."::$var"};
+}
+
+sub reval {
+ my ($obj, $expr) = @_;
+ my $root = $obj->{Root};
+ my $mask = $obj->{Mask};
+ verify_mask($mask);
+
+ my $evalsub = eval sprintf(<<'EOT', $root);
+ package %s;
+ sub {
+ eval $expr;
+ }
+EOT
+ return safe_call_sv($root, $mask, $evalsub);
+}
+
+sub rdo {
+ my ($obj, $file) = @_;
+ my $root = $obj->{Root};
+ my $mask = $obj->{Mask};
+ verify_mask($mask);
+
+ $file =~ s/"/\\"/g; # just in case the filename contains any double quotes
+ my $evalsub = eval sprintf(<<'EOT', $root, $file);
+ package %s;
+ sub {
+ do "%s";
+ }
+EOT
+ return safe_call_sv($root, $mask, $evalsub);
+}
+
+bootstrap Safe $VERSION;
+
+$default_mask = fullmask;
+my $name;
+while (defined ($name = <DATA>)) {
+ chomp $name;
+ next if $name =~ /^#/;
+ my $code = opcode($name);
+ substr($default_mask, $code, 1) = "\0";
+}
+
+1;
+
+__DATA__
+null
+stub
+scalar
+pushmark
+wantarray
+const
+gvsv
+gv
+gelem
+padsv
+padav
+padhv
+padany
+pushre
+rv2gv
+rv2sv
+av2arylen
+rv2cv
+anoncode
+prototype
+refgen
+srefgen
+ref
+bless
+glob
+readline
+rcatline
+regcmaybe
+regcomp
+match
+subst
+substcont
+trans
+sassign
+aassign
+chop
+schop
+chomp
+schomp
+defined
+undef
+study
+pos
+preinc
+i_preinc
+predec
+i_predec
+postinc
+i_postinc
+postdec
+i_postdec
+pow
+multiply
+i_multiply
+divide
+i_divide
+modulo
+i_modulo
+repeat
+add
+i_add
+subtract
+i_subtract
+concat
+stringify
+left_shift
+right_shift
+lt
+i_lt
+gt
+i_gt
+le
+i_le
+ge
+i_ge
+eq
+i_eq
+ne
+i_ne
+ncmp
+i_ncmp
+slt
+sgt
+sle
+sge
+seq
+sne
+scmp
+bit_and
+bit_xor
+bit_or
+negate
+i_negate
+not
+complement
+atan2
+sin
+cos
+rand
+srand
+exp
+log
+sqrt
+int
+hex
+oct
+abs
+length
+substr
+vec
+index
+rindex
+sprintf
+formline
+ord
+chr
+crypt
+ucfirst
+lcfirst
+uc
+lc
+quotemeta
+rv2av
+aelemfast
+aelem
+aslice
+each
+values
+keys
+delete
+exists
+rv2hv
+helem
+hslice
+split
+join
+list
+lslice
+anonlist
+anonhash
+splice
+push
+pop
+shift
+unshift
+reverse
+grepstart
+grepwhile
+mapstart
+mapwhile
+range
+flip
+flop
+and
+or
+xor
+cond_expr
+andassign
+orassign
+method
+entersub
+leavesub
+caller
+warn
+die
+reset
+lineseq
+nextstate
+dbstate
+unstack
+enter
+leave
+scope
+enteriter
+iter
+enterloop
+leaveloop
+return
+last
+next
+redo
+goto
+close
+fileno
+tie
+untie
+dbmopen
+dbmclose
+sselect
+select
+getc
+read
+enterwrite
+leavewrite
+prtf
+print
+sysread
+syswrite
+send
+recv
+eof
+tell
+seek
+truncate
+fcntl
+ioctl
+sockpair
+bind
+connect
+listen
+accept
+shutdown
+gsockopt
+ssockopt
+getsockname
+ftrwrite
+ftsvtx
+open_dir
+readdir
+telldir
+seekdir
+rewinddir
+kill
+getppid
+getpgrp
+setpgrp
+getpriority
+setpriority
+time
+tms
+localtime
+alarm
+dofile
+entereval
+leaveeval
+entertry
+leavetry
+ghbyname
+ghbyaddr
+ghostent
+gnbyname
+gnbyaddr
+gnetent
+gpbyname
+gpbynumber
+gprotoent
+gsbyname
+gsbyport
+gservent
+shostent
+snetent
+sprotoent
+sservent
+ehostent
+enetent
+eprotoent
+eservent
+gpwnam
+gpwuid
+gpwent
+spwent
+epwent
+ggrnam
+ggrgid
+ggrent
+sgrent
+egrent