jail revision 175293
1#!/bin/sh 2# 3# $FreeBSD: head/etc/rc.d/jail 175293 2008-01-13 14:27:53Z simon $ 4# 5 6# PROVIDE: jail 7# REQUIRE: LOGIN cleanvar 8# BEFORE: securelevel 9# KEYWORD: nojail shutdown 10 11# WARNING: This script deals with untrusted data (the data and 12# processes inside the jails) and care must be taken when changing the 13# code related to this! If you have any doubt whether a change is 14# correct and have security impact, please get the patch reviewed by 15# the FreeBSD Security Team prior to commit. 16 17. /etc/rc.subr 18 19name="jail" 20rcvar=`set_rcvar` 21start_cmd="jail_start" 22stop_cmd="jail_stop" 23 24# init_variables _j 25# Initialize the various jail variables for jail _j. 26# 27init_variables() 28{ 29 _j="$1" 30 31 if [ -z "$_j" ]; then 32 warn "init_variables: you must specify a jail" 33 return 34 fi 35 36 eval _rootdir=\"\$jail_${_j}_rootdir\" 37 _devdir="${_rootdir}/dev" 38 _fdescdir="${_devdir}/fd" 39 _procdir="${_rootdir}/proc" 40 eval _hostname=\"\$jail_${_j}_hostname\" 41 eval _ip=\"\$jail_${_j}_ip\" 42 eval _interface=\"\${jail_${_j}_interface:-${jail_interface}}\" 43 eval _exec=\"\$jail_${_j}_exec\" 44 eval _exec_start=\"\${jail_${_j}_exec_start:-${jail_exec_start}}\" 45 46 i=1 47 while [ true ]; do 48 eval _exec_afterstart${i}=\"\${jail_${_j}_exec_afterstart${i}:-\${jail_exec_afterstart${i}}}\" 49 [ -z "$(eval echo \"\$_exec_afterstart${i}\")" ] && break 50 i=$((i + 1)) 51 done 52 53 eval _exec_stop=\"\${jail_${_j}_exec_stop:-${jail_exec_stop}}\" 54 if [ -n "${_exec}" ]; then 55 # simple/backward-compatible execution 56 _exec_start="${_exec}" 57 _exec_stop="" 58 else 59 # flexible execution 60 if [ -z "${_exec_start}" ]; then 61 _exec_start="/bin/sh /etc/rc" 62 if [ -z "${_exec_stop}" ]; then 63 _exec_stop="/bin/sh /etc/rc.shutdown" 64 fi 65 fi 66 fi 67 68 # The default jail ruleset will be used by rc.subr if none is specified. 69 eval _ruleset=\"\${jail_${_j}_devfs_ruleset:-${jail_devfs_ruleset}}\" 70 eval _devfs=\"\${jail_${_j}_devfs_enable:-${jail_devfs_enable}}\" 71 [ -z "${_devfs}" ] && _devfs="NO" 72 eval _fdescfs=\"\${jail_${_j}_fdescfs_enable:-${jail_fdescfs_enable}}\" 73 [ -z "${_fdescfs}" ] && _fdescfs="NO" 74 eval _procfs=\"\${jail_${_j}_procfs_enable:-${jail_procfs_enable}}\" 75 [ -z "${_procfs}" ] && _procfs="NO" 76 77 eval _mount=\"\${jail_${_j}_mount_enable:-${jail_mount_enable}}\" 78 [ -z "${_mount}" ] && _mount="NO" 79 # "/etc/fstab.${_j}" will be used for {,u}mount(8) if none is specified. 80 eval _fstab=\"\${jail_${_j}_fstab:-${jail_fstab}}\" 81 [ -z "${_fstab}" ] && _fstab="/etc/fstab.${_j}" 82 eval _flags=\"\${jail_${_j}_flags:-${jail_flags}}\" 83 [ -z "${_flags}" ] && _flags="-l -U root" 84 eval _consolelog=\"\${jail_${_j}_consolelog:-${jail_consolelog}}\" 85 [ -z "${_consolelog}" ] && _consolelog="/var/log/jail_${_j}_console.log" 86 87 # Debugging aid 88 # 89 debug "$_j devfs enable: $_devfs" 90 debug "$_j fdescfs enable: $_fdescfs" 91 debug "$_j procfs enable: $_procfs" 92 debug "$_j mount enable: $_mount" 93 debug "$_j hostname: $_hostname" 94 debug "$_j ip: $_ip" 95 debug "$_j interface: $_interface" 96 debug "$_j root: $_rootdir" 97 debug "$_j devdir: $_devdir" 98 debug "$_j fdescdir: $_fdescdir" 99 debug "$_j procdir: $_procdir" 100 debug "$_j ruleset: $_ruleset" 101 debug "$_j fstab: $_fstab" 102 debug "$_j exec start: $_exec_start" 103 debug "$_j consolelog: $_consolelog" 104 105 i=1 106 while [ true ]; do 107 eval out=\"\${_exec_afterstart${i}:-''}\" 108 109 if [ -z "$out" ]; then 110 break; 111 fi 112 113 debug "$_j exec after start #${i}: ${out}" 114 i=$((i + 1)) 115 done 116 117 debug "$_j exec stop: $_exec_stop" 118 debug "$_j flags: $_flags" 119 debug "$_j consolelog: $_consolelog" 120 121 if [ -z "${_hostname}" ]; then 122 err 3 "$name: No hostname has been defined for ${_j}" 123 fi 124 if [ -z "${_rootdir}" ]; then 125 err 3 "$name: No root directory has been defined for ${_j}" 126 fi 127 if [ -z "${_ip}" ]; then 128 err 3 "$name: No IP address has been defined for ${_j}" 129 fi 130 131} 132 133# set_sysctl rc_knob mib msg 134# If the mib sysctl is set according to what rc_knob 135# specifies, this function does nothing. However if 136# rc_knob is set differently than mib, then the mib 137# is set accordingly and msg is displayed followed by 138# an '=" sign and the word 'YES' or 'NO'. 139# 140set_sysctl() 141{ 142 _knob="$1" 143 _mib="$2" 144 _msg="$3" 145 146 _current=`${SYSCTL} -n $_mib 2>/dev/null` 147 if checkyesno $_knob ; then 148 if [ "$_current" -ne 1 ]; then 149 echo -n " ${_msg}=YES" 150 ${SYSCTL_W} 1>/dev/null ${_mib}=1 151 fi 152 else 153 if [ "$_current" -ne 0 ]; then 154 echo -n " ${_msg}=NO" 155 ${SYSCTL_W} 1>/dev/null ${_mib}=0 156 fi 157 fi 158} 159 160# is_current_mountpoint() 161# Is the directory mount point for a currently mounted file 162# system? 163# 164is_current_mountpoint() 165{ 166 local _dir _dir2 167 168 _dir=$1 169 170 _dir=`echo $_dir | sed -Ee 's#//+#/#g' -e 's#/$##'` 171 [ ! -d "${_dir}" ] && return 1 172 _dir2=`df ${_dir} | tail +2 | awk '{ print $6 }'` 173 [ "${_dir}" = "${_dir2}" ] 174 return $? 175} 176 177# is_symlinked_mountpoint() 178# Is a mount point, or any of its parent directories, a symlink? 179# 180is_symlinked_mountpoint() 181{ 182 local _dir 183 184 _dir=$1 185 186 [ -L "$_dir" ] && return 0 187 [ "$_dir" = "/" ] && return 1 188 is_symlinked_mountpoint `dirname $_dir` 189 return $? 190} 191 192# secure_umount 193# Try to unmount a mount point without being vulnerable to 194# symlink attacks. 195# 196secure_umount() 197{ 198 local _dir 199 200 _dir=$1 201 202 if is_current_mountpoint ${_dir}; then 203 umount -f ${_dir} >/dev/null 2>&1 204 else 205 debug "Nothing mounted on ${_dir} - not unmounting" 206 fi 207} 208 209 210# jail_umount_fs 211# This function unmounts certain special filesystems in the 212# currently selected jail. The caller must call the init_variables() 213# routine before calling this one. 214# 215jail_umount_fs() 216{ 217 local _device _mountpt _rest 218 219 if checkyesno _fdescfs; then 220 if [ -d "${_fdescdir}" ] ; then 221 secure_umount ${_fdescdir} 222 fi 223 fi 224 if checkyesno _devfs; then 225 if [ -d "${_devdir}" ] ; then 226 secure_umount ${_devdir} 227 fi 228 fi 229 if checkyesno _procfs; then 230 if [ -d "${_procdir}" ] ; then 231 secure_umount ${_procdir} 232 fi 233 fi 234 if checkyesno _mount; then 235 [ -f "${_fstab}" ] || warn "${_fstab} does not exist" 236 tail -r ${_fstab} | while read _device _mountpt _rest; do 237 case ":${_device}" in 238 :#* | :) 239 continue 240 ;; 241 esac 242 secure_umount ${_mountpt} 243 done 244 fi 245} 246 247# jail_mount_fstab() 248# Mount file systems from a per jail fstab while trying to 249# secure against symlink attacks at the mount points. 250# 251# If we are certain we cannot secure against symlink attacks we 252# do not mount all of the file systems (since we cannot just not 253# mount the file system with the problematic mount point). 254# 255# The caller must call the init_variables() routine before 256# calling this one. 257# 258jail_mount_fstab() 259{ 260 local _device _mountpt _rest 261 262 while read _device _mountpt _rest; do 263 case ":${_device}" in 264 :#* | :) 265 continue 266 ;; 267 esac 268 if is_symlinked_mountpoint ${_mountpt}; then 269 warn "${_mountpt} has symlink as parent - not mounting from ${_fstab}" 270 return 271 fi 272 done <${_fstab} 273 mount -a -F "${_fstab}" 274} 275 276jail_start() 277{ 278 echo -n 'Configuring jails:' 279 set_sysctl jail_set_hostname_allow security.jail.set_hostname_allowed \ 280 set_hostname_allow 281 set_sysctl jail_socket_unixiproute_only \ 282 security.jail.socket_unixiproute_only unixiproute_only 283 set_sysctl jail_sysvipc_allow security.jail.sysvipc_allowed \ 284 sysvipc_allow 285 echo '.' 286 287 echo -n 'Starting jails:' 288 _tmp_dir=`mktemp -d /tmp/jail.XXXXXXXX` || \ 289 err 3 "$name: Can't create temp dir, exiting..." 290 for _jail in ${jail_list} 291 do 292 init_variables $_jail 293 if [ -f /var/run/jail_${_jail}.id ]; then 294 echo -n " [${_hostname} already running (/var/run/jail_${_jail}.id exists)]" 295 continue; 296 fi 297 if [ -n "${_interface}" ]; then 298 ifconfig ${_interface} alias ${_ip} netmask 255.255.255.255 299 fi 300 if checkyesno _mount; then 301 info "Mounting fstab for jail ${_jail} (${_fstab})" 302 if [ ! -f "${_fstab}" ]; then 303 err 3 "$name: ${_fstab} does not exist" 304 fi 305 jail_mount_fstab 306 fi 307 if checkyesno _devfs; then 308 # If devfs is already mounted here, skip it. 309 df -t devfs "${_devdir}" >/dev/null 310 if [ $? -ne 0 ]; then 311 if is_symlinked_mountpoint ${_devdir}; then 312 warn "${_devdir} has symlink as parent - not starting jail ${_jail}" 313 continue 314 fi 315 info "Mounting devfs on ${_devdir}" 316 devfs_mount_jail "${_devdir}" ${_ruleset} 317 # Transitional symlink for old binaries 318 if [ ! -L "${_devdir}/log" ]; then 319 __pwd="`pwd`" 320 cd "${_devdir}" 321 ln -sf ../var/run/log log 322 cd "$__pwd" 323 fi 324 fi 325 326 # XXX - It seems symlinks don't work when there 327 # is a devfs(5) device of the same name. 328 # Jail console output 329 # __pwd="`pwd`" 330 # cd "${_devdir}" 331 # ln -sf ../var/log/console console 332 # cd "$__pwd" 333 fi 334 if checkyesno _fdescfs; then 335 if is_symlinked_mountpoint ${_fdescdir}; then 336 warn "${_fdescdir} has symlink as parent, not mounting" 337 else 338 info "Mounting fdescfs on ${_fdescdir}" 339 mount -t fdescfs fdesc "${_fdescdir}" 340 fi 341 fi 342 if checkyesno _procfs; then 343 if is_symlinked_mountpoint ${_procdir}; then 344 warn "${_procdir} has symlink as parent, not mounting" 345 else 346 info "Mounting procfs onto ${_procdir}" 347 if [ -d "${_procdir}" ] ; then 348 mount -t procfs proc "${_procdir}" 349 fi 350 fi 351 fi 352 _tmp_jail=${_tmp_dir}/jail.$$ 353 eval jail ${_flags} -i ${_rootdir} ${_hostname} \ 354 ${_ip} ${_exec_start} > ${_tmp_jail} 2>&1 355 356 if [ "$?" -eq 0 ] ; then 357 _jail_id=$(head -1 ${_tmp_jail}) 358 i=1 359 while [ true ]; do 360 eval out=\"\${_exec_afterstart${i}:-''}\" 361 362 if [ -z "$out" ]; then 363 break; 364 fi 365 366 jexec "${_jail_id}" ${out} 367 i=$((i + 1)) 368 done 369 370 echo -n " $_hostname" 371 tail +2 ${_tmp_jail} >${_consolelog} 372 echo ${_jail_id} > /var/run/jail_${_jail}.id 373 else 374 jail_umount_fs 375 if [ -n "${_interface}" ]; then 376 ifconfig ${_interface} -alias ${_ip} 377 fi 378 echo " cannot start jail \"${_jail}\": " 379 tail +2 ${_tmp_jail} 380 fi 381 rm -f ${_tmp_jail} 382 done 383 rmdir ${_tmp_dir} 384 echo '.' 385} 386 387jail_stop() 388{ 389 echo -n 'Stopping jails:' 390 for _jail in ${jail_list} 391 do 392 if [ -f "/var/run/jail_${_jail}.id" ]; then 393 _jail_id=$(cat /var/run/jail_${_jail}.id) 394 if [ ! -z "${_jail_id}" ]; then 395 init_variables $_jail 396 if [ -n "${_exec_stop}" ]; then 397 eval env -i /usr/sbin/jexec ${_jail_id} ${_exec_stop} \ 398 >> ${_consolelog} 2>&1 399 fi 400 killall -j ${_jail_id} -TERM > /dev/null 2>&1 401 sleep 1 402 killall -j ${_jail_id} -KILL > /dev/null 2>&1 403 jail_umount_fs 404 echo -n " $_hostname" 405 fi 406 if [ -n "${_interface}" ]; then 407 ifconfig ${_interface} -alias ${_ip} 408 fi 409 rm /var/run/jail_${_jail}.id 410 else 411 echo " cannot stop jail ${_jail}. No jail id in /var/run" 412 fi 413 done 414 echo '.' 415} 416 417load_rc_config $name 418cmd="$1" 419if [ $# -gt 0 ]; then 420 shift 421fi 422if [ -n "$*" ]; then 423 jail_list="$*" 424fi 425run_rc_command "${cmd}" 426