Navigation Menu

Skip to content

Commit

Permalink
dns: reject messages with lengths larger than DNSHeader with prejudice
Browse files Browse the repository at this point in the history
This also includes when decompressing name entries.
  • Loading branch information
kaniini authored and rburchell committed Mar 21, 2012
1 parent 64b1cf2 commit c245955
Showing 1 changed file with 33 additions and 10 deletions.
43 changes: 33 additions & 10 deletions src/dns.cpp
Expand Up @@ -35,6 +35,8 @@ looks like this, walks like this or tastes like this.
#include "dns.h"
#include "timer.h"

#define DN_COMP_BITMASK 0xC000 /* highest 6 bits in a DN label header */

/** Masks to mask off the responses we get from the DNSRequest methods
*/
enum QueryInfo
Expand Down Expand Up @@ -158,7 +160,10 @@ int CachedQuery::CalcTTLRemaining()
/* Allocate the processing buffer */
DNSRequest::DNSRequest(DNS* dns, int rid, const std::string &original) : dnsobj(dns)
{
res = new unsigned char[512];
/* hardening against overflow here: make our work buffer twice the theoretical
* maximum size so that hostile input doesn't screw us over.
*/
res = new unsigned char[sizeof(DNSHeader) * 2];
*res = 0;
orig = original;
RequestTimeout* RT = new RequestTimeout(ServerInstance->Config->dns_timeout ? ServerInstance->Config->dns_timeout : 5, this, rid);
Expand Down Expand Up @@ -687,9 +692,9 @@ DNSResult DNS::GetResult()
/** A result is ready, process it */
DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
{
unsigned i = 0;
unsigned i = 0, o;
int q = 0;
int curanswer, o;
int curanswer;
ResourceRecord rr;
unsigned short ptr;

Expand All @@ -714,7 +719,7 @@ DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
/* Subtract the length of the header from the length of the packet */
length -= 12;

while ((unsigned int)q < header.qdcount && i < length)
while ((unsigned int)q < header.qdcount && i < (unsigned) length)
{
if (header.payload[i] > 63)
{
Expand All @@ -735,7 +740,7 @@ DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
while ((unsigned)curanswer < header.ancount)
{
q = 0;
while (q == 0 && i < length)
while (q == 0 && i < (unsigned) length)
{
if (header.payload[i] > 63)
{
Expand All @@ -752,7 +757,7 @@ DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
else i += header.payload[i] + 1; /* skip length and label */
}
}
if (length - i < 10)
if ((unsigned) length - i < 10)
return std::make_pair((unsigned char*)NULL,"Incorrectly sized DNS reply");

/* XXX: We actually initialise 'rr' here including its ttl field */
Expand Down Expand Up @@ -787,17 +792,31 @@ DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)

switch (rr.type)
{
/*
* CNAME and PTR are compressed. We need to decompress them.
*/
case DNS_QUERY_CNAME:
/* CNAME and PTR have the same processing code */
case DNS_QUERY_PTR:
o = 0;
q = 0;
while (q == 0 && i < length && o + 256 < 1023)
while (q == 0 && i < (unsigned) length && o + 256 < 1023)
{
/* DN label found (byte over 63) */
if (header.payload[i] > 63)
{
memcpy(&ptr,&header.payload[i],2);
i = ntohs(ptr) - 0xC000 - 12;

i = ntohs(ptr);

/* check that highest two bits are set. if not, we've been had */
if (!(i & DN_COMP_BITMASK))
return std::make_pair((unsigned char *) NULL, "DN label decompression header is bogus");

/* mask away the two highest bits. */
i &= ~DN_COMP_BITMASK;

/* and decrease length by 12 bytes. */
i =- 12;
}
else
{
Expand All @@ -810,7 +829,11 @@ DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
res[o] = 0;
if (o != 0)
res[o++] = '.';
memcpy(&res[o],&header.payload[i + 1],header.payload[i]);

if (o + header.payload[i] > sizeof(DNSHeader))
return std::make_pair((unsigned char *) NULL, "DN label decompression is impossible -- malformed/hostile packet?");

memcpy(&res[o], &header.payload[i + 1], header.payload[i]);
o += header.payload[i];
i += header.payload[i] + 1;
}
Expand Down

0 comments on commit c245955

Please sign in to comment.