Skip to content

Commit 56a6b60

Browse files
gasbytesdanielinux
authored andcommitted
validate the dhcp server-id in dhcp_msg_type so a forged dhcpnak with an absent or mismatched server identifier can no longer deconfigure the lease
this pretty much mirrors the guard already in dhcp_parse_ack
1 parent ba78b52 commit 56a6b60

3 files changed

Lines changed: 74 additions & 10 deletions

File tree

src/test/unit/unit.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1289,7 +1289,7 @@ Suite *wolf_suite(void)
12891289
#ifdef IP_MULTICAST
12901290
tcase_add_test(tc_core, test_poll_tx_udp_multicast_arp_skipped_uses_mcast_mac);
12911291
#endif /* IP_MULTICAST */
1292-
/* --- unit_tests_dhcp_edges.c (43 tests) --- */
1292+
/* --- unit_tests_dhcp_edges.c (46 tests) --- */
12931293
tcase_add_test(tc_core, test_dhcp_schedule_lease_timer_zero_lease_noop);
12941294
tcase_add_test(tc_core, test_dhcp_schedule_lease_timer_null_noop);
12951295
tcase_add_test(tc_core, test_dhcp_schedule_lease_timer_renew_gt_lease_clamped);
@@ -1307,6 +1307,9 @@ Suite *wolf_suite(void)
13071307
tcase_add_test(tc_core, test_dhcp_msg_type_len_ne_1_not_returned);
13081308
tcase_add_test(tc_core, test_dhcp_msg_type_pad_bytes_skipped);
13091309
tcase_add_test(tc_core, test_dhcp_msg_type_truncated_option_returns_neg1);
1310+
tcase_add_test(tc_core, test_dhcp_msg_type_nak_wrong_server_id_rejected);
1311+
tcase_add_test(tc_core, test_dhcp_msg_type_nak_absent_server_id_rejected);
1312+
tcase_add_test(tc_core, test_dhcp_msg_type_nak_matching_server_id_accepted);
13101313
tcase_add_test(tc_core, test_dhcp_parse_offer_type_ack_not_offer_rejected);
13111314
tcase_add_test(tc_core, test_dhcp_parse_offer_subnet_mask_len_lt4_rejected);
13121315
tcase_add_test(tc_core, test_dhcp_parse_offer_inner_truncated_opt2_rejected);

src/test/unit/unit_tests_dhcp_edges.c

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,8 +371,62 @@ START_TEST(test_dhcp_msg_type_truncated_option_returns_neg1)
371371
}
372372
END_TEST
373373

374+
START_TEST(test_dhcp_msg_type_nak_wrong_server_id_rejected)
375+
{
376+
struct wolfIP s;
377+
struct dhcp_msg msg;
378+
uint8_t *p;
379+
380+
wolfIP_init(&s);
381+
s.dhcp_xid = 0xDEADBEEFU;
382+
s.dhcp_server_ip = 0x0A000001U; /* committed server */
383+
384+
build_dhcp_msg_base(&s, &msg, DHCP_NAK);
385+
p = (uint8_t *)msg.options + 3; /* after MSG_TYPE TLV */
386+
append_opt4(&p, DHCP_OPTION_SERVER_ID, 0x0A000002U); /* different server */
387+
append_end(&p);
388+
389+
ck_assert_int_eq(dhcp_msg_type(&s, &msg, sizeof(msg)), -1);
390+
}
391+
END_TEST
392+
393+
START_TEST(test_dhcp_msg_type_nak_absent_server_id_rejected)
394+
{
395+
struct wolfIP s;
396+
struct dhcp_msg msg;
397+
398+
wolfIP_init(&s);
399+
s.dhcp_xid = 0xDEADBEEFU;
400+
s.dhcp_server_ip = 0x0A000001U; /* committed server */
401+
402+
build_dhcp_msg_base(&s, &msg, DHCP_NAK); /* no SERVER_ID option */
403+
((uint8_t *)msg.options)[3] = DHCP_OPTION_END;
404+
405+
ck_assert_int_eq(dhcp_msg_type(&s, &msg, sizeof(msg)), -1);
406+
}
407+
END_TEST
408+
409+
START_TEST(test_dhcp_msg_type_nak_matching_server_id_accepted)
410+
{
411+
struct wolfIP s;
412+
struct dhcp_msg msg;
413+
uint8_t *p;
414+
415+
wolfIP_init(&s);
416+
s.dhcp_xid = 0xDEADBEEFU;
417+
s.dhcp_server_ip = 0x0A000001U; /* committed server */
418+
419+
build_dhcp_msg_base(&s, &msg, DHCP_NAK);
420+
p = (uint8_t *)msg.options + 3;
421+
append_opt4(&p, DHCP_OPTION_SERVER_ID, 0x0A000001U); /* committed server */
422+
append_end(&p);
423+
424+
ck_assert_int_eq(dhcp_msg_type(&s, &msg, sizeof(msg)), DHCP_NAK);
425+
}
426+
END_TEST
427+
374428
/* -------------------------------------------------------------------------
375-
* dhcp_parse_offer additional branches
429+
* dhcp_parse_offer - additional branches
376430
* ---------------------------------------------------------------------- */
377431

