Annotation of loncom/xml/Safe.pm, revision 1.1

1.1     ! albertel    1: package Safe;
        !             2: 
        !             3: use 5.003_11;
        !             4: use strict;
        !             5: 
        !             6: our $VERSION = "2.061";
        !             7: 
        !             8: use Carp;
        !             9: 
        !            10: use Opcode 1.01, qw(
        !            11:     opset opset_to_ops opmask_add
        !            12:     empty_opset full_opset invert_opset verify_opset
        !            13:     opdesc opcodes opmask define_optag opset_to_hex
        !            14: );
        !            15: 
        !            16: *ops_to_opset = \&opset;   # Temporary alias for old Penguins
        !            17: 
        !            18: 
        !            19: my $default_root  = 0;
        !            20: my $default_share = ['*_']; #, '*main::'];
        !            21: 
        !            22: sub new {
        !            23:     my($class, $root, $mask) = @_;
        !            24:     my $obj = {};
        !            25:     bless $obj, $class;
        !            26: 
        !            27:     if (defined($root)) {
        !            28: 	croak "Can't use \"$root\" as root name"
        !            29: 	    if $root =~ /^main\b/ or $root !~ /^\w[:\w]*$/;
        !            30: 	$obj->{Root}  = $root;
        !            31: 	$obj->{Erase} = 0;
        !            32:     }
        !            33:     else {
        !            34: 	$obj->{Root}  = "Safe::Root".$default_root++;
        !            35: 	$obj->{Erase} = 1;
        !            36:     }
        !            37: 
        !            38:     # use permit/deny methods instead till interface issues resolved
        !            39:     # XXX perhaps new Safe 'Root', mask => $mask, foo => bar, ...;
        !            40:     croak "Mask parameter to new no longer supported" if defined $mask;
        !            41:     $obj->permit_only(':default');
        !            42: 
        !            43:     # We must share $_ and @_ with the compartment or else ops such
        !            44:     # as split, length and so on won't default to $_ properly, nor
        !            45:     # will passing argument to subroutines work (via @_). In fact,
        !            46:     # for reasons I don't completely understand, we need to share
        !            47:     # the whole glob *_ rather than $_ and @_ separately, otherwise
        !            48:     # @_ in non default packages within the compartment don't work.
        !            49:     $obj->share_from('main', $default_share);
        !            50:     return $obj;
        !            51: }
        !            52: 
        !            53: sub DESTROY {
        !            54:     my $obj = shift;
        !            55:     $obj->erase('DESTROY') if $obj->{Erase};
        !            56: }
        !            57: 
        !            58: sub erase {
        !            59:     my ($obj, $action) = @_;
        !            60:     my $pkg = $obj->root();
        !            61:     my ($stem, $leaf);
        !            62: 
        !            63:     no strict 'refs';
        !            64:     $pkg = "main::$pkg\::";	# expand to full symbol table name
        !            65:     ($stem, $leaf) = $pkg =~ m/(.*::)(\w+::)$/;
        !            66: 
        !            67:     # The 'my $foo' is needed! Without it you get an
        !            68:     # 'Attempt to free unreferenced scalar' warning!
        !            69:     my $stem_symtab = *{$stem}{HASH};
        !            70: 
        !            71:     #warn "erase($pkg) stem=$stem, leaf=$leaf";
        !            72:     #warn " stem_symtab hash ".scalar(%$stem_symtab)."\n";
        !            73: 	# ", join(', ', %$stem_symtab),"\n";
        !            74: 
        !            75: #    delete $stem_symtab->{$leaf};
        !            76: 
        !            77:     my $leaf_glob   = $stem_symtab->{$leaf};
        !            78:     my $leaf_symtab = *{$leaf_glob}{HASH};
        !            79: #    warn " leaf_symtab ", join(', ', %$leaf_symtab),"\n";
        !            80:     %$leaf_symtab = ();
        !            81:     #delete $leaf_symtab->{'__ANON__'};
        !            82:     #delete $leaf_symtab->{'foo'};
        !            83:     #delete $leaf_symtab->{'main::'};
        !            84: #    my $foo = undef ${"$stem\::"}{"$leaf\::"};
        !            85: 
        !            86:     if ($action and $action eq 'DESTROY') {
        !            87:         delete $stem_symtab->{$leaf};
        !            88:     } else {
        !            89:         $obj->share_from('main', $default_share);
        !            90:     }
        !            91:     1;
        !            92: }
        !            93: 
        !            94: 
        !            95: sub reinit {
        !            96:     my $obj= shift;
        !            97:     $obj->erase;
        !            98:     $obj->share_redo;
        !            99: }
        !           100: 
        !           101: sub root {
        !           102:     my $obj = shift;
        !           103:     croak("Safe root method now read-only") if @_;
        !           104:     return $obj->{Root};
        !           105: }
        !           106: 
        !           107: 
        !           108: sub mask {
        !           109:     my $obj = shift;
        !           110:     return $obj->{Mask} unless @_;
        !           111:     $obj->deny_only(@_);
        !           112: }
        !           113: 
        !           114: # v1 compatibility methods
        !           115: sub trap   { shift->deny(@_)   }
        !           116: sub untrap { shift->permit(@_) }
        !           117: 
        !           118: sub deny {
        !           119:     my $obj = shift;
        !           120:     $obj->{Mask} |= opset(@_);
        !           121: }
        !           122: sub deny_only {
        !           123:     my $obj = shift;
        !           124:     $obj->{Mask} = opset(@_);
        !           125: }
        !           126: 
        !           127: sub permit {
        !           128:     my $obj = shift;
        !           129:     # XXX needs testing
        !           130:     $obj->{Mask} &= invert_opset opset(@_);
        !           131: }
        !           132: sub permit_only {
        !           133:     my $obj = shift;
        !           134:     $obj->{Mask} = invert_opset opset(@_);
        !           135: }
        !           136: 
        !           137: 
        !           138: sub dump_mask {
        !           139:     my $obj = shift;
        !           140:     print opset_to_hex($obj->{Mask}),"\n";
        !           141: }
        !           142: 
        !           143: 
        !           144: 
        !           145: sub share {
        !           146:     my($obj, @vars) = @_;
        !           147:     $obj->share_from(scalar(caller), \@vars);
        !           148: }
        !           149: 
        !           150: sub share_from {
        !           151:     my $obj = shift;
        !           152:     my $pkg = shift;
        !           153:     my $vars = shift;
        !           154:     my $no_record = shift || 0;
        !           155:     my $root = $obj->root();
        !           156:     croak("vars not an array ref") unless ref $vars eq 'ARRAY';
        !           157: 	no strict 'refs';
        !           158:     # Check that 'from' package actually exists
        !           159:     croak("Package \"$pkg\" does not exist")
        !           160: 	unless keys %{"$pkg\::"};
        !           161:     my $arg;
        !           162:     foreach $arg (@$vars) {
        !           163: 	# catch some $safe->share($var) errors:
        !           164: 	croak("'$arg' not a valid symbol table name")
        !           165: 	    unless $arg =~ /^[\$\@%*&]?\w[\w:]*$/
        !           166: 	    	or $arg =~ /^\$\W$/;
        !           167: 	my ($var, $type);
        !           168: 	$type = $1 if ($var = $arg) =~ s/^(\W)//;
        !           169: 	# warn "share_from $pkg $type $var";
        !           170: 	*{$root."::$var"} = (!$type)       ? \&{$pkg."::$var"}
        !           171: 			  : ($type eq '&') ? \&{$pkg."::$var"}
        !           172: 			  : ($type eq '$') ? \${$pkg."::$var"}
        !           173: 			  : ($type eq '@') ? \@{$pkg."::$var"}
        !           174: 			  : ($type eq '%') ? \%{$pkg."::$var"}
        !           175: 			  : ($type eq '*') ?  *{$pkg."::$var"}
        !           176: 			  : croak(qq(Can't share "$type$var" of unknown type));
        !           177:     }
        !           178:     $obj->share_record($pkg, $vars) unless $no_record or !$vars;
        !           179: }
        !           180: 
        !           181: sub share_record {
        !           182:     my $obj = shift;
        !           183:     my $pkg = shift;
        !           184:     my $vars = shift;
        !           185:     my $shares = \%{$obj->{Shares} ||= {}};
        !           186:     # Record shares using keys of $obj->{Shares}. See reinit.
        !           187:     @{$shares}{@$vars} = ($pkg) x @$vars if @$vars;
        !           188: }
        !           189: sub share_redo {
        !           190:     my $obj = shift;
        !           191:     my $shares = \%{$obj->{Shares} ||= {}};
        !           192: 	my($var, $pkg);
        !           193:     while(($var, $pkg) = each %$shares) {
        !           194: 	# warn "share_redo $pkg\:: $var";
        !           195: 	$obj->share_from($pkg,  [ $var ], 1);
        !           196:     }
        !           197: }
        !           198: sub share_forget {
        !           199:     delete shift->{Shares};
        !           200: }
        !           201: 
        !           202: sub varglob {
        !           203:     my ($obj, $var) = @_;
        !           204:     no strict 'refs';
        !           205:     return *{$obj->root()."::$var"};
        !           206: }
        !           207: 
        !           208: 
        !           209: sub reval {
        !           210:     my ($obj, $__SAFE_LOCAL_expr, $strict) = @_;
        !           211:     my $root = $obj->{Root};
        !           212: 
        !           213:     # Create anon sub ref in root of compartment.
        !           214:     # Uses a closure (on $expr) to pass in the code to be executed.
        !           215:     # (eval on one line to keep line numbers as expected by caller)
        !           216: 	my $evalcode = sprintf('package %s; sub { eval $__SAFE_LOCAL_expr; }', $root);
        !           217:     my $evalsub;
        !           218: 
        !           219: 	if ($strict) { use strict; $evalsub = eval $evalcode; }
        !           220: 	else         {  no strict; $evalsub = eval $evalcode; }
        !           221: 
        !           222:     return Opcode::_safe_call_sv($root, $obj->{Mask}, $evalsub);
        !           223: }
        !           224: 
        !           225: sub rdo {
        !           226:     my ($obj, $file) = @_;
        !           227:     my $root = $obj->{Root};
        !           228: 
        !           229:     my $evalsub = eval
        !           230: 	    sprintf('package %s; sub { do $file }', $root);
        !           231:     return Opcode::_safe_call_sv($root, $obj->{Mask}, $evalsub);
        !           232: }
        !           233: 
        !           234: 
        !           235: 1;
        !           236: 
        !           237: __END__
        !           238: 
        !           239: =head1 NAME
        !           240: 
        !           241: Safe - Compile and execute code in restricted compartments
        !           242: 
        !           243: =head1 SYNOPSIS
        !           244: 
        !           245:   use Safe;
        !           246: 
        !           247:   $compartment = new Safe;
        !           248: 
        !           249:   $compartment->permit(qw(time sort :browse));
        !           250: 
        !           251:   $result = $compartment->reval($unsafe_code);
        !           252: 
        !           253: =head1 DESCRIPTION
        !           254: 
        !           255: The Safe extension module allows the creation of compartments
        !           256: in which perl code can be evaluated. Each compartment has
        !           257: 
        !           258: =over 8
        !           259: 
        !           260: =item a new namespace
        !           261: 
        !           262: The "root" of the namespace (i.e. "main::") is changed to a
        !           263: different package and code evaluated in the compartment cannot
        !           264: refer to variables outside this namespace, even with run-time
        !           265: glob lookups and other tricks.
        !           266: 
        !           267: Code which is compiled outside the compartment can choose to place
        !           268: variables into (or I<share> variables with) the compartment's namespace
        !           269: and only that data will be visible to code evaluated in the
        !           270: compartment.
        !           271: 
        !           272: By default, the only variables shared with compartments are the
        !           273: "underscore" variables $_ and @_ (and, technically, the less frequently
        !           274: used %_, the _ filehandle and so on). This is because otherwise perl
        !           275: operators which default to $_ will not work and neither will the
        !           276: assignment of arguments to @_ on subroutine entry.
        !           277: 
        !           278: =item an operator mask
        !           279: 
        !           280: Each compartment has an associated "operator mask". Recall that
        !           281: perl code is compiled into an internal format before execution.
        !           282: Evaluating perl code (e.g. via "eval" or "do 'file'") causes
        !           283: the code to be compiled into an internal format and then,
        !           284: provided there was no error in the compilation, executed.
        !           285: Code evaluated in a compartment compiles subject to the
        !           286: compartment's operator mask. Attempting to evaluate code in a
        !           287: compartment which contains a masked operator will cause the
        !           288: compilation to fail with an error. The code will not be executed.
        !           289: 
        !           290: The default operator mask for a newly created compartment is
        !           291: the ':default' optag.
        !           292: 
        !           293: It is important that you read the Opcode(3) module documentation
        !           294: for more information, especially for detailed definitions of opnames,
        !           295: optags and opsets.
        !           296: 
        !           297: Since it is only at the compilation stage that the operator mask
        !           298: applies, controlled access to potentially unsafe operations can
        !           299: be achieved by having a handle to a wrapper subroutine (written
        !           300: outside the compartment) placed into the compartment. For example,
        !           301: 
        !           302:     $cpt = new Safe;
        !           303:     sub wrapper {
        !           304:         # vet arguments and perform potentially unsafe operations
        !           305:     }
        !           306:     $cpt->share('&wrapper');
        !           307: 
        !           308: =back
        !           309: 
        !           310: 
        !           311: =head1 WARNING
        !           312: 
        !           313: The authors make B<no warranty>, implied or otherwise, about the
        !           314: suitability of this software for safety or security purposes.
        !           315: 
        !           316: The authors shall not in any case be liable for special, incidental,
        !           317: consequential, indirect or other similar damages arising from the use
        !           318: of this software.
        !           319: 
        !           320: Your mileage will vary. If in any doubt B<do not use it>.
        !           321: 
        !           322: 
        !           323: =head2 RECENT CHANGES
        !           324: 
        !           325: The interface to the Safe module has changed quite dramatically since
        !           326: version 1 (as supplied with Perl5.002). Study these pages carefully if
        !           327: you have code written to use Safe version 1 because you will need to
        !           328: makes changes.
        !           329: 
        !           330: 
        !           331: =head2 Methods in class Safe
        !           332: 
        !           333: To create a new compartment, use
        !           334: 
        !           335:     $cpt = new Safe;
        !           336: 
        !           337: Optional argument is (NAMESPACE), where NAMESPACE is the root namespace
        !           338: to use for the compartment (defaults to "Safe::Root0", incremented for
        !           339: each new compartment).
        !           340: 
        !           341: Note that version 1.00 of the Safe module supported a second optional
        !           342: parameter, MASK.  That functionality has been withdrawn pending deeper
        !           343: consideration. Use the permit and deny methods described below.
        !           344: 
        !           345: The following methods can then be used on the compartment
        !           346: object returned by the above constructor. The object argument
        !           347: is implicit in each case.
        !           348: 
        !           349: 
        !           350: =over 8
        !           351: 
        !           352: =item permit (OP, ...)
        !           353: 
        !           354: Permit the listed operators to be used when compiling code in the
        !           355: compartment (in I<addition> to any operators already permitted).
        !           356: 
        !           357: =item permit_only (OP, ...)
        !           358: 
        !           359: Permit I<only> the listed operators to be used when compiling code in
        !           360: the compartment (I<no> other operators are permitted).
        !           361: 
        !           362: =item deny (OP, ...)
        !           363: 
        !           364: Deny the listed operators from being used when compiling code in the
        !           365: compartment (other operators may still be permitted).
        !           366: 
        !           367: =item deny_only (OP, ...)
        !           368: 
        !           369: Deny I<only> the listed operators from being used when compiling code
        !           370: in the compartment (I<all> other operators will be permitted).
        !           371: 
        !           372: =item trap (OP, ...)
        !           373: 
        !           374: =item untrap (OP, ...)
        !           375: 
        !           376: The trap and untrap methods are synonyms for deny and permit
        !           377: respectfully.
        !           378: 
        !           379: =item share (NAME, ...)
        !           380: 
        !           381: This shares the variable(s) in the argument list with the compartment.
        !           382: This is almost identical to exporting variables using the L<Exporter(3)>
        !           383: module.
        !           384: 
        !           385: Each NAME must be the B<name> of a variable, typically with the leading
        !           386: type identifier included. A bareword is treated as a function name.
        !           387: 
        !           388: Examples of legal names are '$foo' for a scalar, '@foo' for an
        !           389: array, '%foo' for a hash, '&foo' or 'foo' for a subroutine and '*foo'
        !           390: for a glob (i.e.  all symbol table entries associated with "foo",
        !           391: including scalar, array, hash, sub and filehandle).
        !           392: 
        !           393: Each NAME is assumed to be in the calling package. See share_from
        !           394: for an alternative method (which share uses).
        !           395: 
        !           396: =item share_from (PACKAGE, ARRAYREF)
        !           397: 
        !           398: This method is similar to share() but allows you to explicitly name the
        !           399: package that symbols should be shared from. The symbol names (including
        !           400: type characters) are supplied as an array reference.
        !           401: 
        !           402:     $safe->share_from('main', [ '$foo', '%bar', 'func' ]);
        !           403: 
        !           404: 
        !           405: =item varglob (VARNAME)
        !           406: 
        !           407: This returns a glob reference for the symbol table entry of VARNAME in
        !           408: the package of the compartment. VARNAME must be the B<name> of a
        !           409: variable without any leading type marker. For example,
        !           410: 
        !           411:     $cpt = new Safe 'Root';
        !           412:     $Root::foo = "Hello world";
        !           413:     # Equivalent version which doesn't need to know $cpt's package name:
        !           414:     ${$cpt->varglob('foo')} = "Hello world";
        !           415: 
        !           416: 
        !           417: =item reval (STRING)
        !           418: 
        !           419: This evaluates STRING as perl code inside the compartment.
        !           420: 
        !           421: The code can only see the compartment's namespace (as returned by the
        !           422: B<root> method). The compartment's root package appears to be the
        !           423: C<main::> package to the code inside the compartment.
        !           424: 
        !           425: Any attempt by the code in STRING to use an operator which is not permitted
        !           426: by the compartment will cause an error (at run-time of the main program
        !           427: but at compile-time for the code in STRING).  The error is of the form
        !           428: "%s trapped by operation mask operation...".
        !           429: 
        !           430: If an operation is trapped in this way, then the code in STRING will
        !           431: not be executed. If such a trapped operation occurs or any other
        !           432: compile-time or return error, then $@ is set to the error message, just
        !           433: as with an eval().
        !           434: 
        !           435: If there is no error, then the method returns the value of the last
        !           436: expression evaluated, or a return statement may be used, just as with
        !           437: subroutines and B<eval()>. The context (list or scalar) is determined
        !           438: by the caller as usual.
        !           439: 
        !           440: This behaviour differs from the beta distribution of the Safe extension
        !           441: where earlier versions of perl made it hard to mimic the return
        !           442: behaviour of the eval() command and the context was always scalar.
        !           443: 
        !           444: Some points to note:
        !           445: 
        !           446: If the entereval op is permitted then the code can use eval "..." to
        !           447: 'hide' code which might use denied ops. This is not a major problem
        !           448: since when the code tries to execute the eval it will fail because the
        !           449: opmask is still in effect. However this technique would allow clever,
        !           450: and possibly harmful, code to 'probe' the boundaries of what is
        !           451: possible.
        !           452: 
        !           453: Any string eval which is executed by code executing in a compartment,
        !           454: or by code called from code executing in a compartment, will be eval'd
        !           455: in the namespace of the compartment. This is potentially a serious
        !           456: problem.
        !           457: 
        !           458: Consider a function foo() in package pkg compiled outside a compartment
        !           459: but shared with it. Assume the compartment has a root package called
        !           460: 'Root'. If foo() contains an eval statement like eval '$foo = 1' then,
        !           461: normally, $pkg::foo will be set to 1.  If foo() is called from the
        !           462: compartment (by whatever means) then instead of setting $pkg::foo, the
        !           463: eval will actually set $Root::pkg::foo.
        !           464: 
        !           465: This can easily be demonstrated by using a module, such as the Socket
        !           466: module, which uses eval "..." as part of an AUTOLOAD function. You can
        !           467: 'use' the module outside the compartment and share an (autoloaded)
        !           468: function with the compartment. If an autoload is triggered by code in
        !           469: the compartment, or by any code anywhere that is called by any means
        !           470: from the compartment, then the eval in the Socket module's AUTOLOAD
        !           471: function happens in the namespace of the compartment. Any variables
        !           472: created or used by the eval'd code are now under the control of
        !           473: the code in the compartment.
        !           474: 
        !           475: A similar effect applies to I<all> runtime symbol lookups in code
        !           476: called from a compartment but not compiled within it.
        !           477: 
        !           478: 
        !           479: 
        !           480: =item rdo (FILENAME)
        !           481: 
        !           482: This evaluates the contents of file FILENAME inside the compartment.
        !           483: See above documentation on the B<reval> method for further details.
        !           484: 
        !           485: =item root (NAMESPACE)
        !           486: 
        !           487: This method returns the name of the package that is the root of the
        !           488: compartment's namespace.
        !           489: 
        !           490: Note that this behaviour differs from version 1.00 of the Safe module
        !           491: where the root module could be used to change the namespace. That
        !           492: functionality has been withdrawn pending deeper consideration.
        !           493: 
        !           494: =item mask (MASK)
        !           495: 
        !           496: This is a get-or-set method for the compartment's operator mask.
        !           497: 
        !           498: With no MASK argument present, it returns the current operator mask of
        !           499: the compartment.
        !           500: 
        !           501: With the MASK argument present, it sets the operator mask for the
        !           502: compartment (equivalent to calling the deny_only method).
        !           503: 
        !           504: =back
        !           505: 
        !           506: 
        !           507: =head2 Some Safety Issues
        !           508: 
        !           509: This section is currently just an outline of some of the things code in
        !           510: a compartment might do (intentionally or unintentionally) which can
        !           511: have an effect outside the compartment.
        !           512: 
        !           513: =over 8
        !           514: 
        !           515: =item Memory
        !           516: 
        !           517: Consuming all (or nearly all) available memory.
        !           518: 
        !           519: =item CPU
        !           520: 
        !           521: Causing infinite loops etc.
        !           522: 
        !           523: =item Snooping
        !           524: 
        !           525: Copying private information out of your system. Even something as
        !           526: simple as your user name is of value to others. Much useful information
        !           527: could be gleaned from your environment variables for example.
        !           528: 
        !           529: =item Signals
        !           530: 
        !           531: Causing signals (especially SIGFPE and SIGALARM) to affect your process.
        !           532: 
        !           533: Setting up a signal handler will need to be carefully considered
        !           534: and controlled.  What mask is in effect when a signal handler
        !           535: gets called?  If a user can get an imported function to get an
        !           536: exception and call the user's signal handler, does that user's
        !           537: restricted mask get re-instated before the handler is called?
        !           538: Does an imported handler get called with its original mask or
        !           539: the user's one?
        !           540: 
        !           541: =item State Changes
        !           542: 
        !           543: Ops such as chdir obviously effect the process as a whole and not just
        !           544: the code in the compartment. Ops such as rand and srand have a similar
        !           545: but more subtle effect.
        !           546: 
        !           547: =back
        !           548: 
        !           549: =head2 AUTHOR
        !           550: 
        !           551: Originally designed and implemented by Malcolm Beattie,
        !           552: mbeattie@sable.ox.ac.uk.
        !           553: 
        !           554: Reworked to use the Opcode module and other changes added by Tim Bunce
        !           555: E<lt>F<Tim.Bunce@ig.co.uk>E<gt>.
        !           556: 
        !           557: =cut
        !           558: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>