summaryrefslogtreecommitdiff
path: root/accel-pppd
diff options
context:
space:
mode:
authorDenys Fedoryshchenko <denys.f@collabora.com>2026-09-28 21:50:27 +0300
committerGitHub <noreply@github.com>2026-09-28 21:50:27 +0300
commit50c54c477624d5a9b668225fde2b9844d362760a (patch)
treed1fe53a3c637f8b963027aa483053ae7afb55b5e /accel-pppd
parentc9d938173838d6955c824d141f24672d7037eb4e (diff)
parent06aa0ef737d6993fd18e7db1bc8fc35601079505 (diff)
downloadaccel-ppp-master.tar.gz
accel-ppp-master.zip
Merge pull request #362 from nuclearcat/fix/ng40-additional-safeguardsHEADmaster
Additional safeguards
Diffstat (limited to 'accel-pppd')
-rw-r--r--accel-pppd/ctrl/pppoe/pppoe.c6
-rw-r--r--accel-pppd/ifcfg.c15
-rw-r--r--accel-pppd/ipv6/dhcpv6.c80
-rw-r--r--accel-pppd/ipv6/dhcpv6.h1
-rw-r--r--accel-pppd/ipv6/dhcpv6_packet.c54
-rw-r--r--accel-pppd/net.c8
-rw-r--r--accel-pppd/ppp/ppp_lcp.c7
-rw-r--r--accel-pppd/radius/acct.c33
-rw-r--r--accel-pppd/radius/auth.c16
-rw-r--r--accel-pppd/radius/dm_coa.c8
-rw-r--r--accel-pppd/radius/packet.c1
-rw-r--r--accel-pppd/radius/radius.c7
-rw-r--r--accel-pppd/radius/req.c71
-rw-r--r--accel-pppd/radius/serv.c38
-rw-r--r--accel-pppd/triton/mempool.c2
15 files changed, 236 insertions, 111 deletions
diff --git a/accel-pppd/ctrl/pppoe/pppoe.c b/accel-pppd/ctrl/pppoe/pppoe.c
index bd92cbf8..6161cc03 100644
--- a/accel-pppd/ctrl/pppoe/pppoe.c
+++ b/accel-pppd/ctrl/pppoe/pppoe.c
@@ -1156,6 +1156,7 @@ static void pppoe_recv_PADR(struct pppoe_serv_t *serv, uint8_t *pack, int size)
struct pppoe_tag *service_name_tag = NULL;
struct pppoe_tag *tr101_tag = NULL;
int n, service_match = 0;
+ int service_name_count = 0;
struct pppoe_conn_t *conn;
int vendor_id;
uint16_t ppp_max_payload = 0;
@@ -1204,6 +1205,7 @@ static void pppoe_recv_PADR(struct pppoe_serv_t *serv, uint8_t *pack, int size)
goto padr_tags_done;
case TAG_SERVICE_NAME:
service_name_tag = tag;
+ service_name_count++;
if (tag->tag_len == 0)
service_match = 1;
else if (conf_service_name[0]) {
@@ -1251,9 +1253,9 @@ padr_tags_done:
return;
}
- if (!service_name_tag) {
+ if (service_name_count != 1) {
if (conf_verbose)
- log_warn("pppoe: discard PADR packet (no Service-Name tag present)\n");
+ log_warn("pppoe: discard PADR packet (expected exactly one Service-Name tag)\n");
return;
}
diff --git a/accel-pppd/ifcfg.c b/accel-pppd/ifcfg.c
index 66f5cc6b..c672f6c0 100644
--- a/accel-pppd/ifcfg.c
+++ b/accel-pppd/ifcfg.c
@@ -349,12 +349,21 @@ int __export ap_session_rename(struct ap_session *ses, const char *ifname, int l
#ifdef HAVE_VRF
int __export ap_session_vrf(struct ap_session *ses, const char *vrf_name, int len)
{
- if (len == -1)
- len = strlen(vrf_name);
-
int vrf_ifindex = 0;
+ char name[IFNAMSIZ];
+
+ if (len == -1)
+ len = vrf_name ? strnlen(vrf_name, IFNAMSIZ) : 0;
+ if (len < 0 || len >= IFNAMSIZ || (len && !vrf_name) ||
+ (len && memchr(vrf_name, 0, len))) {
+ log_ppp_error("invalid vrf name\n");
+ return -1;
+ }
if (len) {
+ memcpy(name, vrf_name, len);
+ name[len] = 0;
+ vrf_name = name;
vrf_ifindex = ses->net->get_ifindex(vrf_name);
if (vrf_ifindex < 0) {
log_ppp_error("vrf '%s' not found\n", vrf_name);
diff --git a/accel-pppd/ipv6/dhcpv6.c b/accel-pppd/ipv6/dhcpv6.c
index a0b159cf..9b957f0f 100644
--- a/accel-pppd/ipv6/dhcpv6.c
+++ b/accel-pppd/ipv6/dhcpv6.c
@@ -205,7 +205,7 @@ static void insert_dp_routes(struct ap_session *ses, struct dhcpv6_pd *pd, struc
pd->dp_active = 1;
}
-static void insert_status(struct dhcpv6_packet *pkt, struct dhcpv6_option *opt, int code)
+static int insert_status(struct dhcpv6_packet *pkt, struct dhcpv6_option *opt, int code)
{
struct dhcpv6_option *opt1;
struct dhcpv6_opt_status *status;
@@ -215,11 +215,15 @@ static void insert_status(struct dhcpv6_packet *pkt, struct dhcpv6_option *opt,
else
opt1 = dhcpv6_option_alloc(pkt, D6_OPTION_STATUS_CODE, sizeof(struct dhcpv6_opt_status) - sizeof(struct dhcpv6_opt_hdr));
+ if (!opt1)
+ return -1;
+
status = (struct dhcpv6_opt_status *)opt1->hdr;
status->code = htons(code);
+ return 0;
}
-static void insert_oro(struct dhcpv6_packet *reply, struct dhcpv6_option *opt)
+static int insert_oro(struct dhcpv6_packet *reply, struct dhcpv6_option *opt)
{
struct dhcpv6_option *opt1;
int i, j, dns_count;
@@ -236,21 +240,28 @@ static void insert_oro(struct dhcpv6_packet *reply, struct dhcpv6_option *opt)
dns, MAX_DNS_COUNT);
if (dns_count) {
opt1 = dhcpv6_option_alloc(reply, D6_OPTION_DNS_SERVERS, dns_count * sizeof(addr));
+ if (!opt1)
+ return -1;
for (j = 0, addr_ptr = opt1->hdr->data; j < dns_count; j++, addr_ptr += sizeof(addr))
memcpy(addr_ptr, dns + j, sizeof(addr));
}
} else if (code == D6_OPTION_DOMAIN_LIST) {
if (conf_dnssl_size) {
opt1 = dhcpv6_option_alloc(reply, D6_OPTION_DOMAIN_LIST, conf_dnssl_size);
+ if (!opt1)
+ return -1;
memcpy(opt1->hdr->data, conf_dnssl, conf_dnssl_size);
}
} else if (code == D6_OPTION_AFTR_NAME) {
if (conf_aftr_gw_size) {
opt1 = dhcpv6_option_alloc(reply, D6_OPTION_AFTR_NAME, conf_aftr_gw_size);
+ if (!opt1)
+ return -1;
memcpy(opt1->hdr->data, conf_aftr_gw, conf_aftr_gw_size);
}
}
}
+ return 0;
}
static void dhcpv6_send_reply(struct dhcpv6_packet *req, struct dhcpv6_pd *pd, int code)
@@ -277,6 +288,8 @@ static void dhcpv6_send_reply(struct dhcpv6_packet *req, struct dhcpv6_pd *pd, i
continue;
opt1 = dhcpv6_option_alloc(reply, D6_OPTION_IA_NA, sizeof(struct dhcpv6_opt_ia_na) - sizeof(struct dhcpv6_opt_hdr));
+ if (!opt1)
+ goto out;
memcpy(opt1->hdr + 1, opt->hdr + 1, ntohs(opt1->hdr->len));
ia_na = (struct dhcpv6_opt_ia_na *)opt1->hdr;
@@ -284,9 +297,11 @@ static void dhcpv6_send_reply(struct dhcpv6_packet *req, struct dhcpv6_pd *pd, i
ia_na->T2 = conf_pref_lifetime == -1 ? -1 : htonl((conf_pref_lifetime * 4) / 5);
if (req->hdr->type == D6_RENEW && pd->addr_iaid != ia_na->iaid) {
- insert_status(reply, opt1, D6_STATUS_NoBinding);
+ if (insert_status(reply, opt1, D6_STATUS_NoBinding))
+ goto out;
} else if (list_empty(&ses->ipv6->addr_list) || f) {
- insert_status(reply, opt1, D6_STATUS_NoAddrsAvail);
+ if (insert_status(reply, opt1, D6_STATUS_NoAddrsAvail))
+ goto out;
} else {
if (req->hdr->type == D6_REQUEST || req->rapid_commit)
@@ -296,6 +311,8 @@ static void dhcpv6_send_reply(struct dhcpv6_packet *req, struct dhcpv6_pd *pd, i
list_for_each_entry(a, &ses->ipv6->addr_list, entry) {
opt2 = dhcpv6_nested_option_alloc(reply, opt1, D6_OPTION_IAADDR, sizeof(*ia_addr) - sizeof(struct dhcpv6_opt_hdr));
+ if (!opt2)
+ goto out;
ia_addr = (struct dhcpv6_opt_ia_addr *)opt2->hdr;
build_ip6_addr(a, ses->ipv6->peer_intf_id, &addr);
@@ -342,13 +359,16 @@ static void dhcpv6_send_reply(struct dhcpv6_packet *req, struct dhcpv6_pd *pd, i
if (!f1) {
opt3 = dhcpv6_nested_option_alloc(reply, opt1, D6_OPTION_IAADDR, sizeof(*ia_addr) - sizeof(struct dhcpv6_opt_hdr));
+ if (!opt3)
+ goto out;
memcpy(opt3->hdr->data, opt2->hdr->data, sizeof(*ia_addr) - sizeof(struct dhcpv6_opt_hdr));
ia_addr = (struct dhcpv6_opt_ia_addr *)opt3->hdr;
ia_addr->pref_lifetime = 0;
ia_addr->valid_lifetime = 0;
- insert_status(reply, opt3, D6_STATUS_NotOnLink);
+ if (insert_status(reply, opt3, D6_STATUS_NotOnLink))
+ goto out;
}
}
}
@@ -363,6 +383,8 @@ static void dhcpv6_send_reply(struct dhcpv6_packet *req, struct dhcpv6_pd *pd, i
continue;
opt1 = dhcpv6_option_alloc(reply, D6_OPTION_IA_PD, sizeof(struct dhcpv6_opt_ia_na) - sizeof(struct dhcpv6_opt_hdr));
+ if (!opt1)
+ goto out;
memcpy(opt1->hdr + 1, opt->hdr + 1, ntohs(opt1->hdr->len));
ia_na = (struct dhcpv6_opt_ia_na *)opt1->hdr;
@@ -380,9 +402,11 @@ static void dhcpv6_send_reply(struct dhcpv6_packet *req, struct dhcpv6_pd *pd, i
}
if ((req->hdr->type == D6_RENEW) && pd->dp_iaid != ia_na->iaid) {
- insert_status(reply, opt1, D6_STATUS_NoBinding);
+ if (insert_status(reply, opt1, D6_STATUS_NoBinding))
+ goto out;
} else if (!ses->ipv6_dp || list_empty(&ses->ipv6_dp->prefix_list) || f2) {
- insert_status(reply, opt1, D6_STATUS_NoPrefixAvail);
+ if (insert_status(reply, opt1, D6_STATUS_NoPrefixAvail))
+ goto out;
} else {
if (req->hdr->type == D6_REQUEST || req->rapid_commit) {
@@ -395,6 +419,8 @@ static void dhcpv6_send_reply(struct dhcpv6_packet *req, struct dhcpv6_pd *pd, i
list_for_each_entry(a, &ses->ipv6_dp->prefix_list, entry) {
opt2 = dhcpv6_nested_option_alloc(reply, opt1, D6_OPTION_IAPREFIX, sizeof(*ia_prefix) - sizeof(struct dhcpv6_opt_hdr));
+ if (!opt2)
+ goto out;
ia_prefix = (struct dhcpv6_opt_ia_prefix *)opt2->hdr;
memcpy(&ia_prefix->prefix, &a->addr, sizeof(a->addr));
@@ -424,12 +450,15 @@ static void dhcpv6_send_reply(struct dhcpv6_packet *req, struct dhcpv6_pd *pd, i
if (!f1) {
opt3 = dhcpv6_nested_option_alloc(reply, opt1, D6_OPTION_IAPREFIX, sizeof(*ia_prefix) - sizeof(struct dhcpv6_opt_hdr));
+ if (!opt3)
+ goto out;
memcpy(opt3->hdr->data, opt2->hdr->data, sizeof(*ia_prefix) - sizeof(struct dhcpv6_opt_hdr));
ia_prefix = (struct dhcpv6_opt_ia_prefix *)opt3->hdr;
ia_prefix->pref_lifetime = 0;
ia_prefix->valid_lifetime = 0;
- insert_status(reply, opt3, D6_STATUS_NotOnLink);
+ if (insert_status(reply, opt3, D6_STATUS_NotOnLink))
+ goto out;
}
}
}
@@ -444,21 +473,27 @@ static void dhcpv6_send_reply(struct dhcpv6_packet *req, struct dhcpv6_pd *pd, i
continue;
opt1 = dhcpv6_option_alloc(reply, D6_OPTION_IA_TA, sizeof(struct dhcpv6_opt_ia_ta) - sizeof(struct dhcpv6_opt_hdr));
+ if (!opt1)
+ goto out;
memcpy(opt1->hdr + 1, opt->hdr + 1, ntohs(opt1->hdr->len));
- insert_status(reply, opt1, D6_STATUS_NoAddrsAvail);
+ if (insert_status(reply, opt1, D6_STATUS_NoAddrsAvail))
+ goto out;
// Option Request
} else if (ntohs(opt->hdr->code) == D6_OPTION_ORO) {
- insert_oro(reply, opt);
-
- } else if (ntohs(opt->hdr->code) == D6_OPTION_RAPID_COMMIT) {
- if (req->hdr->type == D6_SOLICIT)
- dhcpv6_option_alloc(reply, D6_OPTION_RAPID_COMMIT, 0);
+ if (insert_oro(reply, opt))
+ goto out;
}
}
+ if (req->hdr->type == D6_SOLICIT && req->rapid_commit &&
+ !dhcpv6_option_alloc(reply, D6_OPTION_RAPID_COMMIT, 0))
+ goto out;
+
opt1 = dhcpv6_option_alloc(reply, D6_OPTION_PREFERENCE, 1);
+ if (!opt1)
+ goto out;
*(uint8_t *)opt1->hdr->data = 255;
//insert_status(reply, NULL, D6_STATUS_Success);
@@ -472,6 +507,7 @@ static void dhcpv6_send_reply(struct dhcpv6_packet *req, struct dhcpv6_pd *pd, i
net->sendto(pd->hnd.fd, reply->hdr, reply->endptr - (void *)reply->hdr, 0, (struct sockaddr *)&req->addr, sizeof(req->addr));
+out:
dhcpv6_packet_free(reply);
}
@@ -496,6 +532,8 @@ static void dhcpv6_send_reply2(struct dhcpv6_packet *req, struct dhcpv6_pd *pd,
// IA_NA
if (ntohs(opt->hdr->code) == D6_OPTION_IA_NA) {
opt1 = dhcpv6_option_alloc(reply, D6_OPTION_IA_NA, sizeof(struct dhcpv6_opt_ia_na) - sizeof(struct dhcpv6_opt_hdr));
+ if (!opt1)
+ goto out;
memcpy(opt1->hdr + 1, opt->hdr + 1, ntohs(opt1->hdr->len));
ia_na = (struct dhcpv6_opt_ia_na *)opt1->hdr;
@@ -525,6 +563,8 @@ static void dhcpv6_send_reply2(struct dhcpv6_packet *req, struct dhcpv6_pd *pd,
}
opt3 = dhcpv6_nested_option_alloc(reply, opt1, D6_OPTION_IAADDR, sizeof(*ia_addr) - sizeof(struct dhcpv6_opt_hdr));
+ if (!opt3)
+ goto out;
memcpy(opt3->hdr->data, opt2->hdr->data, sizeof(*ia_addr) - sizeof(struct dhcpv6_opt_hdr));
ia_addr = (struct dhcpv6_opt_ia_addr *)opt3->hdr;
@@ -549,6 +589,8 @@ static void dhcpv6_send_reply2(struct dhcpv6_packet *req, struct dhcpv6_pd *pd,
// IA_PD
} else if (ntohs(opt->hdr->code) == D6_OPTION_IA_PD) {
opt1 = dhcpv6_option_alloc(reply, D6_OPTION_IA_PD, sizeof(struct dhcpv6_opt_ia_na) - sizeof(struct dhcpv6_opt_hdr));
+ if (!opt1)
+ goto out;
memcpy(opt1->hdr + 1, opt->hdr + 1, ntohs(opt1->hdr->len));
ia_na = (struct dhcpv6_opt_ia_na *)opt1->hdr;
@@ -589,6 +631,8 @@ static void dhcpv6_send_reply2(struct dhcpv6_packet *req, struct dhcpv6_pd *pd,
}
opt3 = dhcpv6_nested_option_alloc(reply, opt1, D6_OPTION_IAPREFIX, sizeof(*ia_prefix) - sizeof(struct dhcpv6_opt_hdr));
+ if (!opt3)
+ goto out;
memcpy(opt3->hdr->data, opt2->hdr->data, sizeof(*ia_prefix) - sizeof(struct dhcpv6_opt_hdr));
ia_prefix = (struct dhcpv6_opt_ia_prefix *)opt3->hdr;
@@ -609,11 +653,15 @@ static void dhcpv6_send_reply2(struct dhcpv6_packet *req, struct dhcpv6_pd *pd,
f2 = 1;
}
// Option Request
- } else if (ntohs(opt->hdr->code) == D6_OPTION_ORO)
- insert_oro(reply, opt);
+ } else if (ntohs(opt->hdr->code) == D6_OPTION_ORO) {
+ if (insert_oro(reply, opt))
+ goto out;
+ }
}
opt1 = dhcpv6_option_alloc(reply, D6_OPTION_PREFERENCE, 1);
+ if (!opt1)
+ goto out;
*(uint8_t *)opt1->hdr->data = 255;
//insert_status(reply, NULL, D6_STATUS_Success);
diff --git a/accel-pppd/ipv6/dhcpv6.h b/accel-pppd/ipv6/dhcpv6.h
index 2326e639..7dfc71a1 100644
--- a/accel-pppd/ipv6/dhcpv6.h
+++ b/accel-pppd/ipv6/dhcpv6.h
@@ -10,6 +10,7 @@
#define DHCPV6_CLIENT_PORT 546
#define DHCPV6_SERV_PORT 547
+#define DHCPV6_HOP_COUNT_LIMIT 8
#define D6_OPTION_CLIENTID 1
#define D6_OPTION_SERVERID 2
diff --git a/accel-pppd/ipv6/dhcpv6_packet.c b/accel-pppd/ipv6/dhcpv6_packet.c
index f1c168b6..9190c505 100644
--- a/accel-pppd/ipv6/dhcpv6_packet.c
+++ b/accel-pppd/ipv6/dhcpv6_packet.c
@@ -63,6 +63,26 @@ static struct dict_option known_options[] = {
{ 0 }
};
+/* RFC 6334: one uncompressed, terminated DNS name. */
+static int validate_aftr_name(const uint8_t *data, unsigned int len)
+{
+ unsigned int offset = 0, label_len;
+
+ if (len <= 3 || len > 255)
+ return 0;
+
+ while (offset < len) {
+ label_len = data[offset++];
+ if (!label_len)
+ return offset > 1 && offset == len;
+ if (label_len > 63 || label_len > len - offset)
+ return 0;
+ offset += label_len;
+ }
+
+ return 0;
+}
+
static void *parse_option(void *ptr, void *endptr, struct list_head *opt_list)
{
struct dict_option *dopt;
@@ -75,6 +95,12 @@ static void *parse_option(void *ptr, void *endptr, struct list_head *opt_list)
return NULL;
}
+ if (opth->code == htons(D6_OPTION_AFTR_NAME) &&
+ !validate_aftr_name(opth->data, ntohs(opth->len))) {
+ log_warn("dhcpv6: invalid AFTR-Name option\n");
+ return NULL;
+ }
+
opt = _malloc(sizeof(*opt));
if (!opt) {
log_emerg("out of memory\n");
@@ -117,6 +143,7 @@ struct dhcpv6_packet *dhcpv6_packet_parse(const void *buf, size_t size)
struct dhcpv6_relay *rel;
struct dhcpv6_relay_hdr *rhdr;
struct dhcpv6_msg_hdr *inner_hdr;
+ int aftr_name_seen = 0, relay_depth = 0;
void *ptr, *endptr, *relay_end, *inner_end;
if (size < sizeof(struct dhcpv6_msg_hdr)) {
@@ -141,6 +168,10 @@ struct dhcpv6_packet *dhcpv6_packet_parse(const void *buf, size_t size)
endptr = ((void *)pkt->hdr) + size;
while (pkt->hdr->type == D6_RELAY_FORW) {
+ if (relay_depth++ >= DHCPV6_HOP_COUNT_LIMIT) {
+ log_warn("dhcpv6: relay nesting limit exceeded\n");
+ goto error;
+ }
rhdr = (struct dhcpv6_relay_hdr *)pkt->hdr;
if (((void *)rhdr) + sizeof(*rhdr) > endptr) {
log_warn("dhcpv6: invalid packet received\n");
@@ -205,8 +236,18 @@ struct dhcpv6_packet *dhcpv6_packet_parse(const void *buf, size_t size)
pkt->clientid = ptr;
else if (opth->code == htons(D6_OPTION_SERVERID))
pkt->serverid = ptr;
- else if (opth->code == htons(D6_OPTION_RAPID_COMMIT))
+ else if (opth->code == htons(D6_OPTION_AFTR_NAME)) {
+ if (aftr_name_seen++) {
+ log_warn("dhcpv6: duplicate AFTR-Name option\n");
+ goto error;
+ }
+ } else if (opth->code == htons(D6_OPTION_RAPID_COMMIT)) {
+ if (pkt->rapid_commit || opth->len) {
+ log_warn("dhcpv6: invalid or duplicate Rapid-Commit option\n");
+ goto error;
+ }
pkt->rapid_commit = 1;
+ }
ptr = parse_option(ptr, endptr, &pkt->opt_list);
if (!ptr)
@@ -224,7 +265,8 @@ struct dhcpv6_option *dhcpv6_option_alloc(struct dhcpv6_packet *pkt, int code, i
{
struct dhcpv6_option *opt;
- if ((void *)pkt->hdr->data + BUF_SIZE - pkt->endptr < sizeof(struct dhcpv6_opt_hdr) + len)
+ if (len < 0 || len > BUF_SIZE ||
+ (char *)(pkt + 1) + BUF_SIZE - (char *)pkt->endptr < sizeof(struct dhcpv6_opt_hdr) + (size_t)len)
return NULL;
opt = _malloc(sizeof(*opt));
@@ -251,7 +293,8 @@ 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)
+ if (len < 0 || len > BUF_SIZE ||
+ (char *)(pkt + 1) + BUF_SIZE - (char *)pkt->endptr < sizeof(struct dhcpv6_opt_hdr) + (size_t)len)
return NULL;
opt = _malloc(sizeof(*opt));
@@ -297,7 +340,7 @@ void dhcpv6_fill_relay_info(struct dhcpv6_packet *pkt)
memcpy(&rhdr->peer_addr, &rel->peer_addr, sizeof(rhdr->peer_addr));
opt = (struct dhcpv6_opt_hdr *)rhdr->data;
opt->code = htons(D6_OPTION_RELAY_MSG);
- opt->len = (uint8_t *)pkt->endptr - rhdr->data;
+ opt->len = htons((uint8_t *)pkt->endptr - opt->data);
}
rel = list_entry(pkt->relay_list.next, typeof(*rel), entry);
@@ -325,6 +368,9 @@ struct dhcpv6_packet *dhcpv6_packet_alloc_reply(struct dhcpv6_packet *req, int t
while (!list_empty(&req->relay_list)) {
rel = list_entry(req->relay_list.next, typeof(*rel), entry);
+ if ((char *)(pkt + 1) + BUF_SIZE - (char *)pkt->hdr <
+ sizeof(struct dhcpv6_relay_hdr) + sizeof(struct dhcpv6_opt_hdr) + sizeof(*pkt->hdr))
+ goto error;
rel->hdr = (void *)pkt->hdr;
pkt->hdr = (void *)rel->hdr + sizeof(struct dhcpv6_relay_hdr) + sizeof(struct dhcpv6_opt_hdr);
list_move_tail(&rel->entry, &pkt->relay_list);
diff --git a/accel-pppd/net.c b/accel-pppd/net.c
index c619deed..b31cdf44 100644
--- a/accel-pppd/net.c
+++ b/accel-pppd/net.c
@@ -223,9 +223,15 @@ static int def_get_ifindex(const char *ifname)
{
struct kern_net *n = container_of(net, typeof(*n), net);
struct ifreq ifr;
+ size_t len;
+
+ if (!ifname || (len = strnlen(ifname, IFNAMSIZ)) >= IFNAMSIZ) {
+ log_ppp_error("invalid interface name\n");
+ return -1;
+ }
memset(&ifr, 0, sizeof(ifr));
- strcpy(ifr.ifr_name, ifname);
+ memcpy(ifr.ifr_name, ifname, len);
if (ioctl(n->sock, SIOCGIFINDEX, &ifr)) {
log_ppp_error("ioctl(SIOCGIFINDEX): %s\n", strerror(errno));
diff --git a/accel-pppd/ppp/ppp_lcp.c b/accel-pppd/ppp/ppp_lcp.c
index 2424ca94..4af28dd0 100644
--- a/accel-pppd/ppp/ppp_lcp.c
+++ b/accel-pppd/ppp/ppp_lcp.c
@@ -634,7 +634,10 @@ static void lcp_recv_echo_repl(struct ppp_lcp_t *lcp, uint8_t *data, int size)
static void send_echo_reply(struct ppp_lcp_t *lcp)
{
struct lcp_hdr_t *hdr = (struct lcp_hdr_t*)lcp->ppp->buf;
- //uint32_t magic = *(uint32_t *)(hdr + 1);
+ uint16_t len = ntohs(hdr->len);
+
+ if (len > lcp->ppp->mtu)
+ return;
lcp->echo_sent = 0;
lcp->last_echo_ts = _time();
@@ -646,7 +649,7 @@ static void send_echo_reply(struct ppp_lcp_t *lcp)
if (conf_ppp_verbose)
log_ppp_debug("send [LCP EchoRep id=%x <magic %08x>]\n", hdr->id, lcp->magic);
- ppp_chan_send(lcp->ppp, hdr, min(ntohs(hdr->len), lcp->ppp->mtu) + 2);
+ ppp_chan_send(lcp->ppp, hdr, len + 2);
}
static void send_echo_request(struct triton_timer_t *t)
diff --git a/accel-pppd/radius/acct.c b/accel-pppd/radius/acct.c
index 0448fc95..8a344516 100644
--- a/accel-pppd/radius/acct.c
+++ b/accel-pppd/radius/acct.c
@@ -6,7 +6,6 @@
#include <sys/ioctl.h>
#include <netinet/in.h>
-#include <openssl/md5.h>
#include "linux_ppp.h"
@@ -22,30 +21,6 @@
#define INTERIM_SAFE_TIME 10
-static int req_set_RA(struct rad_req_t *req)
-{
- char *secret;
- MD5_CTX ctx;
-
- secret = rad_server_secret_dup(req->serv);
- if (!secret)
- return -1;
-
- if (rad_packet_build(req->pack, req->RA)) {
- _free(secret);
- return -1;
- }
-
- MD5_Init(&ctx);
- MD5_Update(&ctx, req->pack->buf, req->pack->len);
- MD5_Update(&ctx, secret, strlen(secret));
- MD5_Final(req->pack->buf + 4, &ctx);
-
- _free(secret);
-
- return 0;
-}
-
static int req_set_stat(struct rad_req_t *req, struct ap_session *ses)
{
struct timespec ts;
@@ -185,9 +160,6 @@ static void rad_acct_interim_update(struct triton_timer_t *t)
rpd->acct_req->ts = ts.tv_sec;
rpd->acct_req->pack->id++;
- if (!rpd->acct_req->before_send)
- req_set_RA(rpd->acct_req);
-
rpd->acct_req->timeout.expire_tv.tv_sec = conf_timeout;
rpd->acct_req->try = 0;
@@ -218,8 +190,6 @@ static int rad_acct_before_send(struct rad_req_t *req)
clock_gettime(CLOCK_MONOTONIC, &ts);
rad_packet_change_int(req->pack, NULL, "Acct-Delay-Time", ts.tv_sec - req->ts + conf_acct_delay_start);
- req_set_RA(req);
-
return 0;
}
@@ -314,8 +284,6 @@ static int __rad_acct_start(struct radius_pd_t *rpd)
if (conf_acct_delay_time)
req->before_send = rad_acct_before_send;
- else if (req_set_RA(req))
- goto out_err;
req->recv = rad_acct_start_recv;
req->timeout.expire = rad_acct_start_timeout;
@@ -547,7 +515,6 @@ int rad_acct_stop(struct radius_pd_t *rpd)
rad_packet_change_val(req->pack, NULL, "Acct-Status-Type", "Stop");
req_set_stat(req, rpd->ses);
- req_set_RA(req);
req->recv = rad_acct_stop_recv;
req->timeout.expire = rad_acct_stop_timeout;
diff --git a/accel-pppd/radius/auth.c b/accel-pppd/radius/auth.c
index d2cb9803..63b1345e 100644
--- a/accel-pppd/radius/auth.c
+++ b/accel-pppd/radius/auth.c
@@ -22,7 +22,7 @@ static int decrypt_chap_mppe_keys(struct rad_req_t *req, struct rad_attr_t *attr
uint8_t md5[MD5_DIGEST_LENGTH];
uint8_t sha1[SHA_DIGEST_LENGTH];
uint8_t plain[32];
- char *secret;
+ const char *secret;
int i;
if (attr->len != 32) {
@@ -30,7 +30,7 @@ static int decrypt_chap_mppe_keys(struct rad_req_t *req, struct rad_attr_t *attr
return -1;
}
- secret = rad_server_secret_dup(req->serv);
+ secret = (const char *)req->pack->secret;
if (!secret)
return -1;
@@ -59,7 +59,6 @@ static int decrypt_chap_mppe_keys(struct rad_req_t *req, struct rad_attr_t *attr
SHA1_Final(sha1, &sha1_ctx);
memcpy(key, sha1, 16);
- _free(secret);
return 0;
}
@@ -69,7 +68,7 @@ static int decrypt_mppe_key(struct rad_req_t *req, struct rad_attr_t *attr, uint
MD5_CTX md5_ctx;
uint8_t md5[16];
uint8_t plain[32];
- char *secret;
+ const char *secret;
int i;
if (attr->len != 34) {
@@ -82,7 +81,7 @@ static int decrypt_mppe_key(struct rad_req_t *req, struct rad_attr_t *attr, uint
return -1;
}
- secret = rad_server_secret_dup(req->serv);
+ secret = (const char *)req->pack->secret;
if (!secret)
return -1;
@@ -99,7 +98,6 @@ static int decrypt_mppe_key(struct rad_req_t *req, struct rad_attr_t *attr, uint
if (plain[0] != 16) {
log_ppp_warn("radius: %s: incorrect key length (%i)\n", attr->attr->name, plain[0]);
- _free(secret);
return -1;
}
@@ -111,7 +109,6 @@ static int decrypt_mppe_key(struct rad_req_t *req, struct rad_attr_t *attr, uint
plain[16] ^= md5[0];
memcpy(key, plain + 1, 16);
- _free(secret);
return 0;
}
@@ -288,17 +285,16 @@ int rad_auth_pap(struct radius_pd_t *rpd, const char *username, va_list args)
const char *passwd = va_arg(args, const char *);
uint8_t *epasswd;
int epasswd_len;
- char *secret;
+ const char *secret;
if (!req)
return PWDB_DENIED;
- secret = rad_server_secret_dup(req->serv);
+ secret = (const char *)req->pack->secret;
if (!secret)
return PWDB_DENIED;
epasswd = encrypt_password(passwd, secret, req->RA, &epasswd_len);
- _free(secret);
if (!epasswd)
return PWDB_DENIED;
diff --git a/accel-pppd/radius/dm_coa.c b/accel-pppd/radius/dm_coa.c
index df8cdf3e..728fd938 100644
--- a/accel-pppd/radius/dm_coa.c
+++ b/accel-pppd/radius/dm_coa.c
@@ -146,16 +146,16 @@ static void disconnect_request(struct radius_pd_t *rpd)
}
#ifdef HAVE_VRF
-int rad_update_vrf(struct radius_pd_t *rpd, const char *vrf_name)
+static int rad_update_vrf(struct radius_pd_t *rpd, const char *vrf_name, int len)
{
- if (*vrf_name == '0') {
+ if (len == 1 && *vrf_name == '0') {
// Delete interface from VRF
if (!ap_session_vrf(rpd->ses, NULL, 0))
return 1;
}
else {
// Add interface to VRF
- if(!ap_session_vrf(rpd->ses, vrf_name, -1))
+ if(!ap_session_vrf(rpd->ses, vrf_name, len))
return 1;
}
@@ -215,7 +215,7 @@ static void coa_request(struct radius_pd_t *rpd)
#ifdef HAVE_VRF
attr = rad_packet_find_attr(rpd->dm_coa_req, "Accel-PPP", "Accel-VRF-Name");
if (attr){
- if(!rad_update_vrf(rpd, attr->val.string)){
+ if(!rad_update_vrf(rpd, attr->val.string, attr->len)){
goto out;
}
}
diff --git a/accel-pppd/radius/packet.c b/accel-pppd/radius/packet.c
index 4a0ab244..9f135507 100644
--- a/accel-pppd/radius/packet.c
+++ b/accel-pppd/radius/packet.c
@@ -881,6 +881,7 @@ int rad_packet_send(struct rad_packet_t *pack, int fd, struct sockaddr_in *addr)
uint8_t hmac[HMAC_MD5_LEN];
uint8_t *ptr = pack->buf;
uint8_t *hmac_ptr = ptr + PACKET_SIGNED_OFFSET;
+ memset(hmac_ptr, 0, HMAC_MD5_LEN);
if (hmac_md5((const uint8_t *)pack->secret, strlen((const char *)pack->secret), pack->buf, pack->len, hmac) < 0) {
log_emerg("radius:packet: failed to calculate HMAC\n");
return -1;
diff --git a/accel-pppd/radius/radius.c b/accel-pppd/radius/radius.c
index fa77a916..89f1e398 100644
--- a/accel-pppd/radius/radius.c
+++ b/accel-pppd/radius/radius.c
@@ -545,9 +545,16 @@ int rad_proc_attrs(struct rad_req_t *req)
} else if (attr->vendor->id == VENDOR_Accel_PPP) {
switch (attr->attr->id) {
case Accel_VRF_Name:
+ if (attr->len < 0 || attr->len >= IFNAMSIZ ||
+ memchr(attr->val.string, 0, attr->len)) {
+ log_ppp_warn("radius: invalid Accel-VRF-Name, rejecting Access-Accept\n");
+ return -1;
+ }
if (rpd->ses->vrf_name)
_free(rpd->ses->vrf_name);
rpd->ses->vrf_name = _malloc(attr->len + 1);
+ if (!rpd->ses->vrf_name)
+ return -1;
memcpy(rpd->ses->vrf_name, attr->val.string, attr->len);
rpd->ses->vrf_name[attr->len] = 0;
break;
diff --git a/accel-pppd/radius/req.c b/accel-pppd/radius/req.c
index 72c46b16..9a957493 100644
--- a/accel-pppd/radius/req.c
+++ b/accel-pppd/radius/req.c
@@ -5,6 +5,8 @@
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
+#include <openssl/md5.h>
+#include <openssl/crypto.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
@@ -17,6 +19,49 @@
#define HMAC_MD5_LEN 16
+/* Keep the signing secret and authenticator with the packet across reloads. */
+static int rad_req_set_RA(struct rad_req_t *req)
+{
+ char *secret = rad_server_secret_dup(req->serv);
+ MD5_CTX ctx;
+
+ if (!secret)
+ return -1;
+
+ memset(req->RA, 0, sizeof(req->RA));
+ if (rad_packet_build(req->pack, req->RA)) {
+ _free(secret);
+ return -1;
+ }
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, req->pack->buf, req->pack->len);
+ MD5_Update(&ctx, secret, strlen(secret));
+ MD5_Final(req->pack->buf + 4, &ctx);
+ memcpy(req->RA, req->pack->buf + 4, sizeof(req->RA));
+ _free(req->pack->secret);
+ req->pack->secret = (uint8_t *)secret;
+ return 0;
+}
+
+static int verify_response_authenticator(struct rad_req_t *req, struct rad_packet_t *pack)
+{
+ uint8_t expected[MD5_DIGEST_LENGTH];
+ MD5_CTX ctx;
+
+ if (!pack || !pack->buf || pack->len < 20 ||
+ !req->pack->buf || !req->pack->secret)
+ return -1;
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, pack->buf, 4);
+ MD5_Update(&ctx, req->pack->buf + 4, 16);
+ MD5_Update(&ctx, pack->buf + 20, pack->len - 20);
+ MD5_Update(&ctx, req->pack->secret, strlen((char *)req->pack->secret));
+ MD5_Final(expected, &ctx);
+ return CRYPTO_memcmp(expected, pack->buf + 4, sizeof(expected));
+}
+
static int make_socket(struct rad_req_t *req);
static mempool_t req_pool;
@@ -75,12 +120,15 @@ static struct rad_req_t *__rad_req_alloc(struct radius_pd_t *rpd, int code, cons
if (!req->pack)
goto out_err;
- if (code == CODE_ACCESS_REQUEST && conf_blast_protection) {
- uint8_t buf[HMAC_MD5_LEN] = {0};
- req->pack->message_authenticator = 1;
+ if (code == CODE_ACCESS_REQUEST) {
req->pack->secret = (uint8_t *)rad_server_secret_dup(req->serv);
if (!req->pack->secret)
goto out_err;
+ }
+
+ if (code == CODE_ACCESS_REQUEST && conf_blast_protection) {
+ uint8_t buf[HMAC_MD5_LEN] = {0};
+ req->pack->message_authenticator = 1;
if (rad_packet_add_octets(req->pack, NULL, "Message-Authenticator", buf, HMAC_MD5_LEN)) {
_free(req->pack->secret);
req->pack->secret = NULL;
@@ -380,6 +428,10 @@ int __rad_req_send(struct rad_req_t *req, int async)
if (req->before_send && req->before_send(req))
goto out_err;
+ /* Re-sign accounting after server selection, including retries without delay-time. */
+ if (req->pack->code == CODE_ACCOUNTING_REQUEST && rad_req_set_RA(req))
+ goto out_err;
+
if (!req->pack->buf && rad_packet_build(req->pack, req->RA))
goto out_err;
@@ -461,12 +513,15 @@ int rad_req_read(struct triton_md_handler_t *h)
if (rad_packet_recv(h->fd, &pack, NULL))
return 0;
- rad_server_reply(req->serv);
-
- if (pack->id == req->pack->id)
- break;
+ if (!pack)
+ return 0;
+ if (pack->id != req->pack->id || verify_response_authenticator(req, pack)) {
+ rad_packet_free(pack);
+ continue;
+ }
- rad_packet_free(pack);
+ rad_server_reply(req->serv);
+ break;
}
req->reply = pack;
diff --git a/accel-pppd/radius/serv.c b/accel-pppd/radius/serv.c
index 71398c82..34367731 100644
--- a/accel-pppd/radius/serv.c
+++ b/accel-pppd/radius/serv.c
@@ -11,7 +11,6 @@
#include <netinet/in.h>
#include <arpa/inet.h>
-#include <openssl/md5.h>
#include "log.h"
#include "triton.h"
@@ -315,10 +314,19 @@ void rad_server_req_exit(struct rad_req_t *req)
int rad_server_realloc(struct rad_req_t *req)
{
struct rad_server_t *s = __rad_server_get(req->type, req->serv, 0, 0);
+ char *secret;
if (!s)
return -1;
+ secret = rad_server_secret_dup(s);
+ if (!secret) {
+ rad_server_put(s, req->type);
+ return -1;
+ }
+ _free(req->pack->secret);
+ req->pack->secret = (uint8_t *)secret;
+
if (req->serv)
rad_server_put(req->serv, req->type);
@@ -449,30 +457,6 @@ void rad_server_stat_interim_query(struct rad_server_t *s, unsigned int dt)
stat_accm_add(s->stat.interim_query_5m, dt);
}
-static int req_set_RA(struct rad_req_t *req)
-{
- char *secret;
- MD5_CTX ctx;
-
- secret = rad_server_secret_dup(req->serv);
- if (!secret)
- return -1;
-
- if (rad_packet_build(req->pack, req->RA)) {
- _free(secret);
- return -1;
- }
-
- MD5_Init(&ctx);
- MD5_Update(&ctx, req->pack->buf, req->pack->len);
- MD5_Update(&ctx, secret, strlen(secret));
- MD5_Final(req->pack->buf + 4, &ctx);
-
- _free(secret);
-
- return 0;
-}
-
static void acct_on_sent(struct rad_req_t *req, int res)
{
if (!res && !req->hnd.tpd) {
@@ -556,11 +540,9 @@ static void send_acct_on(struct rad_server_t *s)
if (rad_packet_add_ipaddr(req->pack, NULL, "NAS-IP-Address", conf_nas_ip_address))
goto out_err;
- if (req_set_RA(req))
+ if (__rad_req_send(req, 0))
goto out_err;
- __rad_req_send(req, 0);
-
triton_timer_add(&s->ctx, &req->timeout, 0);
return;
diff --git a/accel-pppd/triton/mempool.c b/accel-pppd/triton/mempool.c
index a3c72dae..c37b66da 100644
--- a/accel-pppd/triton/mempool.c
+++ b/accel-pppd/triton/mempool.c
@@ -162,6 +162,8 @@ void __export mempool_free(void *ptr)
it->magic1 = 0;
#endif
+ memset(it->ptr, 0, p->size);
+
spin_lock(&p->lock);
#ifdef MEMDEBUG
list_del(&it->entry);