1207151Smarius#!/bin/sh
2207151Smarius#
3207151Smarius# $FreeBSD: releng/10.2/etc/rc.d/jail 278484 2015-02-10 01:05:51Z jamie $
4207151Smarius#
5207151Smarius
6207151Smarius# PROVIDE: jail
7207151Smarius# REQUIRE: LOGIN FILESYSTEMS
8207151Smarius# BEFORE: securelevel
9207151Smarius# KEYWORD: nojail shutdown
10207151Smarius
11207151Smarius. /etc/rc.subr
12207151Smarius
13207151Smariusname="jail"
14207151Smariusrcvar="jail_enable"
15207151Smarius
16207151Smariusstart_cmd="jail_start"
17207151Smariusstart_postcmd="jail_warn"
18207151Smariusstop_cmd="jail_stop"
19207151Smariusconfig_cmd="jail_config"
20207151Smariusconsole_cmd="jail_console"
21207151Smariusstatus_cmd="jail_status"
22207151Smariusextra_commands="config console status"
23207151Smarius: ${jail_conf:=/etc/jail.conf}
24207151Smarius: ${jail_program:=/usr/sbin/jail}
25207151Smarius: ${jail_consolecmd:=/usr/bin/login -f root}
26207151Smarius: ${jail_jexec:=/usr/sbin/jexec}
27207151Smarius: ${jail_jls:=/usr/sbin/jls}
28207151Smarius
29207151Smariusneed_dad_wait=
30207151Smarius
31207151Smarius# extract_var jail name param num defval
32207151Smarius#	Extract value from ${jail_$jail_$name} or ${jail_$name} and
33207151Smarius#	set it to $param.  If not defined, $defval is used.
34207151Smarius#	When $num is [0-9]*, ${jail_$jail_$name$num} are looked up and
35207151Smarius#	$param is set by using +=.
36207151Smarius#	When $num is YN or NY, the value is interpret as boolean.
37207151Smariusextract_var()
38207151Smarius{
39207151Smarius	local i _j _name _param _num _def _name1 _name2
40207151Smarius	_j=$1
41207151Smarius	_name=$2
42207151Smarius	_param=$3
43207151Smarius	_num=$4
44207151Smarius	_def=$5
45207151Smarius
46207151Smarius	case $_num in
47207151Smarius	YN)
48207151Smarius		_name1=jail_${_j}_${_name}
49207151Smarius		_name2=jail_${_name}
50207151Smarius		eval $_name1=\"\${$_name1:-\${$_name2:-$_def}}\"
51207151Smarius		if checkyesno $_name1; then
52207151Smarius			echo "	$_param = 1;"
53207151Smarius		else
54207151Smarius			echo "	$_param = 0;"
55207151Smarius		fi
56207151Smarius	;;
57207151Smarius	NY)
58207151Smarius		_name1=jail_${_j}_${_name}
59207151Smarius		_name2=jail_${_name}
60207151Smarius		eval $_name1=\"\${$_name1:-\${$_name2:-$_def}}\"
61207151Smarius		if checkyesno $_name1; then
62207151Smarius			echo "	$_param = 0;"
63207151Smarius		else
64207151Smarius			echo "	$_param = 1;"
65207151Smarius		fi
66207151Smarius	;;
67207151Smarius	[0-9]*)
68207151Smarius		i=$_num
69207151Smarius		while : ; do
70207151Smarius			_name1=jail_${_j}_${_name}${i}
71207151Smarius			_name2=jail_${_name}${i}
72207151Smarius			eval _tmpargs=\"\${$_name1:-\${$_name2:-$_def}}\"
73207151Smarius			if [ -n "$_tmpargs" ]; then 
74207151Smarius				echo "	$_param += \"$_tmpargs\";"
75207151Smarius			else
76207151Smarius				break;
77207151Smarius			fi
78207151Smarius			i=$(($i + 1))
79207151Smarius		done
80207151Smarius	;;
81207151Smarius	*)
82207151Smarius		_name1=jail_${_j}_${_name}
83207151Smarius		_name2=jail_${_name}
84207151Smarius		eval _tmpargs=\"\${$_name1:-\${$_name2:-$_def}}\"
85207151Smarius		if [ -n "$_tmpargs" ]; then
86207151Smarius			echo "	$_param = \"$_tmpargs\";"
87207151Smarius		fi
88207151Smarius	;;
89207151Smarius	esac
90207151Smarius}
91207151Smarius
92207151Smarius# parse_options _j
93207151Smarius#	Parse options and create a temporary configuration file if necessary.
94207151Smarius#
95207151Smariusparse_options()
96207151Smarius{
97207151Smarius	local _j _p
98207151Smarius	_j=$1
99207151Smarius
100207151Smarius	_confwarn=0
101207151Smarius	if [ -z "$_j" ]; then
102207151Smarius		warn "parse_options: you must specify a jail"
103207151Smarius		return
104207151Smarius	fi
105207151Smarius	eval _jconf=\"\${jail_${_j}_conf:-/etc/jail.${_j}.conf}\"
106207151Smarius	eval _rootdir=\"\$jail_${_j}_rootdir\"
107207151Smarius	eval _hostname=\"\$jail_${_j}_hostname\"
108207151Smarius	if [ -z "$_rootdir" -o \
109207151Smarius	     -z "$_hostname" ]; then
110207151Smarius		if [ -r "$_jconf" ]; then
111207151Smarius			_conf="$_jconf"
112207151Smarius			return 0
113207151Smarius		elif [ -r "$jail_conf" ]; then
114207151Smarius			_conf="$jail_conf"
115207151Smarius			return 0
116207151Smarius		else
117207151Smarius			warn "Invalid configuration for $_j " \
118207151Smarius			    "(no jail.conf, no hostname, or no path).  " \
119207151Smarius			    "Jail $_j was ignored."
120207151Smarius		fi
121207151Smarius		return 1
122207151Smarius	fi
123207151Smarius	eval _ip=\"\$jail_${_j}_ip\"
124207151Smarius	if [ -z "$_ip" ] && ! check_kern_features vimage; then
125207151Smarius		warn "no ipaddress specified and no vimage support.  " \
126207151Smarius		    "Jail $_j was ignored."
127207151Smarius		return 1
128207151Smarius	fi
129207151Smarius	_conf=/var/run/jail.${_j}.conf
130207151Smarius	#
131207151Smarius	# To relieve confusion, show a warning message.
132207151Smarius	#
133207151Smarius	_confwarn=1
134207151Smarius	if [ -r "$jail_conf" -o -r "$_jconf" ]; then
135207151Smarius		if ! checkyesno jail_parallel_start; then
136207151Smarius			warn "$_conf is created and used for jail $_j."
137207151Smarius		fi
138207151Smarius	fi
139207151Smarius	/usr/bin/install -m 0644 -o root -g wheel /dev/null $_conf || return 1
140207151Smarius
141207151Smarius	eval : \${jail_${_j}_flags:=${jail_flags}}
142207151Smarius	eval _exec=\"\$jail_${_j}_exec\"
143207151Smarius	eval _exec_start=\"\$jail_${_j}_exec_start\"
144207151Smarius	eval _exec_stop=\"\$jail_${_j}_exec_stop\"
145207151Smarius	if [ -n "${_exec}" ]; then
146207151Smarius		#   simple/backward-compatible execution
147207151Smarius		_exec_start="${_exec}"
148207151Smarius		_exec_stop=""
149207151Smarius	else
150207151Smarius		#   flexible execution
151207151Smarius		if [ -z "${_exec_start}" ]; then
152207151Smarius			_exec_start="/bin/sh /etc/rc"
153207151Smarius			if [ -z "${_exec_stop}" ]; then
154207151Smarius				_exec_stop="/bin/sh /etc/rc.shutdown"
155207151Smarius			fi
156207151Smarius		fi
157207151Smarius	fi
158207151Smarius	eval _interface=\"\${jail_${_j}_interface:-${jail_interface}}\"
159207151Smarius	eval _parameters=\"\${jail_${_j}_parameters:-${jail_parameters}}\"
160207151Smarius	eval _fstab=\"\${jail_${_j}_fstab:-${jail_fstab:-/etc/fstab.$_j}}\"
161207151Smarius	(
162207151Smarius		date +"# Generated by rc.d/jail at %Y-%m-%d %H:%M:%S"
163207151Smarius		echo "$_j {"
164207151Smarius		extract_var $_j hostname host.hostname - ""
165207151Smarius		extract_var $_j rootdir path - ""
166207151Smarius		if [ -n "$_ip" ]; then
167207151Smarius			extract_var $_j interface interface - ""
168207151Smarius			jail_handle_ips_option $_ip $_interface
169207151Smarius			alias=0
170207151Smarius			while : ; do
171207151Smarius				eval _x=\"\$jail_${_j}_ip_multi${alias}\"
172207151Smarius				[ -z "$_x" ] && break
173207151Smarius
174207151Smarius				jail_handle_ips_option $_x $_interface
175207151Smarius				alias=$(($alias + 1))
176207151Smarius			done
177207151Smarius			case $need_dad_wait in
178207151Smarius			1)
179207151Smarius				# Sleep to let DAD complete before
180207151Smarius				# starting services.
181207151Smarius				echo "	exec.start += \"sleep " \
182207151Smarius				$(($(${SYSCTL_N} net.inet6.ip6.dad_count) + 1)) \
183207151Smarius				"\";"
184207151Smarius			;;
185207151Smarius			esac
186207151Smarius			# These are applicable only to non-vimage jails. 
187207151Smarius			extract_var $_j fib exec.fib - ""
188207151Smarius			extract_var $_j socket_unixiproute_only \
189207151Smarius			    allow.raw_sockets NY YES
190207151Smarius		else
191207151Smarius			echo "	vnet;"
192207151Smarius			extract_var $_j vnet_interface vnet.interface - ""
193207151Smarius		fi
194207151Smarius
195207151Smarius		echo "	exec.clean;"
196207151Smarius		echo "	exec.system_user = \"root\";"
197207151Smarius		echo "	exec.jail_user = \"root\";"
198207151Smarius		extract_var $_j exec_prestart exec.prestart 0 ""
199207151Smarius		extract_var $_j exec_poststart exec.poststart 0 ""
200207151Smarius		extract_var $_j exec_prestop exec.prestop 0 ""
201207151Smarius		extract_var $_j exec_poststop exec.poststop 0 ""
202207151Smarius
203207151Smarius		echo "	exec.start += \"$_exec_start\";"
204207151Smarius		extract_var $_j exec_afterstart exec.start 1 ""
205207151Smarius		echo "	exec.stop = \"$_exec_stop\";"
206207151Smarius
207207151Smarius		extract_var $_j consolelog exec.consolelog - \
208207151Smarius		    /var/log/jail_${_j}_console.log
209207151Smarius
210207151Smarius		if [ -r $_fstab ]; then
211207151Smarius			echo "	mount.fstab = \"$_fstab\";"
212207151Smarius		fi
213207151Smarius
214207151Smarius		eval : \${jail_${_j}_devfs_enable:=${jail_devfs_enable:-NO}}
215207151Smarius		if checkyesno jail_${_j}_devfs_enable; then
216207151Smarius			echo "	mount.devfs;"
217			eval _ruleset=\${jail_${_j}_devfs_ruleset:-${jail_devfs_ruleset}}
218			case $_ruleset in
219			"")	;;
220			[0-9]*) echo "	devfs_ruleset = \"$_ruleset\";" ;;
221			devfsrules_jail)
222				# XXX: This is the default value,
223				# Let jail(8) to use the default because
224				# mount(8) only accepts an integer. 
225				# This should accept a ruleset name.
226			;;
227			*)	warn "devfs_ruleset must be an integer." ;;
228			esac
229		fi
230		eval : \${jail_${_j}_fdescfs_enable:=${jail_fdescfs_enable:-NO}}
231		if checkyesno jail_${_j}_fdescfs_enable; then
232			echo "	mount.fdescfs;"
233		fi
234		eval : \${jail_${_j}_procfs_enable:=${jail_procfs_enable:-NO}}
235		if checkyesno jail_${_j}_procfs_enable; then
236			echo "	mount.procfs;"
237		fi
238
239		eval : \${jail_${_j}_mount_enable:=${jail_mount_enable:-NO}}
240		if checkyesno jail_${_j}_mount_enable; then
241			echo "	allow.mount;" >> $_conf
242		fi
243
244		extract_var $_j set_hostname_allow allow.set_hostname YN NO
245		extract_var $_j sysvipc_allow allow.sysvipc YN NO
246		for _p in $_parameters; do
247			echo "	${_p%\;};"
248		done
249		echo "}"
250	) >> $_conf
251
252	return 0
253}
254
255# jail_extract_address argument iface
256#	The second argument is the string from one of the _ip
257#	or the _multi variables. In case of a comma separated list
258#	only one argument must be passed in at a time.
259#	The function alters the _type, _iface, _addr and _mask variables.
260#
261jail_extract_address()
262{
263	local _i _interface
264	_i=$1
265	_interface=$2
266
267	if [ -z "${_i}" ]; then
268		warn "jail_extract_address: called without input"
269		return
270	fi
271
272	# Check if we have an interface prefix given and split into
273	# iFace and rest.
274	case "${_i}" in
275	*\|*)	# ifN|.. prefix there
276		_iface=${_i%%|*}
277		_r=${_i##*|}
278		;;
279	*)	_iface=""
280		_r=${_i}
281		;;
282	esac
283
284	# In case the IP has no interface given, check if we have a global one.
285	_iface=${_iface:-${_interface}}
286
287	# Set address, cut off any prefix/netmask/prefixlen.
288	_addr=${_r}
289	_addr=${_addr%%[/ ]*}
290
291	# Theoretically we can return here if interface is not set,
292	# as we only care about the _mask if we call ifconfig.
293	# This is not done because we may want to santize IP addresses
294	# based on _type later, and optionally change the type as well.
295
296	# Extract the prefix/netmask/prefixlen part by cutting off the address.
297	_mask=${_r}
298	_mask=`expr "${_mask}" : "${_addr}\(.*\)"`
299
300	# Identify type {inet,inet6}.
301	case "${_addr}" in
302	*\.*\.*\.*)	_type="inet" ;;
303	*:*)		_type="inet6" ;;
304	*)		warn "jail_extract_address: type not identified"
305			;;
306	esac
307
308	# Handle the special /netmask instead of /prefix or
309	# "netmask xxx" case for legacy IP.
310	# We do NOT support shortend class-full netmasks.
311	if [ "${_type}" = "inet" ]; then
312		case "${_mask}" in
313		/*\.*\.*\.*)	_mask=" netmask ${_mask#/}" ;;
314		*)		;;
315		esac
316
317		# In case _mask is still not set use /32.
318		_mask=${_mask:-/32}
319
320	elif [ "${_type}" = "inet6" ]; then
321		# In case _mask is not set for IPv6, use /128.
322		_mask=${_mask:-/128}
323	fi
324}
325
326# jail_handle_ips_option input iface
327#	Handle a single argument imput which can be a comma separated
328#	list of addresses (theoretically with an option interface and
329#	prefix/netmask/prefixlen).
330#
331jail_handle_ips_option()
332{
333	local _x _type _i _defif
334	_x=$1
335	_defif=$2
336
337	if [ -z "${_x}" ]; then
338		# No IP given. This can happen for the primary address
339		# of each address family.
340		return
341	fi
342
343	# Loop, in case we find a comma separated list, we need to handle
344	# each argument on its own.
345	while [ ${#_x} -gt 0 ]; do
346		case "${_x}" in
347		*,*)	# Extract the first argument and strip it off the list.
348			_i=`expr "${_x}" : '^\([^,]*\)'`
349			_x=`expr "${_x}" : "^[^,]*,\(.*\)"`
350		;;
351		*)	_i=${_x}
352			_x=""
353		;;
354		esac
355
356		_type=""
357		_addr=""
358		_mask=""
359		_iface=""
360		jail_extract_address $_i $_defif
361
362		# make sure we got an address.
363		case $_addr in
364		"")	continue ;;
365		*)	;;
366		esac
367
368		# Append address to list of addresses for the jail command.
369		case $_type in
370		inet)
371			echo "	ip4.addr += \"${_iface:+${_iface}|}${_addr}${_mask}\";"
372		;;
373		inet6)
374			echo "	ip6.addr += \"${_iface:+${_iface}|}${_addr}${_mask}\";"
375			need_dad_wait=1
376		;;
377		esac
378	done
379}
380
381jail_config()
382{
383	local _j
384
385	case $1 in
386	_ALL)	return ;;
387	esac
388	for _j in $@; do
389		_j=$(echo $_j | tr /. _)
390		if parse_options $_j; then 
391			echo "$_j: parameters are in $_conf."
392		fi
393	done
394}
395
396jail_console()
397{
398	local _j _cmd
399
400	# One argument that is not _ALL.
401	case $#:$1 in
402	0:*|1:_ALL)	err 3 "Specify a jail name." ;;
403	1:*)		;;
404	esac
405	_j=$(echo $1 | tr /. _)
406	shift
407	case $# in
408	0)	eval _cmd=\${jail_${_j}_consolecmd:-$jail_consolecmd} ;;
409	*)	_cmd=$@ ;;
410	esac
411	$jail_jexec $_j $_cmd
412}
413
414jail_status()
415{
416
417	$jail_jls -N
418}
419
420jail_start()
421{
422	local _j _jid _jl
423
424	if [ $# = 0 ]; then
425		return
426	fi
427	echo -n 'Starting jails:'
428	case $1 in
429	_ALL)
430		command=$jail_program
431		rc_flags=$jail_flags
432		command_args="-f $jail_conf -c"
433		_tmp=`mktemp -t jail` || exit 3
434		if $command $rc_flags $command_args >> $_tmp 2>&1; then
435			$jail_jls jid name | while read IN; do
436				set -- $IN
437				echo -n " $2"
438				echo $1 > /var/run/jail_$2.id
439			done
440		else
441			tail -1 $_tmp
442		fi
443		rm -f $_tmp
444		echo '.'
445		return
446	;;
447	esac
448	if checkyesno jail_parallel_start; then
449		#
450		# Start jails in parallel and then check jail id when
451		# jail_parallel_start is YES.
452		#
453		_jl=
454		for _j in $@; do
455			_j=$(echo $_j | tr /. _)
456			parse_options $_j || continue
457
458			_jl="$_jl $_j"
459			eval rc_flags=\${jail_${_j}_flags:-$jail_flags}
460			eval command=\${jail_${_j}_program:-$jail_program}
461			command_args="-i -f $_conf -c $_j"
462			$command $rc_flags $command_args \
463			    >/dev/null 2>&1 </dev/null &
464		done
465		sleep 1
466		for _j in $_jl; do
467			echo -n " ${_hostname:-${_j}}"
468			if _jid=$($jail_jls -j $_j jid); then
469				echo "$_jid" > /var/run/jail_${_j}.id
470			else
471				rm -f /var/run/jail_${_j}.id
472				echo " cannot start jail " \
473				    "\"${_hostname:-${_j}}\": "
474			fi
475		done
476	else
477		#
478		# Start jails one-by-one when jail_parallel_start is NO.
479		#
480		for _j in $@; do
481			_j=$(echo $_j | tr /. _)
482			parse_options $_j || continue
483
484			eval rc_flags=\${jail_${_j}_flags:-$jail_flags}
485			eval command=\${jail_${_j}_program:-$jail_program}
486			command_args="-i -f $_conf -c $_j"
487			_tmp=`mktemp -t jail` || exit 3
488			if $command $rc_flags $command_args \
489			    >> $_tmp 2>&1 </dev/null; then
490				echo -n " ${_hostname:-${_j}}"
491				_jid=$($jail_jls -j $_j jid)
492				echo $_jid > /var/run/jail_${_j}.id
493			else
494				rm -f /var/run/jail_${_j}.id
495				echo " cannot start jail " \
496				    "\"${_hostname:-${_j}}\": "
497				cat $_tmp
498			fi
499			rm -f $_tmp
500		done
501	fi
502	echo '.'
503}
504
505jail_stop()
506{
507	local _j
508
509	if [ $# = 0 ]; then
510		return
511	fi
512	echo -n 'Stopping jails:'
513	case $1 in
514	_ALL)
515		command=$jail_program
516		rc_flags=$jail_flags
517		command_args="-f $jail_conf -r"
518		$jail_jls name | while read _j; do
519			echo -n " $_j"
520			_tmp=`mktemp -t jail` || exit 3
521			$command $rc_flags $command_args $_j >> $_tmp 2>&1
522			if $jail_jls -j $_j > /dev/null 2>&1; then
523				tail -1 $_tmp
524			else
525				rm -f /var/run/jail_${_j}.id
526			fi
527			rm -f $_tmp
528		done
529		echo '.'
530		return
531	;;
532	esac
533	for _j in $@; do
534		_j=$(echo $_j | tr /. _)
535		parse_options $_j || continue
536		if ! $jail_jls -j $_j > /dev/null 2>&1; then
537			continue
538		fi
539		eval command=\${jail_${_j}_program:-$jail_program}
540		echo -n " ${_hostname:-${_j}}"
541		_tmp=`mktemp -t jail` || exit 3
542		$command -q -f $_conf -r $_j >> $_tmp 2>&1
543		if $jail_jls -j $_j > /dev/null 2>&1; then
544			tail -1 $_tmp
545		else
546			rm -f /var/run/jail_${_j}.id
547		fi
548		rm -f $_tmp
549	done
550	echo '.'
551}
552
553jail_warn()
554{
555
556	# To relieve confusion, show a warning message.
557	case $_confwarn in
558	1)	warn "Per-jail configuration via jail_* variables " \
559		    "is obsolete.  Please consider to migrate to $jail_conf."
560	;;
561	esac
562}
563
564load_rc_config $name
565case $# in
5661)	run_rc_command $@ ${jail_list:-_ALL} ;;
567*)	run_rc_command $@ ;;
568esac
569