Skip to content

Commit de894f5

Browse files
committed
fix: harden DNS, ODoH, proxy, and utility bounds
Verify and fix the confirmed findings from VULN-FINDINGS.json. This tightens DNS reply parsing bounds in dohd, adds explicit ODoH output-capacity checks for both server and ns2dohd paths, defers SIGHUP/SIGUSR1 reload work out of signal context, closes the duplicate-upstream-reply lifecycle hole, constrains dohproxyd dynamic targets and stream lifetime handling, widens heap timer ids, and makes Base64URL decode/check length- and capacity-bounded. Add focused regression tests for DNS parser bounds, URL64 capacity handling, and heap id behavior.
1 parent e8f6fc9 commit de894f5

13 files changed

Lines changed: 423 additions & 189 deletions

File tree

ns2dohd/ns2dohd.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1346,7 +1346,11 @@ int main(int argc, char *argv[])
13461346
goto odoh_fail;
13471347
}
13481348
if (odoh_client_decrypt_response(&odoh_client, odoh_rep, (uint16_t)reply_len,
1349-
dns_rep, &dns_out_len) != 0) {
1349+
dns_rep, sizeof(dns_rep), &dns_out_len) != 0) {
1350+
stats.errors++;
1351+
goto odoh_fail;
1352+
}
1353+
if (dns_out_len > sizeof(dns_rep)) {
13501354
stats.errors++;
13511355
goto odoh_fail;
13521356
}

proxy/dohproxyd.c

Lines changed: 104 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,95 @@ struct client {
100100
struct client *next;
101101
};
102102

103+
static int target_host_has_forbidden_syntax(const char *host)
104+
{
105+
struct in_addr a4;
106+
struct in6_addr a6;
107+
size_t i;
108+
109+
if (!host || *host == '\0')
110+
return 1;
111+
if (strchr(host, '@') || strchr(host, '/') || strchr(host, '\\') ||
112+
strchr(host, '?') || strchr(host, '#') || strchr(host, '%'))
113+
return 1;
114+
for (i = 0; host[i] != '\0'; i++) {
115+
if (isspace((unsigned char)host[i]))
116+
return 1;
117+
}
118+
if (strchr(host, ':') != NULL)
119+
return inet_pton(AF_INET6, host, &a6) != 1;
120+
if (inet_pton(AF_INET, host, &a4) == 1)
121+
return 0;
122+
for (i = 0; host[i] != '\0'; i++) {
123+
unsigned char ch = (unsigned char)host[i];
124+
if (!(isalnum(ch) || ch == '.' || ch == '-'))
125+
return 1;
126+
}
127+
return 0;
128+
}
129+
130+
static int sockaddr_is_public(const struct sockaddr *sa)
131+
{
132+
if (sa->sa_family == AF_INET) {
133+
const struct sockaddr_in *sin = (const struct sockaddr_in *)sa;
134+
uint32_t ip = ntohl(sin->sin_addr.s_addr);
135+
136+
if ((ip >> 24) == 10 || (ip >> 24) == 127 || (ip >> 24) == 0)
137+
return 0;
138+
if ((ip & 0xFFF00000U) == 0xAC100000U)
139+
return 0;
140+
if ((ip & 0xFFFF0000U) == 0xC0A80000U)
141+
return 0;
142+
if ((ip & 0xFFFF0000U) == 0xA9FE0000U)
143+
return 0;
144+
if ((ip & 0xF0000000U) == 0xE0000000U)
145+
return 0;
146+
return 1;
147+
}
148+
if (sa->sa_family == AF_INET6) {
149+
const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa;
150+
const uint8_t *ip = sin6->sin6_addr.s6_addr;
151+
152+
if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr) ||
153+
IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) ||
154+
IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
155+
return 0;
156+
if ((ip[0] & 0xFE) == 0xFC)
157+
return 0;
158+
if (ip[0] == 0xFE && (ip[1] & 0xC0) == 0x80)
159+
return 0;
160+
return 1;
161+
}
162+
return 0;
163+
}
164+
165+
static int target_is_allowed(const char *host, const char *port, const char *path)
166+
{
167+
struct addrinfo hints, *res = NULL, *rp;
168+
int allowed = 0;
169+
170+
if (!path || path[0] != '/' || target_host_has_forbidden_syntax(host))
171+
return 0;
172+
if (strcmp(port, "443") != 0)
173+
return 0;
174+
175+
memset(&hints, 0, sizeof(hints));
176+
hints.ai_family = AF_UNSPEC;
177+
hints.ai_socktype = SOCK_STREAM;
178+
if (getaddrinfo(host, port, &hints, &res) != 0)
179+
return 0;
180+
181+
allowed = 1;
182+
for (rp = res; rp; rp = rp->ai_next) {
183+
if (!sockaddr_is_public(rp->ai_addr)) {
184+
allowed = 0;
185+
break;
186+
}
187+
}
188+
freeaddrinfo(res);
189+
return allowed;
190+
}
191+
103192
static int lfd = -1;
104193
static WOLFSSL_CTX *srv_ctx = NULL;
105194
static WOLFSSL_CTX *cli_ctx = NULL;
@@ -735,6 +824,8 @@ static int forward_to_dynamic_target(struct req *req, uint8_t *out, uint32_t *ou
735824
return -1;
736825
if (parse_url(full, &tc.up) != 0)
737826
return -1;
827+
if (!target_is_allowed(tc.up.host, tc.up.port, tc.up.path))
828+
return -1;
738829
tc.fd = -1;
739830

740831
if (forward_to_upstream(&tc, req, "application/oblivious-dns-message", out, out_len) != 0) {
@@ -821,6 +912,11 @@ static int in_header_cb(nghttp2_session *session,
821912
if (frame->hd.type != NGHTTP2_HEADERS || frame->headers.cat != NGHTTP2_HCAT_REQUEST)
822913
return 0;
823914

915+
if (req->stream_id != 0 && frame->hd.stream_id != (int32_t)req->stream_id) {
916+
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
917+
frame->hd.stream_id, NGHTTP2_REFUSED_STREAM);
918+
return 0;
919+
}
824920
if (frame->hd.stream_id != (int32_t)req->stream_id)
825921
req->stream_id = frame->hd.stream_id;
826922

@@ -944,18 +1040,21 @@ static int in_frame_recv_cb(nghttp2_session *session,
9441040
}
9451041

9461042
nghttp2_session_send(session);
947-
free(req->resp);
948-
memset(req, 0, sizeof(*req));
9491043
return 0;
9501044
}
9511045

9521046
static int in_stream_close_cb(nghttp2_session *session, int32_t stream_id,
9531047
uint32_t error_code, void *user_data)
9541048
{
1049+
struct client *cl = (struct client *)user_data;
1050+
struct req *req = cl ? &cl->req : NULL;
1051+
9551052
(void)session;
956-
(void)stream_id;
9571053
(void)error_code;
958-
(void)user_data;
1054+
if (req && req->stream_id == (uint32_t)stream_id) {
1055+
free(req->resp);
1056+
memset(req, 0, sizeof(*req));
1057+
}
9591058
return 0;
9601059
}
9611060

@@ -982,7 +1081,7 @@ static void client_read(int fd, short revents, void *arg)
9821081
}
9831082

9841083
nghttp2_session_callbacks *cbs = NULL;
985-
nghttp2_settings_entry iv[1] = {{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 }};
1084+
nghttp2_settings_entry iv[1] = {{ NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 1 }};
9861085

9871086
if (nghttp2_session_callbacks_new(&cbs) != 0) {
9881087
free_client(cl);

0 commit comments

Comments
 (0)