Hi Patrik,
Patrik Flykt <patrik.flykt(a)linux.intel.com> writes:
Be more strict with incoming DNS requests. Verify that there is
only one entry in the question section and none in the answer or
name server sections.
Ensure that the question section has a proper IN class and
compute the remaing length and the pointer position with respect
to the end of the question section. If there is an EDNS0 extension,
log its length.
---
src/dnsproxy.c | 59 ++++++++++++++++++++++++++++++++++------------------------
1 file changed, 35 insertions(+), 24 deletions(-)
diff --git a/src/dnsproxy.c b/src/dnsproxy.c
index e50f2740..06c53b26 100644
--- a/src/dnsproxy.c
+++ b/src/dnsproxy.c
@@ -2919,26 +2919,33 @@ static struct connman_notifier dnsproxy_notifier = {
static unsigned char opt_edns0_type[2] = { 0x00, 0x29 };
-static int parse_request(unsigned char *buf, int len,
+static int parse_request(unsigned char *buf, size_t len,
char *name, unsigned int size)
{
struct domain_hdr *hdr = (void *) buf;
uint16_t qdcount = ntohs(hdr->qdcount);
+ uint16_t ancount = ntohs(hdr->ancount);
+ uint16_t nscount = ntohs(hdr->nscount);
uint16_t arcount = ntohs(hdr->arcount);
unsigned char *ptr;
- char *last_label = NULL;
unsigned int remain, used = 0;
- if (len < 12)
+ if (len < sizeof(struct domain_hdr) + 5 || hdr->qr ||
+ qdcount != 1 || ancount || nscount) {
+ DBG("Dropped DNS request qr %d with len %zd qdcount %d "
+ "ancount %d nscount %d", hdr->qr, len, qdcount, ancount,
+ nscount);
+
+ return -EINVAL;
+ }
+
+ if (!name || !size)
return -EINVAL;
debug("id 0x%04x qr %d opcode %d qdcount %d arcount %d",
hdr->id, hdr->qr, hdr->opcode,
qdcount, arcount);
- if (hdr->qr != 0 || qdcount != 1)
- return -EINVAL;
-
name[0] = '\0';
ptr = buf + sizeof(struct domain_hdr);
@@ -2948,7 +2955,22 @@ static int parse_request(unsigned char *buf, int len,
uint8_t label_len = *ptr;
if (label_len == 0x00) {
- last_label = (char *) (ptr + 1);
+ uint16_t class;
+
+ if (remain < 5) {
+ DBG("Dropped malformed DNS query");
+ return -EINVAL;
+ }
+
+ class = ptr[3] << 8 | ptr[4];
+ if (class != 1 && class != 255) {
+ DBG("Dropped non-IN DNS class %d", class);
+
+ return -EINVAL;
+ }
+
+ ptr += 5;
+ remain -= 5;
break;
I assume the '5' which appears 4 times is this the length for QTYPE and
QCLASS? Again, I would prefer to add defines for those constants. It
makes simpler to review.
}
@@ -2964,26 +2986,15 @@ static int parse_request(unsigned char *buf, int len,
remain -= label_len + 1;
}
- if (last_label && arcount && remain >= 9 && last_label[4] ==
0 &&
- !memcmp(last_label + 5, opt_edns0_type, 2)) {
+ if (arcount && remain >= 11 && !ptr[0] &&
+ ptr[1] == opt_edns0_type[0] && ptr[2] == opt_edns0_type[1]) {
uint16_t edns0_bufsize;
Is 'remain >= 11' the min length for an EDSN0? The size of
the fixed part of an OPT RR?
- edns0_bufsize = last_label[7] << 8 | last_label[8];
+ edns0_bufsize = ptr[3] << 8 | ptr[4];
- debug("EDNS0 buffer size %u", edns0_bufsize);
-
- /* This is an evil hack until full TCP support has been
- * implemented.
- *
- * Somtimes the EDNS0 request gets send with a too-small
- * buffer size. Since glibc doesn't seem to crash when it
- * gets a response biffer then it requested, just bump
- * the buffer size up to 4KiB.
- */
- if (edns0_bufsize < 0x1000) {
- last_label[7] = 0x10;
- last_label[8] = 0x00;
- }
+ DBG("EDNS0 buffer size %u", edns0_bufsize);
+ } else if (!arcount && remain) {
+ DBG("DNS request with %d garbage bytes", remain);
}
debug("query %s", name);
The rest looks good.
Thanks,
Daniel
ps: you made me start reading those rfc :)