Skip to content

Commit b5d8145

Browse files
committed
Client Out Of Order Messaging Checking
1. Add macro for logging an expected message. 2. Add an expected message ID to the HandshakeInfo. 3. Add a message ID for "none (0)". 4. Add a check in IsMessageAllowedClient() for the expected message ID. Clear it if successful. 5. The KEXDH messages sent to the server have expected responses. Set them if sending the message is successful. 6. Add the set of message ID ranges and macros for testing if a message ID is in a specific range. 7. Add flags for having sent the kexinit message and received it. Tweak the checks for isKeying and these flags. 8. IsMessageAllowedClient() to check for appropriate messages at the appropriate time during the connect.
1 parent 967d6c5 commit b5d8145

File tree

3 files changed

+177
-46
lines changed

3 files changed

+177
-46
lines changed

src/internal.c

Lines changed: 124 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -612,8 +612,8 @@ INLINE static int IsMessageAllowedKeying(WOLFSSH *ssh, byte msg)
612612
return 0;
613613
}
614614

615-
/* case of resending SSH_MSG_KEXINIT */
616-
if (msg == MSGID_KEXINIT) {
615+
/* case of peer resending SSH_MSG_KEXINIT */
616+
if ((ssh->isKeying & WOLFSSH_PEER_IS_KEYING) && msg == MSGID_KEXINIT) {
617617
WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by during rekeying", msg);
618618
ssh->error = WS_REKEYING;
619619
return 0;
@@ -632,34 +632,50 @@ INLINE static int IsMessageAllowedKeying(WOLFSSH *ssh, byte msg)
632632
#ifndef NO_WOLFSSH_SERVER
633633
INLINE static int IsMessageAllowedServer(WOLFSSH *ssh, byte msg)
634634
{
635+
/* Only the server should send these messages, never receive. */
636+
if (msg == MSGID_SERVICE_ACCEPT) {
637+
WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by %s %s",
638+
msg, "client", "ever");
639+
return 0;
640+
}
641+
642+
/* Transport Layer Generic messages are always allowed. */
643+
if (MSGIDLIMIT_TRANS_GEN(msg)) {
644+
return 1;
645+
}
646+
635647
/* Has client userauth started? */
648+
/* Allows the server to receive up to KEXDH GEX Request during KEX. */
636649
if (ssh->acceptState < ACCEPT_KEYED) {
637-
if (msg > MSGID_KEXDH_LIMIT) {
650+
if (msg > MSGID_KEXDH_GEX_REQUEST) {
638651
return 0;
639652
}
640653
}
641654
/* Is server userauth complete? */
642655
if (ssh->acceptState < ACCEPT_SERVER_USERAUTH_SENT) {
656+
/* The server should only receive the user auth request message,
657+
* it should not accept the other user auth messages, it sends
658+
* them. (>50) */
643659
/* Explicitly check for messages not allowed before user
644660
* authentication has comleted. */
645-
if (msg >= MSGID_USERAUTH_LIMIT) {
646-
WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by server "
647-
"before user authentication is complete", msg);
661+
if (MSGIDLIMIT_POST_USERAUTH(msg)) {
662+
WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by %s %s",
663+
msg, "server", "before user authentication is complete");
648664
return 0;
649665
}
650666
/* Explicitly check for the user authentication messages that
651667
* only the server sends, it shouldn't receive them. */
652-
if ((msg > MSGID_USERAUTH_RESTRICT) &&
668+
if ((msg > MSGID_USERAUTH_REQUEST) &&
653669
(msg != MSGID_USERAUTH_INFO_RESPONSE)) {
654-
WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by server "
655-
"during user authentication", msg);
670+
WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by %s %s",
671+
msg, "server", "during user authentication");
656672
return 0;
657673
}
658674
}
659675
else {
660-
if (msg >= MSGID_USERAUTH_RESTRICT && msg < MSGID_USERAUTH_LIMIT) {
661-
WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by server "
662-
"after user authentication", msg);
676+
if (msg >= MSGID_USERAUTH_REQUEST && msg < MSGID_GLOBAL_REQUEST) {
677+
WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by %s %s",
678+
msg, "server", "after user authentication");
663679
return 0;
664680
}
665681
}
@@ -672,37 +688,95 @@ INLINE static int IsMessageAllowedServer(WOLFSSH *ssh, byte msg)
672688
#ifndef NO_WOLFSSH_CLIENT
673689
INLINE static int IsMessageAllowedClient(WOLFSSH *ssh, byte msg)
674690
{
675-
/* Has client userauth started? */
676-
if (ssh->connectState < CONNECT_CLIENT_KEXDH_INIT_SENT) {
677-
if (msg >= MSGID_KEXDH_LIMIT) {
691+
/* Only the client should send these messages, never receive. */
692+
if (msg == MSGID_SERVICE_REQUEST || msg == MSGID_USERAUTH_REQUEST) {
693+
WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by %s %s",
694+
msg, "client", "ever");
695+
return 0;
696+
}
697+
698+
if (msg == MSGID_SERVICE_ACCEPT) {
699+
if (ssh->connectState == CONNECT_CLIENT_USERAUTH_REQUEST_SENT) {
700+
return 1;
701+
}
702+
else {
703+
WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by %s %s",
704+
msg, "client", "after starting user auth");
678705
return 0;
679706
}
680707
}
681-
/* Is client userauth complete? */
682-
if (ssh->connectState < CONNECT_SERVER_USERAUTH_ACCEPT_DONE) {
683-
/* Explicitly check for messages not allowed before user
684-
* authentication has comleted. */
685-
if (msg >= MSGID_USERAUTH_LIMIT) {
686-
WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by client "
687-
"before user authentication is complete", msg);
708+
709+
/* Transport Layer Generic messages are always allowed. */
710+
if (MSGIDLIMIT_TRANS_GEN(msg)) {
711+
return 1;
712+
}
713+
714+
/* Is KEX complete? */
715+
if (MSGIDLIMIT_TRANS(msg)) {
716+
if (ssh->isKeying & WOLFSSH_PEER_IS_KEYING) {
717+
/* MSGID_KEXINIT not allowed when keying. */
718+
if (msg == MSGID_KEXINIT) {
719+
WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by %s %s",
720+
msg, "client", "when keying");
721+
return 0;
722+
}
723+
724+
/* Error if expecting a specific message and didn't receive. */
725+
if (ssh->handshake && ssh->handshake->expectMsgId != MSGID_NONE) {
726+
if (msg != ssh->handshake->expectMsgId) {
727+
WLOG(WS_LOG_DEBUG,
728+
"Message ID %u not the expected message %u",
729+
msg, ssh->handshake->expectMsgId);
730+
return 0;
731+
}
732+
else {
733+
/* Got the expected message, clear expectation. */
734+
ssh->handshake->expectMsgId = MSGID_NONE;
735+
return 1;
736+
}
737+
}
738+
}
739+
else {
740+
/* MSGID_KEXINIT only allowed when not keying. */
741+
if (msg == MSGID_KEXINIT) {
742+
return 1;
743+
}
744+
745+
/* All other transport KEX and ALGO messages are not allowed
746+
* when not keying. */
747+
WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by %s %s",
748+
msg, "client", "when not keying");
688749
return 0;
689750
}
690-
/* Explicitly check for the user authentication message that
691-
* only the client sends, it shouldn't receive it. */
692-
if (msg == MSGID_USERAUTH_RESTRICT) {
693-
WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by client "
694-
"during user authentication", msg);
751+
}
752+
753+
/* Is client userauth complete? */
754+
if (ssh->connectState >= CONNECT_KEYED
755+
&& ssh->connectState < CONNECT_SERVER_USERAUTH_ACCEPT_DONE) {
756+
/* The endpoints should not allow message IDs greater than or
757+
* equal to msgid 80 before user authentication is complete.
758+
* Per RFC 4252 section 6. */
759+
if (MSGIDLIMIT_POST_USERAUTH(msg)) {
760+
WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by %s %s",
761+
msg, "client", "before user authentication is complete");
695762
return 0;
696763
}
764+
else if (MSGIDLIMIT_AUTH(msg)) {
765+
return 1;
766+
}
697767
}
698768
else {
699-
if (msg >= MSGID_USERAUTH_RESTRICT && msg < MSGID_USERAUTH_LIMIT) {
700-
WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by client "
701-
"after user authentication", msg);
769+
if (MSGIDLIMIT_POST_USERAUTH(msg)) {
770+
return 1;
771+
}
772+
else if (MSGIDLIMIT_AUTH(msg)) {
773+
WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by %s %s",
774+
msg, "client", "after user authentication");
702775
return 0;
703776
}
704777
}
705-
return 1;
778+
779+
return 0;
706780
}
707781
#endif /* NO_WOLFSSH_CLIENT */
708782

