Blame lib/Taint/Runtime.pm

Packit dfe23c
package Taint::Runtime;
Packit dfe23c
Packit dfe23c
=head1 NAME
Packit dfe23c
Packit dfe23c
Taint::Runtime - Runtime enable taint checking
Packit dfe23c
Packit dfe23c
=cut
Packit dfe23c
Packit dfe23c
use strict;
Packit dfe23c
use Exporter;
Packit dfe23c
use vars qw(@ISA %EXPORT_TAGS @EXPORT_OK @EXPORT $VERSION $TAINT);
Packit dfe23c
use XSLoader;
Packit dfe23c
Packit dfe23c
@ISA = qw(Exporter);
Packit dfe23c
%EXPORT_TAGS = (
Packit dfe23c
                'all' => [qw(
Packit dfe23c
                             taint_start
Packit dfe23c
                             taint_stop
Packit dfe23c
                             taint_enabled
Packit dfe23c
                             tainted
Packit dfe23c
                             is_tainted
Packit dfe23c
                             taint
Packit dfe23c
                             untaint
Packit dfe23c
                             taint_env
Packit dfe23c
                             taint_deeply
Packit dfe23c
                             $TAINT
Packit dfe23c
                             ) ],
Packit dfe23c
                );
Packit dfe23c
@EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
Packit dfe23c
@EXPORT = qw(taint_start taint_stop);
Packit dfe23c
Packit dfe23c
$VERSION = '0.03';
Packit dfe23c
XSLoader::load('Taint::Runtime', $VERSION);
Packit dfe23c
Packit dfe23c
###----------------------------------------------------------------###
Packit dfe23c
Packit dfe23c
tie $TAINT, __PACKAGE__;
Packit dfe23c
Packit dfe23c
sub TIESCALAR {
Packit dfe23c
  return bless [], __PACKAGE__;
Packit dfe23c
}
Packit dfe23c
Packit dfe23c
sub FETCH {
Packit dfe23c
  _taint_enabled() ? 1 : 0;
Packit dfe23c
}
Packit dfe23c
Packit dfe23c
sub STORE {
Packit dfe23c
  my ($self, $val) = @_;
Packit dfe23c
  $val = 0 if ! $val || $val eq 'disable';
Packit dfe23c
  $val ? _taint_start() : _taint_stop();
Packit dfe23c
}
Packit dfe23c
Packit dfe23c
###----------------------------------------------------------------###
Packit dfe23c
Packit dfe23c
### allow for special enable/disable keywords
Packit dfe23c
sub import {
Packit dfe23c
  my $change;
Packit dfe23c
  for my $i (reverse 1 .. $#_) {
Packit dfe23c
    next if $_[$i] !~ /^(dis|en)able$/;
Packit dfe23c
    my $val = $1 eq 'dis' ? 0 : 1;
Packit dfe23c
    splice @_, $i, 1, ();
Packit dfe23c
    die 'Cannot both enable and disable $TAINT during import' if defined $change && $change != $val;
Packit dfe23c
    $TAINT = $val;
Packit dfe23c
  }
Packit dfe23c
  __PACKAGE__->export_to_level(1, @_);
Packit dfe23c
}
Packit dfe23c
Packit dfe23c
###----------------------------------------------------------------###
Packit dfe23c
Packit dfe23c
sub taint_start { _taint_start(); }
Packit dfe23c
Packit dfe23c
sub taint_stop  { _taint_stop() }
Packit dfe23c
Packit dfe23c
sub taint_enabled { _taint_enabled() }
Packit dfe23c
Packit dfe23c
sub tainted { _tainted() }
Packit dfe23c
Packit dfe23c
sub is_tainted { return if ! defined $_[0]; ! eval { eval '#'.substr($_[0], 0, 0); 1 } }
Packit dfe23c
Packit dfe23c
# slower on tainted and undef
Packit dfe23c
# modified version from standard lib/perl/5.8.5/tainted.pl
Packit dfe23c
sub is_tainted2 { local $^W = 0; local $@; eval { kill 0 * $_[0] }; $@ =~ /^Insecure/ }
Packit dfe23c
Packit dfe23c
sub taint {
Packit dfe23c
  my $str = shift;
Packit dfe23c
  my $ref = ref($str) ? $str : \$str;
Packit dfe23c
  $$ref = '' if ! defined $$ref;
Packit dfe23c
  $$ref .= tainted();
Packit dfe23c
  return ref($str) ? 1 : $str;
Packit dfe23c
}
Packit dfe23c
Packit dfe23c
sub untaint {
Packit dfe23c
  my $str = shift;
Packit dfe23c
  my $ref = ref($str) ? $str : \$str;
Packit dfe23c
  if (! defined $$ref) {
Packit dfe23c
    $$ref = undef;
Packit dfe23c
  } else {
Packit dfe23c
    $$ref = ($$ref =~ /(.*)/s) ? $1 : do { require Carp; Carp::confess("Couldn't find data to untaint") };
Packit dfe23c
  }
Packit dfe23c
  return ref($str) ? 1 : $str;
Packit dfe23c
}
Packit dfe23c
Packit dfe23c
###----------------------------------------------------------------###
Packit dfe23c
Packit dfe23c
sub taint_env {
Packit dfe23c
  taint_deeply(\%ENV);
Packit dfe23c
}
Packit dfe23c
Packit dfe23c
sub taint_deeply {
Packit dfe23c
  my ($ref, $seen) = @_;
Packit dfe23c
Packit dfe23c
  return if ! defined $ref; # can undefined be tainted ?
Packit dfe23c
Packit dfe23c
  if (! ref $ref) {
Packit dfe23c
    taint \$_[0]; # better be modifyable
Packit dfe23c
    return;
Packit dfe23c
Packit dfe23c
  } elsif (UNIVERSAL::isa($ref, 'SCALAR')) {
Packit dfe23c
    taint $ref;
Packit dfe23c
    return;
Packit dfe23c
  }
Packit dfe23c
Packit dfe23c
  ### avoid circular descent
Packit dfe23c
  $seen ||= {};
Packit dfe23c
  return if $seen->{$ref};
Packit dfe23c
  $seen->{$ref} = 1;
Packit dfe23c
Packit dfe23c
  if (UNIVERSAL::isa($ref, 'ARRAY')) {
Packit dfe23c
    taint_deeply($_, $seen) foreach @$ref;
Packit dfe23c
Packit dfe23c
  } elsif (UNIVERSAL::isa($ref, 'HASH')) {
Packit dfe23c
    while (my ($key, $val) = each %$ref) {
Packit dfe23c
      taint_deeply($key);
Packit dfe23c
      taint_deeply($val, $seen);
Packit dfe23c
      $ref->{$key} = $val;
Packit dfe23c
    }
Packit dfe23c
  } else {
Packit dfe23c
    # not really sure if or what to do for GLOBS or CODE refs
Packit dfe23c
  }
Packit dfe23c
}
Packit dfe23c
Packit dfe23c
###----------------------------------------------------------------###
Packit dfe23c
Packit dfe23c
1;
Packit dfe23c
Packit dfe23c
__END__
Packit dfe23c
Packit dfe23c
=head1 SYNOPSIS
Packit dfe23c
Packit dfe23c
  ### sample "enable" usage
