bsd-jail-object/Object.pm
branchperl-modules
changeset 0 315eb12b224b
equal deleted inserted replaced
-1:000000000000 0:315eb12b224b
       
     1 
       
     2 package BSD::Jail::Object;
       
     3 use strict;
       
     4 use warnings;
       
     5 use vars qw/ @ISA @EXPORT_OK /;
       
     6 use Exporter;
       
     7 
       
     8 our $VERSION = '0.02';
       
     9 @ISA         = qw/ Exporter /;
       
    10 @EXPORT_OK   = qw/ jids /;
       
    11 
       
    12 use Inline C       => 'DATA',
       
    13            NAME    => 'BSD::Jail::Object',
       
    14            VERSION => '0.02';
       
    15 
       
    16 sub new
       
    17 {
       
    18     my ( $class, $opts ) = @_;
       
    19 
       
    20     my $self = {};
       
    21     bless $self, $class;
       
    22     return $self unless $opts;
       
    23 
       
    24     if ( ref $opts eq 'HASH' ) {
       
    25 
       
    26         # create a new jail
       
    27 
       
    28         if ( $< ) {
       
    29             $@ = "jail() requires root";
       
    30             return;
       
    31         }
       
    32 
       
    33         unless ( $opts->{'path'}     &&
       
    34                  $opts->{'hostname'} &&
       
    35                  $opts->{'ip'} ) {
       
    36             $@ = "Missing arguments to create() - need 'path', 'hostname', and 'ip'";
       
    37             return;
       
    38         }
       
    39 
       
    40         my $jid = _create( $opts->{'path'}, $opts->{'hostname'}, $opts->{'ip'} )
       
    41             or return;
       
    42 
       
    43         $self->{'_data'} = [
       
    44             $jid, $opts->{'ip'}, $opts->{'hostname'}, $opts->{'path'}
       
    45         ];
       
    46     
       
    47         return $self;
       
    48     }
       
    49     else {
       
    50 
       
    51         # this object should be linked to an existing jail
       
    52         return $self->_init( $opts );
       
    53 
       
    54     }
       
    55 }
       
    56 
       
    57 sub _init
       
    58 {
       
    59     my $self = shift;
       
    60     my $key  = shift;
       
    61 
       
    62     return unless $key;
       
    63 
       
    64     my ( @data, $type );
       
    65     if ( $key =~ /^\d+$/ ) {
       
    66         $type = 'jid';
       
    67         @data = _find_jail( 0, $key );
       
    68     }
       
    69     elsif ( $key =~ /^\d+\.\d+\.\d+\.\d+$/ ) {
       
    70         $type = 'ip';
       
    71         @data = _find_jail( 1, $key );
       
    72     }
       
    73     else {
       
    74         $type = 'hostname';
       
    75         @data = _find_jail( 2, $key );
       
    76     }
       
    77 
       
    78     unless ( scalar @data ) {
       
    79         $@ = "No such jail $type: $key";
       
    80         return;
       
    81     }
       
    82 
       
    83     $self->{'_data'} = \@data;
       
    84     return $self;
       
    85 }
       
    86 
       
    87 sub jid       { shift()->{'_data'}->[0] }
       
    88 sub ip        { shift()->{'_data'}->[1] }
       
    89 sub hostname  { shift()->{'_data'}->[2] }
       
    90 sub path      { shift()->{'_data'}->[3] }
       
    91 
       
    92 sub attach
       
    93 {
       
    94     my $self = shift;
       
    95     return unless $self->jid;
       
    96 
       
    97     if ( $< ) {
       
    98         $@ = "jail_attach() requires root";
       
    99         return;
       
   100     }
       
   101 
       
   102     return _attach( $self->jid );
       
   103 }
       
   104 
       
   105 sub jids
       
   106 {
       
   107     return if ref $_[0]; # shouldn't be used as an object method
       
   108 
       
   109     my %opts = @_;
       
   110 
       
   111     my @jids = _find_jids();
       
   112     return @jids unless $opts{'instantiate'};
       
   113 
       
   114     map { $_ = __PACKAGE__->new( $_ ) } @jids;
       
   115     return @jids;
       
   116 }
       
   117 
       
   118 1;
       
   119 
       
   120 __DATA__
       
   121 
       
   122 =pod
       
   123 
       
   124 =head1 DESCRIPTION
       
   125 
       
   126 This is an object oriented wrapper around the FreeBSD jail subsystem.
       
   127 
       
   128 A 5.x or higher FreeBSD system is required.
       
   129 
       
   130 =head1 SYNOPSIS
       
   131 
       
   132 Here is an exact replica of the 'jls' utility in just a few lines of perl:
       
   133 
       
   134  use BSD::Jail::Object 'jids';
       
   135 
       
   136  print "   JID  IP Address      Hostname                      Path\n";
       
   137  printf "%6d  %-15.15s %-29.29s %.74s\n",
       
   138         $_->jid, $_->ip, $_->hostname, $_->path foreach jids( instantiate => 1 );
       
   139 
       
   140 And here's 'jexec' (actually, a jexec that lets you optionally select by
       
   141 something other than jid):
       
   142 
       
   143  my $j = BSD::Jail::Object->new( $ARGV[0] ) or die $@;
       
   144  $j->attach && chdir('/') && exec $ARGV[1] or exit;
       
   145 
       
   146 =head1 EXAMPLES
       
   147 
       
   148 =over 4
       
   149 
       
   150 =item B<Create a new jail>
       
   151 
       
   152  $options = {
       
   153      path     => '/tmp',
       
   154      ip       => '127.0.0.1',
       
   155      hostname => 'example.com'
       
   156  };
       
   157 
       
   158  $j = BSD::Jail::Object->new( $options ) or die $@;
       
   159 
       
   160 =item B<Attach to an existing jail>
       
   161 
       
   162  $j = BSD::Jail::Object->new( 'example.com' );
       
   163  $j->attach;
       
   164 
       
   165 =item B<Do something in all jails>
       
   166 
       
   167  foreach $j ( jids(instantiate => 1) ) {
       
   168 
       
   169      if ( fork ) {
       
   170          $j->attach;
       
   171 
       
   172          #
       
   173          # do something exciting
       
   174          #
       
   175 
       
   176          exit;
       
   177      }
       
   178  }
       
   179 
       
   180 =item B<Get information on a jail>
       
   181 
       
   182 (See the B<SYNOPSIS> section above)
       
   183 
       
   184 =back
       
   185 
       
   186 =head1 OBJECT METHODS
       
   187 
       
   188 =head2 new()
       
   189 
       
   190 Instantiate a new BSD::Jail::Object object, either by associating
       
   191 ourselves with an already running jail, or by creating a new one from
       
   192 scratch.
       
   193 
       
   194 To associate with an already active jail, I<new()> accepts a jid,
       
   195 hostname, or ip address.  Errors are placed into $@.
       
   196 
       
   197  # existing jail, find by jid
       
   198  $j = BSD::Jail::Object->new( 23 ) or die $@;
       
   199 
       
   200  # existing jail, find by hostname
       
   201  $j = BSD::Jail::Object->new( 'example.com' ) or die $@;
       
   202 
       
   203  # existing jail, find by ip address
       
   204  $j = BSD::Jail::Object->new( '127.0.0.1' ) or die $@;
       
   205 
       
   206 Note that if you're selecting a jail by hostname or IP, those aren't
       
   207 always unique values.  Two jails could be running with the same hostname
       
   208 or IP address - this module will always select the highest numbered jid
       
   209 in that case.  If you need to be sure you're in the 'right' jail when
       
   210 there are duplicates, select by JID.
       
   211 
       
   212 Create a new jail by passing a hash reference.  Required keys are
       
   213 'hostname', 'ip', and 'path'.  See the I<jail(8)> man page for specifics
       
   214 on these keys.
       
   215 
       
   216  # create a new jail under /tmp
       
   217  $j = BSD::Jail::Object->new({
       
   218         hostname => 'example.com',
       
   219         ip       => '127.0.0.1',
       
   220         path     => '/tmp'
       
   221  }) or die $@;
       
   222 
       
   223 =head2 jid()
       
   224 
       
   225 Get the current jail identifier.  JIDs are assigned sequentially from
       
   226 the kernel.
       
   227 
       
   228 =head2 hostname()
       
   229 
       
   230 Get the current jail hostname.
       
   231 
       
   232 =head2 path()
       
   233 
       
   234 Get the root path the jail was bound to.
       
   235 
       
   236 =head2 attach()
       
   237 
       
   238 Imprison ourselves within a jail.  Note that this generally requires
       
   239 root access, and is a one way operation.  Once the script process
       
   240 is imprisioned, there is no way to perform a jailbreak!  You'd need
       
   241 to I<fork()> if you intended to attach to more than one jail.  See
       
   242 I<EXAMPLES>.
       
   243 
       
   244 =head1 EXPORTABLE METHODS
       
   245 
       
   246 =head2 jids()
       
   247 
       
   248 Returns an array of active JIDs.  Can also return them as
       
   249 pre-instantiated objects by passing 'instantiate => 1' as an argument.
       
   250 
       
   251  my @jail_jids    = jids();
       
   252  my @jail_objects = jids( instantiate => 1 );
       
   253 
       
   254 Only exported upon request.
       
   255 
       
   256 =head1 ACKNOWLEDGEMENTS
       
   257 
       
   258 Most of the jail specific C code was based on work 
       
   259 by Mike Barcroft <mike@freebsd.org> and Poul-Henning Kamp <phk@freebsd.org>
       
   260 for the FreeBSD Project.
       
   261 
       
   262 =head1 AUTHOR
       
   263 
       
   264 Mahlon E. Smith I<mahlon@martini.nu> for Spime Solutions Group
       
   265 I<(www.spime.net)>
       
   266 
       
   267 =cut
       
   268 
       
   269 __C__
       
   270 
       
   271 #include <stdio.h>
       
   272 #include <stdlib.h>
       
   273 #include <unistd.h>
       
   274 
       
   275 #include <arpa/inet.h>
       
   276 #include <errno.h>
       
   277 #include <limits.h>
       
   278 
       
   279 #include <sys/param.h>
       
   280 #include <sys/jail.h>
       
   281 
       
   282 size_t
       
   283 sysctl_len()
       
   284 {
       
   285     size_t len;
       
   286     if ( sysctlbyname( "security.jail.list", NULL, &len, NULL, 0 ) == -1 ) return 0;
       
   287 
       
   288     return len;
       
   289 }
       
   290 
       
   291 // get jail structure from kernel
       
   292 struct xprison
       
   293 *get_xp()
       
   294 {
       
   295     struct xprison *sxp, *xp;
       
   296     size_t len;
       
   297 
       
   298     len = sysctl_len();
       
   299     if ( len <= 0 ) return NULL;
       
   300 
       
   301     sxp = xp = malloc(len);
       
   302     if ( sxp == NULL ) return NULL;
       
   303 
       
   304     // populate the xprison list
       
   305     if ( sysctlbyname( "security.jail.list", xp, &len, NULL, 0 ) == -1 ) {
       
   306         if (errno == ENOMEM) {
       
   307             free( sxp );
       
   308             return NULL;
       
   309         }
       
   310         return NULL;
       
   311     }
       
   312 
       
   313     // check if kernel and userland is in sync
       
   314     if ( len < sizeof(*xp) || len % sizeof(*xp) ||
       
   315             xp->pr_version != XPRISON_VERSION ) {
       
   316         warn("%s", "Kernel out of sync with userland");
       
   317         return NULL;
       
   318     }
       
   319 
       
   320     free( sxp );
       
   321     return xp;
       
   322 }
       
   323 
       
   324 // fetch a specific jail's information
       
   325 void
       
   326 _find_jail( int compare, char *string )
       
   327 { 
       
   328     struct xprison *xp;
       
   329     struct in_addr in;
       
   330     size_t i, len;
       
   331     Inline_Stack_Vars;
       
   332 
       
   333     Inline_Stack_Reset;
       
   334     xp  = get_xp();
       
   335     len = sysctl_len();
       
   336 
       
   337     /*
       
   338        compare == 0    jid
       
   339        compare == 1    ip address
       
   340        compare == 2    hostname
       
   341     */
       
   342 
       
   343     for (i = 0; i < len / sizeof(*xp); i++) {
       
   344         in.s_addr = ntohl(xp->pr_ip);
       
   345         if (
       
   346                 ( compare == 0 && xp->pr_id == atoi(string) )
       
   347                 ||
       
   348                 ( compare == 1 && strcmp( string, inet_ntoa(in) ) == 0 )
       
   349                 ||
       
   350                 ( compare == 2 && strcmp( string, xp->pr_host ) == 0 )
       
   351            ) {
       
   352             Inline_Stack_Push( sv_2mortal( newSViv( xp->pr_id ) ));
       
   353             Inline_Stack_Push( sv_2mortal( newSVpvf( inet_ntoa(in) ) ));
       
   354             Inline_Stack_Push( sv_2mortal( newSVpvf( xp->pr_host ) ));
       
   355             Inline_Stack_Push( sv_2mortal( newSVpvf( xp->pr_path ) ));
       
   356             break;
       
   357         }
       
   358         else {
       
   359             xp++;
       
   360         }
       
   361     }
       
   362 
       
   363     Inline_Stack_Done;
       
   364 }
       
   365 
       
   366 // return an array of all current jail ids
       
   367 void
       
   368 _find_jids()
       
   369 { 
       
   370     struct xprison *xp;
       
   371     size_t i, len;
       
   372     Inline_Stack_Vars;
       
   373 
       
   374     Inline_Stack_Reset;
       
   375     xp  = get_xp();
       
   376     len = sysctl_len();
       
   377 
       
   378     for (i = 0; i < len / sizeof(*xp); i++) {
       
   379         Inline_Stack_Push( sv_2mortal( newSViv( xp->pr_id ) ));
       
   380         xp++;
       
   381     }
       
   382 
       
   383     Inline_Stack_Done;
       
   384 }
       
   385 
       
   386 // attach to a jail
       
   387 int
       
   388 _attach( int jid )
       
   389 {
       
   390     return ( jail_attach(jid) == -1 ? 0 : 1 );
       
   391 }
       
   392 
       
   393 // create a new jail
       
   394 int
       
   395 _create( char *path, char *hostname, char *ipaddr )
       
   396 {
       
   397     struct in_addr ip;
       
   398     struct jail    j;
       
   399     int            jid;
       
   400 
       
   401     if ( inet_aton( ipaddr, &ip ) == 0 ) return 0;
       
   402     
       
   403     j.path      = path;
       
   404     j.hostname  = hostname;
       
   405     j.ip_number = ntohl( ip.s_addr );
       
   406     j.version   = 0;
       
   407 
       
   408     if ( (jid = jail( &j )) == -1 ) return 0;
       
   409 
       
   410     return jid;
       
   411 }
       
   412