@@ -711,7 +785,7 @@ INLINE static int IsMessageAllowedClient(WOLFSSH *ssh, byte msg)
711785
* Returns 1 if allowed 0 if not allowed. */
712786
INLINE static int IsMessageAllowed(WOLFSSH *ssh, byte msg, byte state)
713787
{
714-
if (state == WS_MSG_SEND && !IsMessageAllowedKeying(ssh, msg)) {
788+
if (!IsMessageAllowedKeying(ssh, msg)) {
715789
return 0;
716790
}
717791

@@ -725,6 +799,7 @@ INLINE static int IsMessageAllowed(WOLFSSH *ssh, byte msg, byte state)
725799
return IsMessageAllowedClient(ssh, msg);
726800
}
727801
#endif /* NO_WOLFSSH_CLIENT */
802+
(void)state;
728803
return 0;
729804
}
730805

@@ -5872,8 +5947,10 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
58725947
ret = GenerateKeys(ssh, hashId, !ssh->handshake->useEccMlKem);
58735948
}
58745949

5875-
if (ret == WS_SUCCESS)
5950+
if (ret == WS_SUCCESS) {
58765951
ret = SendNewKeys(ssh);
5952+
ssh->handshake->expectMsgId = MSGID_NEWKEYS;
5953+
}
58775954

