File: bastille-tmpdir-defense.sh

package info (click to toggle)
bastille 1%3A2.1.1-13
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 2,376 kB
  • ctags: 514
  • sloc: perl: 13,477; sh: 2,199; ansic: 951; makefile: 195; csh: 17; python: 9
file content (263 lines) | stat: -rw-r--r-- 9,657 bytes parent folder | download | duplicates (3)
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