#!/bin/sh

### This script can help create chrooted environment. 
### It was tested ONLY WITH APACHE (with mod_ssl) AND PERL,
### but at the end of
### it you will find big section "apllication specific".
### So if you will chroot something else (proftpd, ldap,
### or what you want) you can add aplication specific 
### part to the end of this script (if needed).
### In example apache needs /etc/mime.types to be copied
### but this is NOT a part of apache package. So at the
### end script just tests if package name is apache, and copies
### this file to new chroot.
###
### It works ONLY WITH DEBIAN (because of package system),
### and was tested ONLY WITH DEBIAN 3.0 (because I have no other :-).
### Use it on your own risk.
### 
### What it does. It takes two arguments:
### debian package name;
### chroot directory.
### It makes some tests and asks you about configuration.
### Then it copies some files, libraries and devices to
### new chroot (you can add or remove if needed [lines 302-309]).
### When package files are copied, script looks what libraries are 
### needed. After libraries are copied it goes to application
### specific section. For apache it can create startup script
### which you may copy to /etc/init.d/apache *(real path, not
### a chroot)*, so your chrooted apache can start at startup.
###
### USAGE NOTES
###
### Usage: this_script package_name chroot_dir
### I.e. if you need chrooted apache, do:
### dpkg -l | grep apache
### and look what packages you need.
### I took apache, apache-common, libapache-mod-ssl.
### (./mkchroot apache /var/chroot/apache
###  ./mkchroot apache-common /var/chroot/apache
###  ./mkchroot libapache-mod-ssl /var/chroot/apache )
### 
### So you MUST run this scipt for EACH package once.
### For perl I took perl-base and perl.
### 
### UPGRADES OF CHROOTED APPLICATIONS
###
### You can upgrade (apt-get upgrade package) package you need
### BUT BE SURE, YOU HAVE EXACT WORKING CONFIGURATION OUTSIDE
### THE JAIL. In other case, configuration, ssl-keys, etc. will
### be OVERWRITTEN!
###
### LICENCE. You can use, modify, distribute this script, 
### but leave my name in authors section.
### 
###
### Written by Martynas Domarkas (OTPABA) md@hansa.lt 2003 01 05.
###
### I DO NOT SUPPORT ANY SOFTWARE, so I'm not sure I'll answer
### your questions :-)
###
### Last note: this script does NOT check any package dependencies.
### The point is, that i.e. if you want a chrooted apache, debian package
### depends on perl. But if you find perl buggy or just hate it (like I do)
### you will not use cgi's written in perl, and no mod_perl of course. 
### Then you need no perl package in your chroot! ;-)

### 2003 09 19 	changed function file_list_for_tar(). There was a bug when copying
###		targets of symlinks.
###		Reported by Marc Lubarsky.

### 2003 09 19	added copying of /etc/hosts.




CHRPACK=$1
CHROOTD=$2
clear
### Function "Print how to use" ###
usage() {
echo "
	Usage:
	$0 package_name chroot_dir
"
}
#################################

### Check for command invocation syntax ###
if [ "$CHRPACK" == "" ] || [ "$CHROOTD" == "" ]; then
usage   # Function from above
exit 1
fi
###########################################


### Ask some stupid questions, but it's better than README ;-)) ###
echo "
	1) Did you read what is written in head of this file?

	2) Package you are chrooting is installed using debian tools
	and *CONFIGURED* ? 
		(if you upgrade your system and try mkchroot
	again without actual configuration you currently have in chroot
		YOU WILL LOSE YOUR CONFIGURATION!)
		
	3) Is this machine running Linux Debian 3.0 ?
	4) Do you know what does it mean \"chroot\" at all?
	5) Do you have dpkg, gawk (awk), tar, file, grep, egrep, ldd, mkdir in your PATH?

	If all 5 answers was \"YES!!!!\", then press ENTER. If not, better press ctrl-C
	and read beggining of this script.
"
read junk
##############################################################

### Check for user ###
if [ `id -u` -ne 0 ]; then
echo "
You are not root. 
It is possible, that you will not be able access some files."
echo -n "
Do you want to continue? [y/n] : "
	read ANS
	if [ "$ANS" != "y" ]; then
	echo "OK, exiting..."
	exit 0
	fi
fi
#####################


#################### FUNCTIONS ####################

### Function for checking of command existence ###
### It will be used to be sure that we can continue running this script ###
cmd_exist() {
which $1 > /dev/null
if [ $? -ne 0 ]; then
echo "Command \"$1\" not found"
echo "Exiting..."
exit 1
fi
}