Packit dfe23c
Packit dfe23c
  #!/usr/bin/perl -w
Packit dfe23c
  use Taint::Runtime qw(enable taint_env);
Packit dfe23c
  taint_env();
Packit dfe23c
  # having the keyword enable in the import list starts taint
Packit dfe23c
Packit dfe23c
Packit dfe23c
  ### sample $TAINT usage
Packit dfe23c
Packit dfe23c
  #!/usr/bin/perl -w
Packit dfe23c
  use Taint::Runtime qw($TAINT taint_env);
Packit dfe23c
  $TAINT = 1;
Packit dfe23c
  taint_env();
Packit dfe23c
Packit dfe23c
  # taint is now enabled
Packit dfe23c
Packit dfe23c
  if (1) {
Packit dfe23c
    local $TAINT = 0;
Packit dfe23c
Packit dfe23c
    # do something we trust
Packit dfe23c
  }
Packit dfe23c
Packit dfe23c
  # back to an untrustwory area
Packit dfe23c
Packit dfe23c
Packit dfe23c
Packit dfe23c
  ### sample functional usage
Packit dfe23c
Packit dfe23c
  #!/usr/bin/perl -w
Packit dfe23c
  use strict;
Packit dfe23c
  use Taint::Runtime qw(taint_start is_tainted taint_env
Packit dfe23c
                        taint untaint
Packit dfe23c
                        taint_enabled);
Packit dfe23c
Packit dfe23c
  ### other operations here
Packit dfe23c
Packit dfe23c
  taint_start(); # taint should become active
Packit dfe23c
  taint_env(); # %ENV was previously untainted
