# HG changeset patch # User Mahlon E. Smith # Date 1235796886 0 # Node ID 5fd07a9e7e7bf70bee3673dc2bc50346125052b3 # Parent e908d309e7ec1a36b00d8d19126733291e01d2fb * Added jail header dependencies for mkmf Makefile. * Renamed header file (bsdjail -> jail) * Fixed copyright * Initial commit of primary functionality * Attach to a running jail * Create a new jail * List and instantiate existing jails * Fetch information about existing jails * Still needs... * Attach "in a block" fleshed out * Documentation and better comments * Tests (of some kind, this will be tough) * Update for not-yet-MFC'ed multiple IP jail patch in FBSD 7.2-STABLE diff -r e908d309e7ec -r 5fd07a9e7e7b ext/bsdjail.h --- a/ext/bsdjail.h Thu Dec 25 08:35:46 2008 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,79 +0,0 @@ -/* - * bsdjail.h - Header for the bsdjail extension - * - * vim: set nosta noet ts=4 sw=4: - * - * $Id$ - * - * Authors: - * * Michael Granger - * * Mahlon E. Smith - * - * Copyright (c) 2008, Michael Granger and Mahlon Smith - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of the author/s, nor the names of the project's - * contributors may be used to endorse or promote products derived from this - * software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef _BSDJAIL_H_ -#define _BSDJAIL_H_ - -#include -#include /* For rb_dbl2big() */ - -#include -#include -#include -#include -#include -#include -#include -#include - - -/* Debugging functions/macros */ -#ifdef HAVE_STDARG_PROTOTYPES -#include -#define va_init_list(a,b) va_start(a,b) -extern void rbjail_debug(const char *fmt, ...); -#else -#include -#define va_init_list(a,b) va_start(a) -extern void rbjail_debug(fmt, va_alist); -#endif - - -/* Debugging macro */ -#if DEBUG -# define debugMsg(f) rbjail_debug f -#else /* ! DEBUG */ -# define debugMsg(f) -#endif /* DEBUG */ - - -#endif /* _BSDJAIL_H_ */ - diff -r e908d309e7ec -r 5fd07a9e7e7b ext/depend --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ext/depend Sat Feb 28 04:54:46 2009 +0000 @@ -0,0 +1,1 @@ +jail.o: jail.c jail.h diff -r e908d309e7ec -r 5fd07a9e7e7b ext/extconf.rb --- a/ext/extconf.rb Thu Dec 25 08:35:46 2008 +0000 +++ b/ext/extconf.rb Sat Feb 28 04:54:46 2009 +0000 @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# vim: set nosta noet ts=4 sw=4: require 'mkmf' require 'fileutils' @@ -21,16 +22,19 @@ exit( 1 ) end - -dir_config( 'bsdjail' ) +dir_config( 'bsd/jail' ) -have_header( 'stdio.h' ) or fail "Can't find the stdio.h header." -have_header( 'sys/param.h' ) or fail "Can't find the sys/param.h header." -have_header( 'sys/jail.h' ) or fail "Can't find the sys/jail.h header." -have_header( 'sys/types.h' ) or fail "Can't find the sys/types.h header." -have_header( 'unistd.h' ) or fail "Can't find the unistd.h header." +have_header( 'stdio.h' ) or fail "Can't find the stdio.h header." +have_header( 'sys/param.h' ) or fail "Can't find the sys/param.h header." +have_header( 'sys/jail.h' ) or fail "Can't find the sys/jail.h header." +have_header( 'sys/types.h' ) or fail "Can't find the sys/types.h header." +have_header( 'sys/sysctl.h' ) or fail "Can't find the sys/sysctl.h header." +have_header( 'arpa/inet.h' ) or fail "Can't find the arpa/inet.h header." +have_header( 'errno.h' ) or fail "Can't find the arpa/inet.h header." +have_header( 'unistd.h' ) or fail "Can't find the unistd.h header." -have_func( "jail_attach" ) or fail "Can't find jail_attach in the stdlib." +have_func( "jail" ) or fail "Can't find jail() in the stdlib." +have_func( "jail_attach" ) or fail "Can't find jail_attach() in the stdlib." -create_makefile( 'bsdjail' ) +create_makefile( 'bsd/jail' ) diff -r e908d309e7ec -r 5fd07a9e7e7b ext/jail.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ext/jail.c Sat Feb 28 04:54:46 2009 +0000 @@ -0,0 +1,439 @@ +/* + * jail.c - Ruby jparallel + * + * vim: set nosta noet ts=4 sw=4: + * + * $Id$ + * + * Authors: + * * Michael Granger + * * Mahlon E. Smith + * + * Copyright (c) 2008, Michael Granger and Mahlon E. Smith + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of the author/s, nor the names of the project's + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + + +#include "jail.h" + +VALUE rbjail_mBSD; +VALUE rbjail_cBSDJail; +VALUE rbjail_cIPAddr; + + +/* + * Debug logging function + */ +void +#ifdef HAVE_STDARG_PROTOTYPES +rbjail_debug(const char *fmt, ...) +#else +rbjail_debug( const char *fmt, va_dcl ) +#endif +{ + char buf[BUFSIZ], buf2[BUFSIZ]; + va_list args; + + if ( !RTEST(ruby_debug) ) return; + + snprintf( buf, BUFSIZ, "Debug>>> %s", fmt ); + + va_init_list( args, fmt ); + vsnprintf( buf2, BUFSIZ, buf, args ); + fputs( buf2, stderr ); + fputs( "\n", stderr ); + fflush( stderr ); + va_end( args ); +} + + +/* + * Object validity checker. Returns the data pointer. + */ +static struct xprison * +rbjail_check_jail( VALUE self ) { + debugMsg(( "Checking a BSD::Jail object (%d).", self )); + Check_Type( self, T_DATA ); + + if ( !rb_obj_is_kind_of(self, rbjail_cBSDJail) ) { + rb_raise( rb_eTypeError, "wrong argument type %s (expected BSD::Jail)", + rb_class2name(CLASS_OF( self )) ); + } + + return DATA_PTR( self ); +} + + +/* + * Fetch the data pointer and check it for sanity. + */ +static struct xprison * +rbjail_get_jailptr( VALUE self ) { + struct xprison *ptr = rbjail_check_jail( self ); + + debugMsg(( "Fetching a Jail (%p).", ptr )); + if ( !ptr ) + rb_raise( rb_eRuntimeError, "uninitialized Jail" ); + + return ptr; +} + + +/* + * Copy memory from the given 'xp' to a ruby managed object. + */ +static VALUE +rbjail_alloc( VALUE class, struct xprison *xp ) +{ + struct xprison *rbjail_xp = ALLOC( struct xprison ); + VALUE rbjail = rb_funcall( class, rb_intern("allocate"), 0 ); + + // replace the null pointer obj with an xprison ptr. + // + memcpy( rbjail_xp, xp, sizeof( struct xprison ) ); + DATA_PTR( rbjail ) = rbjail_xp; + + return rbjail; +} + + +/* + * + */ +static VALUE +rbjail_get_ip( VALUE self ) +{ + struct xprison *xp = rbjail_get_jailptr( self ); + struct in_addr in; + char *ip; + + in.s_addr = ntohl( xp->pr_ip ); + ip = inet_ntoa(in); + + return rb_funcall( rbjail_cIPAddr, rb_intern("new"), 1, rb_str_new2(ip) ); +} + + +/* + * + */ +static VALUE +rbjail_get_jid( VALUE self ) +{ + struct xprison *xp = rbjail_get_jailptr( self ); + return INT2FIX( xp->pr_id ); +} + + +/* + * + */ +static VALUE +rbjail_get_host( VALUE self ) +{ + struct xprison *xp = rbjail_get_jailptr( self ); + return rb_str_new2( xp->pr_host ); +} + + +/* + * + */ +static VALUE +rbjail_get_path( VALUE self ) +{ + struct xprison *xp = rbjail_get_jailptr( self ); + return rb_str_new2( xp->pr_path ); +} + + +/* + * GC Free function + */ +static void +rbjail_gc_free( struct xprison *ptr ) { + if ( ptr ) { + xfree( ptr ); + } + + else { + debugMsg(( "Not freeing an uninitialized jail" )); + } +} + + +/* + * Allocate a new ruby object with a NULL pointer. + */ +static VALUE +rbjail_s_alloc( VALUE class ) +{ + return Data_Wrap_Struct( class, NULL, rbjail_gc_free, NULL ); +} + + +/* + * Create a new jail object. + * Returns the +id+ of the newly created jail. + */ +static VALUE +rbjail_jail( int argc, VALUE *argv, VALUE self ) { + struct jail j; + struct in_addr in; + VALUE ip, path, host, securelevel; + int id; + + rb_scan_args( argc, argv, "31", &ip, &path, &host, &securelevel ); + + if ( inet_aton( RSTRING_PTR( rb_obj_as_string(ip) ), &in ) == 0 ) + rb_raise( rb_eArgError, "Could not make sense of ip number: %s", ip ); + + SafeStringValue(path); + SafeStringValue(host); + + j.version = 0; + j.path = RSTRING_PTR( path ); + j.hostname = RSTRING_PTR( host ); + j.ip_number = ntohl( in.s_addr ); + id = jail(&j); + + if ( id == -1 ) rb_sys_fail( "jail" ); + if ( chdir("/") == -1 ) rb_sys_fail( "chdir" ); + + debugMsg(( "New jail created with id: %d\n", id )); + return INT2FIX( id ); +} + + +/* + * Iterate over the currently instantiated jails, returning a jail + * object that matches the given string/ip/JID -- or nil if none do. + * Without an argument, return an array of all JIDs. + */ +static VALUE +rbjail_find( int argc, VALUE *argv, VALUE self ) +{ + struct xprison *sxp, *xp; + struct in_addr in; + size_t i, len; + + VALUE arg, rbjail; + VALUE jails = rb_ary_new(); + int jid = 0, compare = 0; + char *str = ""; + + rb_scan_args( argc, argv, "01", &arg ); + + // An argument was passed, so let's figure out what it was + // and try to compare it to the current jails. + // + if ( argc == 1 ) { + switch ( TYPE(arg) ) { + + // find by JID + // + case T_FIXNUM: + jid = FIX2INT( arg ); + break; + + // find by IP/hostname + // + case T_OBJECT: + case T_DATA: + case T_STRING: + str = RSTRING_PTR( rb_obj_as_string(arg) ); + compare = 1; + break; + + default: + rb_raise( rb_eTypeError, "invalid argument to find(): %s", + RSTRING_PTR(rb_inspect(arg)) ); + } + } + + // Get the size of the xprison and allocate memory to it. + // + if ( sysctlbyname("security.jail.list", NULL, &len, NULL, 0) == -1 ) + rb_sys_fail("sysctlbyname(): security.jail.list"); + if ( len <= 0 ) { + rb_sys_fail("sysctlbyname(): unable to determine xprison size"); + return Qnil; + } + + sxp = xp = malloc( len ); + if ( sxp == NULL ) { + rb_sys_fail("sysctlbyname(): unable to allocate memory"); + return Qnil; + } + + // Get and sanity check the current prison list + // + if ( sysctlbyname("security.jail.list", xp, &len, NULL, 0) == -1 ) { + if ( errno == ENOMEM ) free( sxp ); + rb_sys_fail("sysctlbyname(): out of memory"); + return Qnil; + } + if ( len < sizeof(*xp) || len % sizeof(*xp) || xp->pr_version != XPRISON_VERSION ) + rb_fatal("Kernel and userland out of sync"); + + // No arguments to find() -- return an array of all JIDs + // + if ( argc == 0 ) { + for ( i = 0; i < len / sizeof(*xp); i++ ) { + rb_ary_push( jails, rbjail_alloc( self, xp ) ); + xp++; + } + + free( sxp ); + return jails; + } + + // Argument passed to find(): walk the jail list, comparing the arg + // with each current jail. Return first match. + // + for ( i = 0; i < len / sizeof(*xp); i++ ) { + in.s_addr = ntohl(xp->pr_ip); + if (( compare == 0 && xp->pr_id == jid ) || + ( compare == 1 && + (( strcmp( str, inet_ntoa(in) ) == 0 ) || + ( strcmp( str, xp->pr_host ) == 0 )) + )) { + + debugMsg(( "Located jail: %d", xp->pr_id )); + rbjail = rbjail_alloc( self, xp ); + + free( sxp ); + return rbjail; + } + else { + + xp++; + } + } + + free( sxp ); + return Qnil; +} + + +static void +rbjail_do_jail_attach( int jid ) +{ + if ( jail_attach(jid) == -1 ) + rb_sys_fail( "jail_attach" ); +} + + +/* Mostly ripped off from Ruby's process.c */ +static VALUE +rbjail_attach_block( int jid ) +{ + int pid; + + rb_secure(2); + + fflush(stdout); + fflush(stderr); + + switch ( pid = fork() ) { + case 0: + rb_thread_atfork(); + if ( rb_block_given_p() ) { + int status; + + rbjail_do_jail_attach( jid ); + rb_protect( rb_yield, Qundef, &status ); + ruby_stop( status ); + } + return Qnil; + + case -1: + rb_sys_fail( "fork(2)" ); + return Qnil; + + default: + return INT2FIX( pid ); + } +} + +static VALUE +rbjail_attach( int argc, VALUE *argv, VALUE self ) +{ + VALUE jidnum, rval; + int jid; + + rb_scan_args( argc, argv, "1", &jidnum ); + jid = NUM2INT( jidnum ); + + if ( rb_block_given_p() ) { + rval = rbjail_attach_block( jid ); + } + + else { + rbjail_do_jail_attach( jid ); + rval = Qtrue; + } + + return rval; +} + + +/* + * Ruby initializer. + */ +void +Init_jail( void ) +{ + rbjail_mBSD = rb_define_module( "BSD" ); + rbjail_cBSDJail = rb_define_class_under( rbjail_mBSD, "Jail", rb_cObject ); + + rb_require("ipaddr"); + rbjail_cIPAddr = rb_const_get( rb_cObject, rb_intern("IPAddr") ); + + rb_define_alloc_func( rbjail_cBSDJail, rbjail_s_alloc ); + + // Make the 'new' method private. + rb_funcall( rbjail_cBSDJail, rb_intern("private_class_method"), 1, ID2SYM(rb_intern("new")) ); + + // main utility functions + // + rb_define_singleton_method( rbjail_cBSDJail, "jail", rbjail_jail, -1 ); + rb_define_singleton_method( rbjail_cBSDJail, "find", rbjail_find, -1 ); + rb_define_method( rbjail_cBSDJail, "attach", rbjail_attach, -1 ); + // rb_define_alias( rbjail_cBSDJail, "list", "find" ); + + // accessor functions + // + rb_define_method( rbjail_cBSDJail, "jid", rbjail_get_jid, 0 ); + rb_define_method( rbjail_cBSDJail, "ip", rbjail_get_ip, 0 ); + rb_define_method( rbjail_cBSDJail, "host", rbjail_get_host, 0 ); + rb_define_method( rbjail_cBSDJail, "path", rbjail_get_path, 0 ); +} + + diff -r e908d309e7ec -r 5fd07a9e7e7b ext/jail.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ext/jail.h Sat Feb 28 04:54:46 2009 +0000 @@ -0,0 +1,78 @@ +/* + * jail.h - Header for the ruby jail extension + * + * vim: set nosta noet ts=4 sw=4: + * + * $Id$ + * + * Authors: + * * Michael Granger + * * Mahlon E. Smith + * + * Copyright (c) 2008, Michael Granger and Mahlon Smith + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of the author/s, nor the names of the project's + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _BSDJAIL_H_ +#define _BSDJAIL_H_ + +#include "ruby.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Debugging functions/macros */ +#ifdef HAVE_STDARG_PROTOTYPES +#include +#define va_init_list(a,b) va_start(a,b) +extern void rbjail_debug(const char *fmt, ...); +#else +#include +#define va_init_list(a,b) va_start(a) +extern void rbjail_debug(fmt, va_alist); +#endif + + +/* Debugging macro */ +#if DEBUG +# define debugMsg(f) rbjail_debug f +#else /* ! DEBUG */ +# define debugMsg(f) +#endif /* DEBUG */ + + +#endif /* _BSDJAIL_H_ */ +