diff options
author | Vladislav Grishenko <themiron@mail.ru> | 2020-04-13 20:36:38 +0500 |
---|---|---|
committer | Vladislav Grishenko <themiron@mail.ru> | 2020-04-13 20:36:38 +0500 |
commit | ed7b28722ec8513838b49699a862a69055c8c596 (patch) | |
tree | a8ced26efa0f3459c1e29412d81aa067f4eff818 | |
parent | 4b5561ef0c6162cf4c70e62524477675c7079ca0 (diff) | |
download | accel-ppp-ed7b28722ec8513838b49699a862a69055c8c596.tar.gz accel-ppp-ed7b28722ec8513838b49699a862a69055c8c596.zip |
dhcpv4/dhcpv6: improve packet validation
-rw-r--r-- | accel-pppd/ctrl/ipoe/dhcpv4.c | 24 | ||||
-rw-r--r-- | accel-pppd/ipv6/dhcpv6.c | 2 | ||||
-rw-r--r-- | accel-pppd/ipv6/dhcpv6.h | 2 | ||||
-rw-r--r-- | accel-pppd/ipv6/dhcpv6_packet.c | 60 |
4 files changed, 69 insertions, 19 deletions
diff --git a/accel-pppd/ctrl/ipoe/dhcpv4.c b/accel-pppd/ctrl/ipoe/dhcpv4.c index a822627..557e58a 100644 --- a/accel-pppd/ctrl/ipoe/dhcpv4.c +++ b/accel-pppd/ctrl/ipoe/dhcpv4.c @@ -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"); @@ -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) @@ -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) @@ -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; diff --git a/accel-pppd/ipv6/dhcpv6.c b/accel-pppd/ipv6/dhcpv6.c index fc9f4ca..a48d7a1 100644 --- a/accel-pppd/ipv6/dhcpv6.c +++ b/accel-pppd/ipv6/dhcpv6.c @@ -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; diff --git a/accel-pppd/ipv6/dhcpv6.h b/accel-pppd/ipv6/dhcpv6.h index 28ea3cf..669155a 100644 --- a/accel-pppd/ipv6/dhcpv6.h +++ b/accel-pppd/ipv6/dhcpv6.h @@ -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, ...)); diff --git a/accel-pppd/ipv6/dhcpv6_packet.c b/accel-pppd/ipv6/dhcpv6_packet.c index aae260e..4173ba7 100644 --- a/accel-pppd/ipv6/dhcpv6_packet.c +++ b/accel-pppd/ipv6/dhcpv6_packet.c @@ -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; } @@ -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"); @@ -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)); @@ -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)) { @@ -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"); @@ -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"); @@ -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) |