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