test-exec.sh revision 323129
1#	$OpenBSD: test-exec.sh,v 1.53 2016/04/15 02:57:10 djm Exp $
2#	Placed in the Public Domain.
3
4#SUDO=sudo
5
6# Unbreak GNU head(1)
7_POSIX2_VERSION=199209
8export _POSIX2_VERSION
9
10case `uname -s 2>/dev/null` in
11OSF1*)
12	BIN_SH=xpg4
13	export BIN_SH
14	;;
15CYGWIN_NT-5.0)
16	os=cygwin
17	TEST_SSH_IPV6=no
18	;;
19CYGWIN*)
20	os=cygwin
21	;;
22esac
23
24if [ ! -z "$TEST_SSH_PORT" ]; then
25	PORT="$TEST_SSH_PORT"
26else
27	PORT=4242
28fi
29
30if [ -x /usr/ucb/whoami ]; then
31	USER=`/usr/ucb/whoami`
32elif whoami >/dev/null 2>&1; then
33	USER=`whoami`
34elif logname >/dev/null 2>&1; then
35	USER=`logname`
36else
37	USER=`id -un`
38fi
39
40OBJ=$1
41if [ "x$OBJ" = "x" ]; then
42	echo '$OBJ not defined'
43	exit 2
44fi
45if [ ! -d $OBJ ]; then
46	echo "not a directory: $OBJ"
47	exit 2
48fi
49SCRIPT=$2
50if [ "x$SCRIPT" = "x" ]; then
51	echo '$SCRIPT not defined'
52	exit 2
53fi
54if [ ! -f $SCRIPT ]; then
55	echo "not a file: $SCRIPT"
56	exit 2
57fi
58if $TEST_SHELL -n $SCRIPT; then
59	true
60else
61	echo "syntax error in $SCRIPT"
62	exit 2
63fi
64unset SSH_AUTH_SOCK
65
66SRC=`dirname ${SCRIPT}`
67
68# defaults
69SSH=ssh
70SSHD=sshd
71SSHAGENT=ssh-agent
72SSHADD=ssh-add
73SSHKEYGEN=ssh-keygen
74SSHKEYSCAN=ssh-keyscan
75SFTP=sftp
76SFTPSERVER=/usr/libexec/openssh/sftp-server
77SCP=scp
78
79# Interop testing
80PLINK=plink
81PUTTYGEN=puttygen
82CONCH=conch
83
84if [ "x$TEST_SSH_SSH" != "x" ]; then
85	SSH="${TEST_SSH_SSH}"
86fi
87if [ "x$TEST_SSH_SSHD" != "x" ]; then
88	SSHD="${TEST_SSH_SSHD}"
89fi
90if [ "x$TEST_SSH_SSHAGENT" != "x" ]; then
91	SSHAGENT="${TEST_SSH_SSHAGENT}"
92fi
93if [ "x$TEST_SSH_SSHADD" != "x" ]; then
94	SSHADD="${TEST_SSH_SSHADD}"
95fi
96if [ "x$TEST_SSH_SSHKEYGEN" != "x" ]; then
97	SSHKEYGEN="${TEST_SSH_SSHKEYGEN}"
98fi
99if [ "x$TEST_SSH_SSHKEYSCAN" != "x" ]; then
100	SSHKEYSCAN="${TEST_SSH_SSHKEYSCAN}"
101fi
102if [ "x$TEST_SSH_SFTP" != "x" ]; then
103	SFTP="${TEST_SSH_SFTP}"
104fi
105if [ "x$TEST_SSH_SFTPSERVER" != "x" ]; then
106	SFTPSERVER="${TEST_SSH_SFTPSERVER}"
107fi
108if [ "x$TEST_SSH_SCP" != "x" ]; then
109	SCP="${TEST_SSH_SCP}"
110fi
111if [ "x$TEST_SSH_PLINK" != "x" ]; then
112	# Find real binary, if it exists
113	case "${TEST_SSH_PLINK}" in
114	/*) PLINK="${TEST_SSH_PLINK}" ;;
115	*) PLINK=`which ${TEST_SSH_PLINK} 2>/dev/null` ;;
116	esac
117fi
118if [ "x$TEST_SSH_PUTTYGEN" != "x" ]; then
119	# Find real binary, if it exists
120	case "${TEST_SSH_PUTTYGEN}" in
121	/*) PUTTYGEN="${TEST_SSH_PUTTYGEN}" ;;
122	*) PUTTYGEN=`which ${TEST_SSH_PUTTYGEN} 2>/dev/null` ;;
123	esac
124fi
125if [ "x$TEST_SSH_CONCH" != "x" ]; then
126	# Find real binary, if it exists
127	case "${TEST_SSH_CONCH}" in
128	/*) CONCH="${TEST_SSH_CONCH}" ;;
129	*) CONCH=`which ${TEST_SSH_CONCH} 2>/dev/null` ;;
130	esac
131fi
132
133SSH_PROTOCOLS=`$SSH -Q protocol-version`
134if [ "x$TEST_SSH_PROTOCOLS" != "x" ]; then
135	SSH_PROTOCOLS="${TEST_SSH_PROTOCOLS}"
136fi
137
138# Path to sshd must be absolute for rexec
139case "$SSHD" in
140/*) ;;
141*) SSHD=`which $SSHD` ;;
142esac
143
144case "$SSHAGENT" in
145/*) ;;
146*) SSHAGENT=`which $SSHAGENT` ;;
147esac
148
149# Record the actual binaries used.
150SSH_BIN=${SSH}
151SSHD_BIN=${SSHD}
152SSHAGENT_BIN=${SSHAGENT}
153SSHADD_BIN=${SSHADD}
154SSHKEYGEN_BIN=${SSHKEYGEN}
155SSHKEYSCAN_BIN=${SSHKEYSCAN}
156SFTP_BIN=${SFTP}
157SFTPSERVER_BIN=${SFTPSERVER}
158SCP_BIN=${SCP}
159
160if [ "x$USE_VALGRIND" != "x" ]; then
161	mkdir -p $OBJ/valgrind-out
162	VG_TEST=`basename $SCRIPT .sh`
163
164	# Some tests are difficult to fix.
165	case "$VG_TEST" in
166	connect-privsep|reexec)
167		VG_SKIP=1 ;;
168	esac
169
170	if [ x"$VG_SKIP" = "x" ]; then
171		VG_IGNORE="/bin/*,/sbin/*,/usr/*,/var/*"
172		VG_LOG="$OBJ/valgrind-out/${VG_TEST}."
173		VG_OPTS="--track-origins=yes --leak-check=full"
174		VG_OPTS="$VG_OPTS --trace-children=yes"
175		VG_OPTS="$VG_OPTS --trace-children-skip=${VG_IGNORE}"
176		VG_PATH="valgrind"
177		if [ "x$VALGRIND_PATH" != "x" ]; then
178			VG_PATH="$VALGRIND_PATH"
179		fi
180		VG="$VG_PATH $VG_OPTS"
181		SSH="$VG --log-file=${VG_LOG}ssh.%p $SSH"
182		SSHD="$VG --log-file=${VG_LOG}sshd.%p $SSHD"
183		SSHAGENT="$VG --log-file=${VG_LOG}ssh-agent.%p $SSHAGENT"
184		SSHADD="$VG --log-file=${VG_LOG}ssh-add.%p $SSHADD"
185		SSHKEYGEN="$VG --log-file=${VG_LOG}ssh-keygen.%p $SSHKEYGEN"
186		SSHKEYSCAN="$VG --log-file=${VG_LOG}ssh-keyscan.%p $SSHKEYSCAN"
187		SFTP="$VG --log-file=${VG_LOG}sftp.%p ${SFTP}"
188		SCP="$VG --log-file=${VG_LOG}scp.%p $SCP"
189		cat > $OBJ/valgrind-sftp-server.sh << EOF
190#!/bin/sh
191exec $VG --log-file=${VG_LOG}sftp-server.%p $SFTPSERVER "\$@"
192EOF
193		chmod a+rx $OBJ/valgrind-sftp-server.sh
194		SFTPSERVER="$OBJ/valgrind-sftp-server.sh"
195	fi
196fi
197
198# Logfiles.
199# SSH_LOGFILE should be the debug output of ssh(1) only
200# SSHD_LOGFILE should be the debug output of sshd(8) only
201# REGRESS_LOGFILE is the output of the test itself stdout and stderr
202if [ "x$TEST_SSH_LOGFILE" = "x" ]; then
203	TEST_SSH_LOGFILE=$OBJ/ssh.log
204fi
205if [ "x$TEST_SSHD_LOGFILE" = "x" ]; then
206	TEST_SSHD_LOGFILE=$OBJ/sshd.log
207fi
208if [ "x$TEST_REGRESS_LOGFILE" = "x" ]; then
209	TEST_REGRESS_LOGFILE=$OBJ/regress.log
210fi
211
212# truncate logfiles
213>$TEST_SSH_LOGFILE
214>$TEST_SSHD_LOGFILE
215>$TEST_REGRESS_LOGFILE
216
217# Create wrapper ssh with logging.  We can't just specify "SSH=ssh -E..."
218# because sftp and scp don't handle spaces in arguments.
219SSHLOGWRAP=$OBJ/ssh-log-wrapper.sh
220echo "#!/bin/sh" > $SSHLOGWRAP
221echo "exec ${SSH} -E${TEST_SSH_LOGFILE} "'"$@"' >>$SSHLOGWRAP
222
223chmod a+rx $OBJ/ssh-log-wrapper.sh
224REAL_SSH="$SSH"
225SSH="$SSHLOGWRAP"
226
227# Some test data.  We make a copy because some tests will overwrite it.
228# The tests may assume that $DATA exists and is writable and $COPY does
229# not exist.  Tests requiring larger data files can call increase_datafile_size
230# [kbytes] to ensure the file is at least that large.
231DATANAME=data
232DATA=$OBJ/${DATANAME}
233cat ${SSHAGENT_BIN} >${DATA}
234chmod u+w ${DATA}
235COPY=$OBJ/copy
236rm -f ${COPY}
237
238increase_datafile_size()
239{
240	while [ `du -k ${DATA} | cut -f1` -lt $1 ]; do
241		cat ${SSHAGENT_BIN} >>${DATA}
242	done
243}
244
245# these should be used in tests
246export SSH SSHD SSHAGENT SSHADD SSHKEYGEN SSHKEYSCAN SFTP SFTPSERVER SCP
247#echo $SSH $SSHD $SSHAGENT $SSHADD $SSHKEYGEN $SSHKEYSCAN $SFTP $SFTPSERVER $SCP
248
249# Portable specific functions
250have_prog()
251{
252	saved_IFS="$IFS"
253	IFS=":"
254	for i in $PATH
255	do
256		if [ -x $i/$1 ]; then
257			IFS="$saved_IFS"
258			return 0
259		fi
260	done
261	IFS="$saved_IFS"
262	return 1
263}
264
265jot() {
266	awk "BEGIN { for (i = $2; i < $2 + $1; i++) { printf \"%d\n\", i } exit }"
267}
268
269# Check whether preprocessor symbols are defined in config.h.
270config_defined ()
271{
272	str=$1
273	while test "x$2" != "x" ; do
274		str="$str|$2"
275		shift
276	done
277	egrep "^#define.*($str)" ${BUILDDIR}/config.h >/dev/null 2>&1
278}
279
280md5 () {
281	if have_prog md5sum; then
282		md5sum
283	elif have_prog openssl; then
284		openssl md5
285	elif have_prog cksum; then
286		cksum
287	elif have_prog sum; then
288		sum
289	else
290		wc -c
291	fi
292}
293# End of portable specific functions
294
295# helper
296cleanup ()
297{
298	if [ "x$SSH_PID" != "x" ]; then
299		if [ $SSH_PID -lt 2 ]; then
300			echo bad pid for ssh: $SSH_PID
301		else
302			kill $SSH_PID
303		fi
304	fi
305	if [ -f $PIDFILE ]; then
306		pid=`$SUDO cat $PIDFILE`
307		if [ "X$pid" = "X" ]; then
308			echo no sshd running
309		else
310			if [ $pid -lt 2 ]; then
311				echo bad pid for sshd: $pid
312			else
313				$SUDO kill $pid
314				trace "wait for sshd to exit"
315				i=0;
316				while [ -f $PIDFILE -a $i -lt 5 ]; do
317					i=`expr $i + 1`
318					sleep $i
319				done
320				test -f $PIDFILE && \
321				    fatal "sshd didn't exit port $PORT pid $pid"
322			fi
323		fi
324	fi
325}
326
327start_debug_log ()
328{
329	echo "trace: $@" >$TEST_REGRESS_LOGFILE
330	echo "trace: $@" >$TEST_SSH_LOGFILE
331	echo "trace: $@" >$TEST_SSHD_LOGFILE
332}
333
334save_debug_log ()
335{
336	echo $@ >>$TEST_REGRESS_LOGFILE
337	echo $@ >>$TEST_SSH_LOGFILE
338	echo $@ >>$TEST_SSHD_LOGFILE
339	(cat $TEST_REGRESS_LOGFILE; echo) >>$OBJ/failed-regress.log
340	(cat $TEST_SSH_LOGFILE; echo) >>$OBJ/failed-ssh.log
341	(cat $TEST_SSHD_LOGFILE; echo) >>$OBJ/failed-sshd.log
342}
343
344trace ()
345{
346	start_debug_log $@
347	if [ "X$TEST_SSH_TRACE" = "Xyes" ]; then
348		echo "$@"
349	fi
350}
351
352verbose ()
353{
354	start_debug_log $@
355	if [ "X$TEST_SSH_QUIET" != "Xyes" ]; then
356		echo "$@"
357	fi
358}
359
360warn ()
361{
362	echo "WARNING: $@" >>$TEST_SSH_LOGFILE
363	echo "WARNING: $@"
364}
365
366fail ()
367{
368	save_debug_log "FAIL: $@"
369	RESULT=1
370	echo "$@"
371
372}
373
374fatal ()
375{
376	save_debug_log "FATAL: $@"
377	printf "FATAL: "
378	fail "$@"
379	cleanup
380	exit $RESULT
381}
382
383ssh_version ()
384{
385	echo ${SSH_PROTOCOLS} | grep "$1" >/dev/null
386}
387
388RESULT=0
389PIDFILE=$OBJ/pidfile
390
391trap fatal 3 2
392
393if ssh_version 1; then
394	PROTO="2,1"
395else
396	PROTO="2"
397fi
398
399# create server config
400cat << EOF > $OBJ/sshd_config
401	StrictModes		no
402	Port			$PORT
403	Protocol		$PROTO
404	AddressFamily		inet
405	ListenAddress		127.0.0.1
406	#ListenAddress		::1
407	PidFile			$PIDFILE
408	AuthorizedKeysFile	$OBJ/authorized_keys_%u
409	LogLevel		DEBUG3
410	AcceptEnv		_XXX_TEST_*
411	AcceptEnv		_XXX_TEST
412	Subsystem	sftp	$SFTPSERVER
413EOF
414
415# This may be necessary if /usr/src and/or /usr/obj are group-writable,
416# but if you aren't careful with permissions then the unit tests could
417# be abused to locally escalate privileges.
418if [ ! -z "$TEST_SSH_UNSAFE_PERMISSIONS" ]; then
419	echo "StrictModes no" >> $OBJ/sshd_config
420fi
421
422if [ ! -z "$TEST_SSH_SSHD_CONFOPTS" ]; then
423	trace "adding sshd_config option $TEST_SSH_SSHD_CONFOPTS"
424	echo "$TEST_SSH_SSHD_CONFOPTS" >> $OBJ/sshd_config
425fi
426
427# server config for proxy connects
428cp $OBJ/sshd_config $OBJ/sshd_proxy
429
430# allow group-writable directories in proxy-mode
431echo 'StrictModes no' >> $OBJ/sshd_proxy
432
433# create client config
434cat << EOF > $OBJ/ssh_config
435Host *
436	Protocol		$PROTO
437	Hostname		127.0.0.1
438	HostKeyAlias		localhost-with-alias
439	Port			$PORT
440	User			$USER
441	GlobalKnownHostsFile	$OBJ/known_hosts
442	UserKnownHostsFile	$OBJ/known_hosts
443	RSAAuthentication	yes
444	PubkeyAuthentication	yes
445	ChallengeResponseAuthentication	no
446	HostbasedAuthentication	no
447	PasswordAuthentication	no
448	RhostsRSAAuthentication	no
449	BatchMode		yes
450	StrictHostKeyChecking	yes
451	LogLevel		DEBUG3
452EOF
453
454if [ ! -z "$TEST_SSH_SSH_CONFOPTS" ]; then
455	trace "adding ssh_config option $TEST_SSH_SSH_CONFOPTS"
456	echo "$TEST_SSH_SSH_CONFOPTS" >> $OBJ/ssh_config
457fi
458
459rm -f $OBJ/known_hosts $OBJ/authorized_keys_$USER
460
461if ssh_version 1; then
462	SSH_KEYTYPES="rsa rsa1"
463else
464	SSH_KEYTYPES="rsa ed25519"
465fi
466trace "generate keys"
467for t in ${SSH_KEYTYPES}; do
468	# generate user key
469	if [ ! -f $OBJ/$t ] || [ ${SSHKEYGEN_BIN} -nt $OBJ/$t ]; then
470		rm -f $OBJ/$t
471		${SSHKEYGEN} -q -N '' -t $t  -f $OBJ/$t ||\
472			fail "ssh-keygen for $t failed"
473	fi
474
475	# known hosts file for client
476	(
477		printf 'localhost-with-alias,127.0.0.1,::1 '
478		cat $OBJ/$t.pub
479	) >> $OBJ/known_hosts
480
481	# setup authorized keys
482	cat $OBJ/$t.pub >> $OBJ/authorized_keys_$USER
483	echo IdentityFile $OBJ/$t >> $OBJ/ssh_config
484
485	# use key as host key, too
486	$SUDO cp $OBJ/$t $OBJ/host.$t
487	echo HostKey $OBJ/host.$t >> $OBJ/sshd_config
488
489	# don't use SUDO for proxy connect
490	echo HostKey $OBJ/$t >> $OBJ/sshd_proxy
491done
492chmod 644 $OBJ/authorized_keys_$USER
493
494# Activate Twisted Conch tests if the binary is present
495REGRESS_INTEROP_CONCH=no
496if test -x "$CONCH" ; then
497	REGRESS_INTEROP_CONCH=yes
498fi
499
500# If PuTTY is present and we are running a PuTTY test, prepare keys and
501# configuration
502REGRESS_INTEROP_PUTTY=no
503if test -x "$PUTTYGEN" -a -x "$PLINK" ; then
504	REGRESS_INTEROP_PUTTY=yes
505fi
506case "$SCRIPT" in
507*putty*)	;;
508*)		REGRESS_INTEROP_PUTTY=no ;;
509esac
510
511if test "$REGRESS_INTEROP_PUTTY" = "yes" ; then
512	mkdir -p ${OBJ}/.putty
513
514	# Add a PuTTY key to authorized_keys
515	rm -f ${OBJ}/putty.rsa2
516	puttygen -t rsa -o ${OBJ}/putty.rsa2 < /dev/null > /dev/null
517	puttygen -O public-openssh ${OBJ}/putty.rsa2 \
518	    >> $OBJ/authorized_keys_$USER
519
520	# Convert rsa2 host key to PuTTY format
521	${SRC}/ssh2putty.sh 127.0.0.1 $PORT $OBJ/rsa > \
522	    ${OBJ}/.putty/sshhostkeys
523	${SRC}/ssh2putty.sh 127.0.0.1 22 $OBJ/rsa >> \
524	    ${OBJ}/.putty/sshhostkeys
525
526	# Setup proxied session
527	mkdir -p ${OBJ}/.putty/sessions
528	rm -f ${OBJ}/.putty/sessions/localhost_proxy
529	echo "Hostname=127.0.0.1" >> ${OBJ}/.putty/sessions/localhost_proxy
530	echo "PortNumber=$PORT" >> ${OBJ}/.putty/sessions/localhost_proxy
531	echo "ProxyMethod=5" >> ${OBJ}/.putty/sessions/localhost_proxy
532	echo "ProxyTelnetCommand=sh ${SRC}/sshd-log-wrapper.sh ${TEST_SSHD_LOGFILE} ${SSHD} -i -f $OBJ/sshd_proxy" >> ${OBJ}/.putty/sessions/localhost_proxy
533
534	REGRESS_INTEROP_PUTTY=yes
535fi
536
537# create a proxy version of the client config
538(
539	cat $OBJ/ssh_config
540	echo proxycommand ${SUDO} sh ${SRC}/sshd-log-wrapper.sh ${TEST_SSHD_LOGFILE} ${SSHD} -i -f $OBJ/sshd_proxy
541) > $OBJ/ssh_proxy
542
543# check proxy config
544${SSHD} -t -f $OBJ/sshd_proxy	|| fatal "sshd_proxy broken"
545
546start_sshd ()
547{
548	# start sshd
549	$SUDO ${SSHD} -f $OBJ/sshd_config "$@" -t || fatal "sshd_config broken"
550	$SUDO ${SSHD} -f $OBJ/sshd_config "$@" -E$TEST_SSHD_LOGFILE
551
552	trace "wait for sshd"
553	i=0;
554	while [ ! -f $PIDFILE -a $i -lt 10 ]; do
555		i=`expr $i + 1`
556		sleep $i
557	done
558
559	test -f $PIDFILE || fatal "no sshd running on port $PORT"
560}
561
562# source test body
563. $SCRIPT
564
565# kill sshd
566cleanup
567if [ $RESULT -eq 0 ]; then
568	verbose ok $tid
569else
570	echo failed $tid
571fi
572exit $RESULT
573