Packit dfe23c
Packit dfe23c
  print taint_enabled() ? "enabled\n" : "not enabled\n";
Packit dfe23c
Packit dfe23c
  my $var = taint("some string");
Packit dfe23c
Packit dfe23c
  print is_tainted($var) ? "tainted\n" : "not tainted\n";
Packit dfe23c
Packit dfe23c
  $var = untaint($var);
Packit dfe23c
  # OR
Packit dfe23c
  untaint \$var;
Packit dfe23c
Packit dfe23c
  print is_tainted($var) ? "tainted\n" : "not tainted\n";
Packit dfe23c
Packit dfe23c
Packit dfe23c
Packit dfe23c
=head1 DESCRIPTION
Packit dfe23c
Packit dfe23c
First - you probably shouldn't use this module to control taint.
Packit dfe23c
You should probably use the -T switch on the commandline instead.
Packit dfe23c
There are a somewhat limited number of legitimate use cases where
Packit dfe23c
you should use this module instead of the -T switch.  Unless you
Packit dfe23c
have a specific and good reason for not using the -T option, you
Packit dfe23c
should use the -T option.
Packit dfe23c
Packit dfe23c
Taint is a good thing.  However, few people (that I work with or talk
Packit dfe23c
to or discuss items with) use taint even though they should.  The goal of
Packit dfe23c
this module isn't to use taint less, but to actually encourage its use
Packit dfe23c
more.  This module aims to make using taint as painless as possible (This
Packit dfe23c
can be an argument against it - often implementation of security implies
Packit dfe23c
pain - so taking away pain might lessen security - sort of).
Packit dfe23c
Packit dfe23c
In general - the more secure your script needs to be - the earlier
Packit dfe23c
on in your program that tainting should be enabled.  For most setuid scripts,
Packit dfe23c
you should enable taint by using the -T switch.  Without doing so you allow
Packit dfe23c
for a non-root user to override @INC which allows for them to put their
Packit dfe23c
own module in the place of trusted modules.  This is bad.  This is very bad.
Packit dfe23c
Use the -T switch.
Packit dfe23c
Packit dfe23c
There are some common places where this module may be useful, and where
Packit dfe23c
most people don't use it.  One such place is in a web server.  The -T switch
Packit dfe23c
removes PERL5LIB and PERLLIB and '.' from @INC (or remove them before
Packit dfe23c
they can be added).  This makes sense under setuid.  The use of the -T switch
Packit dfe23c
in a CGI environment may cause a bit of a headache.  For new development,
Packit dfe23c
CGI scripts it may be possible to use the -T switch and for mod_perl environments
Packit dfe23c
there is the PerlTaint variable.  Both of these methods will enable taint
Packit dfe23c
and from that point on development should be done with taint.
Packit dfe23c
Packit dfe23c
However, many (possibly most) perl web server implentations add their
Packit dfe23c
own paths to the PERL5LIB.  All CGI's and mod_perl scripts can then have access.
Packit dfe23c
Using the -T switch throws a wrench into the works as suddenly PERL5LIB
Packit dfe23c
disappears (mod_perl can easily have the extra directories added again
Packit dfe23c
using <perl>push @INC, '/our/lib/dir';</perl>).  The company I work for
Packit dfe23c
has 200 plus user visible scripts mixed with some mod_perl.  Currently
Packit dfe23c
none of the scripts use taint.  We would like for them all to, but it
Packit dfe23c
is not feasible to make the change all at once.  Taint::Runtime allows for moving legacy
Packit dfe23c
scripts over one at a time.
Packit dfe23c
Packit dfe23c
Again, if you are using setuid - don't use this script.
Packit dfe23c
Packit dfe23c
If you are not using setuid and have reasons not to use the -T and are
Packit dfe23c
using this module, make sure that taint is enabled before processing
Packit dfe23c
any user data.  Also remember that BECAUSE THE -T SWITCH WAS NOT USED
Packit dfe23c
%ENV IS INITIALLY NOT MARKED AS TAINTED.  Call taint_env() to mark
Packit dfe23c
it as tainted (especially important in CGI scripts which all read from
Packit dfe23c
$ENV{'QUERY_STRING'}).
Packit dfe23c
Packit dfe23c
If you are not using the -T switch, you most likely should use the
Packit dfe23c
following at the very top of your script:
Packit dfe23c
Packit dfe23c
  #!/usr/bin/perl -w
Packit dfe23c
Packit dfe23c
  use strict;
