summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVladislav Grishenko <themiron@mail.ru>2020-04-13 20:36:38 +0500
committerVladislav Grishenko <themiron@mail.ru>2020-04-13 20:36:38 +0500
commited7b28722ec8513838b49699a862a69055c8c596 (patch)
treea8ced26efa0f3459c1e29412d81aa067f4eff818
parent4b5561ef0c6162cf4c70e62524477675c7079ca0 (diff)
downloadaccel-ppp-xebd-ed7b28722ec8513838b49699a862a69055c8c596.tar.gz
accel-ppp-xebd-ed7b28722ec8513838b49699a862a69055c8c596.zip
dhcpv4/dhcpv6: improve packet validation
-rw-r--r--accel-pppd/ctrl/ipoe/dhcpv4.c24
-rw-r--r--accel-pppd/ipv6/dhcpv6.c2
-rw-r--r--accel-pppd/ipv6/dhcpv6.h2
-rw-r--r--accel-pppd/ipv6/dhcpv6_packet.c60
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)