58785955
if (sigKeyBlock_ptr)
58795956
WFREE(sigKeyBlock_ptr, ssh->ctx->heap, DYNTYPE_PRIVKEY);
@@ -10648,8 +10725,9 @@ int SendKexInit(WOLFSSH* ssh)
1064810725
ret = BundlePacket(ssh);
1064910726
}
1065010727

10651-
if (ret == WS_SUCCESS)
10728+
if (ret == WS_SUCCESS) {
1065210729
ret = wolfSSH_SendPacket(ssh);
10730+
}
1065310731

1065410732
if (ret != WS_WANT_WRITE && ret != WS_SUCCESS)
1065510733
PurgePacket(ssh);
@@ -12612,6 +12690,11 @@ int SendKexDhGexRequest(WOLFSSH* ssh)
1261212690
if (ret == WS_SUCCESS)
1261312691
ret = wolfSSH_SendPacket(ssh);
1261412692

12693+
if (ret == WS_SUCCESS) {
12694+
WLOG_EXPECT_MSGID(MSGID_KEXDH_GEX_GROUP);
12695+
ssh->handshake->expectMsgId = MSGID_KEXDH_GEX_GROUP;
12696+
}
12697+
1261512698
WLOG(WS_LOG_DEBUG, "Leaving SendKexDhGexRequest(), ret = %d", ret);
1261612699
return ret;
1261712700
}
@@ -12700,6 +12783,7 @@ int SendKexDhInit(WOLFSSH* ssh)
1270012783
#endif
1270112784
int ret = WS_SUCCESS;
1270212785
byte msgId = MSGID_KEXDH_INIT;
12786+
byte expectMsgId = MSGID_KEXDH_REPLY;
1270312787
byte e[MAX_KEX_KEY_SZ+1]; /* plus 1 in case of padding. */
1270412788
word32 eSz = (word32)sizeof(e);
1270512789
byte ePad = 0;
@@ -12751,6 +12835,7 @@ int SendKexDhInit(WOLFSSH* ssh)
1275112835
generator = ssh->handshake->generator;
1275212836
generatorSz = ssh->handshake->generatorSz;
1275312837
msgId = MSGID_KEXDH_GEX_INIT;
12838+
expectMsgId = MSGID_KEXDH_GEX_REPLY;
1275412839
break;
1275512840
#endif
1275612841
#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256
@@ -12962,6 +13047,11 @@ int SendKexDhInit(WOLFSSH* ssh)
1296213047
if (ret == WS_SUCCESS)
1296313048
ret = wolfSSH_SendPacket(ssh);
1296413049

