diff options
| author | Denys Fedoryshchenko <denys.f@collabora.com> | 2026-09-28 21:50:27 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-09-28 21:50:27 +0300 |
| commit | 50c54c477624d5a9b668225fde2b9844d362760a (patch) | |
| tree | d1fe53a3c637f8b963027aa483053ae7afb55b5e /accel-pppd | |
| parent | c9d938173838d6955c824d141f24672d7037eb4e (diff) | |
| parent | 06aa0ef737d6993fd18e7db1bc8fc35601079505 (diff) | |
| download | accel-ppp-master.tar.gz accel-ppp-master.zip | |
Additional safeguards
Diffstat (limited to 'accel-pppd')
| -rw-r--r-- | accel-pppd/ctrl/pppoe/pppoe.c | 6 | ||||
| -rw-r--r-- | accel-pppd/ifcfg.c | 15 | ||||
| -rw-r--r-- | accel-pppd/ipv6/dhcpv6.c | 80 | ||||
| -rw-r--r-- | accel-pppd/ipv6/dhcpv6.h | 1 | ||||
| -rw-r--r-- | accel-pppd/ipv6/dhcpv6_packet.c | 54 | ||||
| -rw-r--r-- | accel-pppd/net.c | 8 | ||||
| -rw-r--r-- | accel-pppd/ppp/ppp_lcp.c | 7 | ||||
| -rw-r--r-- | accel-pppd/radius/acct.c | 33 | ||||
| -rw-r--r-- | accel-pppd/radius/auth.c | 16 | ||||
| -rw-r--r-- | accel-pppd/radius/dm_coa.c | 8 | ||||
| -rw-r--r-- | accel-pppd/radius/packet.c | 1 | ||||
| -rw-r--r-- | accel-pppd/radius/radius.c | 7 | ||||
| -rw-r--r-- | accel-pppd/radius/req.c | 71 | ||||
| -rw-r--r-- | accel-pppd/radius/serv.c | 38 | ||||
| -rw-r--r-- | accel-pppd/triton/mempool.c | 2 |
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); |
