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