### Function for getting needed libraries for package ###
### It uses list of files given by "dpkg -L $PACKAGE" looks for executables and prints out 
### file names of needed libraries
get_libs_for_pack() {
dpkg -L $CHRPACK | while read GLFP
do
if [ ! -d $GLFP ]; then
file $GLFP|egrep "(ELF.*executable|ELF.*shared obj)" > /dev/null
if [ $? -eq 0 ]; then
ldd $GLFP | $AWK '{ print $3 }'; fi; fi; done | sort -u
}
#########################################################

### Function for getting needed libraries for file 
### (same as above but this takes as argument just one file
get_libs_for_file() {
file $1 | egrep "(ELF.*executable|ELF.*shared obj)" > /dev/null
if [ $? -eq 0 ]; then
ldd $1 | $AWK '{ print $3 }'; fi
}
######################################################

### Function for checking if library does NOT exist in chroot ###
check_lib_for_exsist() {
while read CLFN
do
if [ ! -e $CHROOTD/$CLFN ]; then
echo $CLFN
fi
done
}
#################################################################

### Function to prepare list of files for tar ###
### It checks for symlinks, targets and it's output is one single space separated line ###

file_list_for_tar() {
while read FLFT
do

# Check for accidental newline
if [ ! -z $FLFT ]; then

# You will ask me why I cycle again. It is because I like "break" more than "else" :-)
file $FLFT | while read LO
do

# Check if the file exist
if [ ! -f "$FLFT" ]; then
break
fi

# Output filename to stdout (other functions will read this)
echo -n "$FLFT "

# Is it a symlink?
echo $LO | grep "sym.*link" > /dev/null
if [ $? -eq 0 ]; then

# Symlinks often are made with realative path. So we need know WHERE the symlink is.
prefdir=`dirname $FLFT`
TG=`echo $LO | $AWK '{ print $NF }'`
# Is the path absolute (absolute paths allways begisn with a "/")
echo "$TG" | grep "^/" > /dev/null
	if [ $? -eq 0 ]; then
	TGF=$TG
	else
	# Path to target was realative - adding directory prefix.
	TGF=$prefdir/$TG
	fi
	
	# Shit happens... :-)
	if [ ! -f "$FLFT" ]; then
	break
	fi
	
echo -n "$TGF "
fi
done

fi
done
}

### Old one. Buggy.
#file_list_for_tar() {
#while read FLFT
#do
#file $FLFT | $AWK '{ gsub(/:$/, "", $1); print }' | while read LO
#	do
#	prefdir=`echo $LO | $AWK -F"/" '{ ORS=""; for(i=2; i<NF; i++) { print "/" $i } } END { print "/" }'`
#	echo $LO|grep "sym.*link" > /dev/null
#		if [ $? -eq 0 ]; then
#		echo $LO | $AWK '{ ORS=" "; print $1 }'
#		TG=`echo $LO | $AWK '{ print $NF }'`
#		TGF=`find "$prefdir" -name "$TG"`
#		echo -n "$TGF "
#		else
#		echo $LO | $AWK '{ ORS=" "; print $1 }'
#		fi
#	done
#done
#}
###################################################################################

### Function for copy given files to chroot ###
cp_to_chroot() {
read CTC
if [ ! -z "$CTC" ]; then
cd $CHROOTD
tar cvf - $CTC | tar xf -
fi
}
##############################################

### Function to copy single commands (binaries) with libraries to chroot ###
cp_cmd_to_chroot() {
CMD=$1
if [ ! -z "$CMD" ]; then
	if [ -f "$CMD" ]; then
	get_libs_for_file $CMD | check_lib_for_exsist | file_list_for_tar | cp_to_chroot
		if [ ! -f $CHROOTD/$CMD ]; then
		cd $CHROOTD
		tar cvf - "$CMD" | tar xf -
		fi
	else
	echo "$CMD does not exist"
	exit 1
	fi
else
echo "Error"
exit 1
fi
}

############### END OF FUNCTIONS ####################

### OK lets begin! ###

### Looking for needed software ###
### GAWK: ###
which gawk > /dev/null
if [ $? -ne 0 ]; then
	which awk > /dev/null
	if [ $? -ne 0 ]; then
	echo "gawk or awk not found in your PATH"
	echo "Exiting..."
	exit 1
	else
	echo "Found awk instead of gawk. I'll try to use it..."
	AWK=`which awk`
	fi
else
#echo "gawk found. Excelent!"
AWK=`which gawk`
fi

cmd_exist file
cmd_exist ldd
cmd_exist dpkg
cmd_exist grep
cmd_exist egrep
cmd_exist mkdir
cmd_exist tar
cmd_exist dirname