378432
START_TEST(test_dhcp_parse_offer_type_ack_not_offer_rejected)

src/wolfip.c

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7592,6 +7592,9 @@ static int dhcp_msg_type(struct wolfIP *s, struct dhcp_msg *msg, uint32_t msg_le
75927592
{
75937593
uint8_t *opt = (uint8_t *)msg->options;
75947594
uint8_t *opt_end;
7595+
int msg_type = -1;
7596+
int saw_server_id = 0;
7597+
uint32_t server_id = 0;
75957598
if (msg_len < DHCP_HEADER_LEN)
75967599
return -1;
75977600
if (ee32(msg->magic) != DHCP_MAGIC)
@@ -7618,11 +7621,20 @@ static int dhcp_msg_type(struct wolfIP *s, struct dhcp_msg *msg, uint32_t msg_le
76187621
len = opt[1];
76197622
if (opt + 2 + len > opt_end)
76207623
break;
7621-
if (code == DHCP_OPTION_MSG_TYPE && len == 1)
7622-
return opt[2];
7624+
if (code == DHCP_OPTION_MSG_TYPE && len == 1) {
7625+
msg_type = opt[2];
7626+
} else if (code == DHCP_OPTION_SERVER_ID && len >= 4) {
7627+
server_id = DHCP_OPT_data_to_u32((struct dhcp_option *)opt);
7628+
saw_server_id = 1;
7629+
}
76237630
opt += 2 + len;
76247631
}
7625-
return -1;
7632+
/* Reject a reply that does not carry the server identifier of the
7633+
* server we committed to during the OFFER phase. */
7634+
if (s->dhcp_server_ip != 0 &&
7635+
(!saw_server_id || server_id != s->dhcp_server_ip))
7636+
return -1;
7637+
return msg_type;
76267638
}
76277639

76287640
static int dhcp_parse_ack(struct wolfIP *s, struct dhcp_msg *msg, uint32_t msg_len)
@@ -8282,11 +8294,6 @@ static void arp_recv(struct wolfIP *s, unsigned int if_idx, void *buf, int len)
82828294
if (memcmp(s->arp.neighbors[idx].mac, sender_mac, 6) == 0)
82838295
s->arp.neighbors[idx].ts = s->last_tick;
82848296
}
8285-
/* Do not learn a new neighbor from an unsolicited REQUEST:
8286-
* a same-LAN attacker can spoof sip to match an outstanding pending request and install
8287-
* a forged MAC that then locks out the genuine reply. Only REPLYs
8288-
* populate new cache entries; the pending slot is left intact so the genuine
8289-
* reply still resolves. */
82908297
}
82918298
}
82928299
eth_output_add_header(s, if_idx, arp->tma, &arp->eth, ETH_TYPE_ARP);

0 commit comments

Comments
 (0)