Skip to content

Commit 91cb9ad

Browse files
ejohnstownphilljj
authored andcommitted
internal: refuse userauth before service request
The server refuses user auth messages until the ssh-userauth service request is in, per RFC 4252 section 1, and disconnects on them as on any known id at the wrong time. A successful userauth request can no longer satisfy the accept loop and draw SERVICE_ACCEPT for a service the client never requested. - IsMessageAllowedServer() gates ids 50 to 79 on acceptState reaching ACCEPT_CLIENT_USERAUTH_REQUEST_DONE - regress: the filter refuses USERAUTH_REQUEST and INFO_RESPONSE when keyed and admits both after the service request - regress: a userauth request with no service request disconnects - regress: a userauth request pipelined behind the service request still gets SERVICE_ACCEPT, then its answer Issue: #1287
1 parent 21a2001 commit 91cb9ad

3 files changed

Lines changed: 111 additions & 0 deletions

File tree

‎src/internal.c‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,16 @@ INLINE static int IsMessageAllowedServer(WOLFSSH *ssh, byte msg)
847847
}
848848
}
849849

850+
/* No userauth before the service request, RFC 4252 section 1.
851+
* wolfSSH_accept() advances acceptState before reading again, so a
852+
* pipelined request still passes. */
853+
if (MSGIDLIMIT_AUTH(msg)
854+
&& ssh->acceptState < ACCEPT_CLIENT_USERAUTH_REQUEST_DONE) {
855+
WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by %s %s",
856+
msg, "server", "before the service request");
857+
return 0;
858+
}
859+
850860
/* Has client userauth started? */
851861
/* Allows the server to receive up to KEXDH GEX Request during KEX. */
852862
if (ssh->acceptState < ACCEPT_KEYED) {

‎src/ssh.c‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,8 @@ int wolfSSH_accept(WOLFSSH* ssh)
761761
return WS_FATAL_ERROR;
762762
}
763763
}
764+
/* IsMessageAllowedServer() gates userauth on this; set it
765+
* before any further read. */
764766
ssh->acceptState = ACCEPT_CLIENT_USERAUTH_REQUEST_DONE;
765767
WLOG(WS_LOG_DEBUG, acceptState, "CLIENT_USERAUTH_REQUEST_DONE");
766768
FALL_THROUGH;

‎tests/regress.c‎

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5011,6 +5011,26 @@ static void TestServerUserauthBlockedBeforeKeyed(WOLFSSH* ssh)
50115011
}
50125012

50135013

5014+
/* Keyed is not enough: userauth waits for the service request. */
5015+
static void TestServerUserauthBlockedBeforeServiceRequest(WOLFSSH* ssh)
5016+
{
5017+
ResetSession(ssh);
5018+
ssh->acceptState = ACCEPT_KEYED;
5019+
5020+
AssertFalse(wolfSSH_TestIsMessageAllowed(ssh, MSGID_USERAUTH_REQUEST,
5021+
WS_MSG_RECV));
5022+
AssertFalse(wolfSSH_TestIsMessageAllowed(ssh,
5023+
MSGID_USERAUTH_INFO_RESPONSE, WS_MSG_RECV));
5024+
5025+
ssh->acceptState = ACCEPT_CLIENT_USERAUTH_REQUEST_DONE;
5026+
5027+
AssertTrue(wolfSSH_TestIsMessageAllowed(ssh, MSGID_USERAUTH_REQUEST,
5028+
WS_MSG_RECV));
5029+
AssertTrue(wolfSSH_TestIsMessageAllowed(ssh,
5030+
MSGID_USERAUTH_INFO_RESPONSE, WS_MSG_RECV));
5031+
}
5032+
5033+
50145034
/* One packet fed to a keyed but unauthenticated server. The cases below
50155035
* differ only in the packet and the answer it draws. */
50165036
static void RunServerMsgIdAtKeyed(const byte* pkt, word32 pktSz,
@@ -7910,6 +7930,82 @@ static void TestSameUserRetryAllowed(void)
79107930
FreeChannelOpenHarness(&harness);
79117931
}
79127932