### At this point we should be sure, that we found all software needed ###

### Looking for package beeing chrooted ###
dpkg -L $CHRPACK > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "
	dpkg can not find package \"$CHRPACK\".
	If you mistyped command, start $0 again with correct arguments
	Be sure you installed it using usual Debian way (apt-get, dpkg -i, tasksel, dselect...)
	Exiting..."
exit 1
fi
###########################################

### Create new chroot or use an existing directory? ###
if [ -e $CHROOTD ]; then
	if [ ! -d $CHROOTD ]; then
	echo "$CHROOTD exist and it is NOT a directory"
	echo "Exiting..."
	exit 1
	else
	echo -n "$CHROOTD exist. Should I use it? [y/n] : "
		read ANS
		if [ "$ANS" != "y" ]; then
		echo "Exiting..."
		exit 1
		fi
	fi
else
mkdir -p $CHROOTD
fi
########################################################


### Minimal set of files so you can do 'chroot /your/dir' ###
### (ls, cat, tail, strace, less is also added for debuging) ###

echo "
Creating minimal chroot environment...
"
cp_cmd_to_chroot /bin/sh
cp_cmd_to_chroot /bin/bash
cp_cmd_to_chroot /bin/ls
cp_cmd_to_chroot /bin/cat
cp_cmd_to_chroot /usr/bin/strace
cp_cmd_to_chroot /usr/bin/less
cp_cmd_to_chroot /usr/bin/tail

### Little stupid hack. Without "libnss_files.so" apache can not read /etc/passwd for "User" directive 
### but no binaries or modules ar linked with it...
echo /lib/libnss_files.so.2 | check_lib_for_exsist | file_list_for_tar | cp_to_chroot

### Some directories and files that we will need ###
mkdir -p $CHROOTD/var/run
mkdir -p $CHROOTD/var/lock
mkdir -p $CHROOTD/var/log
mkdir -p $CHROOTD/proc
mkdir -p $CHROOTD/dev
mkdir -p $CHROOTD/etc

cp -apR /dev/null $CHROOTD/dev
cp -apR /dev/random $CHROOTD/dev
cp -apR /dev/urandom $CHROOTD/dev
cp -apR /dev/zero $CHROOTD/dev
cp -ap /etc/hosts $CHROOTD/etc

############## END OF MINIMAL CHROOT ####################

### We also need an /etc/passwd and /etc/group ###
### At this moment we add user root to $CHROOTD/etc/passwd 
### and group root to $CHROOTD/etc/group
### You will be asked later for application specific user and group 
if [ ! -f $CHROOTD/etc/passwd ]; then
grep "^root:" /etc/passwd > $CHROOTD/etc/passwd
else
grep "^root:" $CHROOTD/etc/passwd > /dev/null
	if [ $? -ne 0 ]; then
	echo "No entry for user root found in your chrooted /etc/passwd"
	echo -n "Do you want me to add one? [y/n] : "
	read ANS
		if [ "$ANS" != "y" ]; then
		echo "OK, no root - no problems"
		else
		grep "^root:" /etc/passwd >> $CHROOTD/etc/passwd
		fi
	fi
fi

if [ ! -f $CHROOTD/etc/group ]; then
grep "^root:" /etc/group > $CHROOTD/etc/group
else
grep "^root:" $CHROOTD/etc/group > /dev/null
	if [ $? -ne 0 ]; then
	echo "No entry for user root found in your chrooted /etc/group"
	echo -n "Do you want me to add one? [y/n] : " 
	read ANS
		if [ "$ANS" != "y" ]; then
		echo "OK, no root - no problems"
		else
		grep "^root:" /etc/group >> $CHROOTD/etc/group
		fi
	fi
fi
##################################################

### We have passwd and group files. Now we need copy some system users and groups to jail ###
while [ true ]
do
sleep 1
clear

echo "
	You will need a part of /etc/passwd for your software to run.
	In most cases apache run under www-data, bind under bind. But I have no idea
	what is your setup. So, now you can enter username you need in chroot and
	press ENTER. 
	
	If you DO NOT need any user enter NoMore.
"
echo -n "User to add to $CHROOTD/etc/passwd : "

