#!/usr/bin/perl -w # # mkjail # John Simpson 2005-11-18 # # creates a directory structure suitable for a "chroot jail" operation. # copies files into the directory, and runs "ldd" on any files it copies # to see, for executables or libraries, what other libraries they depend on, # and copies those libraries as well. also handles symlinks by copying the # physical files they refer to. # # provided you are copying a shell into the jail, you should be able to do # # chroot /jail /bin/sh # # after running this script and have a working shell, locked into that # directory. # # 2006-02-01 jms1 - moving jail directory and user list to the command # line rather than being hard-coded into the program. the script # should be run as... # # mkjail /jail [userid] [...] # # each "userid" will have a line created in the virtual /etc/passwd # file which has their real numeric uid/gid, but no other real data. # a virtual /etc/group file is also created, with data for all gid's # that the included users belong to. # # note that "root" is ALWAYS added to the virtual /etc/passwd file, # whether you specify it here or not. # ############################################################################### # # Copyright (C) 2005-2006 John Simpson. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, Version 2, as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # or visit http://www.gnu.org/licenses/gpl.txt # ############################################################################### # # configuration # list of files which will always be included in the jail. each of these # files is scanned with "ldd", and any libraries they need will also be # included in the jail. # # the list here was tested and works with RHEL4. my @files = qw ( /bin/bash /bin/cp /bin/ls /bin/mkdir /bin/mv /bin/rm /bin/rmdir /bin/sh /dev/null /dev/zero /etc/passwd /etc/group /home/test /lib/libnss_files.so.2 /usr/bin/scp /usr/libexec/openssh/sftp-server ) ; ############################################################################### # # global variables my $jail = "" ; my @users = () ; my %added = () ; my %done = () ; my %puser = () ; my %pgid = () ; my $ptext = () ; my $gtext = () ; ############################################################################### # # functions sub debug($) # un-comment to see debug messages { # print @_ ; } sub mkdir_for($) ; # pre-declare recursive function sub mkdir_for($) { my $f = shift ; $f =~ m|^(.*)/| ; my $d = $1 ; $d || return ; ( -d $d ) && return ; mkdir_for $d ; print "# mkdir -m 0755 $d\n" ; mkdir ( $d , 0755 ) ; } ############################################################################### ############################################################################### ############################################################################### # # main thread of execution... the magic starts here. $jail = ( shift || die "No jail directory specified\n" ) ; push ( @users , "root" ) ; while ( $#ARGV > -1 ) { my $u = shift ; if ( $u ne "root" ) { push ( @users , $u ) ; } } umask 0 ; for my $u ( @users ) { my @pw = getpwnam ( $u ) ; die "Unknown user $u\n" if ( $#pw < 0 ) ; $ptext .= join ( ":" , $pw[0] , "*" , $pw[2] , $pw[3] , "" , "" , "" ) . "\n" ; $puser{$u} = 1 ; $pgid{$pw[3]} = 1 ; } $gtext = "" ; while ( my @g = getgrent() ) { my $z = "" ; for my $u ( split ( / / , $g[3] ) ) { if ( exists $puser{$u} ) { $z .= "$u," ; } } if ( $z || exists $pgid{$g[2]} ) { $z =~ s/,$// ; $gtext .= "$g[0]:x:$g[2]:$z\n" ; } } print "# mkdir -m 0755 $jail\n" ; mkdir ( $jail , 0755 ) or die "Can\'t create $jail: $!\n" ; map { $added{$_} = 1 } @files ; while ( my $f = shift @files ) { next if ( exists $done{$f} ) ; mkdir_for ( "$jail$f" ) ; if ( $f eq "/etc/passwd" ) { print "# cat > $jail$f <$jail$f" ) or die "Can\'t create $jail$f: $!\n" ; print O $ptext ; close O ; print "# chmod 0644 $jail$f\n" ; chmod ( 0644 , "$jail$f" ) ; } elsif ( $f eq "/etc/group" ) { print "# cat > $jail$f <$jail$f" ) or die "Can\'t create $jail$f: $!\n" ; print O $gtext ; close O ; print "# chmod 0644 $jail$f\n" ; chmod ( 0644 , "$jail$f" ) ; } else { my $cmd = "cp -a \'$f\' \'$jail$f\'" ; print "# $cmd\n" ; my $rv = system ( $cmd ) ; $rv && die "*** ERROR copying $f to $jail$f, cannot continue\n" ; } $done{$f} = 1 ; if ( -l "$jail$f" ) { my $t = readlink ( "$jail$f" ) ; debug "$jail$f is a symlink to $t\n" ; if ( $f =~ m|^(/.*)/| ) { my $d = $1 ; $t = "$d/$t" ; } unless ( $added{$t} ) { debug "Adding library file $t\n" ; push ( @files , $t ) ; $added{$t} = 1 ; } } elsif ( -f "$jail$f" ) { open ( I , "ldd $jail$f 2>/dev/null |" ) or die "Can\'t run ldd $jail$f: $!\n" ; while ( my $line = ) { chomp $line ; $line =~ s/^\s+// ; $line =~ s/\s+$// ; $line =~ s/^.*\=> // ; $line =~ s/\s*\(.+?\)$// ; next if ( $line eq "statically linked" ) ; next if ( $line eq "not a dynamic executable" ) ; unless ( exists $added{$line} ) { debug "Adding library file $line\n" ; push ( @files , $line ) ; $added{$line} = 1 ; } } close I ; } }