13050+
if (ret == WS_SUCCESS) {
13051+
WLOG_EXPECT_MSGID(expectMsgId);
13052+
ssh->handshake->expectMsgId = expectMsgId;
13053+
}
13054+
1296513055
WLOG(WS_LOG_DEBUG, "Leaving SendKexDhInit(), ret = %d", ret);
1296613056
return ret;
1296713057
}

wolfssh/internal.h

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,7 @@ typedef struct Keys {
607607

608608

609609
typedef struct HandshakeInfo {
610+
byte expectMsgId;
610611
byte kexId;
611612
byte kexIdGuess;
612613
byte kexHashId;
@@ -1183,6 +1184,8 @@ enum ProcessReplyStates {
11831184

11841185

11851186
enum WS_MessageIds {
1187+
MSGID_NONE = 0,
1188+
11861189
MSGID_DISCONNECT = 1,
11871190
MSGID_IGNORE = 2,
11881191
MSGID_UNIMPLEMENTED = 3,
@@ -1238,19 +1241,56 @@ enum WS_MessageIds {
12381241
};
12391242

12401243

1241-
/* Allows the server to receive up to KEXDH GEX Request during KEX. */
1242-
#define MSGID_KEXDH_LIMIT MSGID_KEXDH_GEX_REQUEST
1243-
1244-
/* The endpoints should not allow message IDs greater than or
1245-
* equal to msgid 80 before user authentication is complete.
1246-
* Per RFC 4252 section 6. */
1247-
#define MSGID_USERAUTH_LIMIT 80
1244+
/* The following message ID ranges are described in RFC 5251, section 7. */
1245+
enum WS_MessageIdLimits {
1246+
/* Transport Layer Protocol: */
1247+
MSGIDLIMIT_TRANS_MIN = 1,
1248+
MSGIDLIMIT_TRANS_GEN_MIN = 1,
1249+
MSGIDLIMIT_TRANS_GEN_MAX = 19,
1250+
MSGIDLIMIT_TRANS_ALGO_MIN = 20,
1251+
MSGIDLIMIT_TRANS_ALGO_MAX = 29,
1252+
MSGIDLIMIT_TRANS_KEX_MIN = 30,
1253+
MSGIDLIMIT_TRANS_KEX_MAX = 49,
1254+
MSGIDLIMIT_TRANS_MAX = 49,
1255+
/* User Authentication Protocol: */
1256+
MSGIDLIMIT_AUTH_MIN = 50,
1257+
MSGIDLIMIT_AUTH_GEN_MIN = 50,
1258+
MSGIDLIMIT_AUTH_GEN_MAX = 59,
1259+
MSGIDLIMIT_AUTH_METH_MIN = 60,
1260+
MSGIDLIMIT_AUTH_METH_MAX = 79,
1261+
MSGIDLIMIT_AUTH_MAX = 79,
1262+
/* Connection Protocol: */
1263+
MSGIDLIMIT_CONN_MIN = 80,
1264+
MSGIDLIMIT_CONN_GEN_MIN = 80,
1265+
MSGIDLIMIT_CONN_GEN_MAX = 89,
1266+
MSGIDLIMIT_CONN_CHAN_MIN = 90,
1267+
MSGIDLIMIT_CONN_CHAN_MAX = 127,
1268+
MSGIDLIMIT_CONN_MAX = 127,
1269+
/* Reserved For Client Protocols: */
1270+
MSGIDLIMIT_RESERVED_MIN = 128,
1271+
MSGIDLIMIT_RESERVED_MAX = 191,
1272+
/* Local Extensions: */
1273+
MSGIDLIMIT_EXTENDED_MIN = 192,
1274+
MSGIDLIMIT_EXTENDED_MAX = 255,
1275+
};
12481276

1249-
/* The client should only send the user auth request message
1250-
* (50), it should not accept it. The server should only receive
1251-
* the user auth request message, it should not accept the other
1252-
* user auth messages, it sends them. (>50) */
1253-
#define MSGID_USERAUTH_RESTRICT 50
1277+
/* Message ID bounds checking. */
1278+
#define MSGIDLIMIT_BOUND(x,y,z) ((x) >= (y) && (x) <= (z))
1279+
#define MSGIDLIMIT_COMP(x,name) \
1280+
MSGIDLIMIT_BOUND((x),MSGIDLIMIT_##name##_MIN,MSGIDLIMIT_##name##_MAX)
1281+
#define MSGIDLIMIT_TRANS(x) MSGIDLIMIT_COMP((x),TRANS)
1282+
#define MSGIDLIMIT_TRANS_GEN(x) MSGIDLIMIT_COMP((x),TRANS_GEN)
1283+
#define MSGIDLIMIT_TRANS_ALGO(x) MSGIDLIMIT_COMP((x),TRANS_ALGO)
1284+
#define MSGIDLIMIT_TRANS_KEX(x) MSGIDLIMIT_COMP((x),TRANS_KEX)
1285+
#define MSGIDLIMIT_AUTH(x) MSGIDLIMIT_COMP((x),AUTH)
1286+
#define MSGIDLIMIT_AUTH_GEN(x) MSGIDLIMIT_COMP((x),AUTH_GEN)
1287+
#define MSGIDLIMIT_AUTH_METH(x) MSGIDLIMIT_COMP((x),AUTH_METH)
1288+
#define MSGIDLIMIT_CONN(x) MSGIDLIMIT_COMP((x),CONN)
1289+
#define MSGIDLIMIT_CONN_GEN(x) MSGIDLIMIT_COMP((x),CONN_GEN)
1290+
#define MSGIDLIMIT_CONN_CHAN(x) MSGIDLIMIT_COMP((x),CONN_CHAN)
1291+
#define MSGIDLIMIT_RESERVED(x) MSGIDLIMIT_COMP((x),RESERVED)
1292+
#define MSGIDLIMIT_EXTENDED(x) MSGIDLIMIT_COMP((x),EXTENDED)
1293+
#define MSGIDLIMIT_POST_USERAUTH(x) ((x) >= MSGIDLIMIT_CONN_MIN)
12541294

12551295

12561296
#define CHANNEL_EXTENDED_DATA_STDERR WOLFSSH_EXT_DATA_STDERR

wolfssh/log.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ WOLFSSH_API void wolfSSH_Log(enum wolfSSH_LogLevel,
7676
if (wolfSSH_LogEnabled()) \
7777
wolfSSH_Log(__VA_ARGS__); \
7878
} while (0)
79+
#define WLOG_EXPECT_MSGID(x) WLOG(WS_LOG_DEBUG, "Expecting message %d", (x))
7980

8081

8182
#ifdef __cplusplus

0 commit comments

Comments
 (0)