86 lines
3.3 KiB
Diff
86 lines
3.3 KiB
Diff
From 58df5aea0a0c926b2238f65908f5e9f83d1cca25 Mon Sep 17 00:00:00 2001
|
|
From: Remi Gacogne <remi.gacogne@powerdns.com>
|
|
Date: Wed, 5 Dec 2018 17:52:54 +0100
|
|
Subject: [PATCH] BUG: dns: Prevent stack-exhaustion via recursion loop in
|
|
dns_read_name
|
|
|
|
When a compressed pointer is encountered, dns_read_name() will call
|
|
itself with the pointed-to offset in the packet.
|
|
With a specially crafted packet, it was possible to trigger an
|
|
infinite-loop recursion by making the pointer points to itself.
|
|
While it would be possible to handle that particular case differently
|
|
by making sure that the target is different from the current offset,
|
|
it would still be possible to craft a packet with a very long chain
|
|
of valid pointers, always pointing backwards. To prevent a stack
|
|
exhaustion in that case, this patch restricts the number of recursive
|
|
calls to 100, which should be more than enough.
|
|
|
|
To be backported to 1.8, probably also 1.7.
|
|
---
|
|
src/dns.c | 15 +++++++++------
|
|
1 file changed, 9 insertions(+), 6 deletions(-)
|
|
|
|
Index: haproxy-1.8.13/src/dns.c
|
|
===================================================================
|
|
--- haproxy-1.8.13.orig/src/dns.c
|
|
+++ haproxy-1.8.13/src/dns.c
|
|
@@ -391,7 +391,7 @@ static inline unsigned short dns_respons
|
|
*/
|
|
int dns_read_name(unsigned char *buffer, unsigned char *bufend,
|
|
unsigned char *name, char *destination, int dest_len,
|
|
- int *offset)
|
|
+ int *offset, unsigned int depth)
|
|
{
|
|
int nb_bytes = 0, n = 0;
|
|
int label_len;
|
|
@@ -405,8 +405,11 @@ int dns_read_name(unsigned char *buffer,
|
|
if ((buffer + reader[1]) > reader)
|
|
goto err;
|
|
|
|
+ if (depth++ > 100)
|
|
+ goto err;
|
|
+
|
|
n = dns_read_name(buffer, bufend, buffer + reader[1],
|
|
- dest, dest_len - nb_bytes, offset);
|
|
+ dest, dest_len - nb_bytes, offset, depth);
|
|
if (n == 0)
|
|
goto err;
|
|
|
|
@@ -692,7 +695,7 @@ static int dns_validate_dns_response(uns
|
|
* one query per response and the first one can't be compressed
|
|
* (using the 0x0c format) */
|
|
offset = 0;
|
|
- len = dns_read_name(resp, bufend, reader, dns_query->name, DNS_MAX_NAME_SIZE, &offset);
|
|
+ len = dns_read_name(resp, bufend, reader, dns_query->name, DNS_MAX_NAME_SIZE, &offset, 0);
|
|
|
|
if (len == 0)
|
|
return DNS_RESP_INVALID;
|
|
@@ -729,7 +732,7 @@ static int dns_validate_dns_response(uns
|
|
return (DNS_RESP_INVALID);
|
|
|
|
offset = 0;
|
|
- len = dns_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset);
|
|
+ len = dns_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset, 0);
|
|
|
|
if (len == 0) {
|
|
pool_free(dns_answer_item_pool, dns_answer_record);
|
|
@@ -831,7 +834,7 @@ static int dns_validate_dns_response(uns
|
|
}
|
|
|
|
offset = 0;
|
|
- len = dns_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset);
|
|
+ len = dns_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset, 0);
|
|
if (len == 0) {
|
|
pool_free(dns_answer_item_pool, dns_answer_record);
|
|
return DNS_RESP_INVALID;
|
|
@@ -861,7 +864,7 @@ static int dns_validate_dns_response(uns
|
|
dns_answer_record->port = read_n16(reader);
|
|
reader += sizeof(uint16_t);
|
|
offset = 0;
|
|
- len = dns_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset);
|
|
+ len = dns_read_name(resp, bufend, reader, tmpname, DNS_MAX_NAME_SIZE, &offset, 0);
|
|
if (len == 0) {
|
|
pool_free(dns_answer_item_pool, dns_answer_record);
|
|
return DNS_RESP_INVALID;
|
|
|