Packit dfe23c
  use Taint::Runtime qw(enable taint_env);
Packit dfe23c
  taint_env();
Packit dfe23c
Packit dfe23c
Though this module allows for you to turn taint off - you probably shouldn't.
Packit dfe23c
This module is more for you to turn taint on - and once it is on it probably
Packit dfe23c
ought to stay on.
Packit dfe23c
Packit dfe23c
=head1 NON-EXPORTABLE XS FUNCTIONS
Packit dfe23c
Packit dfe23c
The following very basic functions provide the base functionality.
Packit dfe23c
Packit dfe23c
=over 4
Packit dfe23c
Packit dfe23c
=item _taint_start()
Packit dfe23c
Packit dfe23c
Sets PL_tainting
Packit dfe23c
Packit dfe23c
=item _taint_stop()
Packit dfe23c
Packit dfe23c
Sets PL_tainting
Packit dfe23c
Packit dfe23c
=item _taint_enabled()
Packit dfe23c
Packit dfe23c
View of PL_tainting
Packit dfe23c
Packit dfe23c
=item _tainted()
Packit dfe23c
Packit dfe23c
Returns a zero length tainted string.
Packit dfe23c
Packit dfe23c
=back
Packit dfe23c
Packit dfe23c
=head1 $TAINT VARIABLE
Packit dfe23c
Packit dfe23c
The variable $TAINT is tied to the current state of taint.
Packit dfe23c
If $TAINT is set to 0 taint mode is off.  When it is set to
Packit dfe23c
1 taint mode is enabled.
Packit dfe23c
Packit dfe23c
  if (1) {
Packit dfe23c
    local $TAINT = 1;
Packit dfe23c
Packit dfe23c
    # taint is enabled
Packit dfe23c
  }
Packit dfe23c
Packit dfe23c
=head1 EXPORT FUNCTIONS
Packit dfe23c
Packit dfe23c
=over 4
Packit dfe23c
Packit dfe23c
=item enable/disable
Packit dfe23c
Packit dfe23c
Not really functions.  If these keywords are in
Packit dfe23c
the import list, taint will be either enabled
Packit dfe23c
or disabled.
Packit dfe23c
Packit dfe23c
=item taint_start
Packit dfe23c
Packit dfe23c
Start taint mode.  $TAINT will equal 1.
Packit dfe23c
Packit dfe23c
=item taint_stop
Packit dfe23c
Packit dfe23c
Stop taint mode.  $TAINT will equal 0.
Packit dfe23c
Packit dfe23c
=item taint_env
Packit dfe23c
Packit dfe23c
Convenience function that taints the keys and values of %ENV.  If
Packit dfe23c
the -T switch was not used - you most likely should call
Packit dfe23c
this as soon as taint mode is enabled.
Packit dfe23c
Packit dfe23c
=item taint
Packit dfe23c
Packit dfe23c
Taints the passed in variable.  Only works on writeable scalar values.
Packit dfe23c
If a scalar ref is passed in - it is modified.  If a scalar is passed in
Packit dfe23c
(non ref) it is copied, modified and returned.  If a value was undefined,
Packit dfe23c
it becomes a zero length defined and tainted string.
Packit dfe23c
Packit dfe23c
  taint(\$var_to_be_tainted);
Packit dfe23c
Packit dfe23c
  my $tainted_copy = taint($some_var);
Packit dfe23c
Packit dfe23c
For a stronger taint, see the Taint module by Dan Sulgalski which is
Packit dfe23c
capable of tainting most types of data.
Packit dfe23c
Packit dfe23c
=item untaint
Packit dfe23c
Packit dfe23c
Untaints the passed in variable.  Only works on writeable scalar values.
Packit dfe23c
If a scalar ref is passed in - it is modified.  If a scalar is passed in
Packit dfe23c
(non ref) it is copied, modified and returned.  If a value was undefined
Packit dfe23c
it becomes an untainted undefined value.
Packit dfe23c
Packit dfe23c
Note:  Just because the variable is untainted, doesn't mean that it
Packit dfe23c
is safe.  You really should use CGI::Ex::Validate, or Data::FormValidator
Packit dfe23c
or any of the Untaint:: modules.  If you are doing your own validation, and
Packit dfe23c
once you have put the user data through very strict checks, then you
Packit dfe23c
can use untaint.
Packit dfe23c
Packit dfe23c
  if ($var_to_be_untainted =~ /^[\w\.\-]{0,100}$/) {
Packit dfe23c
    untaint(\$var_to_be_untainted);
Packit dfe23c
  }
