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