read USRN
if [ ! -z "$USRN" ]; then
	if [ "$USRN" != "NoMore" ]; then
		grep "^$USRN:" /etc/passwd > /dev/null
		if [ $? -eq 0 ]; then
			grep "^$USRN:" $CHROOTD/etc/passwd 1>/dev/null 2>/dev/null
			if [ $? -ne 0 ]; then
			grep "^$USRN:" /etc/passwd >> $CHROOTD/etc/passwd
			echo ""
			echo -e "\tUser $USRN added"
			echo ""
			echo -e "\tPress ENTER to continue"
			read junk
			else
			echo ""
			echo -e "\t$USRN already is in your $CHROOTD/etc/passwd"
			echo ""
			echo -e "\tPress ENTER to continue"
			read junk
			fi
		else
		echo ""
		echo -e "\t$USRN does not exist in your system"
		echo ""
		echo -e "\tPress ENTER to continue"
		read junk 
		fi
	else
	echo "OK... Continuing"
	break
	fi
else
echo -e "
\tEnter an existing system user or NoMore if you wan't quit adding users to chroot"
echo -e "
\tPress ENTER to continue"
read junk 
fi
done


while [ true ]
do
sleep 1
clear

echo "
	OK, for we have user file. Now repeat the same with groups.
	
	Now you can enter groupname you need in chroot and
	press ENTER. 
	
	If you DO NOT need any group enter NoMore.
"
echo -n "Group to add to $CHROOTD/etc/group : "

read GRPN
if [ ! -z "$GRPN" ]; then
	if [ "$GRPN" != "NoMore" ]; then
		grep "^$GRPN:" /etc/group > /dev/null
		if [ $? -eq 0 ]; then
			grep "^$GRPN:" $CHROOTD/etc/group 1>/dev/null 2>/dev/null
			if [ $? -ne 0 ]; then
			grep "^$GRPN:" /etc/group >> $CHROOTD/etc/group
			echo ""
			echo -e "\tGroup $GRPN added"
			echo ""
			echo -e "\tPress ENTER to continue"
			read junk
			else
			echo ""
			echo -e "\t$GRPN already is in your $CHROOTD/etc/group"
			echo ""
			echo -e "\tPress ENTER to continue"
			read junk
			fi
		else
		echo ""
		echo -e "\t$GRPN does not exist in your system"
		echo ""
		echo -e "\tPress ENTER to continue"
		read junk 
		fi
	else
	echo "OK... Continuing"
	break
	fi
else
echo -e "
\tEnter an existing system group or NoMore if you wan't quit adding groups to chroot"
echo -e "
\tPress ENTER to continue"
read junk 
fi
done

##################################################################################

### Package files. Taken from dpkg -L $PACKAGE without doc's and manuals ###
echo "
Copying package files to $CHROOTD:
"
cd $CHROOTD
LIST=`dpkg -L $CHRPACK | grep -v "share/[man|doc]" | while read F
do
if [ ! -d $F ]; then
echo -n "$F "
else
if [ ! -d $CHROOTD/$F ]; then
mkdir -p $CHROOTD/$F > /dev/null 2>&1
fi
fi
done`
tar cvf - $LIST| tar xf -
#########################################

echo "
Copying libraries needed by $CHRPACK...
"
get_libs_for_pack | check_lib_for_exsist | file_list_for_tar | cp_to_chroot


