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