Skip to content

Commit 9588ac3

Browse files
committed
wolfsshd: refuse sessions it cannot serve
A shell, exec or subsystem request is answered as it arrives, through the channel request callbacks, so a session this build cannot serve is refused with CHANNEL_FAILURE rather than accepted and then dropped once the session is up. What the daemon serves is unchanged. - SessionRequestCb() takes a shell with WOLFSSH_SHELL, an exec with WOLFSSH_SHELL or an scp command with WOLFSSH_SCP, and the sftp subsystem with WOLFSSH_SFTP; anything else is refused and logged - a request whose command did not fit is refused rather than read through a NULL - sshd_bad_subsystem_test.sh asks for an unknown subsystem with the OpenSSH client and expects the refusal
1 parent b33f59d commit 9588ac3

3 files changed

Lines changed: 147 additions & 0 deletions

File tree

apps/wolfsshd/test/run_all_sshd_tests.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ test_cases=(
99
"sshd_large_sftp_test.sh"
1010
"sshd_bad_sftp_test.sh"
1111
"sshd_sftp_idle_cpu_test.sh"
12+
"sshd_bad_subsystem_test.sh"
1213
"sshd_scp_fail.sh"
1314
"sshd_term_close_test.sh"
1415
"sshd_stdin_eof_test.sh"
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/bin/sh
2+
3+
# sshd local test: a subsystem the daemon does not serve is refused at the
4+
# request, so the client sees CHANNEL_FAILURE rather than a session that
5+
# is accepted and then dropped. Uses the system OpenSSH client, since the
6+
# in-tree clients only ask for sftp.
7+
8+
# Not named PWD: the shell rewrites that variable on every cd, so a saved
9+
# copy would not survive the cd to the repository root below.
10+
TESTDIR=`pwd`
11+
cd ../../..
12+
13+
USER=`whoami`
14+
PRIVATE_KEY="./keys/hansel-key-ecc.pem"
15+
16+
if [ -z "$1" ] || [ -z "$2" ]; then
17+
echo "expecting host and port as arguments"
18+
echo "./sshd_bad_subsystem_test.sh 127.0.0.1 22222"
19+
exit 1
20+
fi
21+
22+
if ! command -v ssh >/dev/null 2>&1; then
23+
echo "OpenSSH client not found, skipping"
24+
exit 77
25+
fi
26+
27+
# OpenSSH refuses a key file other users can read.
28+
KEY=`mktemp`
29+
cat "$PRIVATE_KEY" > "$KEY"
30+
chmod 600 "$KEY"
31+
OUT=`mktemp`
32+
33+
ssh_to_sshd() {
34+
ssh -p "$2" -i "$KEY" -o IdentitiesOnly=yes -o StrictHostKeyChecking=no \
35+
-o UserKnownHostsFile=/dev/null -o PreferredAuthentications=publickey \
36+
-o BatchMode=yes -o ConnectTimeout=5 "$USER@$1" "$3" "$4"
37+
}
38+
39+
# Control: the same client and key can run a command.
40+
ssh_to_sshd "$1" "$2" "echo ok" > "$OUT" 2>&1
41+
RESULT=$?
42+
if [ "$RESULT" != "0" ] || ! grep -q "^ok" "$OUT"; then
43+
echo "Control exec through OpenSSH failed ($RESULT):"
44+
cat "$OUT"
45+
rm -f "$KEY" "$OUT"
46+
exit 1
47+
fi
48+
49+
# A subsystem nothing serves: the client reports the refusal and exits
50+
# non-zero.
51+
ssh_to_sshd "$1" "$2" -s no-such-subsystem > "$OUT" 2>&1
52+
RESULT=$?
53+
if [ "$RESULT" = "0" ]; then
54+
echo "Expecting the unknown subsystem request to fail"
55+
cat "$OUT"
56+
rm -f "$KEY" "$OUT"
57+
exit 1
58+
fi
59+
if ! grep -q "subsystem request failed" "$OUT"; then
60+
echo "Expecting the client to report the refused subsystem request:"
61+
cat "$OUT"
62+
rm -f "$KEY" "$OUT"
63+
exit 1
64+
fi
65+
66+
rm -f "$KEY" "$OUT"
67+
cd "$TESTDIR"
68+
exit 0

apps/wolfsshd/wolfsshd.c

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,80 @@ static void CleanupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx,
359359
(void)conf;
360360
}
361361

