Skip to content

Commit d9b0aa3

Browse files
committed
Fix CONNACK Flags Receive-Side Validation
1 parent dec01d1 commit d9b0aa3

2 files changed

Lines changed: 103 additions & 0 deletions

File tree

src/mqtt_packet.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1355,6 +1355,21 @@ int MqttDecode_ConnectAck(byte *rx_buf, int rx_buf_len,
13551355
connect_ack->flags = *rx_payload++;
13561356
connect_ack->return_code = *rx_payload++;
13571357

1358+
/* [MQTT-3.2.2-1] Bits 7-1 of the Connect Acknowledge Flags byte are
1359+
* reserved and MUST be 0. Any other value is a protocol violation;
1360+
* [MQTT-4.8.0-1] requires the receiver to close the connection. */
1361+
if ((connect_ack->flags &
1362+
(byte)~MQTT_CONNECT_ACK_FLAG_SESSION_PRESENT) != 0) {
1363+
return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA);
1364+
}
1365+
/* [MQTT-3.2.2-4] If the CONNACK return code is non-zero (CONNECT
1366+
* refused), Session Present MUST be 0. A refused CONNACK that
1367+
* claims an existing session is malformed. */
1368+
if (connect_ack->return_code != MQTT_CONNECT_ACK_CODE_ACCEPTED &&
1369+
(connect_ack->flags & MQTT_CONNECT_ACK_FLAG_SESSION_PRESENT)) {
1370+
return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA);
1371+
}
1372+
13581373
#ifdef WOLFMQTT_V5
13591374
connect_ack->props = NULL;
13601375
if (connect_ack->protocol_level >= MQTT_CONNECT_PROTOCOL_LEVEL_5) {

tests/test_mqtt_packet.c

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,86 @@ TEST(decode_connack_malformed_remain_len_one)
10041004
ASSERT_EQ(MQTT_CODE_ERROR_MALFORMED_DATA, rc);
10051005
}
10061006