7933+
static int AcceptPasswordUserAuthCb(byte authType, WS_UserAuthData* authData,
7934+
void* ctx)
7935+
{
7936+
(void)authData;
7937+
(void)ctx;
7938+
7939+
authCbInvoked = 1;
7940+
return (authType == WOLFSSH_USERAUTH_PASSWORD) ?
7941+
WOLFSSH_USERAUTH_SUCCESS : WOLFSSH_USERAUTH_FAILURE;
7942+
}
7943+
7944+
/* A userauth request without a service request draws a disconnect, not a
7945+
* SERVICE_ACCEPT, even with a password the callback accepts. */
7946+
static void TestUserAuthBeforeServiceRequestDisconnects(void)
7947+
{
7948+
ChannelOpenHarness harness;
7949+
byte in[128];
7950+
word32 inSz;
7951+
7952+
inSz = BuildUserAuthPasswordRequest("alice", "pw", in, sizeof(in));
7953+
7954+
ResetAuthCbRecord();
7955+
InitChannelOpenHarness(&harness, in, inSz);
7956+
wolfSSH_SetUserAuth(harness.ctx, AcceptPasswordUserAuthCb);
7957+
harness.ssh->acceptState = ACCEPT_KEYED;
7958+
7959+
AssertIntEQ(wolfSSH_accept(harness.ssh), WS_FATAL_ERROR);
7960+
AssertIntEQ(harness.ssh->error, WS_MSGID_NOT_ALLOWED_E);
7961+
AssertIntEQ(authCbInvoked, 0);
7962+
AssertIntEQ(harness.ssh->acceptState, ACCEPT_KEYED);
7963+
AssertTrue(harness.io.outSz > 0);
7964+
AssertIntEQ(ParseMsgId(harness.io.out, harness.io.outSz),
7965+
MSGID_DISCONNECT);
7966+
AssertTrue(harness.ssh->disconnected);
7967+
7968+
FreeChannelOpenHarness(&harness);
7969+
}
7970+
7971+
/* A userauth request queued right behind the service request draws
7972+
* SERVICE_ACCEPT, then its answer. */
7973+
static void TestUserAuthPipelinedAfterServiceRequest(void)
7974+
{
7975+
ChannelOpenHarness harness;
7976+
byte payload[32];
7977+
byte in[256];
7978+
word32 inSz;
7979+
word32 idx = 0;
7980+
word32 off;
7981+
7982+
idx = AppendString(payload, sizeof(payload), idx, "ssh-userauth");
7983+
inSz = WrapPacket(MSGID_SERVICE_REQUEST, payload, idx, in, sizeof(in));
7984+
inSz += BuildUserAuthPasswordRequest("alice", "pw",
7985+
in + inSz, sizeof(in) - inSz);
7986+
7987+
ResetAuthCbRecord();
7988+
InitChannelOpenHarness(&harness, in, inSz);
7989+
wolfSSH_SetUserAuth(harness.ctx, AcceptPasswordUserAuthCb);
7990+
harness.ssh->acceptState = ACCEPT_KEYED;
7991+
7992+
/* Stops for want of the channel open that would follow. */
7993+
AssertIntEQ(wolfSSH_accept(harness.ssh), WS_FATAL_ERROR);
7994+
AssertIntEQ(harness.ssh->error, WS_WANT_READ);
7995+
AssertIntEQ(authCbInvoked, 1);
7996+
AssertIntEQ(harness.ssh->acceptState, ACCEPT_SERVER_USERAUTH_SENT);
7997+
AssertIntEQ(harness.io.inOff, harness.io.inSz);
7998+
AssertIntEQ(ParseMsgId(harness.io.out, harness.io.outSz),
7999+
MSGID_SERVICE_ACCEPT);
8000+
off = NextPacketOffset(harness.io.out, harness.io.outSz);
8001+
AssertIntEQ(ParseMsgId(harness.io.out + off, harness.io.outSz - off),
8002+
MSGID_USERAUTH_SUCCESS);
8003+
off += NextPacketOffset(harness.io.out + off, harness.io.outSz - off);
8004+
AssertIntEQ(off, harness.io.outSz);
8005+
8006+
FreeChannelOpenHarness(&harness);
8007+
}
8008+
79138009
#ifdef WOLFSSH_KEYBOARD_INTERACTIVE
79148010
static word32 BuildUserAuthKeyboardRequest(const char* user,
79158011
byte* out, word32 outSz)
@@ -19042,6 +19138,7 @@ int main(int argc, char** argv)
1904219138
TestServerChannelBlockedBeforeAuth(serverSsh);
1904319139
TestServerChannelAllowedAfterAuth(serverSsh);
1904419140
TestServerUserauthBlockedBeforeKeyed(serverSsh);
19141+
TestServerUserauthBlockedBeforeServiceRequest(serverSsh);
1904519142
TestServerHighMsgIdBeforeAuthDisconnects();
1904619143
TestServerUnknownMsgIdBeforeAuthUnimplemented();
1904719144
TestServerKnownAuthMsgIdBeforeAuthDisconnects();
@@ -19111,6 +19208,8 @@ int main(int argc, char** argv)
1911119208
TestSecondSessionChannelRejected();
1911219209
TestUsernameChangeDisconnects();
1911319210
TestSameUserRetryAllowed();
19211+
TestUserAuthBeforeServiceRequestDisconnects();
19212+
TestUserAuthPipelinedAfterServiceRequest();
1911419213
#ifdef WOLFSSH_KEYBOARD_INTERACTIVE
1911519214
TestKbInfoResponseCountMismatchSendsFailure();
1911619215
TestKbInfoResponseMismatchKeepsFraming();

0 commit comments

Comments
 (0)