@@ -3725,6 +3725,145 @@ static void TestClientServiceAcceptBlockedDuringKeying(WOLFSSH* ssh)
37253725}
37263726
37273727
3728+ /* Until the first userauth request goes out, no userauth message may
3729+ * arrive, the banner included. */
3730+ static void TestClientUserauthReplyBeforeRequest(WOLFSSH* ssh)
3731+ {
3732+ static const byte msgs[] = {
3733+ MSGID_USERAUTH_FAILURE,
3734+ MSGID_USERAUTH_SUCCESS,
3735+ MSGID_USERAUTH_BANNER,
3736+ MSGID_USERAUTH_PK_OK, /* also INFO_REQUEST and PW_CHRQ */
3737+ };
3738+ static const byte states[] = {
3739+ CONNECT_KEYED,
3740+ CONNECT_CLIENT_USERAUTH_REQUEST_SENT,
3741+ CONNECT_SERVER_USERAUTH_REQUEST_DONE,
3742+ };
3743+ word32 i, j;
3744+ int allowed;
3745+
3746+ for (i = 0; i < sizeof(states)/sizeof(*states); i++) {
3747+ for (j = 0; j < sizeof(msgs)/sizeof(*msgs); j++) {
3748+ ResetSession(ssh);
3749+ ssh->connectState = states[i];
3750+ allowed = wolfSSH_TestIsMessageAllowed(ssh, msgs[j],
3751+ WS_MSG_RECV);
3752+ AssertFalse(allowed);
3753+ AssertIntEQ(ssh->error, WS_MSGID_NOT_ALLOWED_E);
3754+ }
3755+ }
3756+
3757+ /* The service accept still answers the service request. */
3758+ ResetSession(ssh);
3759+ ssh->connectState = CONNECT_CLIENT_USERAUTH_REQUEST_SENT;
3760+ allowed = wolfSSH_TestIsMessageAllowed(ssh, MSGID_SERVICE_ACCEPT,
3761+ WS_MSG_RECV);
3762+ AssertTrue(allowed);
3763+
3764+ /* Once the first userauth request is out, all of them are allowed. */
3765+ for (j = 0; j < sizeof(msgs)/sizeof(*msgs); j++) {
3766+ ResetSession(ssh);
3767+ ssh->connectState = CONNECT_CLIENT_USERAUTH_SENT;
3768+ allowed = wolfSSH_TestIsMessageAllowed(ssh, msgs[j],
3769+ WS_MSG_RECV);
3770+ AssertTrue(allowed);
3771+ }
3772+ }
3773+
3774+
3775+ /* A userauth reply to the service request draws a disconnect and leaves
3776+ * serverState alone. */
3777+ static void TestClientUnsolicitedUserauthReplyRejected(byte msgId)
3778+ {
3779+ WOLFSSH_CTX* ctx;
3780+ WOLFSSH* ssh;
3781+ MemIo io;
3782+ byte pkt[32];
3783+ byte out[256];
3784+ word32 pktSz;
3785+
3786+ pktSz = BuildPacket(msgId, pkt, sizeof(pkt));
3787+
3788+ ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
3789+ AssertNotNull(ctx);
3790+ wolfSSH_SetIORecv(ctx, MemRecv);
3791+ wolfSSH_SetIOSend(ctx, MemSend);
3792+
3793+ ssh = wolfSSH_new(ctx);
3794+ AssertNotNull(ssh);
3795+
3796+ MemIoInit(&io, pkt, pktSz, out, sizeof(out));
3797+ wolfSSH_SetIOReadCtx(ssh, &io);
3798+ wolfSSH_SetIOWriteCtx(ssh, &io);
3799+
3800+ ssh->connectState = CONNECT_CLIENT_USERAUTH_REQUEST_SENT;
3801+ ssh->serverState = SERVER_KEXINIT_DONE;
3802+
3803+ AssertIntEQ(wolfSSH_TestDoReceive(ssh), WS_FATAL_ERROR);
3804+ AssertIntEQ(ssh->error, WS_MSGID_NOT_ALLOWED_E);
3805+ AssertIntEQ(ssh->serverState, SERVER_KEXINIT_DONE);
3806+ AssertTrue(io.outSz > 0);
3807+ AssertIntEQ(ParseMsgId(io.out, io.outSz), MSGID_DISCONNECT);
3808+ AssertTrue(ssh->disconnected);
3809+
3810+ wolfSSH_free(ssh);
3811+ wolfSSH_CTX_free(ctx);
3812+ }
3813+
3814+
3815+ /* Unused by a "none" request, which still needs a callback. */
3816+ static int NoneUserAuthCb(byte authType, WS_UserAuthData* authData,
3817+ void* ctx)
3818+ {
3819+ (void)authType;
3820+ (void)authData;
3821+ (void)ctx;
3822+ return WOLFSSH_USERAUTH_INVALID_AUTHTYPE;
3823+ }
3824+
3825+ /* A banner bundled behind the service accept, as wolfSSH's server sends
3826+ * it, is read after wolfSSH_connect() sends the first request. */
3827+ static void TestClientBannerBehindServiceAccept(void)
3828+ {
3829+ ChannelOpenHarness harness;
3830+ byte in[128];
3831+ byte payload[64];
3832+ word32 inSz;
3833+ word32 payloadSz;
3834+
3835+ payloadSz = AppendString(payload, sizeof(payload), 0, "ssh-userauth");
3836+ inSz = WrapPacket(MSGID_SERVICE_ACCEPT, payload, payloadSz,
3837+ in, sizeof(in));
3838+ payloadSz = AppendString(payload, sizeof(payload), 0, "banner");
3839+ payloadSz = AppendString(payload, sizeof(payload), payloadSz, "");
3840+ inSz += WrapPacket(MSGID_USERAUTH_BANNER, payload, payloadSz,
3841+ in + inSz, sizeof(in) - inSz);
3842+
3843+ InitChannelOpenHarnessClient(&harness, in, inSz);
3844+ wolfSSH_SetUserAuth(harness.ctx, NoneUserAuthCb);
3845+ AssertIntEQ(wolfSSH_SetUsername(harness.ssh, "user"), WS_SUCCESS);
3846+ harness.ssh->connectState = CONNECT_CLIENT_USERAUTH_REQUEST_SENT;
3847+ harness.ssh->serverState = SERVER_KEXINIT_DONE;
3848+
3849+ /* Both packets read, then WANT_READ for the auth answer. */
3850+ AssertIntEQ(wolfSSH_connect(harness.ssh), WS_FATAL_ERROR);
3851+ AssertIntEQ(wolfSSH_get_error(harness.ssh), WS_WANT_READ);
3852+ AssertIntEQ(harness.io.inOff, inSz);
3853+ AssertIntEQ(harness.ssh->connectState, CONNECT_CLIENT_USERAUTH_SENT);
3854+ AssertIntEQ(harness.ssh->serverState, SERVER_USERAUTH_REQUEST_DONE);
3855+ AssertFalse(harness.ssh->disconnected);
3856+ /* Only the userauth request went out. */
3857+ AssertTrue(harness.io.outSz > 0);
3858+ AssertIntEQ(ParseMsgId(harness.io.out, harness.io.outSz),
3859+ MSGID_USERAUTH_REQUEST);
3860+ AssertIntEQ(NextPacketOffset(harness.io.out, harness.io.outSz),
3861+ harness.io.outSz);
3862+
3863+ FreeChannelOpenHarness(&harness);
3864+ }
3865+
3866+
37283867/* Drive the whole receive path with a CHANNEL_OPEN sent from a pre-auth
37293868 * connectState: no channel created, and the only thing sent back is the
37303869 * disconnect RFC 4252 section 6 asks for. The connectState gate does the
@@ -18635,6 +18774,14 @@ int main(int argc, char** argv)
1863518774 TestChannelAllowedAfterAuth(ssh);
1863618775 TestClientOnlyKexMsgsBlocked(ssh);
1863718776 TestClientServiceAcceptBlockedDuringKeying(ssh);
18777+ TestClientUserauthReplyBeforeRequest(ssh);
18778+ TestClientUnsolicitedUserauthReplyRejected(MSGID_USERAUTH_SUCCESS);
18779+ TestClientUnsolicitedUserauthReplyRejected(MSGID_USERAUTH_FAILURE);
18780+ #ifdef WOLFSSH_KEYBOARD_INTERACTIVE
18781+ /* Id 60 is only a known id as INFO_REQUEST. */
18782+ TestClientUnsolicitedUserauthReplyRejected(MSGID_USERAUTH_PK_OK);
18783+ #endif
18784+ TestClientBannerBehindServiceAccept();
1863818785 TestChannelOpenRejectedBeforeKex(CONNECT_CLIENT_KEXINIT_SENT);
1863918786 TestChannelOpenRejectedBeforeKex(CONNECT_CLIENT_KEXDH_INIT_SENT);
1864018787 TestSessionOnClientSendsOpenFail();
0 commit comments