jail revision 183100
1164298Srwatson#!/bin/sh 2164298Srwatson# 3164298Srwatson# $FreeBSD: head/etc/rc.d/jail 183100 2008-09-16 20:18:25Z thompsa $ 4164298Srwatson# 5164298Srwatson 6164298Srwatson# PROVIDE: jail 7164298Srwatson# REQUIRE: LOGIN cleanvar 8164298Srwatson# BEFORE: securelevel 9164298Srwatson# KEYWORD: nojail shutdown 10164298Srwatson 11164298Srwatson# WARNING: This script deals with untrusted data (the data and 12164298Srwatson# processes inside the jails) and care must be taken when changing the 13164298Srwatson# code related to this! If you have any doubt whether a change is 14164298Srwatson# correct and have security impact, please get the patch reviewed by 15164298Srwatson# the FreeBSD Security Team prior to commit. 16164298Srwatson 17164298Srwatson. /etc/rc.subr 18164298Srwatson 19164298Srwatsonname="jail" 20164298Srwatsonrcvar=`set_rcvar` 21164298Srwatsonstart_cmd="jail_start" 22164298Srwatsonstop_cmd="jail_stop" 23164298Srwatson 24164298Srwatson# init_variables _j 25164298Srwatson# Initialize the various jail variables for jail _j. 26164298Srwatson# 27164298Srwatsoninit_variables() 28164298Srwatson{ 29164298Srwatson _j="$1" 30164298Srwatson 31164298Srwatson if [ -z "$_j" ]; then 32164298Srwatson warn "init_variables: you must specify a jail" 33164298Srwatson return 34164298Srwatson fi 35164298Srwatson 36164298Srwatson eval _rootdir=\"\$jail_${_j}_rootdir\" 37164298Srwatson _devdir="${_rootdir}/dev" 38164298Srwatson _fdescdir="${_devdir}/fd" 39164298Srwatson _procdir="${_rootdir}/proc" 40164298Srwatson eval _hostname=\"\$jail_${_j}_hostname\" 41164298Srwatson eval _ip=\"\$jail_${_j}_ip\" 42164298Srwatson eval _interface=\"\${jail_${_j}_interface:-${jail_interface}}\" 43164298Srwatson eval _exec=\"\$jail_${_j}_exec\" 44164298Srwatson eval _exec_start=\"\${jail_${_j}_exec_start:-${jail_exec_start}}\" 45164298Srwatson 46164298Srwatson i=1 47164298Srwatson while [ true ]; do 48164298Srwatson eval _exec_afterstart${i}=\"\${jail_${_j}_exec_afterstart${i}:-\${jail_exec_afterstart${i}}}\" 49164298Srwatson [ -z "$(eval echo \"\$_exec_afterstart${i}\")" ] && break 50164298Srwatson i=$((i + 1)) 51164298Srwatson done 52164298Srwatson 53164298Srwatson eval _exec_stop=\"\${jail_${_j}_exec_stop:-${jail_exec_stop}}\" 54164298Srwatson if [ -n "${_exec}" ]; then 55164298Srwatson # simple/backward-compatible execution 56164298Srwatson _exec_start="${_exec}" 57164298Srwatson _exec_stop="" 58164298Srwatson else 59164298Srwatson # flexible execution 60164298Srwatson if [ -z "${_exec_start}" ]; then 61164298Srwatson _exec_start="/bin/sh /etc/rc" 62164298Srwatson if [ -z "${_exec_stop}" ]; then 63164298Srwatson _exec_stop="/bin/sh /etc/rc.shutdown" 64164298Srwatson fi 65164298Srwatson fi 66164298Srwatson fi 67164298Srwatson 68164298Srwatson # The default jail ruleset will be used by rc.subr if none is specified. 69164298Srwatson eval _ruleset=\"\${jail_${_j}_devfs_ruleset:-${jail_devfs_ruleset}}\" 70164298Srwatson eval _devfs=\"\${jail_${_j}_devfs_enable:-${jail_devfs_enable}}\" 71164298Srwatson [ -z "${_devfs}" ] && _devfs="NO" 72164298Srwatson eval _fdescfs=\"\${jail_${_j}_fdescfs_enable:-${jail_fdescfs_enable}}\" 73164298Srwatson [ -z "${_fdescfs}" ] && _fdescfs="NO" 74164298Srwatson eval _procfs=\"\${jail_${_j}_procfs_enable:-${jail_procfs_enable}}\" 75164298Srwatson [ -z "${_procfs}" ] && _procfs="NO" 76164298Srwatson 77164298Srwatson eval _mount=\"\${jail_${_j}_mount_enable:-${jail_mount_enable}}\" 78164298Srwatson [ -z "${_mount}" ] && _mount="NO" 79164298Srwatson # "/etc/fstab.${_j}" will be used for {,u}mount(8) if none is specified. 80164298Srwatson eval _fstab=\"\${jail_${_j}_fstab:-${jail_fstab}}\" 81164298Srwatson [ -z "${_fstab}" ] && _fstab="/etc/fstab.${_j}" 82164298Srwatson eval _flags=\"\${jail_${_j}_flags:-${jail_flags}}\" 83164298Srwatson [ -z "${_flags}" ] && _flags="-l -U root" 84164298Srwatson eval _consolelog=\"\${jail_${_j}_consolelog:-${jail_consolelog}}\" 85164298Srwatson [ -z "${_consolelog}" ] && _consolelog="/var/log/jail_${_j}_console.log" 86164298Srwatson eval _fib=\"\${jail_${_j}_fib:-${jail_fib}}\" 87164298Srwatson 88164298Srwatson # Debugging aid 89164298Srwatson # 90164298Srwatson debug "$_j devfs enable: $_devfs" 91164298Srwatson debug "$_j fdescfs enable: $_fdescfs" 92164298Srwatson debug "$_j procfs enable: $_procfs" 93164298Srwatson debug "$_j mount enable: $_mount" 94164298Srwatson debug "$_j hostname: $_hostname" 95164298Srwatson debug "$_j ip: $_ip" 96164298Srwatson debug "$_j interface: $_interface" 97164298Srwatson debug "$_j fib: $_fib" 98164298Srwatson debug "$_j root: $_rootdir" 99164298Srwatson debug "$_j devdir: $_devdir" 100164298Srwatson debug "$_j fdescdir: $_fdescdir" 101164298Srwatson debug "$_j procdir: $_procdir" 102164298Srwatson debug "$_j ruleset: $_ruleset" 103164298Srwatson debug "$_j fstab: $_fstab" 104164298Srwatson debug "$_j exec start: $_exec_start" 105164298Srwatson debug "$_j consolelog: $_consolelog" 106164298Srwatson 107164298Srwatson i=1 108164298Srwatson while [ true ]; do 109164298Srwatson eval out=\"\${_exec_afterstart${i}:-''}\" 110164298Srwatson 111164298Srwatson if [ -z "$out" ]; then 112164298Srwatson break; 113164298Srwatson fi 114164298Srwatson 115164298Srwatson debug "$_j exec after start #${i}: ${out}" 116164298Srwatson i=$((i + 1)) 117164298Srwatson done 118164298Srwatson 119164298Srwatson debug "$_j exec stop: $_exec_stop" 120164298Srwatson debug "$_j flags: $_flags" 121164298Srwatson debug "$_j consolelog: $_consolelog" 122164298Srwatson 123164298Srwatson if [ -z "${_hostname}" ]; then 124164298Srwatson err 3 "$name: No hostname has been defined for ${_j}" 125164298Srwatson fi 126164298Srwatson if [ -z "${_rootdir}" ]; then 127164298Srwatson err 3 "$name: No root directory has been defined for ${_j}" 128164298Srwatson fi 129164298Srwatson if [ -z "${_ip}" ]; then 130164298Srwatson err 3 "$name: No IP address has been defined for ${_j}" 131164298Srwatson fi 132164298Srwatson 133164298Srwatson} 134164298Srwatson 135164298Srwatson# set_sysctl rc_knob mib msg 136164298Srwatson# If the mib sysctl is set according to what rc_knob 137164298Srwatson# specifies, this function does nothing. However if 138164298Srwatson# rc_knob is set differently than mib, then the mib 139164298Srwatson# is set accordingly and msg is displayed followed by 140164298Srwatson# an '=" sign and the word 'YES' or 'NO'. 141164298Srwatson# 142164298Srwatsonset_sysctl() 143164298Srwatson{ 144164298Srwatson _knob="$1" 145164298Srwatson _mib="$2" 146164298Srwatson _msg="$3" 147164298Srwatson 148164298Srwatson _current=`${SYSCTL} -n $_mib 2>/dev/null` 149164298Srwatson if checkyesno $_knob ; then 150164298Srwatson if [ "$_current" -ne 1 ]; then 151164298Srwatson echo -n " ${_msg}=YES" 152164298Srwatson ${SYSCTL_W} 1>/dev/null ${_mib}=1 153164298Srwatson fi 154164298Srwatson else 155164298Srwatson if [ "$_current" -ne 0 ]; then 156164298Srwatson echo -n " ${_msg}=NO" 157164298Srwatson ${SYSCTL_W} 1>/dev/null ${_mib}=0 158164298Srwatson fi 159164298Srwatson fi 160164298Srwatson} 161164298Srwatson 162164298Srwatson# is_current_mountpoint() 163164298Srwatson# Is the directory mount point for a currently mounted file 164164298Srwatson# system? 165164298Srwatson# 166164298Srwatsonis_current_mountpoint() 167164298Srwatson{ 168164298Srwatson local _dir _dir2 169164298Srwatson 170164298Srwatson _dir=$1 171164298Srwatson 172164298Srwatson _dir=`echo $_dir | sed -Ee 's#//+#/#g' -e 's#/$##'` 173164298Srwatson [ ! -d "${_dir}" ] && return 1 174164298Srwatson _dir2=`df ${_dir} | tail +2 | awk '{ print $6 }'` 175164298Srwatson [ "${_dir}" = "${_dir2}" ] 176164298Srwatson return $? 177164298Srwatson} 178164298Srwatson 179164298Srwatson# is_symlinked_mountpoint() 180164298Srwatson# Is a mount point, or any of its parent directories, a symlink? 181164298Srwatson# 182164298Srwatsonis_symlinked_mountpoint() 183164298Srwatson{ 184164298Srwatson local _dir 185164298Srwatson 186164298Srwatson _dir=$1 187164298Srwatson 188164298Srwatson [ -L "$_dir" ] && return 0 189164298Srwatson [ "$_dir" = "/" ] && return 1 190164298Srwatson is_symlinked_mountpoint `dirname $_dir` 191164298Srwatson return $? 192164298Srwatson} 193164298Srwatson 194164298Srwatson# secure_umount 195164298Srwatson# Try to unmount a mount point without being vulnerable to 196164298Srwatson# symlink attacks. 197164298Srwatson# 198164298Srwatsonsecure_umount() 199164298Srwatson{ 200164298Srwatson local _dir 201164298Srwatson 202164298Srwatson _dir=$1 203164298Srwatson 204164298Srwatson if is_current_mountpoint ${_dir}; then 205164298Srwatson umount -f ${_dir} >/dev/null 2>&1 206164298Srwatson else 207164298Srwatson debug "Nothing mounted on ${_dir} - not unmounting" 208164298Srwatson fi 209164298Srwatson} 210164298Srwatson 211164298Srwatson 212164298Srwatson# jail_umount_fs 213# This function unmounts certain special filesystems in the 214# currently selected jail. The caller must call the init_variables() 215# routine before calling this one. 216# 217jail_umount_fs() 218{ 219 local _device _mountpt _rest 220 221 if checkyesno _fdescfs; then 222 if [ -d "${_fdescdir}" ] ; then 223 secure_umount ${_fdescdir} 224 fi 225 fi 226 if checkyesno _devfs; then 227 if [ -d "${_devdir}" ] ; then 228 secure_umount ${_devdir} 229 fi 230 fi 231 if checkyesno _procfs; then 232 if [ -d "${_procdir}" ] ; then 233 secure_umount ${_procdir} 234 fi 235 fi 236 if checkyesno _mount; then 237 [ -f "${_fstab}" ] || warn "${_fstab} does not exist" 238 tail -r ${_fstab} | while read _device _mountpt _rest; do 239 case ":${_device}" in 240 :#* | :) 241 continue 242 ;; 243 esac 244 secure_umount ${_mountpt} 245 done 246 fi 247} 248 249# jail_mount_fstab() 250# Mount file systems from a per jail fstab while trying to 251# secure against symlink attacks at the mount points. 252# 253# If we are certain we cannot secure against symlink attacks we 254# do not mount all of the file systems (since we cannot just not 255# mount the file system with the problematic mount point). 256# 257# The caller must call the init_variables() routine before 258# calling this one. 259# 260jail_mount_fstab() 261{ 262 local _device _mountpt _rest 263 264 while read _device _mountpt _rest; do 265 case ":${_device}" in 266 :#* | :) 267 continue 268 ;; 269 esac 270 if is_symlinked_mountpoint ${_mountpt}; then 271 warn "${_mountpt} has symlink as parent - not mounting from ${_fstab}" 272 return 273 fi 274 done <${_fstab} 275 mount -a -F "${_fstab}" 276} 277 278jail_start() 279{ 280 echo -n 'Configuring jails:' 281 set_sysctl jail_set_hostname_allow security.jail.set_hostname_allowed \ 282 set_hostname_allow 283 set_sysctl jail_socket_unixiproute_only \ 284 security.jail.socket_unixiproute_only unixiproute_only 285 set_sysctl jail_sysvipc_allow security.jail.sysvipc_allowed \ 286 sysvipc_allow 287 echo '.' 288 289 echo -n 'Starting jails:' 290 _tmp_dir=`mktemp -d /tmp/jail.XXXXXXXX` || \ 291 err 3 "$name: Can't create temp dir, exiting..." 292 for _jail in ${jail_list} 293 do 294 init_variables $_jail 295 if [ -f /var/run/jail_${_jail}.id ]; then 296 echo -n " [${_hostname} already running (/var/run/jail_${_jail}.id exists)]" 297 continue; 298 fi 299 if [ -n "${_interface}" ]; then 300 ifconfig ${_interface} alias ${_ip} netmask 255.255.255.255 301 fi 302 if [ -n "${_fib}" ]; then 303 _setfib="setfib -F '${_fib}'" 304 else 305 _setfib="" 306 fi 307 if checkyesno _mount; then 308 info "Mounting fstab for jail ${_jail} (${_fstab})" 309 if [ ! -f "${_fstab}" ]; then 310 err 3 "$name: ${_fstab} does not exist" 311 fi 312 jail_mount_fstab 313 fi 314 if checkyesno _devfs; then 315 # If devfs is already mounted here, skip it. 316 df -t devfs "${_devdir}" >/dev/null 317 if [ $? -ne 0 ]; then 318 if is_symlinked_mountpoint ${_devdir}; then 319 warn "${_devdir} has symlink as parent - not starting jail ${_jail}" 320 continue 321 fi 322 info "Mounting devfs on ${_devdir}" 323 devfs_mount_jail "${_devdir}" ${_ruleset} 324 # Transitional symlink for old binaries 325 if [ ! -L "${_devdir}/log" ]; then 326 __pwd="`pwd`" 327 cd "${_devdir}" 328 ln -sf ../var/run/log log 329 cd "$__pwd" 330 fi 331 fi 332 333 # XXX - It seems symlinks don't work when there 334 # is a devfs(5) device of the same name. 335 # Jail console output 336 # __pwd="`pwd`" 337 # cd "${_devdir}" 338 # ln -sf ../var/log/console console 339 # cd "$__pwd" 340 fi 341 if checkyesno _fdescfs; then 342 if is_symlinked_mountpoint ${_fdescdir}; then 343 warn "${_fdescdir} has symlink as parent, not mounting" 344 else 345 info "Mounting fdescfs on ${_fdescdir}" 346 mount -t fdescfs fdesc "${_fdescdir}" 347 fi 348 fi 349 if checkyesno _procfs; then 350 if is_symlinked_mountpoint ${_procdir}; then 351 warn "${_procdir} has symlink as parent, not mounting" 352 else 353 info "Mounting procfs onto ${_procdir}" 354 if [ -d "${_procdir}" ] ; then 355 mount -t procfs proc "${_procdir}" 356 fi 357 fi 358 fi 359 _tmp_jail=${_tmp_dir}/jail.$$ 360 eval ${_setfib} jail ${_flags} -i ${_rootdir} ${_hostname} \ 361 ${_ip} ${_exec_start} > ${_tmp_jail} 2>&1 362 363 if [ "$?" -eq 0 ] ; then 364 _jail_id=$(head -1 ${_tmp_jail}) 365 i=1 366 while [ true ]; do 367 eval out=\"\${_exec_afterstart${i}:-''}\" 368 369 if [ -z "$out" ]; then 370 break; 371 fi 372 373 jexec "${_jail_id}" ${out} 374 i=$((i + 1)) 375 done 376 377 echo -n " $_hostname" 378 tail +2 ${_tmp_jail} >${_consolelog} 379 echo ${_jail_id} > /var/run/jail_${_jail}.id 380 else 381 jail_umount_fs 382 if [ -n "${_interface}" ]; then 383 ifconfig ${_interface} -alias ${_ip} 384 fi 385 echo " cannot start jail \"${_jail}\": " 386 tail +2 ${_tmp_jail} 387 fi 388 rm -f ${_tmp_jail} 389 done 390 rmdir ${_tmp_dir} 391 echo '.' 392} 393 394jail_stop() 395{ 396 echo -n 'Stopping jails:' 397 for _jail in ${jail_list} 398 do 399 if [ -f "/var/run/jail_${_jail}.id" ]; then 400 _jail_id=$(cat /var/run/jail_${_jail}.id) 401 if [ ! -z "${_jail_id}" ]; then 402 init_variables $_jail 403 if [ -n "${_exec_stop}" ]; then 404 eval env -i /usr/sbin/jexec ${_jail_id} ${_exec_stop} \ 405 >> ${_consolelog} 2>&1 406 fi 407 killall -j ${_jail_id} -TERM > /dev/null 2>&1 408 sleep 1 409 killall -j ${_jail_id} -KILL > /dev/null 2>&1 410 jail_umount_fs 411 echo -n " $_hostname" 412 fi 413 if [ -n "${_interface}" ]; then 414 ifconfig ${_interface} -alias ${_ip} 415 fi 416 rm /var/run/jail_${_jail}.id 417 else 418 echo " cannot stop jail ${_jail}. No jail id in /var/run" 419 fi 420 done 421 echo '.' 422} 423 424load_rc_config $name 425cmd="$1" 426if [ $# -gt 0 ]; then 427 shift 428fi 429if [ -n "$*" ]; then 430 jail_list="$*" 431fi 432run_rc_command "${cmd}" 433