224 id = jail(&j); |
241 id = jail(&j); |
225 |
242 |
226 if ( id == -1 ) rb_sys_fail( "jail" ); |
243 if ( id == -1 ) rb_sys_fail( "jail" ); |
227 if ( chdir("/") == -1 ) rb_sys_fail( "chdir" ); |
244 if ( chdir("/") == -1 ) rb_sys_fail( "chdir" ); |
228 |
245 |
|
246 if ( securelevel > -1 && securelevel < 4 ) { |
|
247 debugMsg(( "Setting securelevel to: %d", securelevel )); |
|
248 if ( sysctlbyname("kern.securelevel", NULL, 0, &securelevel, sizeof(securelevel)) ) |
|
249 rb_sys_fail( "securelevel" ); |
|
250 } |
|
251 |
229 debugMsg(( "New jail created with id: %d\n", id )); |
252 debugMsg(( "New jail created with id: %d\n", id )); |
230 return INT2FIX( id ); |
253 return INT2FIX( id ); |
231 } |
254 } |
232 |
255 |
233 |
256 |
234 /* |
257 /* |
235 * Iterate over the currently instantiated jails, returning a jail |
258 * call-seq: |
236 * object that matches the given string/ip/JID -- or nil if none do. |
259 * BSD::Jail.find_by_jid( jid ) => BSD::Jail |
237 * Without an argument, return an array of all JIDs. |
260 * |
238 */ |
261 * Iterate over the currently instantiated jails, returning a jail |
|
262 * object that matches the given +jid+, or nil if no jail is found. |
|
263 * |
|
264 */ |
|
265 static VALUE |
|
266 rbjail_find_by_jid( VALUE self, VALUE jid ) |
|
267 { |
|
268 VALUE args[0]; |
|
269 |
|
270 if ( TYPE(jid) != T_FIXNUM ) |
|
271 rb_raise( rb_eTypeError, "invalid argument to find_by_jid(): %s", |
|
272 RSTRING_PTR( rb_inspect(jid)) ); |
|
273 |
|
274 args[0] = jid; |
|
275 return rbjail_find( 1, args, self ); |
|
276 } |
|
277 |
|
278 |
|
279 /* |
|
280 * call-seq: |
|
281 * BSD::Jail.search( id ) => BSD::Jail |
|
282 * BSD::Jail.search( hostname ) => [ BSD::Jail, ... ] |
|
283 * BSD::Jail.search( IPAddr.new('127.0.0.1') ) => [ BSD::Jail, ... ] |
|
284 * BSD::Jail.search( Pathname.new('/tmp') ) => [ BSD::Jail, ... ] |
|
285 * BSD::Jail.search => [ BSD::Jail, BSD::Jail, ... ] |
|
286 * BSD::Jail.search { |obj| block } => obj |
|
287 * |
|
288 * Iterate over the currently instantiated jails, returning a jail |
|
289 * object that matches the given argument. |
|
290 * |
|
291 * If the argument is an integer, it is assumed to be a JID. |
|
292 * Otherwise, it is converted to a string, and compared against |
|
293 * IP addresses, hostnames, and paths. JIDs are unique, so there is |
|
294 * only one object that can match. Only the matching object (or nil) |
|
295 * is returned in that event. IPs, hostnames, and paths can be |
|
296 * shared between jails, so searching on those return an array populated |
|
297 * with matched jail objects -- or if there are no valid matches, an empty array. |
|
298 * |
|
299 * Without an argument, return an array of all jails as objects. |
|
300 * |
|
301 */ |
239 static VALUE |
302 static VALUE |
240 rbjail_find( int argc, VALUE *argv, VALUE self ) |
303 rbjail_find( int argc, VALUE *argv, VALUE self ) |
241 { |
304 { |
242 struct xprison *sxp, *xp; |
305 struct xprison *sxp, *xp; |
243 struct in_addr in; |
306 struct in_addr in; |
300 return Qnil; |
363 return Qnil; |
301 } |
364 } |
302 if ( len < sizeof(*xp) || len % sizeof(*xp) || xp->pr_version != XPRISON_VERSION ) |
365 if ( len < sizeof(*xp) || len % sizeof(*xp) || xp->pr_version != XPRISON_VERSION ) |
303 rb_fatal("Kernel and userland out of sync"); |
366 rb_fatal("Kernel and userland out of sync"); |
304 |
367 |
305 // No arguments to find() -- return an array of all JIDs |
368 // No arguments to find() -- yield each successive jail, |
|
369 // and return an array of all jail objects. |
306 // |
370 // |
307 if ( argc == 0 ) { |
371 if ( argc == 0 ) { |
308 for ( i = 0; i < len / sizeof(*xp); i++ ) { |
372 for ( i = 0; i < len / sizeof(*xp); i++ ) { |
|
373 rbjail = rbjail_alloc( self, xp ); |
|
374 if ( rb_block_given_p() ) rb_yield( rbjail ); |
309 rb_ary_push( jails, rbjail_alloc( self, xp ) ); |
375 rb_ary_push( jails, rbjail_alloc( self, xp ) ); |
310 xp++; |
376 xp++; |
311 } |
377 } |
312 |
378 |
313 free( sxp ); |
379 free( sxp ); |
314 return jails; |
380 return jails; |
315 } |
381 } |
316 |
382 |
317 // Argument passed to find(): walk the jail list, comparing the arg |
383 // Argument passed to find(): walk the jail list, comparing the arg |
318 // with each current jail. Return first match. |
384 // with each current jail. Return all matches. |
319 // |
385 // |
320 for ( i = 0; i < len / sizeof(*xp); i++ ) { |
386 for ( i = 0; i < len / sizeof(*xp); i++ ) { |
321 in.s_addr = ntohl(xp->pr_ip); |
387 in.s_addr = ntohl(xp->pr_ip); |
322 if (( compare == 0 && xp->pr_id == jid ) || |
388 if (( compare == 0 && xp->pr_id == jid ) || |
323 ( compare == 1 && |
389 ( compare == 1 && |
324 (( strcmp( str, inet_ntoa(in) ) == 0 ) || |
390 (( strcmp( str, inet_ntoa(in) ) == 0 ) || |
325 ( strcmp( str, xp->pr_host ) == 0 )) |
391 ( strcmp( str, xp->pr_host ) == 0 ) || |
|
392 ( strcmp( str, xp->pr_path ) == 0 )) |
326 )) { |
393 )) { |
327 |
394 |
328 debugMsg(( "Located jail: %d", xp->pr_id )); |
395 debugMsg(( "Located jail: %d", xp->pr_id )); |
329 rbjail = rbjail_alloc( self, xp ); |
396 |
330 |
397 // If the user already knows the JID, there can only be |
331 free( sxp ); |
398 // one match. Return it immediately. |
332 return rbjail; |
399 // |
|
400 if ( compare == 0 ) { |
|
401 free( sxp ); |
|
402 return rbjail_alloc( self, xp ); |
|
403 } |
|
404 |
|
405 // If searching for anything other than JID, the argument |
|
406 // isn't unique. Put matching jails into an array. |
|
407 // |
|
408 rb_ary_push( jails, rbjail_alloc( self, xp ) ); |
|
409 xp++; |
333 } |
410 } |
334 else { |
411 else { |
335 |
412 |
336 xp++; |
413 xp++; |
337 } |
414 } |
338 } |
415 } |
339 |
416 |
340 free( sxp ); |
417 free( sxp ); |
341 return Qnil; |
418 |
342 } |
419 if ( compare == 0 && rb_ary_shift( jails ) == Qnil ) |
343 |
420 return Qnil; |
344 |
421 |
345 static void |
422 return jails; |
346 rbjail_do_jail_attach( int jid ) |
423 } |
347 { |
424 |
348 if ( jail_attach(jid) == -1 ) |
425 |
349 rb_sys_fail( "jail_attach" ); |
426 /* |
350 } |
427 * call-seq: |
351 |
428 * BSD::Jail.attach( id ) => true |
352 |
429 * BSD::Jail.attach( hostname ) => true |
353 /* Mostly ripped off from Ruby's process.c */ |
430 * |
354 static VALUE |
431 * Attach to a currently running jail instance directly. |
355 rbjail_attach_block( int jid ) |
432 * Operates under the same rules as BSD::Jail#search -- you may |
356 { |
433 * specify a jail by IP Address, hostname, or jid. |
357 int pid; |
434 * |
358 |
435 * Please note that attaching your process into a jail is a one way |
359 rb_secure(2); |
436 * operation that requires root privileges. You must fork() if |
360 |
437 * your process needs to continue in the host environment. |
361 fflush(stdout); |
438 * |
362 fflush(stderr); |
439 */ |
363 |
440 static VALUE |
364 switch ( pid = fork() ) { |
441 rbjail_class_attach( VALUE self, VALUE arg ) |
365 case 0: |
442 { |
366 rb_thread_atfork(); |
443 int jid = 0; |
367 if ( rb_block_given_p() ) { |
444 VALUE jails; |
368 int status; |
445 VALUE find_args [0]; |
369 |
446 |
370 rbjail_do_jail_attach( jid ); |
447 switch ( TYPE(arg) ) { |
371 rb_protect( rb_yield, Qundef, &status ); |
448 |
372 ruby_stop( status ); |
449 // The user knows the JID already, attach directly. |
373 } |
450 // |
374 return Qnil; |
451 case T_FIXNUM: |
375 |
452 |
376 case -1: |
453 jid = FIX2INT( arg ); |
377 rb_sys_fail( "fork(2)" ); |
454 rbjail_do_attach( jid ); |
378 return Qnil; |
455 break; |
|
456 |
|
457 // Find the JID to attach to. First match wins. |
|
458 // |
|
459 case T_OBJECT: |
|
460 case T_DATA: |
|
461 case T_STRING: |
|
462 |
|
463 find_args[0] = arg; |
|
464 jails = rbjail_find( 1, find_args, self ); |
|
465 jid = FIX2INT( rbjail_get_jid( rb_ary_shift(jails) )); |
|
466 rbjail_do_attach( jid ); |
|
467 break; |
379 |
468 |
380 default: |
469 default: |
381 return INT2FIX( pid ); |
470 rb_raise( rb_eTypeError, "invalid argument to attach(): %s", |
382 } |
471 RSTRING_PTR( rb_inspect(arg)) ); |
383 } |
472 } |
384 |
473 |
385 static VALUE |
474 return Qtrue; |
386 rbjail_attach( int argc, VALUE *argv, VALUE self ) |
475 } |
387 { |
476 |
388 VALUE jidnum, rval; |
477 |
389 int jid; |
478 /* -------------------------------------------------------------- |
390 |
479 * Instance methods |
391 rb_scan_args( argc, argv, "1", &jidnum ); |
480 * -------------------------------------------------------------- */ |
392 jid = NUM2INT( jidnum ); |
481 |
|
482 /* |
|
483 * call-seq: |
|
484 * BSD::Jail <=> BSD::Jail |
|
485 * |
|
486 * Interface for Comparable. |
|
487 * |
|
488 */ |
|
489 static VALUE |
|
490 rbjail_compare( VALUE self, VALUE other ) |
|
491 { |
|
492 debugMsg(("self id: %s, other id: %s", |
|
493 RSTRING_PTR(rb_inspect( self )), |
|
494 RSTRING_PTR(rb_inspect( other )))); |
|
495 |
|
496 return rb_funcall( |
|
497 rbjail_get_jid( self ), |
|
498 rb_intern("<=>"), |
|
499 1, |
|
500 rbjail_get_jid( other ) |
|
501 ); |
|
502 } |
|
503 |
|
504 |
|
505 /* |
|
506 * Fetch the configured IP address for the jail. |
|
507 * Returns an IPAddr object. |
|
508 * |
|
509 */ |
|
510 static VALUE |
|
511 rbjail_get_ip( VALUE self ) |
|
512 { |
|
513 struct xprison *xp = rbjail_get_jailptr( self ); |
|
514 struct in_addr in; |
|
515 VALUE args [0]; |
|
516 |
|
517 in.s_addr = ntohl( xp->pr_ip ); |
|
518 args[0] = rb_str_new2( inet_ntoa(in) ); |
|
519 |
|
520 return rb_class_new_instance( 1, args, rbjail_cIPAddr ); |
|
521 } |
|
522 |
|
523 |
|
524 /* |
|
525 * Fetch the assigned JID for the jail. |
|
526 * |
|
527 */ |
|
528 static VALUE |
|
529 rbjail_get_jid( VALUE self ) |
|
530 { |
|
531 struct xprison *xp = rbjail_get_jailptr( self ); |
|
532 return INT2FIX( xp->pr_id ); |
|
533 } |
|
534 |
|
535 |
|
536 /* |
|
537 * Fetch the configured hostname for the jail as a string. |
|
538 * |
|
539 */ |
|
540 static VALUE |
|
541 rbjail_get_host( VALUE self ) |
|
542 { |
|
543 struct xprison *xp = rbjail_get_jailptr( self ); |
|
544 return rb_str_new2( xp->pr_host ); |
|
545 } |
|
546 |
|
547 |
|
548 /* |
|
549 * Fetch the configured path for the jail. |
|
550 * Returns a Pathname object. |
|
551 * |
|
552 */ |
|
553 static VALUE |
|
554 rbjail_get_path( VALUE self ) |
|
555 { |
|
556 struct xprison *xp = rbjail_get_jailptr( self ); |
|
557 VALUE args[0]; |
|
558 |
|
559 args[0] = rb_str_new2( xp->pr_path ); |
|
560 |
|
561 return rb_class_new_instance( 1, args, rbjail_cPathname ); |
|
562 } |
|
563 |
|
564 |
|
565 /* |
|
566 * Return a human readable version of the object. |
|
567 */ |
|
568 static VALUE |
|
569 rbjail_inspect( VALUE self ) |
|
570 { |
|
571 char inspect_str[BUFSIZ]; |
|
572 |
|
573 sprintf( inspect_str, "#<%s:0x%07x jid:%d (%s)>", |
|
574 rb_obj_classname( self ), |
|
575 NUM2UINT(rb_obj_id( self )) * 2, |
|
576 FIX2INT(rbjail_get_jid( self )), |
|
577 RSTRING_PTR(rbjail_get_host( self )) |
|
578 ); |
|
579 |
|
580 return rb_str_new2( inspect_str ); |
|
581 } |
|
582 |
|
583 |
|
584 /* |
|
585 * call-seq: |
|
586 * jail.attach |
|
587 * jail.attach do ... end => child pid |
|
588 * |
|
589 * Attach to the jail. |
|
590 * |
|
591 * Please note that attaching your process into a jail is a one way |
|
592 * operation that requires root privileges. You must fork() if |
|
593 * your process needs to continue in the host environment. |
|
594 * |
|
595 * In the block form, a fork() is performed automatically. |
|
596 * |
|
597 */ |
|
598 static VALUE |
|
599 rbjail_instance_attach( VALUE self ) |
|
600 { |
|
601 int jid = FIX2INT( rbjail_get_jid( self ) ); |
393 |
602 |
394 if ( rb_block_given_p() ) { |
603 if ( rb_block_given_p() ) { |
395 rval = rbjail_attach_block( jid ); |
604 return rbjail_attach_block( jid ); |
396 } |
605 } |
397 |
606 |
398 else { |
607 else { |
399 rbjail_do_jail_attach( jid ); |
608 rbjail_do_attach( jid ); |
400 rval = Qtrue; |
609 } |
401 } |
610 |
402 |
611 return Qtrue; |
403 return rval; |
612 } |
404 } |
613 |
405 |
614 |
406 |
615 /* -------------------------------------------------------------- |
407 /* |
616 * Initalizer |
408 * Ruby initializer. |
617 * -------------------------------------------------------------- */ |
|
618 |
|
619 /* |
|
620 * BSD::Jail |
|
621 * |
|
622 * Ruby bindings for the FreeBSD jail(2) subsystem. |
|
623 * |
|
624 * Example usage: |
|
625 * |
|
626 * require 'bsd/jail' |
|
627 * |
|
628 * # create a new jail |
|
629 * jid = BSD::Jail.create( '127.0.0.1', '/tmp', 'testjail' ) |
|
630 * |
|
631 * # find existing jail(s) |
|
632 * jail = BSD::Jail.find_by_jid( jid ) |
|
633 * jails = BSD::Jail.search( hostname ) |
|
634 * jails = BSD::Jail.search( IPAddr.new('127.0.0.1') ) |
|
635 * jails = BSD::Jail.search( Pathname.new('/tmp') ) |
|
636 * |
|
637 * # attach to jails |
|
638 * BSD::Jail.attach( jid ) |
|
639 * jail.attach do |
|
640 * ... do something fancy! |
|
641 * end |
|
642 * |
|
643 * BSD::Jail includes behaviors from Enumerable and Comparable, |
|
644 * so you can do things such as the following: |
|
645 * |
|
646 * # Are these two instantiated jails the same jid? |
|
647 * BSD::Jail.search( '/tmp' ) == BSD::Jail.search( 'testjail' ) |
|
648 * |
|
649 * # Alternative interface for finding a jail by jid |
|
650 * BSD::Jail.find { |j| j.jid == 3 } |
|
651 * |
|
652 * # Return all jails as an array |
|
653 * jails = BSD::Jail.find_all |
|
654 * |
|
655 * # What jails have IPs within the 192.168.16.0/24 class C netblock? |
|
656 * nb = IPAddr.new( '192.168.16.0/24' ); |
|
657 * jails = BSD::Jail.find? { |j| nb.include?(j.ip) } |
|
658 * |
409 */ |
659 */ |
410 void |
660 void |
411 Init_jail( void ) |
661 Init_jail( void ) |
412 { |
662 { |
|
663 // namespacing |
|
664 // |
413 rbjail_mBSD = rb_define_module( "BSD" ); |
665 rbjail_mBSD = rb_define_module( "BSD" ); |
414 rbjail_cBSDJail = rb_define_class_under( rbjail_mBSD, "Jail", rb_cObject ); |
666 rbjail_cBSDJail = rb_define_class_under( rbjail_mBSD, "Jail", rb_cObject ); |
415 |
667 |
416 rb_require("ipaddr"); |
668 // struct wrapping function |
417 rbjail_cIPAddr = rb_const_get( rb_cObject, rb_intern("IPAddr") ); |
|
418 |
|
419 rb_define_alloc_func( rbjail_cBSDJail, rbjail_s_alloc ); |
669 rb_define_alloc_func( rbjail_cBSDJail, rbjail_s_alloc ); |
420 |
670 |
421 // Make the 'new' method private. |
671 // Make the 'new' method private. |
422 rb_funcall( rbjail_cBSDJail, rb_intern("private_class_method"), 1, ID2SYM(rb_intern("new")) ); |
672 rb_funcall( rbjail_cBSDJail, rb_intern("private_class_method"), 1, ID2SYM(rb_intern("new")) ); |
423 |
673 |
424 // main utility functions |
674 // class methods |
425 // |
675 // |
426 rb_define_singleton_method( rbjail_cBSDJail, "jail", rbjail_jail, -1 ); |
676 rb_define_singleton_method( rbjail_cBSDJail, "attach", rbjail_class_attach, 1 ); |
427 rb_define_singleton_method( rbjail_cBSDJail, "find", rbjail_find, -1 ); |
677 rb_define_singleton_method( rbjail_cBSDJail, "create", rbjail_jail, -1 ); |
428 rb_define_method( rbjail_cBSDJail, "attach", rbjail_attach, -1 ); |
678 rb_define_singleton_method( rbjail_cBSDJail, "search", rbjail_find, -1 ); |
429 // rb_define_alias( rbjail_cBSDJail, "list", "find" ); |
679 rb_define_singleton_method( rbjail_cBSDJail, "find_by_jid", rbjail_find_by_jid, 1 ); |
430 |
680 |
431 // accessor functions |
681 // instance methods |
432 // |
682 // |
|
683 rb_define_method( rbjail_cBSDJail, "attach", rbjail_instance_attach, 0 ); |
|
684 rb_define_method( rbjail_cBSDJail, "host", rbjail_get_host, 0 ); |
|
685 rb_define_alias( rbjail_cBSDJail, "hostname", "host" ); |
|
686 rb_define_method( rbjail_cBSDJail, "inspect", rbjail_inspect, 0 ); |
|
687 rb_define_method( rbjail_cBSDJail, "ip", rbjail_get_ip, 0 ); |
433 rb_define_method( rbjail_cBSDJail, "jid", rbjail_get_jid, 0 ); |
688 rb_define_method( rbjail_cBSDJail, "jid", rbjail_get_jid, 0 ); |
434 rb_define_method( rbjail_cBSDJail, "ip", rbjail_get_ip, 0 ); |
|
435 rb_define_method( rbjail_cBSDJail, "host", rbjail_get_host, 0 ); |
|
436 rb_define_method( rbjail_cBSDJail, "path", rbjail_get_path, 0 ); |
689 rb_define_method( rbjail_cBSDJail, "path", rbjail_get_path, 0 ); |
437 } |
690 |
438 |
691 // Additional (base) modules for accessor wrapping |
439 |
692 // |
|
693 rb_require( "ipaddr" ); |
|
694 rb_require( "pathname" ); |
|
695 rbjail_cIPAddr = rb_const_get( rb_cObject, rb_intern("IPAddr") ); |
|
696 rbjail_cPathname = rb_const_get( rb_cObject, rb_intern("Pathname") ); |
|
697 |
|
698 // Enumerable! |
|
699 // |
|
700 rb_define_singleton_method( rbjail_cBSDJail, "each", rbjail_find, -1 ); |
|
701 rb_extend_object( rbjail_cBSDJail, rb_const_get( rb_cObject, rb_intern("Enumerable") )); |
|
702 |
|
703 // Comparable! |
|
704 // |
|
705 rb_define_method( rbjail_cBSDJail, "<=>", rbjail_compare, 1 ); |
|
706 rb_include_module( rbjail_cBSDJail, rb_const_get( rb_cObject, rb_intern("Comparable") )); |
|
707 } |
|
708 |
|
709 |