xref: /dragonfly/etc/rc.d/jail (revision 8e1c6f81)
1#!/bin/sh
2#
3# $FreeBSD: src/etc/rc.d/jail,v 1.23.2.9 2007/01/11 18:16:58 simon Exp $
4# $DragonFly: src/etc/rc.d/jail,v 1.5 2007/08/10 21:01:05 swildner Exp $
5#
6
7# PROVIDE: jail
8# REQUIRE: LOGIN
9# BEFORE: securelevel
10
11. /etc/rc.subr
12
13name="jail"
14rcvar=`set_rcvar`
15start_cmd="jail_start"
16stop_cmd="jail_stop"
17
18# init_variables _j
19#	Initialize the various jail variables for jail _j.
20#
21init_variables()
22{
23	_j="$1"
24
25	if [ -z "$_j" ]; then
26		warn "init_variables: you must specify a jail"
27		return
28	fi
29
30	eval _rootdir=\"\$jail_${_j}_rootdir\"
31	_fdescdir="${_rootdir}/dev/fd"
32	_procdir="${_rootdir}/proc"
33	eval _hostname=\"\$jail_${_j}_hostname\"
34	eval _ip=\"\$jail_${_j}_ip\"
35	eval _interface=\"\${jail_${_j}_interface:-${jail_interface}}\"
36	eval _exec=\"\$jail_${_j}_exec\"
37	eval _exec_start=\"\${jail_${_j}_exec_start:-${jail_exec_start}}\"
38	eval _exec_stop=\"\${jail_${_j}_exec_stop:-${jail_exec_stop}}\"
39	if [ -n "${_exec}" ]; then
40		#   simple/backward-compatible execution
41		_exec_start="${_exec}"
42		_exec_stop=""
43	else
44		#   flexible execution
45		if [ -z "${_exec_start}" ]; then
46			_exec_start="/bin/sh /etc/rc"
47			if [ -z "${_exec_stop}" ]; then
48				_exec_stop="/bin/sh /etc/rc.shutdown"
49			fi
50		fi
51	fi
52
53	eval _fdesc=\"\${jail_${_j}_fdesc_enable:-${jail_fdesc_enable}}\"
54	[ -z "${_fdesc}" ] && _fdesc="NO"
55	eval _procfs=\"\${jail_${_j}_procfs_enable:-${jail_procfs_enable}}\"
56	[ -z "${_procfs}" ] && _procfs="NO"
57
58	eval _mount=\"\${jail_${_j}_mount_enable:-${jail_mount_enable}}\"
59	[ -z "${_mount}" ] && _mount="NO"
60	# "/etc/fstab.${_j}" will be used for {,u}mount(8) if none is specified.
61	eval _fstab=\"\${jail_${_j}_fstab:-${jail_fstab}}\"
62	[ -z "${_fstab}" ] && _fstab="/etc/fstab.${_j}"
63	eval _flags=\"\${jail_${_j}_flags:-${jail_flags}}\"
64	[ -z "${_flags}" ] && _flags="-l -U root"
65	eval _consolelog=\"\${jail_${_j}_consolelog:-${jail_consolelog}}\"
66	[ -z "${_consolelog}" ] && _consolelog="/var/log/jail_${_j}_console.log"
67
68	# Debugging aid
69	#
70	debug "$_j fdesc enable: $_fdesc"
71	debug "$_j procfs enable: $_procfs"
72	debug "$_j mount enable: $_mount"
73	debug "$_j hostname: $_hostname"
74	debug "$_j ip: $_ip"
75	debug "$_j interface: $_interface"
76	debug "$_j root: $_rootdir"
77	debug "$_j fdescdir: $_fdescdir"
78	debug "$_j procdir: $_procdir"
79	debug "$_j fstab: $_fstab"
80	debug "$_j exec start: $_exec_start"
81	debug "$_j exec stop: $_exec_stop"
82	debug "$_j flags: $_flags"
83	debug "$_j consolelog: $_consolelog"
84
85	if [ -z "${_hostname}" ]; then
86		err 3 "$name: No hostname has been defined for ${_j}"
87	fi
88	if [ -z "${_rootdir}" ]; then
89		err 3 "$name: No root directory has been defined for ${_j}"
90	fi
91	if [ -z "${_ip}" ]; then
92		err 3 "$name: No IP address has been defined for ${_j}"
93	fi
94
95}
96
97# set_sysctl rc_knob mib msg
98#	If the mib sysctl is set according to what rc_knob
99#	specifies, this function does nothing. However if
100#	rc_knob is set differently than mib, then the mib
101#	is set accordingly and msg is displayed followed by
102#	an '=" sign and the word 'YES' or 'NO'.
103#
104set_sysctl()
105{
106	_knob="$1"
107	_mib="$2"
108	_msg="$3"
109
110	_current=`${SYSCTL} -n $_mib 2>/dev/null`
111	if checkyesno $_knob ; then
112		if [ "$_current" -ne 1 ]; then
113			echo -n " ${_msg}=YES"
114			${SYSCTL_W} 1>/dev/null ${_mib}=1
115		fi
116	else
117		if [ "$_current" -ne 0 ]; then
118			echo -n " ${_msg}=NO"
119			${SYSCTL_W} 1>/dev/null ${_mib}=0
120		fi
121	fi
122}
123
124# is_current_mountpoint()
125#	Is the directory mount point for a currently mounted file
126#	system?
127#
128is_current_mountpoint()
129{
130	local _dir _dir2
131
132	_dir=$1
133
134	_dir=`echo $_dir | sed -Ee 's#//+#/#g' -e 's#/$##'`
135	[ ! -d "${_dir}" ] && return 1
136	_dir2=`df ${_dir} | tail +2 | awk '{ print $6 }'`
137	[ "${_dir}" = "${_dir2}" ]
138	return $?
139}
140
141# is_symlinked_mountpoint()
142#	Is a mount point, or any of its parent directories, a symlink?
143#
144is_symlinked_mountpoint()
145{
146	local _dir
147
148	_dir=$1
149
150	[ -L "$_dir" ] && return 0
151	[ "$_dir" = "/" ] && return 1
152	is_symlinked_mountpoint `dirname $_dir`
153	return $?
154}
155
156# secure_umount
157#	Try to unmount a mount point without being vulnerable to
158#	symlink attacks.
159#
160secure_umount()
161{
162	local _dir
163
164	_dir=$1
165
166	if is_current_mountpoint ${_dir}; then
167		umount -f ${_dir} >/dev/null 2>&1
168	else
169		debug "Nothing mounted on ${_dir} - not unmounting"
170	fi
171}
172
173
174# jail_umount_fs
175#	This function unmounts certain special filesystems in the
176#	currently selected jail. The caller must call the init_variables()
177#	routine before calling this one.
178#
179jail_umount_fs()
180{
181	local _device _mountpt _rest
182
183	if checkyesno _fdesc; then
184		if [ -d "${_fdescdir}" ] ; then
185			secure_umount ${_fdescdir}
186		fi
187	fi
188	if checkyesno _procfs; then
189		if [ -d "${_procdir}" ] ; then
190			secure_umount ${_procdir}
191		fi
192	fi
193	if checkyesno _mount; then
194		[ -f "${_fstab}" ] || warn "${_fstab} does not exist"
195		tail -r ${_fstab} | while read _device _mountpt _rest; do
196			case ":${_device}" in
197			:#* | :)
198				continue
199				;;
200			esac
201			secure_umount ${_mountpt}
202		done
203	fi
204}
205
206# jail_mount_fstab()
207#	Mount file systems from a per jail fstab while trying to
208#	secure against symlink attacks at the mount points.
209#
210#	If we are certain we cannot secure against symlink attacks we
211#	do not mount all of the file systems (since we cannot just not
212#	mount the file system with the problematic mount point).
213#
214#	The caller must call the init_variables() routine before
215#	calling this one.
216#
217jail_mount_fstab()
218{
219	local _device _mountpt _rest
220
221	while read _device _mountpt _rest; do
222		case ":${_device}" in
223		:#* | :)
224			continue
225			;;
226		esac
227		if is_symlinked_mountpoint ${_mountpt}; then
228			warn "${_mountpt} has symlink as parent - not mounting from ${_fstab}"
229			return
230		fi
231	done <${_fstab}
232	mount -a -F "${_fstab}"
233}
234
235jail_start()
236{
237	echo -n 'Configuring jails:'
238	set_sysctl jail_set_hostname_allow jail.set_hostname_allowed \
239	    set_hostname_allow
240	set_sysctl jail_socket_unixiproute_only \
241	    jail.socket_unixiproute_only unixiproute_only
242	set_sysctl jail_sysvipc_allow jail.sysvipc_allowed \
243	    sysvipc_allow
244	echo '.'
245
246	echo -n 'Starting jails:'
247	_tmp_dir=`mktemp -d /tmp/jail.XXXXXXXX` || \
248	    err 3 "$name: Can't create temp dir, exiting..."
249	for _jail in ${jail_list}
250	do
251		init_variables $_jail
252		if [ -f /var/run/jail_${_jail}.id ]; then
253			echo -n " [${_hostname} already running (/var/run/jail_${_jail}.id exists)]"
254			continue;
255		fi
256		if [ -n "${_interface}" ]; then
257			ifconfig ${_interface} alias ${_ip} netmask 255.255.255.255
258		fi
259		if checkyesno _mount; then
260			info "Mounting fstab for jail ${_jail} (${_fstab})"
261			if [ ! -f "${_fstab}" ]; then
262				err 3 "$name: ${_fstab} does not exist"
263			fi
264			jail_mount_fstab
265		fi
266		if checkyesno _fdesc; then
267			if is_symlinked_mountpoint ${_fdescdir}; then
268				warn "${_fdescdir} has symlink as parent, not mounting"
269			else
270				info "Mounting fdesc on ${_fdescdir}"
271				mount -t fdesc fdesc "${_fdescdir}"
272			fi
273		fi
274		if checkyesno _procfs; then
275			if is_symlinked_mountpoint ${_procdir}; then
276				warn "${_procdir} has symlink as parent, not mounting"
277			else
278				info "Mounting procfs onto ${_procdir}"
279				if [ -d "${_procdir}" ] ; then
280					mount -t procfs proc "${_procdir}"
281				fi
282			fi
283		fi
284		_tmp_jail=${_tmp_dir}/jail.$$
285		eval jail ${_flags} -i ${_rootdir} ${_hostname} \
286			${_ip} ${_exec_start} > ${_tmp_jail} 2>&1
287			if [ "$?" -eq 0 ] ; then
288				echo -n " $_hostname"
289				_jail_id=$(head -1 ${_tmp_jail})
290				tail +2 ${_tmp_jail} >${_consolelog}
291				echo ${_jail_id} > /var/run/jail_${_jail}.id
292			else
293				jail_umount_fs
294				if [ -n "${_interface}" ]; then
295					ifconfig ${_interface} -alias ${_ip}
296				fi
297				echo " cannot start jail \"${_jail}\": "
298				tail +2 ${_tmp_jail}
299			fi
300		rm -f ${_tmp_jail}
301	done
302	rmdir ${_tmp_dir}
303	echo '.'
304}
305
306jail_stop()
307{
308	echo -n 'Stopping jails:'
309	for _jail in ${jail_list}
310	do
311		if [ -f "/var/run/jail_${_jail}.id" ]; then
312			_jail_id=$(cat /var/run/jail_${_jail}.id)
313			if [ ! -z "${_jail_id}" ]; then
314				init_variables $_jail
315				if [ -n "${_exec_stop}" ]; then
316					eval env -i /usr/sbin/jexec ${_jail_id} ${_exec_stop} \
317						>> ${_consolelog} 2>&1
318				fi
319				killall -j ${_jail_id} -TERM > /dev/null 2>&1
320				sleep 1
321				killall -j ${_jail_id} -KILL > /dev/null 2>&1
322				jail_umount_fs
323				echo -n " $_hostname"
324			fi
325			if [ -n "${_interface}" ]; then
326				ifconfig ${_interface} -alias ${_ip}
327			fi
328			rm /var/run/jail_${_jail}.id
329		else
330			echo " cannot stop jail ${_jail}. No jail id in /var/run"
331		fi
332	done
333	echo '.'
334}
335
336load_rc_config $name
337cmd="$1"
338if [ $# -gt 0 ]; then
339	shift
340fi
341if [ -n "$*" ]; then
342	jail_list="$*"
343fi
344run_rc_command "${cmd}"
345