1007+
/* [MQTT-3.2.2-1] / [MQTT-3.2.2-4] CONNACK Flags receive-side validation.
1008+
*
1009+
* The Connect Acknowledge Flags byte has only bit 0 (Session Present)
1010+
* defined; bits 7-1 are reserved and MUST be 0. Additionally, a non-zero
1011+
* return code (refusal) MUST come back with Session Present = 0. The
1012+
* decoder must reject violations so the client closes the connection
1013+
* per [MQTT-4.8.0-1].
1014+
*
1015+
* Helper: build a 4-byte v3.1.1 CONNACK with the given flags+return_code
1016+
* and ask MqttDecode_ConnectAck what it returns. */
1017+
static int decode_connack_flags(byte flags, byte return_code)
1018+
{
1019+
byte buf[4];
1020+
MqttConnectAck ack;
1021+
buf[0] = MQTT_PACKET_TYPE_SET(MQTT_PACKET_TYPE_CONNECT_ACK);
1022+
buf[1] = 2;
1023+
buf[2] = flags;
1024+
buf[3] = return_code;
1025+
XMEMSET(&ack, 0, sizeof(ack));
1026+
return MqttDecode_ConnectAck(buf, (int)sizeof(buf), &ack);
1027+
}
1028+
1029+
TEST(decode_connack_flags_session_present_accepted)
1030+
{
1031+
/* SP=1 with return_code=0 is the canonical resumed-session case. */
1032+
int rc = decode_connack_flags(0x01, MQTT_CONNECT_ACK_CODE_ACCEPTED);
1033+
ASSERT_TRUE(rc > 0);
1034+
}
1035+
1036+
TEST(decode_connack_flags_no_session_accepted)
1037+
{
1038+
int rc = decode_connack_flags(0x00, MQTT_CONNECT_ACK_CODE_ACCEPTED);
1039+
ASSERT_TRUE(rc > 0);
1040+
}
1041+
1042+
TEST(decode_connack_flags_reserved_bit_1_rejected)
1043+
{
1044+
/* 0x02: bit 1 set (reserved). */
1045+
ASSERT_EQ(MQTT_CODE_ERROR_MALFORMED_DATA,
1046+
decode_connack_flags(0x02, MQTT_CONNECT_ACK_CODE_ACCEPTED));
1047+
}
1048+
1049+
TEST(decode_connack_flags_reserved_bit_7_rejected)
1050+
{
1051+
/* 0x80: bit 7 set (reserved). */
1052+
ASSERT_EQ(MQTT_CODE_ERROR_MALFORMED_DATA,
1053+
decode_connack_flags(0x80, MQTT_CONNECT_ACK_CODE_ACCEPTED));
1054+
}
1055+
1056+
TEST(decode_connack_flags_all_reserved_rejected)
1057+
{
1058+
/* 0xFE: bits 7-1 all set, SP=0. */
1059+
ASSERT_EQ(MQTT_CODE_ERROR_MALFORMED_DATA,
1060+
decode_connack_flags(0xFE, MQTT_CONNECT_ACK_CODE_ACCEPTED));
1061+
}
1062+
1063+
TEST(decode_connack_flags_all_bits_rejected)
1064+
{
1065+
/* 0xFF: bits 7-1 set + SP=1. Reserved-bit check fires first. */
1066+
ASSERT_EQ(MQTT_CODE_ERROR_MALFORMED_DATA,
1067+
decode_connack_flags(0xFF, MQTT_CONNECT_ACK_CODE_ACCEPTED));
1068+
}
1069+
1070+
TEST(decode_connack_refused_with_session_present_rejected)
1071+
{
1072+
/* [MQTT-3.2.2-4]: refused CONNACK MUST have SP=0. flags=0x01 with a
1073+
* non-zero return code is malformed. */
1074+
ASSERT_EQ(MQTT_CODE_ERROR_MALFORMED_DATA,
1075+
decode_connack_flags(0x01, MQTT_CONNECT_ACK_CODE_REFUSED_PROTO));
1076+
ASSERT_EQ(MQTT_CODE_ERROR_MALFORMED_DATA,
1077+
decode_connack_flags(0x01, MQTT_CONNECT_ACK_CODE_REFUSED_ID));
1078+
}
1079+
1080+
TEST(decode_connack_refused_without_session_present_accepted)
1081+
{
1082+
/* Refusal with SP=0 is the legal shape. */
1083+
int rc = decode_connack_flags(0x00, MQTT_CONNECT_ACK_CODE_REFUSED_PROTO);
1084+
ASSERT_TRUE(rc > 0);
1085+
}
1086+
10071087
/* ============================================================================
10081088
* MqttEncode_Subscribe
10091089
* ============================================================================ */
@@ -2718,6 +2798,14 @@ void run_mqtt_packet_tests(void)
27182798
RUN_TEST(decode_connack_valid);
27192799
RUN_TEST(decode_connack_malformed_remain_len_zero);
27202800
RUN_TEST(decode_connack_malformed_remain_len_one);
2801+
RUN_TEST(decode_connack_flags_session_present_accepted);
2802+
RUN_TEST(decode_connack_flags_no_session_accepted);
2803+
RUN_TEST(decode_connack_flags_reserved_bit_1_rejected);
2804+
RUN_TEST(decode_connack_flags_reserved_bit_7_rejected);
2805+
RUN_TEST(decode_connack_flags_all_reserved_rejected);
2806+
RUN_TEST(decode_connack_flags_all_bits_rejected);
2807+
RUN_TEST(decode_connack_refused_with_session_present_rejected);
2808+
RUN_TEST(decode_connack_refused_without_session_present_accepted);
27212809

27222810
/* MqttEncode_Subscribe */
27232811
RUN_TEST(encode_subscribe_packet_id_zero);

0 commit comments

Comments
 (0)