1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
|
#!/bin/sh
#
# bastille-tmpdir-defense.sh
#
# version 1.11
#
# This script is designed to complement bastille-tmpdir.sh
# for "defending" TMPDIR directories on systems running
# pruning applications like 'tmpwatch'; making sure the
# atime/mtime of $TMPDIR are updated periodically so that
# the pruning application won't remove it.
#
# This script also warns the user if it sees problems
# with TMPDIR.
#
# ARGUMENTS:
# as of v 1.2, this expects one argument: the PID
# of the login shell it was spawned for; this enables
# the script to gracefully kill itself *iff* /proc is
# available
#
# ------------- **** Important: required apps **** -------------
#
# You should have the following apps installed, and in a normal search path,
# for this script to work properly:
# grep, sed, which, uname, cut, ls, grep, mkdir, echo, cat, id, touch, expr
#
# The following apps are recommended:
# printf, od
# Also a readable /dev/urandom device is recommended.
#
# ------------- **** Important: required apps **** -------------
#
# Copyright (c) 2001 Peter Watkins
# with thanks to Sweth
#
# licensed under the terms of the GNU General Public License
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# ---- Settings ----------------------------------------------------
#
# TOUCH_INTERVAL: if > 0, will start a background task that periodically
# makes and removes a directory inside $TMPDIR. The reason
# for this is to avoid a problem on systems that run periodic
# directory cleanups like 'tmpwatch': if a user is logged on
# for a long time without using the $TMPDIR directory, then
# tools like 'tmpwatch' might remove the directory, thereby
# creating an opportunity for an attack.
# N.B. this setting is in *seconds*
TOUCH_INTERVAL=7200 # every two hours
#TOUCH_INTERVAL=432000 # Red Hat's usual tmpwatch interval is
# # 240 hours, == 864000 seconds
# # (see /etc/cron.daily/tmpwatch)
#
# USEAT: used in conjunction with TOUCH_INTERVAL
# if "Y", will try to run this script via 'at' instead
# of using a persistent 'while' loop. The advantage
# to USEAT is that users of text-based logins (getty, SSH, etc.)
# won't see the defense script in their 'jobs' and,
# therefore, anything they background will have a normal
# background number. The main disadvantage of 'at' is
# that if anyone unschedules an 'at' task, the user will
# not be warned, and might be put at risk
# Note #1: TOUCH_INTERVAL must be > 120 to use 'at'
# Note #2: the script will fall back to looping if you
# specify USEAT but the user is not allowed to
# use 'at', so it is safe to leave this as "Y"
USEAT=Y
#
# MYPATH: full path to this script; other scripts call this
# one, so be careful if you want to relocate it
MYPATH=/etc/bastille-tmpdir-defense.sh
#
# WHICH: Please tell me where the real 'which' is (some Linux
# distributions set up an alias for which!)
#WHICH=/usr/bin/which # manual
# or the following will look for which:
for p in /usr/sbin /sbin /usr/bin /bin ; do
[ -x "${p}/which" ] && WHICH="${p}/which"
done
#
# ---- Functions --------------------------------------------------
#
app_available() {
# takes a single argument, an app name, and tests for its availabilty
${WHICH} $1 >/dev/null 2>&1
return $?
}
#
get_random() {
# generate a string of random characters
r=1
[ ! -r /dev/urandom ] && r=0
for a in printf od cut head sed; do
app_available $a || r=0
done
if [ $r -eq 1 ]; then
RANDVAL=`od -N 8 /dev/urandom|sed 's:[ ]*::g'|sed 's:^0*::g'| sed 's:[^0-9]::g'|head -1|cut -c1-8`
RAND=`printf %x "${RANDVAL}" | cut -c -16`
unset RANDVAL
else
# less random, but we're careful with this anyway...
RAND="${RANDOM}"
# Solaris /bin/sh doesn't support $RANDOM !?!
if [ "${RAND}" = "" ]; then
if [ -x /bin/ksh ]; then
# ask the Korn shell for a random number
RAND=`echo 'echo $RANDOM' |/bin/ksh`
else
# use our PID
RAND=$$
fi
fi
fi
unset r
return 0
}
#
safe_hostname() {
# return `uname -n` scrubbed of unexpected chars
MYHOST=`uname -n 2>/dev/null| sed 's:[^a-zA-Z0-9\.]::g; s:\.\.::g' 2>/dev/null| cut -c1-64 2>/dev/null`
[ -z "${MYHOST}" ] && MYHOST=DEFAULT
return 0
}
#
send_warning() {
# notify the user of problems via various mechanisms
# write message to terminal (fallback to 'echo' if not running via 'at')
echo $MSG | write $THIS_USER 2>/dev/null
if [ $? -ne 0 -a "${usingat}" != "Y" ]; then
echo $MSG
fi
# then mail to user
# we must temporarily unset TMPDIR for GNU/Linux's
# 'mail' to be able to write its temp file!
TMPDIR=""; echo $MSG | $MAILER -s "${MSGSUBJ}" $THIS_USER 2>/dev/null ; TMPDIR=$TMP
# how about X?
${WHICH} wish >/dev/null 2>&1 && [ ! -z "${DISPLAY}" ] && echo "frame .t -width 200;message .t.msg -text \"${MSG}\" -width 280;label .t.bmap -bitmap error;catch { .t.bmap configure -fg red };button .b -text OK -command exit;bind . <Return> exit\n;wm title . \"${MSGSUBJ}\";catch {wm geometry . 370x180+270+170};pack .t.bmap .t.msg -side left;pack .b -side bottom -pady 4;pack .t -expand true" | wish
return 0
}
#
# ---- Main -------------------------------------------------------
#
[ ! -z "${TMP}" ] && TMPDIR="${TMP}"
if [ \! -z "${TMPDIR}" -a \! -z "${TOUCH_INTERVAL}" -a "${TOUCH_INTERVAL}" -gt 0 ]; then
#
# we only go through this if we have TMPDIR set
# and TOUCH_INTERVAL set
#
# Make sure we have the pid of our login shell
if [ $# -le 0 ]; then
echo "usage: $0 pidOfLoginShell"
exit 1
fi
#
# Get the pid for the process we should terminate after
pprocpid="$1"
if [ -d /proc -a \( ! -r "/proc/${pprocpid}/fd" \) ]; then
# the process that kicked this off is gone
# so there's no pointin checking (chances
# are that this is running from an 'at' job
# scheduled before the user logged out
exit
fi
#
# Are we running from 'at'?
usingat=N
if [ $# -ge 2 -a "$2" = "useat" ]; then
usingat=Y
fi
#
${WHICH} cp >/dev/null 2>&1
if [ $? -ne 0 -o ! -x "${WHICH}" ]; then
echo "WARNING: 'which' not available; unable to run TMPDIR defense script"
exit 1
fi
#
get_random # set RAND
safe_hostname # set MYHOST
THIS_USER=`id -un`
#
# See if we can schedule this via 'at' instead of running
# as a background task
loopval=1 # keeps test loop running in while loop (default)
if [ "${USEAT}" = "Y" -a "${TOUCH_INTERVAL}" != "" -a "${TOUCH_INTERVAL}" -ge 120 ]; then
x=0
app_available expr && x=1
if [ $x -eq 1 ]; then
minwait=`expr $TOUCH_INTERVAL / 60`
if [ $? -eq 0 ]; then
echo "DISPLAY=\"${DISPLAY}\" export DISPLAY ; ${MYPATH} ${pprocpid} useat" | at now + ${minwait} minutes 2> /dev/null
if [ $? -eq 0 ]; then
loopval=0 # do not run test in loop
TOUCH_INTERVAL=0
fi
fi
fi
unset x
fi
#
# Decide which app to use for emailing warnings
MAILER=mail
app_available mailx && MAILER=mailx
#
if [ $loopval -eq 1 ]; then
# We have NOT scheduled the next test via 'at' and must
# warn the user if we exit unexpectedly
# set an exit handler in case the user kills this script prematurely
XMSG="SECURITY ALERT: TMPDIR problem on host '${MYHOST}': The TMPDIR defense script is no longer protecting TMPDIR on '${MYHOST}'. This is alright if you are logging out of '${MYHOST}', otherwise you should restart the defense script or log out and log back in."
XMSGSUBJ="SECURITY ALERT: TMPDIR problem on ${MYHOST}"
trap "x=0 ; [ \( -z \"${pprocpid}\" \) -o \( -d /proc -a -r \"/proc/${pprocpid}/fd\" \) ] && x=1 ; if [ \$x -eq 1 ]; then MSG=\"\${XMSG}\" MSGSUBJ=\"\${XMSGSUBJ}\" send_warning ; fi ; unset x " EXIT
fi
#
TD="${TMPDIR}/touchdir.${RAND}"
whileval=1
while [ ${whileval} -eq 1 ]; do
if [ -d /proc -a \( ! -r "/proc/${pprocpid}/fd" \) ]; then
# the process that kicked this off is gone
# so there's no point in checking (chances
# are that the user logged out some time ago)
exit
fi
# 'touch' updates atime attr safely, mkdir/rmdir confirm write perms
x=1
touch "${TMPDIR}" && mkdir "${TD}" 2>/dev/null && rmdir "${TD}" 2> /dev/null && x=0
if [ $x -eq 1 ]; then
# set the messages in vars for easier processing
MSG="SECURITY ALERT: TMPDIR problem on host '${MYHOST}': Please check the existence and permissions on ${TMPDIR} and log out if you have any doubts! (error creating TMPDIR/touchdir.${RAND} at `date`)"
MSGSUBJ="SECURITY ALERT: TMPDIR problem on ${MYHOST}"
send_warning
fi
unset x
sleep ${TOUCH_INTERVAL}
whileval=${loopval}
done
#
unset MAILER
unset TD
unset RAND
unset MYHOST
unset THIS_USER
unset pprocpid
unset usingat
unset loopval
fi
unset TOUCH_INTERVAL
unset USEAT
unset WHICH
unset MYPATH
# now undefine the functions
unset app_available
unset get_random
unset safe_hostname
unset send_warning
|