xref: /freebsd/crypto/openssh/regress/test-exec.sh (revision a91a246563dffa876a52f53a98de4af9fa364c52)
1#	$OpenBSD: test-exec.sh,v 1.108 2024/03/08 11:34:10 dtucker Exp $
2#	Placed in the Public Domain.
3
4#SUDO=sudo
5
6if [ ! -z "$TEST_SSH_ELAPSED_TIMES" ]; then
7	STARTTIME=`date '+%s'`
8fi
9
10if [ ! -z "$TEST_SSH_PORT" ]; then
11	PORT="$TEST_SSH_PORT"
12else
13	PORT=4242
14fi
15
16OBJ=$1
17if [ "x$OBJ" = "x" ]; then
18	echo '$OBJ not defined'
19	exit 2
20fi
21if [ ! -d $OBJ ]; then
22	echo "not a directory: $OBJ"
23	exit 2
24fi
25SCRIPT=$2
26if [ "x$SCRIPT" = "x" ]; then
27	echo '$SCRIPT not defined'
28	exit 2
29fi
30if [ ! -f $SCRIPT ]; then
31	echo "not a file: $SCRIPT"
32	exit 2
33fi
34if $TEST_SHELL -n $SCRIPT; then
35	true
36else
37	echo "syntax error in $SCRIPT"
38	exit 2
39fi
40unset SSH_AUTH_SOCK
41
42# Portable-specific settings.
43
44if [ -x /usr/ucb/whoami ]; then
45	USER=`/usr/ucb/whoami`
46elif whoami >/dev/null 2>&1; then
47	USER=`whoami`
48elif logname >/dev/null 2>&1; then
49	USER=`logname`
50else
51	USER=`id -un`
52fi
53if test -z "$LOGNAME"; then
54	LOGNAME="${USER}"
55	export LOGNAME
56fi
57
58# Unbreak GNU head(1)
59_POSIX2_VERSION=199209
60export _POSIX2_VERSION
61
62case `uname -s 2>/dev/null` in
63OSF1*)
64	BIN_SH=xpg4
65	export BIN_SH
66	;;
67CYGWIN*)
68	os=cygwin
69	;;
70esac
71
72# If configure tells us to use a different egrep, create a wrapper function
73# to call it.  This means we don't need to change all the tests that depend
74# on a good implementation.
75if test "x${EGREP}" != "x"; then
76	egrep ()
77{
78	 ${EGREP} "$@"
79}
80fi
81
82SRC=`dirname ${SCRIPT}`
83
84# defaults
85SSH=ssh
86SSHD=sshd
87SSHAGENT=ssh-agent
88SSHADD=ssh-add
89SSHKEYGEN=ssh-keygen
90SSHKEYSCAN=ssh-keyscan
91SFTP=sftp
92SFTPSERVER=/usr/libexec/openssh/sftp-server
93SCP=scp
94
95# Set by make_tmpdir() on demand (below).
96SSH_REGRESS_TMP=
97
98# Interop testing
99PLINK=/usr/local/bin/plink
100PUTTYGEN=/usr/local/bin/puttygen
101CONCH=/usr/local/bin/conch
102DROPBEAR=/usr/local/bin/dropbear
103DBCLIENT=/usr/local/bin/dbclient
104DROPBEARKEY=/usr/local/bin/dropbearkey
105DROPBEARCONVERT=/usr/local/bin/dropbearconvert
106
107# So we can override this in Portable.
108TEST_SHELL="${TEST_SHELL:-/bin/sh}"
109
110# Tools used by multiple tests
111NC=$OBJ/netcat
112# Always use the one configure tells us to, even if that's empty.
113#OPENSSL_BIN="${OPENSSL_BIN:-openssl}"
114
115if [ "x$TEST_SSH_SSH" != "x" ]; then
116	SSH="${TEST_SSH_SSH}"
117fi
118if [ "x$TEST_SSH_SSHD" != "x" ]; then
119	SSHD="${TEST_SSH_SSHD}"
120fi
121if [ "x$TEST_SSH_SSHAGENT" != "x" ]; then
122	SSHAGENT="${TEST_SSH_SSHAGENT}"
123fi
124if [ "x$TEST_SSH_SSHADD" != "x" ]; then
125	SSHADD="${TEST_SSH_SSHADD}"
126fi
127if [ "x$TEST_SSH_SSHKEYGEN" != "x" ]; then
128	SSHKEYGEN="${TEST_SSH_SSHKEYGEN}"
129fi
130if [ "x$TEST_SSH_SSHKEYSCAN" != "x" ]; then
131	SSHKEYSCAN="${TEST_SSH_SSHKEYSCAN}"
132fi
133if [ "x$TEST_SSH_SFTP" != "x" ]; then
134	SFTP="${TEST_SSH_SFTP}"
135fi
136if [ "x$TEST_SSH_SFTPSERVER" != "x" ]; then
137	SFTPSERVER="${TEST_SSH_SFTPSERVER}"
138fi
139if [ "x$TEST_SSH_SCP" != "x" ]; then
140	SCP="${TEST_SSH_SCP}"
141fi
142if [ "x$TEST_SSH_PLINK" != "x" ]; then
143	PLINK="${TEST_SSH_PLINK}"
144fi
145if [ "x$TEST_SSH_PUTTYGEN" != "x" ]; then
146	PUTTYGEN="${TEST_SSH_PUTTYGEN}"
147fi
148if [ "x$TEST_SSH_CONCH" != "x" ]; then
149	CONCH="${TEST_SSH_CONCH}"
150fi
151if [ "x$TEST_SSH_DROPBEAR" != "x" ]; then
152	DROPBEAR="${TEST_SSH_DROPBEAR}"
153fi
154if [ "x$TEST_SSH_DBCLIENT" != "x" ]; then
155	DBCLIENT="${TEST_SSH_DBCLIENT}"
156fi
157if [ "x$TEST_SSH_DROPBEARKEY" != "x" ]; then
158	DROPBEARKEY="${TEST_SSH_DROPBEARKEY}"
159fi
160if [ "x$TEST_SSH_DROPBEARCONVERT" != "x" ]; then
161	DROPBEARCONVERT="${TEST_SSH_DROPBEARCONVERT}"
162fi
163if [ "x$TEST_SSH_PKCS11_HELPER" != "x" ]; then
164	SSH_PKCS11_HELPER="${TEST_SSH_PKCS11_HELPER}"
165fi
166if [ "x$TEST_SSH_SK_HELPER" != "x" ]; then
167	SSH_SK_HELPER="${TEST_SSH_SK_HELPER}"
168fi
169if [ "x$TEST_SSH_OPENSSL" != "x" ]; then
170	OPENSSL_BIN="${TEST_SSH_OPENSSL}"
171fi
172
173# Path to sshd must be absolute for rexec
174case "$SSHD" in
175/*) ;;
176*) SSHD=`which $SSHD` ;;
177esac
178
179case "$SSHAGENT" in
180/*) ;;
181*) SSHAGENT=`which $SSHAGENT` ;;
182esac
183
184# Record the actual binaries used.
185SSH_BIN=${SSH}
186SSHD_BIN=${SSHD}
187SSHAGENT_BIN=${SSHAGENT}
188SSHADD_BIN=${SSHADD}
189SSHKEYGEN_BIN=${SSHKEYGEN}
190SSHKEYSCAN_BIN=${SSHKEYSCAN}
191SFTP_BIN=${SFTP}
192SFTPSERVER_BIN=${SFTPSERVER}
193SCP_BIN=${SCP}
194
195if [ "x$USE_VALGRIND" != "x" ]; then
196	rm -rf $OBJ/valgrind-out $OBJ/valgrind-vgdb
197	mkdir -p $OBJ/valgrind-out $OBJ/valgrind-vgdb
198	# When using sudo ensure low-priv tests can write pipes and logs.
199	if [ "x$SUDO" != "x" ]; then
200		chmod 777 $OBJ/valgrind-out $OBJ/valgrind-vgdb
201	fi
202	VG_TEST=`basename $SCRIPT .sh`
203
204	# Some tests are difficult to fix.
205	case "$VG_TEST" in
206	reexec)
207		VG_SKIP=1 ;;
208	sftp-chroot)
209		if [ "x${SUDO}" != "x" ]; then
210			VG_SKIP=1
211		fi ;;
212	esac
213
214	if [ x"$VG_SKIP" = "x" ]; then
215		VG_LEAK="--leak-check=no"
216		if [ x"$VALGRIND_CHECK_LEAKS" != "x" ]; then
217			VG_LEAK="--leak-check=full"
218		fi
219		VG_IGNORE="/bin/*,/sbin/*,/usr/*,/var/*"
220		VG_LOG="$OBJ/valgrind-out/${VG_TEST}."
221		VG_OPTS="--track-origins=yes $VG_LEAK"
222		VG_OPTS="$VG_OPTS --trace-children=yes"
223		VG_OPTS="$VG_OPTS --trace-children-skip=${VG_IGNORE}"
224		VG_OPTS="$VG_OPTS --vgdb-prefix=$OBJ/valgrind-vgdb/"
225		VG_PATH="valgrind"
226		if [ "x$VALGRIND_PATH" != "x" ]; then
227			VG_PATH="$VALGRIND_PATH"
228		fi
229		VG="$VG_PATH $VG_OPTS"
230		SSH="$VG --log-file=${VG_LOG}ssh.%p $SSH"
231		SSHD="$VG --log-file=${VG_LOG}sshd.%p $SSHD"
232		SSHAGENT="$VG --log-file=${VG_LOG}ssh-agent.%p $SSHAGENT"
233		SSHADD="$VG --log-file=${VG_LOG}ssh-add.%p $SSHADD"
234		SSHKEYGEN="$VG --log-file=${VG_LOG}ssh-keygen.%p $SSHKEYGEN"
235		SSHKEYSCAN="$VG --log-file=${VG_LOG}ssh-keyscan.%p $SSHKEYSCAN"
236		SFTP="$VG --log-file=${VG_LOG}sftp.%p ${SFTP}"
237		SCP="$VG --log-file=${VG_LOG}scp.%p $SCP"
238		cat > $OBJ/valgrind-sftp-server.sh << EOF
239#!/bin/sh
240exec $VG --log-file=${VG_LOG}sftp-server.%p $SFTPSERVER "\$@"
241EOF
242		chmod a+rx $OBJ/valgrind-sftp-server.sh
243		SFTPSERVER="$OBJ/valgrind-sftp-server.sh"
244	fi
245fi
246
247# Logfiles.
248# SSH_LOGFILE should be the debug output of ssh(1) only
249# SSHD_LOGFILE should be the debug output of sshd(8) only
250# REGRESS_LOGFILE is the log of progress of the regress test itself.
251# TEST_SSH_LOGDIR will contain datestamped logs of all binaries run in
252# chronological order.
253if [ "x$TEST_SSH_LOGDIR" = "x" ]; then
254	TEST_SSH_LOGDIR=$OBJ/log
255	mkdir -p $TEST_SSH_LOGDIR
256fi
257if [ "x$TEST_SSH_LOGFILE" = "x" ]; then
258	TEST_SSH_LOGFILE=$OBJ/ssh.log
259fi
260if [ "x$TEST_SSHD_LOGFILE" = "x" ]; then
261	TEST_SSHD_LOGFILE=$OBJ/sshd.log
262fi
263if [ "x$TEST_REGRESS_LOGFILE" = "x" ]; then
264	TEST_REGRESS_LOGFILE=$OBJ/regress.log
265fi
266
267# If set, keep track of successful tests and skip them them if we've
268# previously completed that test.
269if [ "x$TEST_REGRESS_CACHE_DIR" != "x" ]; then
270	if [ ! -d "$TEST_REGRESS_CACHE_DIR" ]; then
271		mkdir -p "$TEST_REGRESS_CACHE_DIR"
272	fi
273	TEST="`basename $SCRIPT .sh`"
274	CACHE="${TEST_REGRESS_CACHE_DIR}/${TEST}.cache"
275	for i in ${SSH} ${SSHD} ${SSHAGENT} ${SSHADD} ${SSHKEYGEN} ${SCP} \
276	    ${SFTP} ${SFTPSERVER} ${SSHKEYSCAN}; do
277		case $i in
278		/*)	bin="$i" ;;
279		*)	bin="`which $i`" ;;
280		esac
281		if [ "$bin" -nt "$CACHE" ]; then
282			rm -f "$CACHE"
283		fi
284	done
285	if [ -f "$CACHE" ]; then
286		echo ok cached $CACHE
287		exit 0
288	fi
289fi
290
291# truncate logfiles
292>$TEST_REGRESS_LOGFILE
293
294# Create ssh and sshd wrappers with logging.  These create a datestamped
295# unique file for every invocation so that we can retain all logs from a
296# given test no matter how many times it's invoked.  It also leaves a
297# symlink with the original name for tests (and people) who look for that.
298
299# For ssh, e can't just specify "SSH=ssh -E..." because sftp and scp don't
300# handle spaces in arguments.  scp and sftp like to use -q so we remove those
301# to preserve our debug logging.  In the rare instance where -q is desirable
302# -qq is equivalent and is not removed.
303SSHLOGWRAP=$OBJ/ssh-log-wrapper.sh
304cat >$SSHLOGWRAP <<EOD
305#!/bin/sh
306timestamp="\`$OBJ/timestamp\`"
307logfile="${TEST_SSH_LOGDIR}/\${timestamp}.ssh.\$\$.log"
308echo "Executing: ${SSH} \$@" log \${logfile} >>$TEST_REGRESS_LOGFILE
309echo "Executing: ${SSH} \$@" >>\${logfile}
310for i in "\$@";do shift;case "\$i" in -q):;; *) set -- "\$@" "\$i";;esac;done
311rm -f $TEST_SSH_LOGFILE
312ln -f -s \${logfile} $TEST_SSH_LOGFILE
313exec ${SSH} -E\${logfile} "\$@"
314EOD
315
316chmod a+rx $OBJ/ssh-log-wrapper.sh
317REAL_SSH="$SSH"
318REAL_SSHD="$SSHD"
319SSH="$SSHLOGWRAP"
320
321SSHDLOGWRAP=$OBJ/sshd-log-wrapper.sh
322cat >$SSHDLOGWRAP <<EOD
323#!/bin/sh
324timestamp="\`$OBJ/timestamp\`"
325logfile="${TEST_SSH_LOGDIR}/\${timestamp}.sshd.\$\$.log"
326rm -f $TEST_SSHD_LOGFILE
327touch \$logfile
328test -z "$SUDO" || chown $USER \$logfile
329ln -f -s \${logfile} $TEST_SSHD_LOGFILE
330echo "Executing: ${SSHD} \$@" log \${logfile} >>$TEST_REGRESS_LOGFILE
331echo "Executing: ${SSHD} \$@" >>\${logfile}
332exec ${SSHD} -E\${logfile} "\$@"
333EOD
334chmod a+rx $OBJ/sshd-log-wrapper.sh
335
336ssh_logfile ()
337{
338	tool="$1"
339	timestamp="`$OBJ/timestamp`"
340	logfile="${TEST_SSH_LOGDIR}/${timestamp}.$tool.$$.log"
341	echo "Logging $tool to log \${logfile}" >>$TEST_REGRESS_LOGFILE
342	echo $logfile
343}
344
345# Some test data.  We make a copy because some tests will overwrite it.
346# The tests may assume that $DATA exists and is writable and $COPY does
347# not exist.  Tests requiring larger data files can call increase_datafile_size
348# [kbytes] to ensure the file is at least that large.
349DATANAME=data
350DATA=$OBJ/${DATANAME}
351cat ${SSHAGENT_BIN} >${DATA}
352chmod u+w ${DATA}
353COPY=$OBJ/copy
354rm -f ${COPY}
355
356increase_datafile_size()
357{
358	while [ `du -k ${DATA} | cut -f1` -lt $1 ]; do
359		cat ${SSHAGENT_BIN} >>${DATA}
360	done
361}
362
363# these should be used in tests
364export SSH SSHD SSHAGENT SSHADD SSHKEYGEN SSHKEYSCAN SFTP SFTPSERVER SCP
365export SSH_PKCS11_HELPER SSH_SK_HELPER
366#echo $SSH $SSHD $SSHAGENT $SSHADD $SSHKEYGEN $SSHKEYSCAN $SFTP $SFTPSERVER $SCP
367
368# Portable specific functions
369which()
370{
371	saved_IFS="$IFS"
372	IFS=":"
373	for i in $PATH
374	do
375		if [ -x $i/$1 ]; then
376			IFS="$saved_IFS"
377			echo "$i/$1"
378			return 0
379		fi
380	done
381	IFS="$saved_IFS"
382	echo "$i/$1"
383	return 1
384}
385
386have_prog()
387{
388	which "$1" >/dev/null 2>&1
389	return $?
390}
391
392jot() {
393	awk "BEGIN { for (i = $2; i < $2 + $1; i++) { printf \"%d\n\", i } exit }"
394}
395if [ ! -x "`which rev`" ]; then
396rev()
397{
398	awk '{for (i=length; i>0; i--) printf "%s", substr($0, i, 1); print ""}'
399}
400fi
401
402# Check whether preprocessor symbols are defined in config.h.
403config_defined ()
404{
405	str=$1
406	while test "x$2" != "x" ; do
407		str="$str|$2"
408		shift
409	done
410	egrep "^#define.*($str)" ${BUILDDIR}/config.h >/dev/null 2>&1
411}
412
413md5 () {
414	if have_prog md5sum; then
415		md5sum
416	elif have_prog openssl; then
417		openssl md5
418	elif have_prog cksum; then
419		cksum
420	elif have_prog sum; then
421		sum
422	elif [ -x ${OPENSSL_BIN} ]; then
423		${OPENSSL_BIN} md5
424	else
425		wc -c
426	fi
427}
428
429# Some platforms don't have hostname at all, but on others uname -n doesn't
430# provide the fully qualified name we need, so in the former case we create
431# our own hostname function.
432if ! have_prog hostname; then
433	hostname() {
434		uname -n
435	}
436fi
437
438make_tmpdir ()
439{
440	SSH_REGRESS_TMP="$($OBJ/mkdtemp openssh-XXXXXXXX)" || \
441	    fatal "failed to create temporary directory"
442}
443# End of portable specific functions
444
445stop_sshd ()
446{
447	if [ -f $PIDFILE ]; then
448		pid=`$SUDO cat $PIDFILE`
449		if [ "X$pid" = "X" ]; then
450			echo no sshd running
451		else
452			if [ $pid -lt 2 ]; then
453				echo bad pid for sshd: $pid
454			else
455				$SUDO kill $pid
456				trace "wait for sshd to exit"
457				i=0;
458				while [ -f $PIDFILE -a $i -lt 5 ]; do
459					i=`expr $i + 1`
460					sleep $i
461				done
462				if test -f $PIDFILE; then
463					if $SUDO kill -0 $pid; then
464						echo "sshd didn't exit " \
465						    "port $PORT pid $pid"
466					else
467						echo "sshd died without cleanup"
468					fi
469					exit 1
470				fi
471			fi
472		fi
473	fi
474}
475
476# helper
477cleanup ()
478{
479	if [ "x$SSH_PID" != "x" ]; then
480		if [ $SSH_PID -lt 2 ]; then
481			echo bad pid for ssh: $SSH_PID
482		else
483			kill $SSH_PID
484		fi
485	fi
486	if [ "x$SSH_REGRESS_TMP" != "x" ]; then
487		rm -rf "$SSH_REGRESS_TMP"
488	fi
489	stop_sshd
490	if [ ! -z "$TEST_SSH_ELAPSED_TIMES" ]; then
491		now=`date '+%s'`
492		elapsed=$(($now - $STARTTIME))
493		echo elapsed $elapsed `basename $SCRIPT .sh`
494	fi
495}
496
497start_debug_log ()
498{
499	echo "trace: $@" >>$TEST_REGRESS_LOGFILE
500	if [ -d "$TEST_SSH_LOGDIR" ]; then
501		rm -f $TEST_SSH_LOGDIR/*
502	fi
503}
504
505save_debug_log ()
506{
507	testname=`echo $tid | tr ' ' _`
508	tarname="$OBJ/failed-$testname-logs.tar"
509
510	for logfile in $TEST_SSH_LOGDIR $TEST_REGRESS_LOGFILE \
511	    $TEST_SSH_LOGFILE $TEST_SSHD_LOGFILE; do
512		if [ ! -z "$SUDO" ] && [ -f "$logfile" ]; then
513			$SUDO chown -R $USER $logfile
514		fi
515	done
516	echo $@ >>$TEST_REGRESS_LOGFILE
517	echo $@ >>$TEST_SSH_LOGFILE
518	echo $@ >>$TEST_SSHD_LOGFILE
519	echo "Saving debug logs to $tarname" >>$TEST_REGRESS_LOGFILE
520	(cat $TEST_REGRESS_LOGFILE; echo) >>$OBJ/failed-regress.log
521	(cat $TEST_SSH_LOGFILE; echo) >>$OBJ/failed-ssh.log
522	(cat $TEST_SSHD_LOGFILE; echo) >>$OBJ/failed-sshd.log
523
524	# Save all logfiles in a tarball.
525	(cd $OBJ &&
526	  logfiles=""
527	  for i in $TEST_REGRESS_LOGFILE $TEST_SSH_LOGFILE $TEST_SSHD_LOGFILE \
528	    $TEST_SSH_LOGDIR; do
529		if [ -e "`basename $i`" ]; then
530			logfiles="$logfiles `basename $i`"
531		else
532			logfiles="$logfiles $i"
533		fi
534	  done
535	  tar cf "$tarname" $logfiles)
536}
537
538trace ()
539{
540	start_debug_log $@
541	if [ "X$TEST_SSH_TRACE" = "Xyes" ]; then
542		echo "$@"
543	fi
544}
545
546verbose ()
547{
548	start_debug_log $@
549	if [ "X$TEST_SSH_QUIET" != "Xyes" ]; then
550		echo "$@"
551	fi
552}
553
554fail ()
555{
556	save_debug_log "FAIL: $@"
557	RESULT=1
558	echo "$@"
559	if test "x$TEST_SSH_FAIL_FATAL" != "x" ; then
560		cleanup
561		exit $RESULT
562	fi
563}
564
565fatal ()
566{
567	save_debug_log "FATAL: $@"
568	printf "FATAL: "
569	fail "$@"
570	cleanup
571	exit $RESULT
572}
573
574# Skip remaining tests in script.
575skip ()
576{
577	echo "SKIPPED: $@"
578	cleanup
579	exit $RESULT
580}
581
582maybe_add_scp_path_to_sshd ()
583{
584	# If we're testing a non-installed scp, add its directory to sshd's
585	# PATH so we can test it.  We don't do this for all tests as it
586	# breaks the SetEnv tests.
587	case "$SCP" in
588	/*)	PATH_WITH_SCP="`dirname $SCP`:$PATH"
589		echo "	SetEnv PATH='$PATH_WITH_SCP'" >>$OBJ/sshd_config
590		echo "	SetEnv PATH='$PATH_WITH_SCP'" >>$OBJ/sshd_proxy ;;
591	esac
592}
593
594RESULT=0
595PIDFILE=$OBJ/pidfile
596
597trap fatal 3 2
598
599# create server config
600cat << EOF > $OBJ/sshd_config
601	StrictModes		no
602	Port			$PORT
603	AddressFamily		inet
604	ListenAddress		127.0.0.1
605	#ListenAddress		::1
606	PidFile			$PIDFILE
607	AuthorizedKeysFile	$OBJ/authorized_keys_%u
608	LogLevel		DEBUG3
609	AcceptEnv		_XXX_TEST_*
610	AcceptEnv		_XXX_TEST
611	Subsystem	sftp	$SFTPSERVER
612EOF
613
614# This may be necessary if /usr/src and/or /usr/obj are group-writable,
615# but if you aren't careful with permissions then the unit tests could
616# be abused to locally escalate privileges.
617if [ ! -z "$TEST_SSH_UNSAFE_PERMISSIONS" ]; then
618	echo "	StrictModes no" >> $OBJ/sshd_config
619else
620	# check and warn if excessive permissions are likely to cause failures.
621	unsafe=""
622	dir="${OBJ}"
623	while test ${dir} != "/"; do
624		if test -d "${dir}" && ! test -h "${dir}"; then
625			perms=`ls -ld ${dir}`
626			case "${perms}" in
627			?????w????*|????????w?*) unsafe="${unsafe} ${dir}" ;;
628			esac
629		fi
630		dir=`dirname ${dir}`
631	done
632	if ! test  -z "${unsafe}"; then
633		cat <<EOD
634
635WARNING: Unsafe (group or world writable) directory permissions found:
636${unsafe}
637
638These could be abused to locally escalate privileges.  If you are
639sure that this is not a risk (eg there are no other users), you can
640bypass this check by setting TEST_SSH_UNSAFE_PERMISSIONS=1
641
642EOD
643	fi
644fi
645
646if [ ! -z "$TEST_SSH_MODULI_FILE" ]; then
647	trace "adding modulifile='$TEST_SSH_MODULI_FILE' to sshd_config"
648	echo "	ModuliFile '$TEST_SSH_MODULI_FILE'" >> $OBJ/sshd_config
649fi
650
651if [ ! -z "$TEST_SSH_SSHD_CONFOPTS" ]; then
652	trace "adding sshd_config option $TEST_SSH_SSHD_CONFOPTS"
653	echo "$TEST_SSH_SSHD_CONFOPTS" >> $OBJ/sshd_config
654fi
655
656# server config for proxy connects
657cp $OBJ/sshd_config $OBJ/sshd_proxy
658
659# allow group-writable directories in proxy-mode
660echo 'StrictModes no' >> $OBJ/sshd_proxy
661
662# create client config
663cat << EOF > $OBJ/ssh_config
664Host *
665	Hostname		127.0.0.1
666	HostKeyAlias		localhost-with-alias
667	Port			$PORT
668	User			$USER
669	GlobalKnownHostsFile	$OBJ/known_hosts
670	UserKnownHostsFile	$OBJ/known_hosts
671	PubkeyAuthentication	yes
672	ChallengeResponseAuthentication	no
673	PasswordAuthentication	no
674	BatchMode		yes
675	StrictHostKeyChecking	yes
676	LogLevel		DEBUG3
677EOF
678
679if [ ! -z "$TEST_SSH_SSH_CONFOPTS" ]; then
680	trace "adding ssh_config option $TEST_SSH_SSH_CONFOPTS"
681	echo "$TEST_SSH_SSH_CONFOPTS" >> $OBJ/ssh_config
682fi
683
684rm -f $OBJ/known_hosts $OBJ/authorized_keys_$USER
685
686SSH_SK_PROVIDER=
687if ! config_defined ENABLE_SK; then
688	trace skipping sk-dummy
689elif [ -f "${SRC}/misc/sk-dummy/obj/sk-dummy.so" ] ; then
690	SSH_SK_PROVIDER="${SRC}/misc/sk-dummy/obj/sk-dummy.so"
691elif [ -f "${OBJ}/misc/sk-dummy/sk-dummy.so" ] ; then
692	SSH_SK_PROVIDER="${OBJ}/misc/sk-dummy/sk-dummy.so"
693elif [ -f "${SRC}/misc/sk-dummy/sk-dummy.so" ] ; then
694	SSH_SK_PROVIDER="${SRC}/misc/sk-dummy/sk-dummy.so"
695fi
696export SSH_SK_PROVIDER
697
698if ! test -z "$SSH_SK_PROVIDER"; then
699	EXTRA_AGENT_ARGS='-P/*' # XXX want realpath(1)...
700	echo "SecurityKeyProvider $SSH_SK_PROVIDER" >> $OBJ/ssh_config
701	echo "SecurityKeyProvider $SSH_SK_PROVIDER" >> $OBJ/sshd_config
702	echo "SecurityKeyProvider $SSH_SK_PROVIDER" >> $OBJ/sshd_proxy
703fi
704export EXTRA_AGENT_ARGS
705
706maybe_filter_sk() {
707	if test -z "$SSH_SK_PROVIDER" ; then
708		grep -v ^sk
709	else
710		cat
711	fi
712}
713
714SSH_KEYTYPES=`$SSH -Q key-plain | maybe_filter_sk`
715SSH_HOSTKEY_TYPES=`$SSH -Q key-plain | maybe_filter_sk`
716
717for t in ${SSH_KEYTYPES}; do
718	# generate user key
719	if [ ! -f $OBJ/$t ] || [ ${SSHKEYGEN_BIN} -nt $OBJ/$t ]; then
720		trace "generating key type $t"
721		rm -f $OBJ/$t
722		${SSHKEYGEN} -q -N '' -t $t  -f $OBJ/$t ||\
723			fail "ssh-keygen for $t failed"
724	else
725		trace "using cached key type $t"
726	fi
727
728	# setup authorized keys
729	cat $OBJ/$t.pub >> $OBJ/authorized_keys_$USER
730	echo IdentityFile $OBJ/$t >> $OBJ/ssh_config
731done
732
733for t in ${SSH_HOSTKEY_TYPES}; do
734	# known hosts file for client
735	(
736		printf 'localhost-with-alias,127.0.0.1,::1 '
737		cat $OBJ/$t.pub
738	) >> $OBJ/known_hosts
739
740	# use key as host key, too
741	(umask 077; $SUDO cp $OBJ/$t $OBJ/host.$t)
742	echo HostKey $OBJ/host.$t >> $OBJ/sshd_config
743
744	# don't use SUDO for proxy connect
745	echo HostKey $OBJ/$t >> $OBJ/sshd_proxy
746done
747chmod 644 $OBJ/authorized_keys_$USER
748
749# Activate Twisted Conch tests if the binary is present
750REGRESS_INTEROP_CONCH=no
751if test -x "$CONCH" ; then
752	REGRESS_INTEROP_CONCH=yes
753fi
754
755# If PuTTY is present, new enough and we are running a PuTTY test, prepare
756# keys and configuration.
757REGRESS_INTEROP_PUTTY=no
758if test -x "$PUTTYGEN" -a -x "$PLINK" &&
759    "$PUTTYGEN" --help 2>&1 | grep -- --new-passphrase >/dev/null; then
760	REGRESS_INTEROP_PUTTY=yes
761fi
762case "$SCRIPT" in
763*putty*)	;;
764*)		REGRESS_INTEROP_PUTTY=no ;;
765esac
766
767puttysetup() {
768	if test "x$REGRESS_INTEROP_PUTTY" != "xyes" ; then
769		skip "putty interop tests not enabled"
770	fi
771
772	mkdir -p ${OBJ}/.putty
773
774	# Add a PuTTY key to authorized_keys
775	rm -f ${OBJ}/putty.rsa2
776	if ! "$PUTTYGEN" -t rsa -o ${OBJ}/putty.rsa2 \
777	    --random-device=/dev/urandom \
778	    --new-passphrase /dev/null < /dev/null > /dev/null; then
779		echo "Your installed version of PuTTY is too old to support --new-passphrase, skipping test" >&2
780		exit 1
781	fi
782	"$PUTTYGEN" -O public-openssh ${OBJ}/putty.rsa2 \
783	    >> $OBJ/authorized_keys_$USER
784
785	# Convert rsa2 host key to PuTTY format
786	cp $OBJ/ssh-rsa $OBJ/ssh-rsa_oldfmt
787	${SSHKEYGEN} -p -N '' -m PEM -f $OBJ/ssh-rsa_oldfmt >/dev/null
788	${SRC}/ssh2putty.sh 127.0.0.1 $PORT $OBJ/ssh-rsa_oldfmt > \
789	    ${OBJ}/.putty/sshhostkeys
790	${SRC}/ssh2putty.sh 127.0.0.1 22 $OBJ/ssh-rsa_oldfmt >> \
791	    ${OBJ}/.putty/sshhostkeys
792	rm -f $OBJ/ssh-rsa_oldfmt
793
794	# Setup proxied session
795	mkdir -p ${OBJ}/.putty/sessions
796	rm -f ${OBJ}/.putty/sessions/localhost_proxy
797	echo "Protocol=ssh" >> ${OBJ}/.putty/sessions/localhost_proxy
798	echo "HostName=127.0.0.1" >> ${OBJ}/.putty/sessions/localhost_proxy
799	echo "PortNumber=$PORT" >> ${OBJ}/.putty/sessions/localhost_proxy
800	echo "ProxyMethod=5" >> ${OBJ}/.putty/sessions/localhost_proxy
801	echo "ProxyTelnetCommand=${OBJ}/sshd-log-wrapper.sh -i -f $OBJ/sshd_proxy" >> ${OBJ}/.putty/sessions/localhost_proxy
802	echo "ProxyLocalhost=1" >> ${OBJ}/.putty/sessions/localhost_proxy
803
804	PUTTYVER="`${PLINK} --version | awk '/plink: Release/{print $3}'`"
805	PUTTYMINORVER="`echo ${PUTTYVER} | cut -f2 -d.`"
806	verbose "plink version ${PUTTYVER} minor ${PUTTYMINORVER}"
807
808	# Re-enable ssh-rsa on older PuTTY versions since they don't do newer
809	# key types.
810	if [ "$PUTTYMINORVER" -lt "76" ]; then
811		echo "HostKeyAlgorithms +ssh-rsa" >> ${OBJ}/sshd_proxy
812		echo "PubkeyAcceptedKeyTypes +ssh-rsa" >> ${OBJ}/sshd_proxy
813	fi
814
815	if [ "$PUTTYMINORVER" -le "64" ]; then
816		echo "KexAlgorithms +diffie-hellman-group14-sha1" \
817		    >>${OBJ}/sshd_proxy
818	fi
819	PUTTYDIR=${OBJ}/.putty
820	export PUTTYDIR
821}
822
823REGRESS_INTEROP_DROPBEAR=no
824if test -x "$DROPBEARKEY" -a -x "$DBCLIENT" -a -x "$DROPBEARCONVERT"; then
825	REGRESS_INTEROP_DROPBEAR=yes
826fi
827case "$SCRIPT" in
828*dropbear*)	;;
829*)		REGRESS_INTEROP_DROPBEAR=no ;;
830esac
831
832if test "$REGRESS_INTEROP_DROPBEAR" = "yes" ; then
833	trace Create dropbear keys and add to authorized_keys
834	mkdir -p $OBJ/.dropbear
835	for i in rsa ecdsa ed25519 dss; do
836		if [ ! -f "$OBJ/.dropbear/id_$i" ]; then
837			($DROPBEARKEY -t $i -f $OBJ/.dropbear/id_$i
838			$DROPBEARCONVERT dropbear openssh \
839			    $OBJ/.dropbear/id_$i $OBJ/.dropbear/ossh.id_$i
840			) > /dev/null 2>&1
841		fi
842		$SSHKEYGEN -y -f $OBJ/.dropbear/ossh.id_$i \
843		   >>$OBJ/authorized_keys_$USER
844	done
845fi
846
847# create a proxy version of the client config
848(
849	cat $OBJ/ssh_config
850	echo proxycommand ${SUDO} env SSH_SK_HELPER=\"$SSH_SK_HELPER\" ${OBJ}/sshd-log-wrapper.sh -i -f $OBJ/sshd_proxy
851) > $OBJ/ssh_proxy
852
853# check proxy config
854${SSHD} -t -f $OBJ/sshd_proxy	|| fatal "sshd_proxy broken"
855
856# extract proxycommand into separate shell script for use by Dropbear.
857echo '#!/bin/sh' >$OBJ/ssh_proxy.sh
858awk '/^proxycommand/' $OBJ/ssh_proxy | sed 's/^proxycommand//' \
859   >>$OBJ/ssh_proxy.sh
860chmod a+x $OBJ/ssh_proxy.sh
861
862start_sshd ()
863{
864	# start sshd
865	logfile="${TEST_SSH_LOGDIR}/sshd.`$OBJ/timestamp`.$$.log"
866	$SUDO ${SSHD} -f $OBJ/sshd_config "$@" -t || fatal "sshd_config broken"
867	$SUDO env SSH_SK_HELPER="$SSH_SK_HELPER" \
868	    ${SSHD} -f $OBJ/sshd_config "$@" -E$TEST_SSHD_LOGFILE
869
870	trace "wait for sshd"
871	i=0;
872	while [ ! -f $PIDFILE -a $i -lt 10 ]; do
873		i=`expr $i + 1`
874		sleep $i
875	done
876
877	test -f $PIDFILE || fatal "no sshd running on port $PORT"
878}
879
880# Find a PKCS#11 library.
881p11_find_lib() {
882	TEST_SSH_PKCS11=""
883	for _lib in "$@" ; do
884		if test -f "$_lib" ; then
885			TEST_SSH_PKCS11="$_lib"
886			return
887		fi
888	done
889}
890
891# Perform PKCS#11 setup: prepares a softhsm2 token configuration, generated
892# keys and loads them into the virtual token.
893PKCS11_OK=
894export PKCS11_OK
895p11_setup() {
896	p11_find_lib \
897		/usr/local/lib/softhsm/libsofthsm2.so \
898		/usr/lib64/pkcs11/libsofthsm2.so \
899		/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so
900	test -z "$TEST_SSH_PKCS11" && return 1
901	verbose "using token library $TEST_SSH_PKCS11"
902	TEST_SSH_PIN=1234
903	TEST_SSH_SOPIN=12345678
904	if [ "x$TEST_SSH_SSHPKCS11HELPER" != "x" ]; then
905		SSH_PKCS11_HELPER="${TEST_SSH_SSHPKCS11HELPER}"
906		export SSH_PKCS11_HELPER
907	fi
908
909	# setup environment for softhsm2 token
910	SSH_SOFTHSM_DIR=$OBJ/SOFTHSM
911	export SSH_SOFTHSM_DIR
912	rm -rf $SSH_SOFTHSM_DIR
913	TOKEN=$SSH_SOFTHSM_DIR/tokendir
914	mkdir -p $TOKEN
915	SOFTHSM2_CONF=$SSH_SOFTHSM_DIR/softhsm2.conf
916	export SOFTHSM2_CONF
917	cat > $SOFTHSM2_CONF << EOF
918# SoftHSM v2 configuration file
919directories.tokendir = ${TOKEN}
920objectstore.backend = file
921# ERROR, WARNING, INFO, DEBUG
922log.level = DEBUG
923# If CKF_REMOVABLE_DEVICE flag should be set
924slots.removable = false
925EOF
926	out=$(softhsm2-util --init-token --free --label token-slot-0 --pin "$TEST_SSH_PIN" --so-pin "$TEST_SSH_SOPIN")
927	slot=$(echo -- $out | sed 's/.* //')
928	trace "generating keys"
929	# RSA key
930	RSA=${SSH_SOFTHSM_DIR}/RSA
931	RSAP8=${SSH_SOFTHSM_DIR}/RSAP8
932	$OPENSSL_BIN genpkey -algorithm rsa > $RSA 2>/dev/null || \
933	    fatal "genpkey RSA fail"
934	$OPENSSL_BIN pkcs8 -nocrypt -in $RSA > $RSAP8 || fatal "pkcs8 RSA fail"
935	softhsm2-util --slot "$slot" --label 01 --id 01 --pin "$TEST_SSH_PIN" \
936	    --import $RSAP8 >/dev/null || fatal "softhsm import RSA fail"
937	chmod 600 $RSA
938	ssh-keygen -y -f $RSA > ${RSA}.pub
939	# ECDSA key
940	ECPARAM=${SSH_SOFTHSM_DIR}/ECPARAM
941	EC=${SSH_SOFTHSM_DIR}/EC
942	ECP8=${SSH_SOFTHSM_DIR}/ECP8
943	$OPENSSL_BIN genpkey -genparam -algorithm ec \
944	    -pkeyopt ec_paramgen_curve:prime256v1 > $ECPARAM || \
945	    fatal "param EC fail"
946	$OPENSSL_BIN genpkey -paramfile $ECPARAM > $EC || \
947	    fatal "genpkey EC fail"
948	$OPENSSL_BIN pkcs8 -nocrypt -in $EC > $ECP8 || fatal "pkcs8 EC fail"
949	softhsm2-util --slot "$slot" --label 02 --id 02 --pin "$TEST_SSH_PIN" \
950	    --import $ECP8 >/dev/null || fatal "softhsm import EC fail"
951	chmod 600 $EC
952	ssh-keygen -y -f $EC > ${EC}.pub
953	# Prepare askpass script to load PIN.
954	PIN_SH=$SSH_SOFTHSM_DIR/pin.sh
955	cat > $PIN_SH << EOF
956#!/bin/sh
957echo "${TEST_SSH_PIN}"
958EOF
959	chmod 0700 "$PIN_SH"
960	PKCS11_OK=yes
961	return 0
962}
963
964# Peforms ssh-add with the right token PIN.
965p11_ssh_add() {
966	env SSH_ASKPASS="$PIN_SH" SSH_ASKPASS_REQUIRE=force ${SSHADD} "$@"
967}
968
969# source test body
970. $SCRIPT
971
972# kill sshd
973cleanup
974
975if [ "x$USE_VALGRIND" != "x" ]; then
976	# If there is an EXIT trap handler, invoke it now.
977	# Some tests set these to clean up processes such as ssh-agent.  We
978	# need to wait for all valgrind processes to complete so we can check
979	# their logs, but since the EXIT traps are not invoked until
980	# test-exec.sh exits, waiting here will deadlock.
981	# This is not very portable but then neither is valgrind itself.
982	# As a bonus, dash (as used on the runners) has a "trap" that doesn't
983	# work in a pipeline (hence the temp file) or a subshell.
984	exithandler=""
985	trap >/tmp/trap.$$ && exithandler=$(cat /tmp/trap.$$ | \
986	    awk -F "'" '/EXIT$/{print $2}')
987	rm -f /tmp/trap.$$
988	if [ "x${exithandler}" != "x" ]; then
989		verbose invoking EXIT trap handler early: ${exithandler}
990		eval "${exithandler}"
991		trap '' EXIT
992	fi
993
994	# wait for any running process to complete
995	wait; sleep 1
996	VG_RESULTS=$(find $OBJ/valgrind-out -type f -print)
997	VG_RESULT_COUNT=0
998	VG_FAIL_COUNT=0
999	for i in $VG_RESULTS; do
1000		if grep "ERROR SUMMARY" $i >/dev/null; then
1001			VG_RESULT_COUNT=$(($VG_RESULT_COUNT + 1))
1002			if ! grep "ERROR SUMMARY: 0 errors" $i >/dev/null; then
1003				VG_FAIL_COUNT=$(($VG_FAIL_COUNT + 1))
1004				RESULT=1
1005				verbose valgrind failure $i
1006				cat $i
1007			fi
1008		fi
1009	done
1010	if [ x"$VG_SKIP" != "x" ]; then
1011		verbose valgrind skipped
1012	else
1013		verbose valgrind results $VG_RESULT_COUNT failures $VG_FAIL_COUNT
1014	fi
1015fi
1016
1017if [ $RESULT -eq 0 ]; then
1018	verbose ok $tid
1019	if [ "x$CACHE" != "x" ]; then
1020		touch "$CACHE"
1021	fi
1022else
1023	echo failed $tid
1024fi
1025exit $RESULT
1026