1#!/bin/sh 2# 3# $FreeBSD$ 4# 5 6# PROVIDE: jail 7# REQUIRE: LOGIN FILESYSTEMS 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="jail_enable" 21 22start_precmd="jail_prestart" 23start_cmd="jail_start" 24stop_cmd="jail_stop" 25 26# init_variables _j 27# Initialize the various jail variables for jail _j. 28# 29init_variables() 30{ 31 _j="$1" 32 33 if [ -z "$_j" ]; then 34 warn "init_variables: you must specify a jail" 35 return 36 fi 37 38 eval _rootdir=\"\$jail_${_j}_rootdir\" 39 _devdir="${_rootdir}/dev" 40 _fdescdir="${_devdir}/fd" 41 _procdir="${_rootdir}/proc" 42 eval _hostname=\"\$jail_${_j}_hostname\" 43 eval _ip=\"\$jail_${_j}_ip\" 44 eval _interface=\"\${jail_${_j}_interface:-${jail_interface}}\" 45 eval _exec=\"\$jail_${_j}_exec\" 46 47 i=0 48 while : ; do 49 eval _exec_prestart${i}=\"\${jail_${_j}_exec_prestart${i}:-\${jail_exec_prestart${i}}}\" 50 [ -z "$(eval echo \"\$_exec_prestart${i}\")" ] && break 51 i=$((i + 1)) 52 done 53 54 eval _exec_start=\"\${jail_${_j}_exec_start:-${jail_exec_start}}\" 55 56 i=1 57 while : ; do 58 eval _exec_afterstart${i}=\"\${jail_${_j}_exec_afterstart${i}:-\${jail_exec_afterstart${i}}}\" 59 [ -z "$(eval echo \"\$_exec_afterstart${i}\")" ] && break 60 i=$((i + 1)) 61 done 62 63 i=0 64 while : ; do 65 eval _exec_poststart${i}=\"\${jail_${_j}_exec_poststart${i}:-\${jail_exec_poststart${i}}}\" 66 [ -z "$(eval echo \"\$_exec_poststart${i}\")" ] && break 67 i=$((i + 1)) 68 done 69 70 i=0 71 while : ; do 72 eval _exec_prestop${i}=\"\${jail_${_j}_exec_prestop${i}:-\${jail_exec_prestop${i}}}\" 73 [ -z "$(eval echo \"\$_exec_prestop${i}\")" ] && break 74 i=$((i + 1)) 75 done 76 77 eval _exec_stop=\"\${jail_${_j}_exec_stop:-${jail_exec_stop}}\" 78 79 i=0 80 while : ; do 81 eval _exec_poststop${i}=\"\${jail_${_j}_exec_poststop${i}:-\${jail_exec_poststop${i}}}\" 82 [ -z "$(eval echo \"\$_exec_poststop${i}\")" ] && break 83 i=$((i + 1)) 84 done 85 86 if [ -n "${_exec}" ]; then 87 # simple/backward-compatible execution 88 _exec_start="${_exec}" 89 _exec_stop="" 90 else 91 # flexible execution 92 if [ -z "${_exec_start}" ]; then 93 _exec_start="/bin/sh /etc/rc" 94 if [ -z "${_exec_stop}" ]; then 95 _exec_stop="/bin/sh /etc/rc.shutdown" 96 fi 97 fi 98 fi 99 100 # The default jail ruleset will be used by rc.subr if none is specified. 101 eval _ruleset=\"\${jail_${_j}_devfs_ruleset:-${jail_devfs_ruleset}}\" 102 eval _devfs=\"\${jail_${_j}_devfs_enable:-${jail_devfs_enable}}\" 103 [ -z "${_devfs}" ] && _devfs="NO" 104 eval _fdescfs=\"\${jail_${_j}_fdescfs_enable:-${jail_fdescfs_enable}}\" 105 [ -z "${_fdescfs}" ] && _fdescfs="NO" 106 eval _procfs=\"\${jail_${_j}_procfs_enable:-${jail_procfs_enable}}\" 107 [ -z "${_procfs}" ] && _procfs="NO" 108 109 eval _mount=\"\${jail_${_j}_mount_enable:-${jail_mount_enable}}\" 110 [ -z "${_mount}" ] && _mount="NO" 111 # "/etc/fstab.${_j}" will be used for {,u}mount(8) if none is specified. 112 eval _fstab=\"\${jail_${_j}_fstab:-${jail_fstab}}\" 113 [ -z "${_fstab}" ] && _fstab="/etc/fstab.${_j}" 114 eval _flags=\"\${jail_${_j}_flags:-${jail_flags}}\" 115 [ -z "${_flags}" ] && _flags="-l -U root" 116 eval _consolelog=\"\${jail_${_j}_consolelog:-${jail_consolelog}}\" 117 [ -z "${_consolelog}" ] && _consolelog="/var/log/jail_${_j}_console.log" 118 eval _parameters=\"\${jail_${_j}_parameters:-${jail_parameters}}\" 119 [ -z "${_parameters}" ] && _parameters="" 120 eval _fib=\"\${jail_${_j}_fib:-${jail_fib}}\" 121 122 # Debugging aid 123 # 124 debug "$_j devfs enable: $_devfs" 125 debug "$_j fdescfs enable: $_fdescfs" 126 debug "$_j procfs enable: $_procfs" 127 debug "$_j mount enable: $_mount" 128 debug "$_j hostname: $_hostname" 129 debug "$_j ip: $_ip" 130 jail_show_addresses ${_j} 131 debug "$_j interface: $_interface" 132 debug "$_j fib: $_fib" 133 debug "$_j root: $_rootdir" 134 debug "$_j devdir: $_devdir" 135 debug "$_j fdescdir: $_fdescdir" 136 debug "$_j procdir: $_procdir" 137 debug "$_j ruleset: $_ruleset" 138 debug "$_j fstab: $_fstab" 139 140 i=0 141 while : ; do 142 eval out=\"\${_exec_prestart${i}:-''}\" 143 if [ -z "$out" ]; then 144 break 145 fi 146 debug "$_j exec pre-start #${i}: ${out}" 147 i=$((i + 1)) 148 done 149 150 debug "$_j exec start: $_exec_start" 151 152 i=1 153 while : ; do 154 eval out=\"\${_exec_afterstart${i}:-''}\" 155 156 if [ -z "$out" ]; then 157 break; 158 fi 159 160 debug "$_j exec after start #${i}: ${out}" 161 i=$((i + 1)) 162 done 163 164 i=0 165 while : ; do 166 eval out=\"\${_exec_poststart${i}:-''}\" 167 if [ -z "$out" ]; then 168 break 169 fi 170 debug "$_j exec post-start #${i}: ${out}" 171 i=$((i + 1)) 172 done 173 174 i=0 175 while : ; do 176 eval out=\"\${_exec_prestop${i}:-''}\" 177 if [ -z "$out" ]; then 178 break 179 fi 180 debug "$_j exec pre-stop #${i}: ${out}" 181 i=$((i + 1)) 182 done 183 184 debug "$_j exec stop: $_exec_stop" 185 186 i=0 187 while : ; do 188 eval out=\"\${_exec_poststop${i}:-''}\" 189 if [ -z "$out" ]; then 190 break 191 fi 192 debug "$_j exec post-stop #${i}: ${out}" 193 i=$((i + 1)) 194 done 195 196 debug "$_j flags: $_flags" 197 debug "$_j consolelog: $_consolelog" 198 debug "$_j parameters: $_parameters" 199 200 if [ -z "${_hostname}" ]; then 201 err 3 "$name: No hostname has been defined for ${_j}" 202 fi 203 if [ -z "${_rootdir}" ]; then 204 err 3 "$name: No root directory has been defined for ${_j}" 205 fi 206} 207 208# set_sysctl rc_knob mib msg 209# If the mib sysctl is set according to what rc_knob 210# specifies, this function does nothing. However if 211# rc_knob is set differently than mib, then the mib 212# is set accordingly and msg is displayed followed by 213# an '=" sign and the word 'YES' or 'NO'. 214# 215set_sysctl() 216{ 217 _knob="$1" 218 _mib="$2" 219 _msg="$3" 220 221 _current=`${SYSCTL} -n $_mib 2>/dev/null` 222 if checkyesno $_knob ; then 223 if [ "$_current" -ne 1 ]; then 224 echo -n " ${_msg}=YES" 225 ${SYSCTL} 1>/dev/null ${_mib}=1 226 fi 227 else 228 if [ "$_current" -ne 0 ]; then 229 echo -n " ${_msg}=NO" 230 ${SYSCTL} 1>/dev/null ${_mib}=0 231 fi 232 fi 233} 234 235# is_current_mountpoint() 236# Is the directory mount point for a currently mounted file 237# system? 238# 239is_current_mountpoint() 240{ 241 local _dir _dir2 242 243 _dir=$1 244 245 _dir=`echo $_dir | sed -Ee 's#//+#/#g' -e 's#/$##'` 246 [ ! -d "${_dir}" ] && return 1 247 _dir2=`df ${_dir} | tail +2 | awk '{ print $6 }'` 248 [ "${_dir}" = "${_dir2}" ] 249 return $? 250} 251 252# is_symlinked_mountpoint() 253# Is a mount point, or any of its parent directories, a symlink? 254# 255is_symlinked_mountpoint() 256{ 257 local _dir 258 259 _dir=$1 260 261 [ -L "$_dir" ] && return 0 262 [ "$_dir" = "/" ] && return 1 263 is_symlinked_mountpoint `dirname $_dir` 264 return $? 265} 266 267# secure_umount 268# Try to unmount a mount point without being vulnerable to 269# symlink attacks. 270# 271secure_umount() 272{ 273 local _dir 274 275 _dir=$1 276 277 if is_current_mountpoint ${_dir}; then 278 umount -f ${_dir} >/dev/null 2>&1 279 else 280 debug "Nothing mounted on ${_dir} - not unmounting" 281 fi 282} 283 284 285# jail_umount_fs 286# This function unmounts certain special filesystems in the 287# currently selected jail. The caller must call the init_variables() 288# routine before calling this one. 289# 290jail_umount_fs() 291{ 292 local _device _mountpt _rest 293 294 if checkyesno _fdescfs; then 295 if [ -d "${_fdescdir}" ] ; then 296 secure_umount ${_fdescdir} 297 fi 298 fi 299 if checkyesno _devfs; then 300 if [ -d "${_devdir}" ] ; then 301 secure_umount ${_devdir} 302 fi 303 fi 304 if checkyesno _procfs; then 305 if [ -d "${_procdir}" ] ; then 306 secure_umount ${_procdir} 307 fi 308 fi 309 if checkyesno _mount; then 310 [ -f "${_fstab}" ] || warn "${_fstab} does not exist" 311 tail -r ${_fstab} | while read _device _mountpt _rest; do 312 case ":${_device}" in 313 :#* | :) 314 continue 315 ;; 316 esac 317 secure_umount ${_mountpt} 318 done 319 fi 320} 321 322# jail_mount_fstab() 323# Mount file systems from a per jail fstab while trying to 324# secure against symlink attacks at the mount points. 325# 326# If we are certain we cannot secure against symlink attacks we 327# do not mount all of the file systems (since we cannot just not 328# mount the file system with the problematic mount point). 329# 330# The caller must call the init_variables() routine before 331# calling this one. 332# 333jail_mount_fstab() 334{ 335 local _device _mountpt _rest 336 337 while read _device _mountpt _rest; do 338 case ":${_device}" in 339 :#* | :) 340 continue 341 ;; 342 esac 343 if is_symlinked_mountpoint ${_mountpt}; then 344 warn "${_mountpt} has symlink as parent - not mounting from ${_fstab}" 345 return 346 fi 347 done <${_fstab} 348 mount -a -F "${_fstab}" 349} 350 351# jail_show_addresses jail 352# Debug print the input for the given _multi aliases 353# for a jail for init_variables(). 354# 355jail_show_addresses() 356{ 357 local _j _type alias 358 _j="$1" 359 alias=0 360 361 if [ -z "${_j}" ]; then 362 warn "jail_show_addresses: you must specify a jail" 363 return 364 fi 365 366 while : ; do 367 eval _addr=\"\$jail_${_j}_ip_multi${alias}\" 368 if [ -n "${_addr}" ]; then 369 debug "${_j} ip_multi${alias}: $_addr" 370 alias=$((${alias} + 1)) 371 else 372 break 373 fi 374 done 375} 376 377# jail_extract_address argument 378# The second argument is the string from one of the _ip 379# or the _multi variables. In case of a comma separated list 380# only one argument must be passed in at a time. 381# The function alters the _type, _iface, _addr and _mask variables. 382# 383jail_extract_address() 384{ 385 local _i 386 _i=$1 387 388 if [ -z "${_i}" ]; then 389 warn "jail_extract_address: called without input" 390 return 391 fi 392 393 # Check if we have an interface prefix given and split into 394 # iFace and rest. 395 case "${_i}" in 396 *\|*) # ifN|.. prefix there 397 _iface=${_i%%|*} 398 _r=${_i##*|} 399 ;; 400 *) _iface="" 401 _r=${_i} 402 ;; 403 esac 404 405 # In case the IP has no interface given, check if we have a global one. 406 _iface=${_iface:-${_interface}} 407 408 # Set address, cut off any prefix/netmask/prefixlen. 409 _addr=${_r} 410 _addr=${_addr%%[/ ]*} 411 412 # Theoretically we can return here if interface is not set, 413 # as we only care about the _mask if we call ifconfig. 414 # This is not done because we may want to santize IP addresses 415 # based on _type later, and optionally change the type as well. 416 417 # Extract the prefix/netmask/prefixlen part by cutting off the address. 418 _mask=${_r} 419 _mask=`expr "${_mask}" : "${_addr}\(.*\)"` 420 421 # Identify type {inet,inet6}. 422 case "${_addr}" in 423 *\.*\.*\.*) _type="inet" ;; 424 *:*) _type="inet6" ;; 425 *) warn "jail_extract_address: type not identified" 426 ;; 427 esac 428 429 # Handle the special /netmask instead of /prefix or 430 # "netmask xxx" case for legacy IP. 431 # We do NOT support shortend class-full netmasks. 432 if [ "${_type}" = "inet" ]; then 433 case "${_mask}" in 434 /*\.*\.*\.*) _mask=" netmask ${_mask#/}" ;; 435 *) ;; 436 esac 437 438 # In case _mask is still not set use /32. 439 _mask=${_mask:-/32} 440 441 elif [ "${_type}" = "inet6" ]; then 442 # In case _maske is not set for IPv6, use /128. 443 _mask=${_mask:-/128} 444 fi 445} 446 447# jail_handle_ips_option {add,del} input 448# Handle a single argument imput which can be a comma separated 449# list of addresses (theoretically with an option interface and 450# prefix/netmask/prefixlen). 451# 452jail_handle_ips_option() 453{ 454 local _x _action _type _i 455 _action=$1 456 _x=$2 457 458 if [ -z "${_x}" ]; then 459 # No IP given. This can happen for the primary address 460 # of each address family. 461 return 462 fi 463 464 # Loop, in case we find a comma separated list, we need to handle 465 # each argument on its own. 466 while [ ${#_x} -gt 0 ]; do 467 case "${_x}" in 468 *,*) # Extract the first argument and strip it off the list. 469 _i=`expr "${_x}" : '^\([^,]*\)'` 470 _x=`expr "${_x}" : "^[^,]*,\(.*\)"` 471 ;; 472 *) _i=${_x} 473 _x="" 474 ;; 475 esac 476 477 _type="" 478 _iface="" 479 _addr="" 480 _mask="" 481 jail_extract_address "${_i}" 482 483 # make sure we got an address. 484 case "${_addr}" in 485 "") continue ;; 486 *) ;; 487 esac 488 489 # Append address to list of addresses for the jail command. 490 case "${_type}" in 491 inet) 492 case "${_addrl}" in 493 "") _addrl="${_addr}" ;; 494 *) _addrl="${_addrl},${_addr}" ;; 495 esac 496 ;; 497 inet6) 498 case "${_addr6l}" in 499 "") _addr6l="${_addr}" ;; 500 *) _addr6l="${_addr6l},${_addr}" ;; 501 esac 502 ;; 503 esac 504 505 # Configure interface alias if requested by a given interface 506 # and if we could correctly parse everything. 507 case "${_iface}" in 508 "") continue ;; 509 esac 510 case "${_type}" in 511 inet) ;; 512 inet6) ipv6_address_count=$((ipv6_address_count + 1)) ;; 513 *) warn "Could not determine address family. Not going" \ 514 "to ${_action} address '${_addr}' for ${_jail}." 515 continue 516 ;; 517 esac 518 case "${_action}" in 519 add) ifconfig ${_iface} ${_type} ${_addr}${_mask} alias 520 ;; 521 del) # When removing the IP, ignore the _mask. 522 ifconfig ${_iface} ${_type} ${_addr} -alias 523 ;; 524 esac 525 done 526} 527 528# jail_ips {add,del} 529# Extract the comma separated list of addresses and return them 530# for the jail command. 531# Handle more than one address via the _multi option as well. 532# If an interface is given also add/remove an alias for the 533# address with an optional netmask. 534# 535jail_ips() 536{ 537 local _action 538 _action=$1 539 540 case "${_action}" in 541 add) ;; 542 del) ;; 543 *) warn "jail_ips: invalid action '${_action}'" 544 return 545 ;; 546 esac 547 548 # Handle addresses. 549 ipv6_address_count=0 550 jail_handle_ips_option ${_action} "${_ip}" 551 # Handle jail_xxx_ip_multi<N> 552 alias=0 553 while : ; do 554 eval _x=\"\$jail_${_jail}_ip_multi${alias}\" 555 case "${_x}" in 556 "") break ;; 557 *) jail_handle_ips_option ${_action} "${_x}" 558 alias=$((${alias} + 1)) 559 ;; 560 esac 561 done 562 case ${ipv6_address_count} in 563 0) ;; 564 *) # Sleep 1 second to let DAD complete before starting services. 565 sleep 1 566 ;; 567 esac 568} 569 570jail_prestart() 571{ 572 if checkyesno jail_parallel_start; then 573 command_args='&' 574 fi 575} 576 577jail_start() 578{ 579 echo -n 'Configuring jails:' 580 set_sysctl jail_set_hostname_allow security.jail.set_hostname_allowed \ 581 set_hostname_allow 582 set_sysctl jail_socket_unixiproute_only \ 583 security.jail.socket_unixiproute_only unixiproute_only 584 set_sysctl jail_sysvipc_allow security.jail.sysvipc_allowed \ 585 sysvipc_allow 586 echo '.' 587 588 echo -n 'Starting jails:' 589 _tmp_dir=`mktemp -d /tmp/jail.XXXXXXXX` || \ 590 err 3 "$name: Can't create temp dir, exiting..." 591 for _jail in ${jail_list} 592 do 593 init_variables $_jail 594 if [ -f /var/run/jail_${_jail}.id ]; then 595 echo -n " [${_hostname} already running (/var/run/jail_${_jail}.id exists)]" 596 continue; 597 fi 598 _addrl="" 599 _addr6l="" 600 jail_ips "add" 601 if [ -n "${_fib}" ]; then 602 _setfib="setfib -F '${_fib}'" 603 else 604 _setfib="" 605 fi 606 if checkyesno _mount; then 607 info "Mounting fstab for jail ${_jail} (${_fstab})" 608 if [ ! -f "${_fstab}" ]; then 609 err 3 "$name: ${_fstab} does not exist" 610 fi 611 jail_mount_fstab 612 fi 613 if checkyesno _devfs; then 614 # If devfs is already mounted here, skip it. 615 df -t devfs "${_devdir}" >/dev/null 616 if [ $? -ne 0 ]; then 617 if is_symlinked_mountpoint ${_devdir}; then 618 warn "${_devdir} has symlink as parent - not starting jail ${_jail}" 619 continue 620 fi 621 info "Mounting devfs on ${_devdir}" 622 devfs_mount_jail "${_devdir}" ${_ruleset} 623 # Transitional symlink for old binaries 624 if [ ! -L "${_devdir}/log" ]; then 625 ln -sf ../var/run/log "${_devdir}/log" 626 fi 627 fi 628 629 # XXX - It seems symlinks don't work when there 630 # is a devfs(5) device of the same name. 631 # Jail console output 632 # __pwd="`pwd`" 633 # cd "${_devdir}" 634 # ln -sf ../var/log/console console 635 # cd "$__pwd" 636 fi 637 if checkyesno _fdescfs; then 638 if is_symlinked_mountpoint ${_fdescdir}; then 639 warn "${_fdescdir} has symlink as parent, not mounting" 640 else 641 info "Mounting fdescfs on ${_fdescdir}" 642 mount -t fdescfs fdesc "${_fdescdir}" 643 fi 644 fi 645 if checkyesno _procfs; then 646 if is_symlinked_mountpoint ${_procdir}; then 647 warn "${_procdir} has symlink as parent, not mounting" 648 else 649 info "Mounting procfs onto ${_procdir}" 650 if [ -d "${_procdir}" ] ; then 651 mount -t procfs proc "${_procdir}" 652 fi 653 fi 654 fi 655 _tmp_jail=${_tmp_dir}/jail.$$ 656 657 i=0 658 while : ; do 659 eval out=\"\${_exec_prestart${i}:-''}\" 660 [ -z "$out" ] && break 661 ${out} 662 i=$((i + 1)) 663 done 664 665 eval ${_setfib} jail -n ${_jail} ${_flags} -i -c path=${_rootdir} host.hostname=${_hostname} \ 666 ${_addrl:+ip4.addr=\"${_addrl}\"} ${_addr6l:+ip6.addr=\"${_addr6l}\"} \ 667 ${_parameters} command=${_exec_start} > ${_tmp_jail} 2>&1 \ 668 </dev/null 669 670 if [ "$?" -eq 0 ] ; then 671 _jail_id=$(head -1 ${_tmp_jail}) 672 i=1 673 while : ; do 674 eval out=\"\${_exec_afterstart${i}:-''}\" 675 676 if [ -z "$out" ]; then 677 break; 678 fi 679 680 jexec "${_jail_id}" ${out} 681 i=$((i + 1)) 682 done 683 684 echo -n " $_hostname" 685 tail +2 ${_tmp_jail} >${_consolelog} 686 echo ${_jail_id} > /var/run/jail_${_jail}.id 687 688 i=0 689 while : ; do 690 eval out=\"\${_exec_poststart${i}:-''}\" 691 [ -z "$out" ] && break 692 ${out} 693 i=$((i + 1)) 694 done 695 else 696 jail_umount_fs 697 jail_ips "del" 698 echo " cannot start jail \"${_jail}\": " 699 tail +2 ${_tmp_jail} 700 fi 701 rm -f ${_tmp_jail} 702 done 703 rmdir ${_tmp_dir} 704 echo '.' 705} 706 707jail_stop() 708{ 709 echo -n 'Stopping jails:' 710 for _jail in ${jail_list} 711 do 712 if [ -f "/var/run/jail_${_jail}.id" ]; then 713 _jail_id=$(cat /var/run/jail_${_jail}.id) 714 if [ ! -z "${_jail_id}" ]; then 715 init_variables $_jail 716 717 i=0 718 while : ; do 719 eval out=\"\${_exec_prestop${i}:-''}\" 720 [ -z "$out" ] && break 721 ${out} 722 i=$((i + 1)) 723 done 724 725 if [ -n "${_exec_stop}" ]; then 726 eval env -i /usr/sbin/jexec ${_jail_id} ${_exec_stop} \ 727 >> ${_consolelog} 2>&1 728 fi 729 killall -j ${_jail_id} -TERM > /dev/null 2>&1 730 sleep 1 731 killall -j ${_jail_id} -KILL > /dev/null 2>&1 732 jail_umount_fs 733 echo -n " $_hostname" 734 735 i=0 736 while : ; do 737 eval out=\"\${_exec_poststop${i}:-''}\" 738 [ -z "$out" ] && break 739 ${out} 740 i=$((i + 1)) 741 done 742 fi 743 jail_ips "del" 744 rm /var/run/jail_${_jail}.id 745 else 746 echo " cannot stop jail ${_jail}. No jail id in /var/run" 747 fi 748 done 749 echo '.' 750} 751 752load_rc_config $name 753cmd="$1" 754if [ $# -gt 0 ]; then 755 shift 756fi 757if [ -n "$*" ]; then 758 jail_list="$*" 759fi 760 761run_rc_command "${cmd}" 762