362+
/* Answers a shell, exec or subsystem request as it arrives: a session this
363+
* build cannot serve is refused with CHANNEL_FAILURE, rather than accepted
364+
* and then dropped once the session is up. Returns 0 to accept and 1 to
365+
* refuse. The command is NULL when the request carried none that fit. */
366+
static int SessionRequestCb(WOLFSSH_CHANNEL* channel, void* vCtx)
367+
{
368+
WOLFSSHD_CONNECTION* conn = (WOLFSSHD_CONNECTION*)vCtx;
369+
const char* cmd;
370+
const char* reason = NULL;
371+
int rej = 1;
372+
373+
if (conn == NULL || channel == NULL) {
374+
return 1;
375+
}
376+
377+
cmd = wolfSSH_ChannelGetSessionCommand(channel);
378+
switch (wolfSSH_ChannelGetSessionType(channel)) {
379+
case WOLFSSH_SESSION_SHELL:
380+
#ifdef WOLFSSH_SHELL
381+
rej = 0;
382+
#else
383+
reason = "shell support is disabled";
384+
#endif
385+
break;
386+
387+
case WOLFSSH_SESSION_EXEC:
388+
if (cmd == NULL) {
389+
reason = "exec request carried no command";
390+
break;
391+
}
392+
#ifdef WOLFSSH_SCP
393+
if (WSTRNCMP(cmd, "scp", 3) == 0) {
394+
rej = 0;
395+
break;
396+
}
397+
#endif
398+
#ifdef WOLFSSH_SHELL
399+
rej = 0;
400+
#else
401+
reason = "exec support is disabled";
402+
#endif
403+
break;
404+
405+
case WOLFSSH_SESSION_SUBSYSTEM:
406+
if (cmd == NULL) {
407+
reason = "subsystem request carried no name";
408+
}
409+
#ifdef WOLFSSH_SFTP
410+
else if (WSTRCMP(cmd, "sftp") == 0) {
411+
rej = 0;
412+
}
413+
#endif
414+
else {
415+
reason = "unknown or unsupported subsystem";
416+
}
417+
break;
418+
419+
case WOLFSSH_SESSION_UNKNOWN:
420+
case WOLFSSH_SESSION_TERMINAL:
421+
default:
422+
reason = "unsupported session type";
423+
break;
424+
}
425+
426+
if (rej) {
427+
wolfSSH_Log(WS_LOG_ERROR,
428+
"[SSHD] Refusing session request from %s: %s [%s]",
429+
conn->ip, reason, cmd != NULL ? cmd : "");
430+
}
431+
432+
return rej;
433+
}
434+
435+
362436
/* Initializes and sets up the WOLFSSH_CTX struct based on the configure options
363437
* return WS_SUCCESS on success
364438
*/
@@ -386,6 +460,9 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx,
386460
if (ret == WS_SUCCESS) {
387461
wolfSSH_SetUserAuth(*ctx, DefaultUserAuth);
388462
wolfSSH_SetUserAuthResult(*ctx, UserAuthResult);
463+
wolfSSH_CTX_SetChannelReqShellCb(*ctx, SessionRequestCb);
464+
wolfSSH_CTX_SetChannelReqExecCb(*ctx, SessionRequestCb);
465+
wolfSSH_CTX_SetChannelReqSubsysCb(*ctx, SessionRequestCb);
389466
}
390467

391468
/* set banner to display on connection */
@@ -2545,6 +2622,7 @@ static void* HandleConnection(void* arg)
25452622
/* let UserAuthResult reach this connection to cancel the grace timer
25462623
* and to reach conn->auth for the cert force-command */
25472624
wolfSSH_SetUserAuthResultCtx(ssh, conn);
2625+
wolfSSH_SetChannelReqCtx(ssh, conn);
25482626
#if defined(WOLFSSH_OSSH_CERTS) && !defined(_WIN32)
25492627
/* Unix-only: each connection is a forked child with its own copy of the
25502628
* auth struct. Windows does not enforce OpenSSH certs. */

0 commit comments

Comments
 (0)