@@ -2808,6 +2808,145 @@ static void TestClientServiceAcceptBlockedDuringKeying(WOLFSSH* ssh)
28082808}
28092809
28102810
2811+ /* Until the first userauth request goes out, no userauth message may
2812+ * arrive, the banner included. */
2813+ static void TestClientUserauthReplyBeforeRequest(WOLFSSH* ssh)
2814+ {
2815+ static const byte msgs[] = {
2816+ MSGID_USERAUTH_FAILURE,
2817+ MSGID_USERAUTH_SUCCESS,
2818+ MSGID_USERAUTH_BANNER,
2819+ MSGID_USERAUTH_PK_OK, /* also INFO_REQUEST and PW_CHRQ */
2820+ };
2821+ static const byte states[] = {
2822+ CONNECT_KEYED,
2823+ CONNECT_CLIENT_USERAUTH_REQUEST_SENT,
2824+ CONNECT_SERVER_USERAUTH_REQUEST_DONE,
2825+ };
2826+ word32 i, j;
2827+ int allowed;
2828+
2829+ for (i = 0; i < sizeof(states)/sizeof(*states); i++) {
2830+ for (j = 0; j < sizeof(msgs)/sizeof(*msgs); j++) {
2831+ ResetSession(ssh);
2832+ ssh->connectState = states[i];
2833+ allowed = wolfSSH_TestIsMessageAllowed(ssh, msgs[j],
2834+ WS_MSG_RECV);
2835+ AssertFalse(allowed);
2836+ AssertIntEQ(ssh->error, WS_MSGID_NOT_ALLOWED_E);
2837+ }
2838+ }
2839+
2840+ /* The service accept still answers the service request. */
2841+ ResetSession(ssh);
2842+ ssh->connectState = CONNECT_CLIENT_USERAUTH_REQUEST_SENT;
2843+ allowed = wolfSSH_TestIsMessageAllowed(ssh, MSGID_SERVICE_ACCEPT,
2844+ WS_MSG_RECV);
2845+ AssertTrue(allowed);
2846+
2847+ /* Once the first userauth request is out, all of them are allowed. */
2848+ for (j = 0; j < sizeof(msgs)/sizeof(*msgs); j++) {
2849+ ResetSession(ssh);
2850+ ssh->connectState = CONNECT_CLIENT_USERAUTH_SENT;
2851+ allowed = wolfSSH_TestIsMessageAllowed(ssh, msgs[j],
2852+ WS_MSG_RECV);
2853+ AssertTrue(allowed);
2854+ }
2855+ }
2856+
2857+
2858+ /* A userauth reply to the service request draws a disconnect and leaves
2859+ * serverState alone. */
2860+ static void TestClientUnsolicitedUserauthReplyRejected(byte msgId)
2861+ {
2862+ WOLFSSH_CTX* ctx;
2863+ WOLFSSH* ssh;
2864+ MemIo io;
2865+ byte pkt[32];
2866+ byte out[256];
2867+ word32 pktSz;
2868+
2869+ pktSz = BuildPacket(msgId, pkt, sizeof(pkt));
2870+
2871+ ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
2872+ AssertNotNull(ctx);
2873+ wolfSSH_SetIORecv(ctx, MemRecv);
2874+ wolfSSH_SetIOSend(ctx, MemSend);
2875+
2876+ ssh = wolfSSH_new(ctx);
2877+ AssertNotNull(ssh);
2878+
2879+ MemIoInit(&io, pkt, pktSz, out, sizeof(out));
2880+ wolfSSH_SetIOReadCtx(ssh, &io);
2881+ wolfSSH_SetIOWriteCtx(ssh, &io);
2882+
2883+ ssh->connectState = CONNECT_CLIENT_USERAUTH_REQUEST_SENT;
2884+ ssh->serverState = SERVER_KEXINIT_DONE;
2885+
2886+ AssertIntEQ(wolfSSH_TestDoReceive(ssh), WS_FATAL_ERROR);
2887+ AssertIntEQ(ssh->error, WS_MSGID_NOT_ALLOWED_E);
2888+ AssertIntEQ(ssh->serverState, SERVER_KEXINIT_DONE);
2889+ AssertTrue(io.outSz > 0);
2890+ AssertIntEQ(ParseMsgId(io.out, io.outSz), MSGID_DISCONNECT);
2891+ AssertTrue(ssh->disconnected);
2892+
2893+ wolfSSH_free(ssh);
2894+ wolfSSH_CTX_free(ctx);
2895+ }
2896+
2897+
2898+ /* Unused by a "none" request, which still needs a callback. */
2899+ static int NoneUserAuthCb(byte authType, WS_UserAuthData* authData,
2900+ void* ctx)
2901+ {
2902+ (void)authType;
2903+ (void)authData;
2904+ (void)ctx;
2905+ return WOLFSSH_USERAUTH_INVALID_AUTHTYPE;
2906+ }
2907+
2908+ /* A banner bundled behind the service accept, as wolfSSH's server sends
2909+ * it, is read after wolfSSH_connect() sends the first request. */
2910+ static void TestClientBannerBehindServiceAccept(void)
2911+ {
2912+ ChannelOpenHarness harness;
2913+ byte in[128];
2914+ byte payload[64];
2915+ word32 inSz;
2916+ word32 payloadSz;
2917+
2918+ payloadSz = AppendString(payload, sizeof(payload), 0, "ssh-userauth");
2919+ inSz = WrapPacket(MSGID_SERVICE_ACCEPT, payload, payloadSz,
2920+ in, sizeof(in));
2921+ payloadSz = AppendString(payload, sizeof(payload), 0, "banner");
2922+ payloadSz = AppendString(payload, sizeof(payload), payloadSz, "");
2923+ inSz += WrapPacket(MSGID_USERAUTH_BANNER, payload, payloadSz,
2924+ in + inSz, sizeof(in) - inSz);
2925+
2926+ InitChannelOpenHarnessClient(&harness, in, inSz);
2927+ wolfSSH_SetUserAuth(harness.ctx, NoneUserAuthCb);
2928+ AssertIntEQ(wolfSSH_SetUsername(harness.ssh, "user"), WS_SUCCESS);
2929+ harness.ssh->connectState = CONNECT_CLIENT_USERAUTH_REQUEST_SENT;
2930+ harness.ssh->serverState = SERVER_KEXINIT_DONE;
2931+
2932+ /* Both packets read, then WANT_READ for the auth answer. */
2933+ AssertIntEQ(wolfSSH_connect(harness.ssh), WS_FATAL_ERROR);
2934+ AssertIntEQ(wolfSSH_get_error(harness.ssh), WS_WANT_READ);
2935+ AssertIntEQ(harness.io.inOff, inSz);
2936+ AssertIntEQ(harness.ssh->connectState, CONNECT_CLIENT_USERAUTH_SENT);
2937+ AssertIntEQ(harness.ssh->serverState, SERVER_USERAUTH_REQUEST_DONE);
2938+ AssertFalse(harness.ssh->disconnected);
2939+ /* Only the userauth request went out. */
2940+ AssertTrue(harness.io.outSz > 0);
2941+ AssertIntEQ(ParseMsgId(harness.io.out, harness.io.outSz),
2942+ MSGID_USERAUTH_REQUEST);
2943+ AssertIntEQ(NextPacketOffset(harness.io.out, harness.io.outSz),
2944+ harness.io.outSz);
2945+
2946+ FreeChannelOpenHarness(&harness);
2947+ }
2948+
2949+
28112950/* Drive the whole receive path with a CHANNEL_OPEN sent from a pre-auth
28122951 * connectState: no channel created, and the only thing sent back is the
28132952 * disconnect RFC 4252 section 6 asks for. The connectState gate does the
@@ -17083,6 +17222,14 @@ int main(int argc, char** argv)
1708317222 TestChannelAllowedAfterAuth(ssh);
1708417223 TestClientOnlyKexMsgsBlocked(ssh);
1708517224 TestClientServiceAcceptBlockedDuringKeying(ssh);
17225+ TestClientUserauthReplyBeforeRequest(ssh);
17226+ TestClientUnsolicitedUserauthReplyRejected(MSGID_USERAUTH_SUCCESS);
17227+ TestClientUnsolicitedUserauthReplyRejected(MSGID_USERAUTH_FAILURE);
17228+ #ifdef WOLFSSH_KEYBOARD_INTERACTIVE
17229+ /* Id 60 is only a known id as INFO_REQUEST. */
17230+ TestClientUnsolicitedUserauthReplyRejected(MSGID_USERAUTH_PK_OK);
17231+ #endif
17232+ TestClientBannerBehindServiceAccept();
1708617233 TestChannelOpenRejectedBeforeKex(CONNECT_CLIENT_KEXINIT_SENT);
1708717234 TestChannelOpenRejectedBeforeKex(CONNECT_CLIENT_KEXDH_INIT_SENT);
1708817235 TestSessionOnClientSendsOpenFail();
0 commit comments