#!/bin/bash ### Imports ################################################################### source ScriptFunctions Import Array Import File Import GoboLinux Import Log Import OptionParser Import UnionFS ### Options ################################################################### scriptDescription="Run the program in a protected sandbox, as superuser, using unionfs" scriptCredits="Copyright (C) 2003. Released under the GNU GPL." helpOnNoArguments=yes scriptUsage="[] []" scriptExample="-r 0.0 -s '.:${goboPrograms}/NaughtyApp/Current' make install" scriptNotes=" To allow mobility within the sandbox, the '.' directory is mounted at a sandbox root (like $sandboxroot). For this reason, use of relative paths like '..' to reach directories higher in the hierarchy than '.' may produce unexpected results. It may also confuse symbolic links that flow through the sandbox. " Add_Option_Entry "w" "writedir" "The dir where writes outside sandbox are written." Add_Option_Entry "d" "directory" "The program should be run at . "\ "This path should be either absolute, or relative to the sandbox root." Add_Option_List "s" "sandbox" "Colon-separated list of areas where the restricted process has write access to." "." Add_Option_List "m" "map" "Colon-separated mapping (lhs=rhs) where writes to rhs are mapped to lhs." "" Parse_Options "$@" Parse_Conf Directories.conf ### Operation ################################################################# Verify_Superuser [ "$1" ] || Die "Please specify a command to be executed inside the sandbox." Union_Is_Supported || Die "unionfs is unavailabe on this system." unionbackend=`Union_Backend` [ "${unionSandbox[0]}" = "" ] && Die "Please set unionSandbox in Settings/Scripts/Directories.conf" [ "$unionSandboxMP" = "" ] && Die "Please set unionSandboxMP in Settings/Scripts/Directories.conf" [ "$unionSandboxRW" = "" ] && Die "Please set unionSandboxRW in Settings/Scripts/Directories.conf" sandbox_mp=`mktemp -d "${unionSandboxMP}.tmp.XXXXXXXXXXX"` 2>/dev/null || Die "Unable to create UnionSandbox mount point at $unionSandboxMP" chmod a+rx "$sandbox_mp" sandbox_rw=`mktemp -d "${unionSandboxRW}.tmp.XXXXXXXXXXX"` 2>/dev/null || Die "Unable to create UnionSandbox read/write mount point at $unionSandboxRW" chmod a+rx "$sandbox_rw" function RealPath { readlink -f $@ } List_To_Array "sandbox" bindmounts bindmounts=(`Map RealPath "${bindmounts[@]}"`) List_To_Array "map" mapmounts # Preset cmd status result=0 # Called at end of script and by signal handler function cleanup() { Log_Normal "Cleaning up." if [ "$goboIndex" ] then Quiet pushd ${unionfsPackageDir} find . -name "*__dir_opaque" | xargs rm -f Quiet popd fi # Remove wrapper rm -f -- "$sandbox_mp/.wrapper" # Clean up mountpoints # unionfs-fuse keeps a few programs busy, so we need to make sure all ocurrences created by us are finished for entry in $(ps xaw | awk {'print $1 " " $5'} | grep "unionfs" | grep ${sandbox_mp} | grep unionfs) do local pid=$(echo "$entry" | cut -d" " -f1) local unionapp=$(echo $entry | cut -d" " -f2) [ "$unionapp" = "unionfs" ] && Quiet kill -9 $pid done umount "$sandbox_mp/$goboStatus" umount "$sandbox_mp/$goboDevices" error=false For_Each_Reverse mapmounts ' rhs=$(echo $each | cut -d= -f2) umount ${sandbox_mp}$rhs || { echo "Error unmounting ${sandbox_mp}${rhs}"; error=true; } ' For_Each_Reverse bindmounts ' umount ${sandbox_mp}${each} || { echo "Error unmounting ${sandbox_mp}${each}"; error=true; } ' For_Each_Reverse unionSandbox ' Union_Umount ${unionbackend} ${sandbox_mp}${each} || { echo "Error unmounting ${sandbox_mp}${each}"; error=true; } Quiet rmdir ${sandbox_mp}${each} Quiet rmdir ${sandbox_rw}${each} ' if [ "$cross_toolchain_dir" ] then Union_Umount ${unionbackend} "${sandbox_mp}/$cross_toolchain_dir" || error=true [ "$error" = "false" ] && Quiet rmdir -p "${sandbox_mp}/$cross_toolchain_dir" fi # Do not attempt to 'rm -rf' if some unmount failed. [ "$error" = "true" ] && exit 1 # Clean up legacy links rm -rf ${sandbox_mp}/usr rm ${sandbox_mp}/bin ${sandbox_mp}/dev ${sandbox_mp}/etc rm ${sandbox_mp}/lib ${sandbox_mp}/proc ${sandbox_mp}/sbin rm ${sandbox_mp}/sys ${sandbox_mp}/tmp ${sandbox_mp}/var # Remove tmp dirs if Is_Entry_Set "writedir" then writedir=`Entry "writedir"` Log_Normal "Moving entries to: $writedir" Assert_Dir $writedir cp -ra "$sandbox_rw/." "$writedir" Quiet mv $sandbox_mp/* $writedir fi rm -rf $sandbox_rw rmdir $sandbox_mp exit $result } function sandbox_die() { message=$1 Log_Error $message result=1 cleanup } function mount_union() { dir=$1 Assert_Dir ${sandbox_mp}${dir} Assert_Dir ${sandbox_rw}${dir} uniondirs=`echo "${sandbox_rw}${dir}=rw:${dir}=ro" | sed "s,$unionfsPackageDir=ro,$unionfsPackageDir=rw,g"` Union_Mount "$unionbackend" "$uniondirs" "${sandbox_mp}${dir}" [ "$?" = "0" ] || sandbox_die "Unable to mount unionfs" } trap "sandbox_die 'Interrupted...'" SIGHUP SIGINT SIGTERM Log_Verbose "Preparing the sandbox..." if [ ! -h ${goboDevices}/root ] then ln -s / ${goboDevices}/root fi # Mount unionfs if [ "$cross_toolchain_dir" ] then mount_union "$cross_toolchain_dir" fi if [ "$goboIndex" ] then unionSandbox=( ${unionSandbox[@]} "$goboIndex" ) fi For_Each unionSandbox 'mount_union $each' # Bind mount writable areas error=false For_Each bindmounts 'mkdir -p ${sandbox_mp}/${each}; mount -o bind $each ${sandbox_mp}/${each}; [ "$?" = "0" ] || error=true' [ "$error" = "false" ] || sandbox_die "Unable to mount writable areas in chroot" error=false For_Each mapmounts 'lhs=$(echo $each | cut -d= -f1) rhs=$(echo $each | cut -d= -f2) Assert_Dir $lhs Assert_Dir ${sandbox_mp}$rhs mount -o bind $lhs ${sandbox_mp}$rhs [ "$?" = "0" ] || error=true ' [ "$error" = "false" ] || sandbox_die "Unable to mount map areas in chroot" # Create legacy links cp -ar /usr "$sandbox_mp" cp -a /bin /dev /etc /lib /proc /sbin /sys /tmp /var "$sandbox_mp" # Create wrapper newpwd="$PWD" Is_Entry_Set "directory" && newpwd=`Entry "directory"` echo "#!/bin/bash" >"${sandbox_mp}/.wrapper" echo "cd $newpwd" >>"${sandbox_mp}/.wrapper" Args_To_Array command >> "${sandbox_mp}/.wrapper" echo "mkdir -p $goboDevices; mount -t tmpfs none $goboDevices" >> "${sandbox_mp}/.wrapper" echo "mkdir -p $goboStatus; mount -t proc none $goboStatus" >> "${sandbox_mp}/.wrapper" echo "cp -aR \"${goboLibraries}/udev/devices/\"* $goboDevices" >> "${sandbox_mp}/.wrapper" if Executable_Exists_In_Path "udevadm" then echo "udevadm trigger && udevadm settle --timeout=10" >> "${sandbox_mp}/.wrapper" else echo "udevtrigger && udevsettle" >> "${sandbox_mp}/.wrapper" fi echo "exec \"\${command[@]}\"" >>"${sandbox_mp}/.wrapper" chmod a+x "${sandbox_mp}/.wrapper" # Run program in chrooted sandbox chroot $sandbox_mp /.wrapper result=$? # Cleanup cleanup