Skip to content

Commit

Permalink
dhcpv4/dhcpv6: improve packet validation
Browse files Browse the repository at this point in the history
  • Loading branch information
themiron committed Apr 13, 2020
1 parent 4b5561e commit ed7b287
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 19 deletions.
24 changes: 17 additions & 7 deletions accel-pppd/ctrl/ipoe/dhcpv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,12 @@ static int dhcpv4_parse_packet(struct dhcpv4_packet *pack, int len)
break;
}

if (ptr + 2 > endptr ||
ptr + 2 + ptr[1] > endptr) {
log_warn("dhcpv4: invalid packet received\n");
return -1;
}

opt = mempool_alloc(opt_pool);
if (!opt) {
log_emerg("out of memory\n");
Expand All @@ -336,9 +342,6 @@ static int dhcpv4_parse_packet(struct dhcpv4_packet *pack, int len)
opt->data = ptr;
ptr += opt->len;

if (ptr > endptr)
return -1;

list_add_tail(&opt->entry, &pack->options);

if (opt->type == 53)
Expand Down Expand Up @@ -429,12 +432,15 @@ int dhcpv4_parse_opt82(struct dhcpv4_option *opt, uint8_t **agent_circuit_id, ui
int type, len;

while (ptr < endptr) {
if (ptr + 2 > endptr ||
ptr + 2 + ptr[1] > endptr) {
log_warn("dhcpv4: invalid packet received\n");
return -1;
}

type = *ptr++;
len = *ptr++;

if (ptr + len > endptr)
return -1;

if (type == 1)
*agent_circuit_id = ptr - 1;
else if (type == 2)
Expand Down Expand Up @@ -663,8 +669,12 @@ static int dhcpv4_send_udp(struct dhcpv4_serv *serv, struct dhcpv4_packet *pack,

int dhcpv4_packet_add_opt(struct dhcpv4_packet *pack, int type, const void *data, int len)
{
struct dhcpv4_option *opt = mempool_alloc(opt_pool);
struct dhcpv4_option *opt;

if (pack->data + BUF_SIZE - pack->ptr < 2 + len)
return -1;

opt = mempool_alloc(opt_pool);
if (!opt) {
log_emerg("out of memory\n");
return -1;
Expand Down
2 changes: 1 addition & 1 deletion accel-pppd/ipv6/dhcpv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ static struct {
uint64_t u64;
} __packed serverid;

static int conf_verbose;
int conf_verbose;
static int conf_pref_lifetime = 604800;
static int conf_valid_lifetime = 2592000;
static struct dhcpv6_opt_serverid *conf_serverid = &serverid.hdr;
Expand Down
2 changes: 2 additions & 0 deletions accel-pppd/ipv6/dhcpv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ struct dhcpv6_packet {
void *endptr;
};

extern int conf_verbose;

struct dhcpv6_packet *dhcpv6_packet_parse(const void *buf, size_t size);
void dhcpv6_packet_free(struct dhcpv6_packet *pkt);
void dhcpv6_packet_print(struct dhcpv6_packet *pkt, void (*print)(const char *fmt, ...));
Expand Down
60 changes: 49 additions & 11 deletions accel-pppd/ipv6/dhcpv6_packet.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ static void *parse_option(void *ptr, void *endptr, struct list_head *opt_list)
struct dhcpv6_opt_hdr *opth = ptr;
struct dhcpv6_option *opt;

if (ptr + sizeof(*opth) + ntohs(opth->len) > endptr) {
if (ptr + sizeof(*opth) > endptr ||
ptr + sizeof(*opth) + ntohs(opth->len) > endptr) {
log_warn("dhcpv6: invalid packet received\n");
return NULL;
}
Expand Down Expand Up @@ -108,6 +109,12 @@ struct dhcpv6_packet *dhcpv6_packet_parse(const void *buf, size_t size)
struct dhcpv6_relay_hdr *rhdr;
void *ptr, *endptr;

if (size < sizeof(struct dhcpv6_msg_hdr)) {
if (conf_verbose)
log_warn("dhcpv6: short packet received\n");
return NULL;
}

pkt = _malloc(sizeof(*pkt) + size);
if (!pkt) {
log_emerg("out of memory\n");
Expand All @@ -121,11 +128,21 @@ struct dhcpv6_packet *dhcpv6_packet_parse(const void *buf, size_t size)
pkt->hdr = (void *)(pkt + 1);

memcpy(pkt->hdr, buf, size);
endptr = ((void *)pkt->hdr) + size;
endptr = ((void *)pkt->hdr) + size;

while (pkt->hdr->type == D6_RELAY_FORW) {
rhdr = (struct dhcpv6_relay_hdr *)pkt->hdr;
if (((void *)rhdr) + sizeof(*rhdr) > endptr) {
log_warn("dhcpv6: invalid packet received\n");
goto error;
}

rel = _malloc(sizeof(*rel));
if (!rel) {
log_emerg("out of memory\n");
goto error;
}

rel->hop_cnt = rhdr->hop_cnt;
memcpy(&rel->link_addr, &rhdr->link_addr, sizeof(rel->link_addr));
memcpy(&rel->peer_addr, &rhdr->peer_addr, sizeof(rel->peer_addr));
Expand All @@ -135,11 +152,10 @@ struct dhcpv6_packet *dhcpv6_packet_parse(const void *buf, size_t size)
ptr = rhdr->data;
while (ptr < endptr) {
opth = ptr;

if (ptr + sizeof(*opth) + ntohs(opth->len) > endptr) {
if (ptr + sizeof(*opth) > endptr ||
ptr + sizeof(*opth) + ntohs(opth->len) > endptr) {
log_warn("dhcpv6: invalid packet received\n");
dhcpv6_packet_free(pkt);
return NULL;
goto error;
}

if (opth->code == htons(D6_OPTION_RELAY_MSG)) {
Expand All @@ -152,29 +168,40 @@ struct dhcpv6_packet *dhcpv6_packet_parse(const void *buf, size_t size)
}

ptr = pkt->hdr->data;

while (ptr < endptr) {
opth = ptr;
if (ptr + sizeof(*opth) > endptr ||
ptr + sizeof(*opth) + ntohs(opth->len) > endptr) {
log_warn("dhcpv6: invalid packet received\n");
goto error;
}

if (opth->code == htons(D6_OPTION_CLIENTID))
pkt->clientid = ptr;
else if (opth->code == htons(D6_OPTION_SERVERID))
pkt->serverid = ptr;
else if (opth->code == htons(D6_OPTION_RAPID_COMMIT))
pkt->rapid_commit = 1;

ptr = parse_option(ptr, endptr, &pkt->opt_list);
if (!ptr) {
dhcpv6_packet_free(pkt);
return NULL;
}
if (!ptr)
goto error;
}

return pkt;

error:
dhcpv6_packet_free(pkt);
return NULL;
}

struct dhcpv6_option *dhcpv6_option_alloc(struct dhcpv6_packet *pkt, int code, int len)
{
struct dhcpv6_option *opt;

if ((void *)pkt->hdr->data + BUF_SIZE - pkt->endptr < sizeof(struct dhcpv6_opt_hdr) + len)
return NULL;

opt = _malloc(sizeof(*opt));
if (!opt) {
log_emerg("out of memory\n");
Expand All @@ -199,6 +226,9 @@ struct dhcpv6_option *dhcpv6_nested_option_alloc(struct dhcpv6_packet *pkt, stru
{
struct dhcpv6_option *opt;

if ((void *)pkt->hdr->data + BUF_SIZE - pkt->endptr < sizeof(struct dhcpv6_opt_hdr) + len)
return NULL;

opt = _malloc(sizeof(*opt));
if (!opt) {
log_emerg("out of memory\n");
Expand Down Expand Up @@ -281,12 +311,20 @@ struct dhcpv6_packet *dhcpv6_packet_alloc_reply(struct dhcpv6_packet *req, int t
pkt->hdr->trans_id = req->hdr->trans_id;

opt = dhcpv6_option_alloc(pkt, D6_OPTION_SERVERID, ntohs(req->serverid->hdr.len));
if (!opt)
goto error;
memcpy(opt->hdr, req->serverid, sizeof(struct dhcpv6_opt_hdr) + ntohs(req->serverid->hdr.len));

opt = dhcpv6_option_alloc(pkt, D6_OPTION_CLIENTID, ntohs(req->clientid->hdr.len));
if (!opt)
goto error;
memcpy(opt->hdr, req->clientid, sizeof(struct dhcpv6_opt_hdr) + ntohs(req->clientid->hdr.len));

return pkt;

error:
dhcpv6_packet_free(pkt);
return NULL;
}

static void free_options(struct list_head *opt_list)
Expand Down

0 comments on commit ed7b287

Please sign in to comment.