Packit dfe23c
Packit dfe23c
  my $untainted_copy = untaint($some_var);
Packit dfe23c
Packit dfe23c
=item taint_enabled
Packit dfe23c
Packit dfe23c
Boolean - Is taint on.
Packit dfe23c
Packit dfe23c
=item tainted
Packit dfe23c
Packit dfe23c
Returns a zero length tainted string.
Packit dfe23c
Packit dfe23c
=item is_tainted
Packit dfe23c
Packit dfe23c
Boolean - True if the passed value is tainted.
Packit dfe23c
Packit dfe23c
=item taint_deeply
Packit dfe23c
Packit dfe23c
Convenience function that attempts to deply recurse a
Packit dfe23c
structure and mark it as tainted.  Takes a hashref, arrayref,
Packit dfe23c
scalar ref, or scalar and recursively untaints the structure.
Packit dfe23c
Packit dfe23c
For a stronger taint, see the Taint module by Dan Sulgalski which is
Packit dfe23c
capable of tainting most types of data.
Packit dfe23c
Packit dfe23c
=back
Packit dfe23c
Packit dfe23c
=head1 TURNING TAINT ON
Packit dfe23c
Packit dfe23c
(Be sure to call taint_env() after turning taint on the first time)
Packit dfe23c
Packit dfe23c
  #!/usr/bin/perl -T
Packit dfe23c
Packit dfe23c
Packit dfe23c
  use Taint::Runtime qw(enable);
Packit dfe23c
  # this does not create a function called enable - just starts taint
Packit dfe23c
Packit dfe23c
  use Taint::Runtime qw($TAINT);
Packit dfe23c
  $TAINT = 1;
Packit dfe23c
Packit dfe23c
Packit dfe23c
  use Taint::Runtime qw(taint_start);
Packit dfe23c
  taint_start;
Packit dfe23c
Packit dfe23c
Packit dfe23c
=head1 TURNING TAINT OFF
Packit dfe23c
Packit dfe23c
  use Taint::Runtime qw(disable);
Packit dfe23c
  # this does not create a function called disable - just stops taint
Packit dfe23c
Packit dfe23c
Packit dfe23c
  use Taint::Runtime qw($TAINT);
Packit dfe23c
  $TAINT = 0;
Packit dfe23c
Packit dfe23c
Packit dfe23c
  use Taint::Runtime qw(taint_stop);
Packit dfe23c
  taint_stop;
Packit dfe23c
Packit dfe23c
Packit dfe23c
=head1 CREDITS
Packit dfe23c
Packit dfe23c
C code was provided by "hv" on perlmonks.  This module wouldn't
Packit dfe23c
really be possible without insight into the internals that "hv"
Packit dfe23c
provided.  His post with the code was shown in this node on
Packit dfe23c
perlmonks:
Packit dfe23c
Packit dfe23c
  http://perlmonks.org/?node_id=434086
Packit dfe23c
Packit dfe23c
The basic premise in that node was the following code:
Packit dfe23c
Packit dfe23c
  use Inline C => 'void _start_taint() { PL_tainting = 1; }';
Packit dfe23c
  use Inline C => 'SV* _tainted() { PL_tainted = 1; return newSVpvn("", 0); }';
Packit dfe23c
Packit dfe23c
In this module, these two lines have instead been turned into
Packit dfe23c
XS for runtime speed (and so you won't need Inline and Parse::RecDescent).
Packit dfe23c
Packit dfe23c
Note: even though "hv" provided the base code example, that doesn't mean that he
Packit dfe23c
necessarily endorses the idea.  If there are disagreements, quirks, annoyances
Packit dfe23c
or any other negative side effects with this module - blame me - not "hv."
Packit dfe23c
Packit dfe23c
=head1 THANKS
Packit dfe23c
Packit dfe23c
Thanks to Alexey A. Kiritchun for pointing out untaint failure on multiline strings.
Packit dfe23c
Packit dfe23c
=head1 AUTHOR
Packit dfe23c
Packit dfe23c
Paul Seamons (2005)
Packit dfe23c
Packit dfe23c
C stub functions by "hv" on perlmonks.org
Packit dfe23c
Packit dfe23c
=head1 LICENSE
Packit dfe23c
Packit dfe23c
This module may be used and distributed under the same
Packit dfe23c
terms as Perl itself.
Packit dfe23c
Packit dfe23c
=cut