xref: /freebsd/crypto/openssh/regress/test-exec.sh (revision 9f44a47fd07924afc035991af15d84e6585dea4f)
1#	$OpenBSD: test-exec.sh,v 1.98 2023/03/02 11:10:27 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=plink
100PUTTYGEN=puttygen
101CONCH=conch
102
103# Tools used by multiple tests
104NC=$OBJ/netcat
105# Always use the one configure tells us to, even if that's empty.
106#OPENSSL_BIN="${OPENSSL_BIN:-openssl}"
107
108if [ "x$TEST_SSH_SSH" != "x" ]; then
109	SSH="${TEST_SSH_SSH}"
110fi
111if [ "x$TEST_SSH_SSHD" != "x" ]; then
112	SSHD="${TEST_SSH_SSHD}"
113fi
114if [ "x$TEST_SSH_SSHAGENT" != "x" ]; then
115	SSHAGENT="${TEST_SSH_SSHAGENT}"
116fi
117if [ "x$TEST_SSH_SSHADD" != "x" ]; then
118	SSHADD="${TEST_SSH_SSHADD}"
119fi
120if [ "x$TEST_SSH_SSHKEYGEN" != "x" ]; then
121	SSHKEYGEN="${TEST_SSH_SSHKEYGEN}"
122fi
123if [ "x$TEST_SSH_SSHKEYSCAN" != "x" ]; then
124	SSHKEYSCAN="${TEST_SSH_SSHKEYSCAN}"
125fi
126if [ "x$TEST_SSH_SFTP" != "x" ]; then
127	SFTP="${TEST_SSH_SFTP}"
128fi
129if [ "x$TEST_SSH_SFTPSERVER" != "x" ]; then
130	SFTPSERVER="${TEST_SSH_SFTPSERVER}"
131fi
132if [ "x$TEST_SSH_SCP" != "x" ]; then
133	SCP="${TEST_SSH_SCP}"
134fi
135if [ "x$TEST_SSH_PLINK" != "x" ]; then
136	# Find real binary, if it exists
137	case "${TEST_SSH_PLINK}" in
138	/*) PLINK="${TEST_SSH_PLINK}" ;;
139	*) PLINK=`which ${TEST_SSH_PLINK} 2>/dev/null` ;;
140	esac
141fi
142if [ "x$TEST_SSH_PUTTYGEN" != "x" ]; then
143	# Find real binary, if it exists
144	case "${TEST_SSH_PUTTYGEN}" in
145	/*) PUTTYGEN="${TEST_SSH_PUTTYGEN}" ;;
146	*) PUTTYGEN=`which ${TEST_SSH_PUTTYGEN} 2>/dev/null` ;;
147	esac
148fi
149if [ "x$TEST_SSH_CONCH" != "x" ]; then
150	# Find real binary, if it exists
151	case "${TEST_SSH_CONCH}" in
152	/*) CONCH="${TEST_SSH_CONCH}" ;;
153	*) CONCH=`which ${TEST_SSH_CONCH} 2>/dev/null` ;;
154	esac
155fi
156if [ "x$TEST_SSH_PKCS11_HELPER" != "x" ]; then
157	SSH_PKCS11_HELPER="${TEST_SSH_PKCS11_HELPER}"
158fi
159if [ "x$TEST_SSH_SK_HELPER" != "x" ]; then
160	SSH_SK_HELPER="${TEST_SSH_SK_HELPER}"
161fi
162if [ "x$TEST_SSH_OPENSSL" != "x" ]; then
163	OPENSSL_BIN="${TEST_SSH_OPENSSL}"
164fi
165
166# Path to sshd must be absolute for rexec
167case "$SSHD" in
168/*) ;;
169*) SSHD=`which $SSHD` ;;
170esac
171
172case "$SSHAGENT" in
173/*) ;;
174*) SSHAGENT=`which $SSHAGENT` ;;
175esac
176
177# Record the actual binaries used.
178SSH_BIN=${SSH}
179SSHD_BIN=${SSHD}
180SSHAGENT_BIN=${SSHAGENT}
181SSHADD_BIN=${SSHADD}
182SSHKEYGEN_BIN=${SSHKEYGEN}
183SSHKEYSCAN_BIN=${SSHKEYSCAN}
184SFTP_BIN=${SFTP}
185SFTPSERVER_BIN=${SFTPSERVER}
186SCP_BIN=${SCP}
187
188if [ "x$USE_VALGRIND" != "x" ]; then
189	rm -rf $OBJ/valgrind-out $OBJ/valgrind-vgdb
190	mkdir -p $OBJ/valgrind-out $OBJ/valgrind-vgdb
191	# When using sudo ensure low-priv tests can write pipes and logs.
192	if [ "x$SUDO" != "x" ]; then
193		chmod 777 $OBJ/valgrind-out $OBJ/valgrind-vgdb
194	fi
195	VG_TEST=`basename $SCRIPT .sh`
196
197	# Some tests are difficult to fix.
198	case "$VG_TEST" in
199	reexec)
200		VG_SKIP=1 ;;
201	sftp-chroot)
202		if [ "x${SUDO}" != "x" ]; then
203			VG_SKIP=1
204		fi ;;
205	esac
206
207	if [ x"$VG_SKIP" = "x" ]; then
208		VG_LEAK="--leak-check=no"
209		if [ x"$VALGRIND_CHECK_LEAKS" != "x" ]; then
210			VG_LEAK="--leak-check=full"
211		fi
212		VG_IGNORE="/bin/*,/sbin/*,/usr/*,/var/*"
213		VG_LOG="$OBJ/valgrind-out/${VG_TEST}."
214		VG_OPTS="--track-origins=yes $VG_LEAK"
215		VG_OPTS="$VG_OPTS --trace-children=yes"
216		VG_OPTS="$VG_OPTS --trace-children-skip=${VG_IGNORE}"
217		VG_OPTS="$VG_OPTS --vgdb-prefix=$OBJ/valgrind-vgdb/"
218		VG_PATH="valgrind"
219		if [ "x$VALGRIND_PATH" != "x" ]; then
220			VG_PATH="$VALGRIND_PATH"
221		fi
222		VG="$VG_PATH $VG_OPTS"
223		SSH="$VG --log-file=${VG_LOG}ssh.%p $SSH"
224		SSHD="$VG --log-file=${VG_LOG}sshd.%p $SSHD"
225		SSHAGENT="$VG --log-file=${VG_LOG}ssh-agent.%p $SSHAGENT"
226		SSHADD="$VG --log-file=${VG_LOG}ssh-add.%p $SSHADD"
227		SSHKEYGEN="$VG --log-file=${VG_LOG}ssh-keygen.%p $SSHKEYGEN"
228		SSHKEYSCAN="$VG --log-file=${VG_LOG}ssh-keyscan.%p $SSHKEYSCAN"
229		SFTP="$VG --log-file=${VG_LOG}sftp.%p ${SFTP}"
230		SCP="$VG --log-file=${VG_LOG}scp.%p $SCP"
231		cat > $OBJ/valgrind-sftp-server.sh << EOF
232#!/bin/sh
233exec $VG --log-file=${VG_LOG}sftp-server.%p $SFTPSERVER "\$@"
234EOF
235		chmod a+rx $OBJ/valgrind-sftp-server.sh
236		SFTPSERVER="$OBJ/valgrind-sftp-server.sh"
237	fi
238fi
239
240# Logfiles.
241# SSH_LOGFILE should be the debug output of ssh(1) only
242# SSHD_LOGFILE should be the debug output of sshd(8) only
243# REGRESS_LOGFILE is the log of progress of the regress test itself.
244# TEST_SSH_LOGDIR will contain datestamped logs of all binaries run in
245# chronological order.
246if [ "x$TEST_SSH_LOGDIR" = "x" ]; then
247	TEST_SSH_LOGDIR=$OBJ/log
248	mkdir -p $TEST_SSH_LOGDIR
249fi
250if [ "x$TEST_SSH_LOGFILE" = "x" ]; then
251	TEST_SSH_LOGFILE=$OBJ/ssh.log
252fi
253if [ "x$TEST_SSHD_LOGFILE" = "x" ]; then
254	TEST_SSHD_LOGFILE=$OBJ/sshd.log
255fi
256if [ "x$TEST_REGRESS_LOGFILE" = "x" ]; then
257	TEST_REGRESS_LOGFILE=$OBJ/regress.log
258fi
259
260# If set, keep track of successful tests and skip them them if we've
261# previously completed that test.
262if [ "x$TEST_REGRESS_CACHE_DIR" != "x" ]; then
263	if [ ! -d "$TEST_REGRESS_CACHE_DIR" ]; then
264		mkdir -p "$TEST_REGRESS_CACHE_DIR"
265	fi
266	TEST="`basename $SCRIPT .sh`"
267	CACHE="${TEST_REGRESS_CACHE_DIR}/${TEST}.cache"
268	for i in ${SSH} ${SSHD} ${SSHAGENT} ${SSHADD} ${SSHKEYGEN} ${SCP} \
269	    ${SFTP} ${SFTPSERVER} ${SSHKEYSCAN}; do
270		case $i in
271		/*)	bin="$i" ;;
272		*)	bin="`which $i`" ;;
273		esac
274		if [ "$bin" -nt "$CACHE" ]; then
275			rm -f "$CACHE"
276		fi
277	done
278	if [ -f "$CACHE" ]; then
279		echo ok cached $CACHE
280		exit 0
281	fi
282fi
283
284# truncate logfiles
285>$TEST_REGRESS_LOGFILE
286
287# Create ssh and sshd wrappers with logging.  These create a datestamped
288# unique file for every invocation so that we can retain all logs from a
289# given test no matter how many times it's invoked.  It also leaves a
290# symlink with the original name for tests (and people) who look for that.
291
292# For ssh, e can't just specify "SSH=ssh -E..." because sftp and scp don't
293# handle spaces in arguments.  scp and sftp like to use -q so we remove those
294# to preserve our debug logging.  In the rare instance where -q is desirable
295# -qq is equivalent and is not removed.
296SSHLOGWRAP=$OBJ/ssh-log-wrapper.sh
297cat >$SSHLOGWRAP <<EOD
298#!/bin/sh
299timestamp="\`$OBJ/timestamp\`"
300logfile="${TEST_SSH_LOGDIR}/\${timestamp}.ssh.\$\$.log"
301echo "Executing: ${SSH} \$@" log \${logfile} >>$TEST_REGRESS_LOGFILE
302echo "Executing: ${SSH} \$@" >>\${logfile}
303for i in "\$@";do shift;case "\$i" in -q):;; *) set -- "\$@" "\$i";;esac;done
304rm -f $TEST_SSH_LOGFILE
305ln -f -s \${logfile} $TEST_SSH_LOGFILE
306exec ${SSH} -E\${logfile} "\$@"
307EOD
308
309chmod a+rx $OBJ/ssh-log-wrapper.sh
310REAL_SSH="$SSH"
311REAL_SSHD="$SSHD"
312SSH="$SSHLOGWRAP"
313
314SSHDLOGWRAP=$OBJ/sshd-log-wrapper.sh
315cat >$SSHDLOGWRAP <<EOD
316#!/bin/sh
317timestamp="\`$OBJ/timestamp\`"
318logfile="${TEST_SSH_LOGDIR}/\${timestamp}.sshd.\$\$.log"
319rm -f $TEST_SSHD_LOGFILE
320ln -f -s \${logfile} $TEST_SSHD_LOGFILE
321echo "Executing: ${SSHD} \$@" log \${logfile} >>$TEST_REGRESS_LOGFILE
322echo "Executing: ${SSHD} \$@" >>\${logfile}
323exec ${SSHD} -E\${logfile} "\$@"
324EOD
325chmod a+rx $OBJ/sshd-log-wrapper.sh
326
327ssh_logfile ()
328{
329	tool="$1"
330	timestamp="`$OBJ/timestamp`"
331	logfile="${TEST_SSH_LOGDIR}/${timestamp}.$tool.$$.log"
332	echo "Logging $tool to log \${logfile}" >>$TEST_REGRESS_LOGFILE
333	echo $logfile
334}
335
336# Some test data.  We make a copy because some tests will overwrite it.
337# The tests may assume that $DATA exists and is writable and $COPY does
338# not exist.  Tests requiring larger data files can call increase_datafile_size
339# [kbytes] to ensure the file is at least that large.
340DATANAME=data
341DATA=$OBJ/${DATANAME}
342cat ${SSHAGENT_BIN} >${DATA}
343chmod u+w ${DATA}
344COPY=$OBJ/copy
345rm -f ${COPY}
346
347increase_datafile_size()
348{
349	while [ `du -k ${DATA} | cut -f1` -lt $1 ]; do
350		cat ${SSHAGENT_BIN} >>${DATA}
351	done
352}
353
354# these should be used in tests
355export SSH SSHD SSHAGENT SSHADD SSHKEYGEN SSHKEYSCAN SFTP SFTPSERVER SCP
356export SSH_PKCS11_HELPER SSH_SK_HELPER
357#echo $SSH $SSHD $SSHAGENT $SSHADD $SSHKEYGEN $SSHKEYSCAN $SFTP $SFTPSERVER $SCP
358
359# Portable specific functions
360which()
361{
362	saved_IFS="$IFS"
363	IFS=":"
364	for i in $PATH
365	do
366		if [ -x $i/$1 ]; then
367			IFS="$saved_IFS"
368			echo "$i/$1"
369			return 0
370		fi
371	done
372	IFS="$saved_IFS"
373	echo "$i/$1"
374	return 1
375}
376
377have_prog()
378{
379	which "$1" >/dev/null 2>&1
380	return $?
381}
382
383jot() {
384	awk "BEGIN { for (i = $2; i < $2 + $1; i++) { printf \"%d\n\", i } exit }"
385}
386if [ ! -x "`which rev`" ]; then
387rev()
388{
389	awk '{for (i=length; i>0; i--) printf "%s", substr($0, i, 1); print ""}'
390}
391fi
392
393# Check whether preprocessor symbols are defined in config.h.
394config_defined ()
395{
396	str=$1
397	while test "x$2" != "x" ; do
398		str="$str|$2"
399		shift
400	done
401	egrep "^#define.*($str)" ${BUILDDIR}/config.h >/dev/null 2>&1
402}
403
404md5 () {
405	if have_prog md5sum; then
406		md5sum
407	elif have_prog openssl; then
408		openssl md5
409	elif have_prog cksum; then
410		cksum
411	elif have_prog sum; then
412		sum
413	elif [ -x ${OPENSSL_BIN} ]; then
414		${OPENSSL_BIN} md5
415	else
416		wc -c
417	fi
418}
419
420# Some platforms don't have hostname at all, but on others uname -n doesn't
421# provide the fully qualified name we need, so in the former case we create
422# our own hostname function.
423if ! have_prog hostname; then
424	hostname() {
425		uname -n
426	}
427fi
428
429make_tmpdir ()
430{
431	SSH_REGRESS_TMP="$($OBJ/mkdtemp openssh-XXXXXXXX)" || \
432	    fatal "failed to create temporary directory"
433}
434# End of portable specific functions
435
436stop_sshd ()
437{
438	if [ -f $PIDFILE ]; then
439		pid=`$SUDO cat $PIDFILE`
440		if [ "X$pid" = "X" ]; then
441			echo no sshd running
442		else
443			if [ $pid -lt 2 ]; then
444				echo bad pid for sshd: $pid
445			else
446				$SUDO kill $pid
447				trace "wait for sshd to exit"
448				i=0;
449				while [ -f $PIDFILE -a $i -lt 5 ]; do
450					i=`expr $i + 1`
451					sleep $i
452				done
453				if test -f $PIDFILE; then
454					if $SUDO kill -0 $pid; then
455						echo "sshd didn't exit " \
456						    "port $PORT pid $pid"
457					else
458						echo "sshd died without cleanup"
459					fi
460					exit 1
461				fi
462			fi
463		fi
464	fi
465}
466
467# helper
468cleanup ()
469{
470	if [ "x$SSH_PID" != "x" ]; then
471		if [ $SSH_PID -lt 2 ]; then
472			echo bad pid for ssh: $SSH_PID
473		else
474			kill $SSH_PID
475		fi
476	fi
477	if [ "x$SSH_REGRESS_TMP" != "x" ]; then
478		rm -rf "$SSH_REGRESS_TMP"
479	fi
480	stop_sshd
481	if [ ! -z "$TEST_SSH_ELAPSED_TIMES" ]; then
482		now=`date '+%s'`
483		elapsed=$(($now - $STARTTIME))
484		echo elapsed $elapsed `basename $SCRIPT .sh`
485	fi
486}
487
488start_debug_log ()
489{
490	echo "trace: $@" >>$TEST_REGRESS_LOGFILE
491	if [ -d "$TEST_SSH_LOGDIR" ]; then
492		rm -f $TEST_SSH_LOGDIR/*
493	fi
494}
495
496save_debug_log ()
497{
498	testname=`echo $tid | tr ' ' _`
499	tarname="$OBJ/failed-$testname-logs.tar"
500
501	echo $@ >>$TEST_REGRESS_LOGFILE
502	echo $@ >>$TEST_SSH_LOGFILE
503	echo $@ >>$TEST_SSHD_LOGFILE
504	echo "Saving debug logs to $tarname" >>$TEST_REGRESS_LOGFILE
505	(cat $TEST_REGRESS_LOGFILE; echo) >>$OBJ/failed-regress.log
506	(cat $TEST_SSH_LOGFILE; echo) >>$OBJ/failed-ssh.log
507	(cat $TEST_SSHD_LOGFILE; echo) >>$OBJ/failed-sshd.log
508
509	# Save all logfiles in a tarball.
510	(cd $OBJ &&
511	  logfiles=""
512	  for i in $TEST_REGRESS_LOGFILE $TEST_SSH_LOGFILE $TEST_SSHD_LOGFILE \
513	    $TEST_SSH_LOGDIR; do
514		if [ -e "`basename $i`" ]; then
515			logfiles="$logfiles `basename $i`"
516		else
517			logfiles="$logfiles $i"
518		fi
519	  done
520	  tar cf "$tarname" $logfiles)
521}
522
523trace ()
524{
525	start_debug_log $@
526	if [ "X$TEST_SSH_TRACE" = "Xyes" ]; then
527		echo "$@"
528	fi
529}
530
531verbose ()
532{
533	start_debug_log $@
534	if [ "X$TEST_SSH_QUIET" != "Xyes" ]; then
535		echo "$@"
536	fi
537}
538
539fail ()
540{
541	save_debug_log "FAIL: $@"
542	RESULT=1
543	echo "$@"
544	if test "x$TEST_SSH_FAIL_FATAL" != "x" ; then
545		cleanup
546		exit $RESULT
547	fi
548}
549
550fatal ()
551{
552	save_debug_log "FATAL: $@"
553	printf "FATAL: "
554	fail "$@"
555	cleanup
556	exit $RESULT
557}
558
559# Skip remaining tests in script.
560skip ()
561{
562	echo "SKIPPED: $@"
563	cleanup
564	exit $RESULT
565}
566
567maybe_add_scp_path_to_sshd ()
568{
569	# If we're testing a non-installed scp, add its directory to sshd's
570	# PATH so we can test it.  We don't do this for all tests as it
571	# breaks the SetEnv tests.
572	case "$SCP" in
573	/*)	PATH_WITH_SCP="`dirname $SCP`:$PATH"
574		echo "	SetEnv PATH='$PATH_WITH_SCP'" >>$OBJ/sshd_config
575		echo "	SetEnv PATH='$PATH_WITH_SCP'" >>$OBJ/sshd_proxy ;;
576	esac
577}
578
579RESULT=0
580PIDFILE=$OBJ/pidfile
581
582trap fatal 3 2
583
584# create server config
585cat << EOF > $OBJ/sshd_config
586	StrictModes		no
587	Port			$PORT
588	AddressFamily		inet
589	ListenAddress		127.0.0.1
590	#ListenAddress		::1
591	PidFile			$PIDFILE
592	AuthorizedKeysFile	$OBJ/authorized_keys_%u
593	LogLevel		DEBUG3
594	AcceptEnv		_XXX_TEST_*
595	AcceptEnv		_XXX_TEST
596	Subsystem	sftp	$SFTPSERVER
597EOF
598
599# This may be necessary if /usr/src and/or /usr/obj are group-writable,
600# but if you aren't careful with permissions then the unit tests could
601# be abused to locally escalate privileges.
602if [ ! -z "$TEST_SSH_UNSAFE_PERMISSIONS" ]; then
603	echo "	StrictModes no" >> $OBJ/sshd_config
604else
605	# check and warn if excessive permissions are likely to cause failures.
606	unsafe=""
607	dir="${OBJ}"
608	while test ${dir} != "/"; do
609		if test -d "${dir}" && ! test -h "${dir}"; then
610			perms=`ls -ld ${dir}`
611			case "${perms}" in
612			?????w????*|????????w?*) unsafe="${unsafe} ${dir}" ;;
613			esac
614		fi
615		dir=`dirname ${dir}`
616	done
617	if ! test  -z "${unsafe}"; then
618		cat <<EOD
619
620WARNING: Unsafe (group or world writable) directory permissions found:
621${unsafe}
622
623These could be abused to locally escalate privileges.  If you are
624sure that this is not a risk (eg there are no other users), you can
625bypass this check by setting TEST_SSH_UNSAFE_PERMISSIONS=1
626
627EOD
628	fi
629fi
630
631if [ ! -z "$TEST_SSH_MODULI_FILE" ]; then
632	trace "adding modulifile='$TEST_SSH_MODULI_FILE' to sshd_config"
633	echo "	ModuliFile '$TEST_SSH_MODULI_FILE'" >> $OBJ/sshd_config
634fi
635
636if [ ! -z "$TEST_SSH_SSHD_CONFOPTS" ]; then
637	trace "adding sshd_config option $TEST_SSH_SSHD_CONFOPTS"
638	echo "$TEST_SSH_SSHD_CONFOPTS" >> $OBJ/sshd_config
639fi
640
641# server config for proxy connects
642cp $OBJ/sshd_config $OBJ/sshd_proxy
643
644# allow group-writable directories in proxy-mode
645echo 'StrictModes no' >> $OBJ/sshd_proxy
646
647# create client config
648cat << EOF > $OBJ/ssh_config
649Host *
650	Hostname		127.0.0.1
651	HostKeyAlias		localhost-with-alias
652	Port			$PORT
653	User			$USER
654	GlobalKnownHostsFile	$OBJ/known_hosts
655	UserKnownHostsFile	$OBJ/known_hosts
656	PubkeyAuthentication	yes
657	ChallengeResponseAuthentication	no
658	PasswordAuthentication	no
659	BatchMode		yes
660	StrictHostKeyChecking	yes
661	LogLevel		DEBUG3
662EOF
663
664if [ ! -z "$TEST_SSH_SSH_CONFOPTS" ]; then
665	trace "adding ssh_config option $TEST_SSH_SSH_CONFOPTS"
666	echo "$TEST_SSH_SSH_CONFOPTS" >> $OBJ/ssh_config
667fi
668
669rm -f $OBJ/known_hosts $OBJ/authorized_keys_$USER
670
671SSH_SK_PROVIDER=
672if ! config_defined ENABLE_SK; then
673	trace skipping sk-dummy
674elif [ -f "${SRC}/misc/sk-dummy/obj/sk-dummy.so" ] ; then
675	SSH_SK_PROVIDER="${SRC}/misc/sk-dummy/obj/sk-dummy.so"
676elif [ -f "${OBJ}/misc/sk-dummy/sk-dummy.so" ] ; then
677	SSH_SK_PROVIDER="${OBJ}/misc/sk-dummy/sk-dummy.so"
678elif [ -f "${SRC}/misc/sk-dummy/sk-dummy.so" ] ; then
679	SSH_SK_PROVIDER="${SRC}/misc/sk-dummy/sk-dummy.so"
680fi
681export SSH_SK_PROVIDER
682
683if ! test -z "$SSH_SK_PROVIDER"; then
684	EXTRA_AGENT_ARGS='-P/*' # XXX want realpath(1)...
685	echo "SecurityKeyProvider $SSH_SK_PROVIDER" >> $OBJ/ssh_config
686	echo "SecurityKeyProvider $SSH_SK_PROVIDER" >> $OBJ/sshd_config
687	echo "SecurityKeyProvider $SSH_SK_PROVIDER" >> $OBJ/sshd_proxy
688fi
689export EXTRA_AGENT_ARGS
690
691maybe_filter_sk() {
692	if test -z "$SSH_SK_PROVIDER" ; then
693		grep -v ^sk
694	else
695		cat
696	fi
697}
698
699SSH_KEYTYPES=`$SSH -Q key-plain | maybe_filter_sk`
700SSH_HOSTKEY_TYPES=`$SSH -Q key-plain | maybe_filter_sk`
701
702for t in ${SSH_KEYTYPES}; do
703	# generate user key
704	if [ ! -f $OBJ/$t ] || [ ${SSHKEYGEN_BIN} -nt $OBJ/$t ]; then
705		trace "generating key type $t"
706		rm -f $OBJ/$t
707		${SSHKEYGEN} -q -N '' -t $t  -f $OBJ/$t ||\
708			fail "ssh-keygen for $t failed"
709	else
710		trace "using cached key type $t"
711	fi
712
713	# setup authorized keys
714	cat $OBJ/$t.pub >> $OBJ/authorized_keys_$USER
715	echo IdentityFile $OBJ/$t >> $OBJ/ssh_config
716done
717
718for t in ${SSH_HOSTKEY_TYPES}; do
719	# known hosts file for client
720	(
721		printf 'localhost-with-alias,127.0.0.1,::1 '
722		cat $OBJ/$t.pub
723	) >> $OBJ/known_hosts
724
725	# use key as host key, too
726	(umask 077; $SUDO cp $OBJ/$t $OBJ/host.$t)
727	echo HostKey $OBJ/host.$t >> $OBJ/sshd_config
728
729	# don't use SUDO for proxy connect
730	echo HostKey $OBJ/$t >> $OBJ/sshd_proxy
731done
732chmod 644 $OBJ/authorized_keys_$USER
733
734# Activate Twisted Conch tests if the binary is present
735REGRESS_INTEROP_CONCH=no
736if test -x "$CONCH" ; then
737	REGRESS_INTEROP_CONCH=yes
738fi
739
740# If PuTTY is present, new enough and we are running a PuTTY test, prepare
741# keys and configuration.
742REGRESS_INTEROP_PUTTY=no
743if test -x "$PUTTYGEN" -a -x "$PLINK" &&
744    "$PUTTYGEN" --help 2>&1 | grep -- --new-passphrase >/dev/null; then
745	REGRESS_INTEROP_PUTTY=yes
746fi
747case "$SCRIPT" in
748*putty*)	;;
749*)		REGRESS_INTEROP_PUTTY=no ;;
750esac
751
752if test "$REGRESS_INTEROP_PUTTY" = "yes" ; then
753	mkdir -p ${OBJ}/.putty
754
755	# Add a PuTTY key to authorized_keys
756	rm -f ${OBJ}/putty.rsa2
757	if ! "$PUTTYGEN" -t rsa -o ${OBJ}/putty.rsa2 \
758	    --random-device=/dev/urandom \
759	    --new-passphrase /dev/null < /dev/null > /dev/null; then
760		echo "Your installed version of PuTTY is too old to support --new-passphrase, skipping test" >&2
761		exit 1
762	fi
763	"$PUTTYGEN" -O public-openssh ${OBJ}/putty.rsa2 \
764	    >> $OBJ/authorized_keys_$USER
765
766	# Convert rsa2 host key to PuTTY format
767	cp $OBJ/ssh-rsa $OBJ/ssh-rsa_oldfmt
768	${SSHKEYGEN} -p -N '' -m PEM -f $OBJ/ssh-rsa_oldfmt >/dev/null
769	${SRC}/ssh2putty.sh 127.0.0.1 $PORT $OBJ/ssh-rsa_oldfmt > \
770	    ${OBJ}/.putty/sshhostkeys
771	${SRC}/ssh2putty.sh 127.0.0.1 22 $OBJ/ssh-rsa_oldfmt >> \
772	    ${OBJ}/.putty/sshhostkeys
773	rm -f $OBJ/ssh-rsa_oldfmt
774
775	# Setup proxied session
776	mkdir -p ${OBJ}/.putty/sessions
777	rm -f ${OBJ}/.putty/sessions/localhost_proxy
778	echo "Protocol=ssh" >> ${OBJ}/.putty/sessions/localhost_proxy
779	echo "HostName=127.0.0.1" >> ${OBJ}/.putty/sessions/localhost_proxy
780	echo "PortNumber=$PORT" >> ${OBJ}/.putty/sessions/localhost_proxy
781	echo "ProxyMethod=5" >> ${OBJ}/.putty/sessions/localhost_proxy
782	echo "ProxyTelnetCommand=${OBJ}/sshd-log-wrapper.sh -i -f $OBJ/sshd_proxy" >> ${OBJ}/.putty/sessions/localhost_proxy
783	echo "ProxyLocalhost=1" >> ${OBJ}/.putty/sessions/localhost_proxy
784
785	PUTTYDIR=${OBJ}/.putty
786	export PUTTYDIR
787fi
788
789# create a proxy version of the client config
790(
791	cat $OBJ/ssh_config
792	echo proxycommand ${SUDO} env SSH_SK_HELPER=\"$SSH_SK_HELPER\" ${OBJ}/sshd-log-wrapper.sh -i -f $OBJ/sshd_proxy
793) > $OBJ/ssh_proxy
794
795# check proxy config
796${SSHD} -t -f $OBJ/sshd_proxy	|| fatal "sshd_proxy broken"
797
798start_sshd ()
799{
800	# start sshd
801	logfile="${TEST_SSH_LOGDIR}/sshd.`$OBJ/timestamp`.$$.log"
802	$SUDO ${SSHD} -f $OBJ/sshd_config "$@" -t || fatal "sshd_config broken"
803	$SUDO env SSH_SK_HELPER="$SSH_SK_HELPER" \
804	    ${SSHD} -f $OBJ/sshd_config "$@" -E$TEST_SSHD_LOGFILE
805
806	trace "wait for sshd"
807	i=0;
808	while [ ! -f $PIDFILE -a $i -lt 10 ]; do
809		i=`expr $i + 1`
810		sleep $i
811	done
812
813	test -f $PIDFILE || fatal "no sshd running on port $PORT"
814}
815
816# source test body
817. $SCRIPT
818
819# kill sshd
820cleanup
821
822if [ "x$USE_VALGRIND" != "x" ]; then
823	# If there is an EXIT trap handler, invoke it now.
824	# Some tests set these to clean up processes such as ssh-agent.  We
825	# need to wait for all valgrind processes to complete so we can check
826	# their logs, but since the EXIT traps are not invoked until
827	# test-exec.sh exits, waiting here will deadlock.
828	# This is not very portable but then neither is valgrind itself.
829	# As a bonus, dash (as used on the runners) has a "trap" that doesn't
830	# work in a pipeline (hence the temp file) or a subshell.
831	exithandler=""
832	trap >/tmp/trap.$$ && exithandler=$(cat /tmp/trap.$$ | \
833	    awk -F "'" '/EXIT$/{print $2}')
834	rm -f /tmp/trap.$$
835	if [ "x${exithandler}" != "x" ]; then
836		verbose invoking EXIT trap handler early: ${exithandler}
837		eval "${exithandler}"
838		trap '' EXIT
839	fi
840
841	# wait for any running process to complete
842	wait; sleep 1
843	VG_RESULTS=$(find $OBJ/valgrind-out -type f -print)
844	VG_RESULT_COUNT=0
845	VG_FAIL_COUNT=0
846	for i in $VG_RESULTS; do
847		if grep "ERROR SUMMARY" $i >/dev/null; then
848			VG_RESULT_COUNT=$(($VG_RESULT_COUNT + 1))
849			if ! grep "ERROR SUMMARY: 0 errors" $i >/dev/null; then
850				VG_FAIL_COUNT=$(($VG_FAIL_COUNT + 1))
851				RESULT=1
852				verbose valgrind failure $i
853				cat $i
854			fi
855		fi
856	done
857	if [ x"$VG_SKIP" != "x" ]; then
858		verbose valgrind skipped
859	else
860		verbose valgrind results $VG_RESULT_COUNT failures $VG_FAIL_COUNT
861	fi
862fi
863
864if [ $RESULT -eq 0 ]; then
865	verbose ok $tid
866	if [ "x$CACHE" != "x" ]; then
867		touch "$CACHE"
868	fi
869else
870	echo failed $tid
871fi
872exit $RESULT
873