### Package specific... APACHE ###
if [ "$CHRPACK" = "apache" ]; then
	if [ ! -d $CHROOTD/etc/apache ]; then
	cd $CHROOTD
	tar cvf - /etc/apache | tar xf -
	else
	cp -apuRi /etc/apache/* $CHROOTD/etc/apache
	fi

	if [ ! -f $CHROOTD/etc/mime.types ]; then
	cp -ap /etc/mime.types $CHROOTD/etc
	fi
echo "
	If you are running Debian 3.0, I can try provide startup script for you.
	It will be placed as /tmp/chrooted.apache.spartup.sh. You will need only
	chmod 750 /tmp/chrooted.apache.spartup.sh
	and
	/tmp/chrooted.apache.spartup.sh start
"
echo -n "Do you want try this script? [y/n] : "
read ANS
if [ "$ANS" = "y" ]; then
STARTUPFILE=/tmp/chrooted.apache.spartup.sh
rm $STARTUPFILE
echo "#!/bin/bash" >> $STARTUPFILE
echo "#" >> $STARTUPFILE
echo "# apache	Start the apache HTTP server." >> $STARTUPFILE
echo "#" >> $STARTUPFILE
echo "" >> $STARTUPFILE
echo "CHRDIR=$CHROOTD" >> $STARTUPFILE
echo "" >> $STARTUPFILE
echo "NAME=apache" >> $STARTUPFILE
echo "PATH=/bin:/usr/bin:/sbin:/usr/sbin" >> $STARTUPFILE
echo "DAEMON=/usr/sbin/apache" >> $STARTUPFILE
echo "SUEXEC=/usr/lib/apache/suexec" >> $STARTUPFILE
echo "PIDFILE=/var/run/\$NAME.pid" >> $STARTUPFILE
echo "CONF=/etc/apache/httpd.conf" >> $STARTUPFILE
echo "APACHECTL=/usr/sbin/apachectl " >> $STARTUPFILE
echo "" >> $STARTUPFILE
echo "trap \"\" 1" >> $STARTUPFILE
echo "export LANG=C" >> $STARTUPFILE
echo "export PATH" >> $STARTUPFILE
echo "" >> $STARTUPFILE
echo "test -f \$DAEMON || exit 0" >> $STARTUPFILE
echo "test -f \$APACHECTL || exit 0" >> $STARTUPFILE
echo "" >> $STARTUPFILE
echo "# ensure we don't leak environment vars into apachectl" >> $STARTUPFILE
echo "APACHECTL=\"env -i LANG=\${LANG} PATH=\${PATH} \$APACHECTL\"" >> $STARTUPFILE
echo "" >> $STARTUPFILE
echo "if egrep -q -i \"^[[:space:]]*ServerType[[:space:]]+inet\" \$CONF" >> $STARTUPFILE
echo "then" >> $STARTUPFILE
echo "    exit 0" >> $STARTUPFILE
echo "fi" >> $STARTUPFILE
echo "" >> $STARTUPFILE
echo "case \"\$1\" in" >> $STARTUPFILE
echo "  start)" >> $STARTUPFILE
echo "    echo -n \"Starting web server: \$NAME\"" >> $STARTUPFILE
echo "    mount -t proc proc $CHROOTD/proc" >> $STARTUPFILE
echo "    start-stop-daemon --start --pidfile \$PIDFILE --exec \$DAEMON --chroot \$CHRDIR" >> $STARTUPFILE
echo "    ;;" >> $STARTUPFILE
echo "" >> $STARTUPFILE
echo "  stop)" >> $STARTUPFILE
echo "    echo -n \"Stopping web server: \$NAME\"" >> $STARTUPFILE
echo "    start-stop-daemon --stop --pidfile \$CHRDIR/\$PIDFILE --oknodo" >> $STARTUPFILE
echo "    umount $CHROOTD/proc" >> $STARTUPFILE
echo "    ;;" >> $STARTUPFILE
echo "" >> $STARTUPFILE
echo "  reload)" >> $STARTUPFILE
echo "    echo -n \"Reloading \$NAME configuration\"" >> $STARTUPFILE
echo "    start-stop-daemon --stop --pidfile \$CHRDIR/\$PIDFILE --signal USR1 --exec \$DAEMON --chroot \$CHRDIR" >> $STARTUPFILE
echo "    ;;" >> $STARTUPFILE
echo "" >> $STARTUPFILE
echo "  reload-modules)" >> $STARTUPFILE
echo "    echo -n \"Reloading \$NAME modules\"" >> $STARTUPFILE
echo "    start-stop-daemon --stop --pidfile \$CHRDIR/\$PIDFILE --oknodo --retry 30" >> $STARTUPFILE
echo "    start-stop-daemon --start --pidfile \$PIDFILE --exec \$DAEMON --chroot \$CHRDIR" >> $STARTUPFILE
echo "    ;;" >> $STARTUPFILE
echo "" >> $STARTUPFILE
echo "  restart)" >> $STARTUPFILE
echo "    \$0 reload-modules" >> $STARTUPFILE
echo "    exit \$?" >> $STARTUPFILE
echo "    ;;" >> $STARTUPFILE
echo "" >> $STARTUPFILE
echo "  force-reload)" >> $STARTUPFILE
echo "    \$0 reload-modules" >> $STARTUPFILE
echo "    exit \$?" >> $STARTUPFILE
echo "    ;;" >> $STARTUPFILE
echo "" >> $STARTUPFILE
echo "  *)" >> $STARTUPFILE
echo "    echo \"Usage: /etc/init.d/\$NAME {start|stop|reload|reload-modules|force-reload|restart}\"" >> $STARTUPFILE
echo "    exit 1" >> $STARTUPFILE
echo "    ;;" >> $STARTUPFILE
echo "esac" >> $STARTUPFILE
echo "" >> $STARTUPFILE
echo "if [ \$? == 0 ]; then" >> $STARTUPFILE
echo "	echo ." >> $STARTUPFILE
echo "	exit 0" >> $STARTUPFILE
echo "else" >> $STARTUPFILE
echo "	echo failed" >> $STARTUPFILE
echo "	exit 1" >> $STARTUPFILE
echo "fi" >> $STARTUPFILE
fi
fi

