Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

harden DNS resolver (insp20 branch) #2

Closed
wants to merge 4 commits into from

1 participant

William Pitcock
William Pitcock

these commits harden the DNS resolver to fix the CERT bug.

new changes:

  • don't explicitly trust rr.rdlength
  • validate lengths on decompression (using same behaviour as charybdis)
  • check A/AAAA replies too, as they could be exploited using similar technique
added some commits March 20, 2012
William Pitcock dns: iterators which are integer should always be unsigned, else an i…
…nteger underflow is possible.

Signed-off-by: William Pitcock <nenolod@dereferenced.org>
a6a07de
William Pitcock dns: reject messages with lengths larger than DNSHeader with prejudice
This also includes when decompressing name entries.
9aa28f3
William Pitcock dns: more hardening
- don't trust rr.rdlength
- don't accept replies we know are impossible for AAAA/A records
- don't try to process record types we do not know about specifically
  (this behaviour just leads to disaster)
84ab047
William Pitcock dns: cleanup ResultIsReady() prototype eac05f8
William Pitcock kaniini closed this March 21, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 4 unique commits by 1 author.

Mar 20, 2012
William Pitcock dns: iterators which are integer should always be unsigned, else an i…
…nteger underflow is possible.

Signed-off-by: William Pitcock <nenolod@dereferenced.org>
a6a07de
William Pitcock dns: reject messages with lengths larger than DNSHeader with prejudice
This also includes when decompressing name entries.
9aa28f3
William Pitcock dns: more hardening
- don't trust rr.rdlength
- don't accept replies we know are impossible for AAAA/A records
- don't try to process record types we do not know about specifically
  (this behaviour just leads to disaster)
84ab047
William Pitcock dns: cleanup ResultIsReady() prototype eac05f8
This page is out of date. Refresh to see the latest.

Showing 1 changed file with 38 additions and 10 deletions. Show diff stats Hide diff stats

  1. 48  src/dns.cpp
48  src/dns.cpp
@@ -38,6 +38,8 @@ looks like this, walks like this or tastes like this.
38 38
 #include "configreader.h"
39 39
 #include "socket.h"
40 40
 
  41
+#define DN_COMP_BITMASK	0xC000		/* highest 6 bits in a DN label header */
  42
+
41 43
 /** Masks to mask off the responses we get from the DNSRequest methods
42 44
  */
43 45
 enum QueryInfo
@@ -98,7 +100,7 @@ class DNSRequest
98 100
 
99 101
 	DNSRequest(DNS* dns, int id, const std::string &original);
100 102
 	~DNSRequest();
101  
-	DNSInfo ResultIsReady(DNSHeader &h, int length);
  103
+	DNSInfo ResultIsReady(DNSHeader &h, unsigned length);
102 104
 	int SendRequests(const DNSHeader *header, const int length, QueryType qt);
103 105
 };
104 106
 
@@ -161,7 +163,10 @@ int CachedQuery::CalcTTLRemaining()
161 163
 /* Allocate the processing buffer */
162 164
 DNSRequest::DNSRequest(DNS* dns, int rid, const std::string &original) : dnsobj(dns)
163 165
 {
164  
-	res = new unsigned char[512];
  166
+	/* hardening against overflow here:  make our work buffer twice the theoretical
  167
+	 * maximum size so that hostile input doesn't screw us over.
  168
+	 */
  169
+	res = new unsigned char[sizeof(DNSHeader) * 2];
165 170
 	*res = 0;
166 171
 	orig = original;
167 172
 	RequestTimeout* RT = new RequestTimeout(ServerInstance->Config->dns_timeout ? ServerInstance->Config->dns_timeout : 5, this, rid);
@@ -688,11 +693,11 @@ DNSResult DNS::GetResult()
688 693
 }
689 694
 
690 695
 /** A result is ready, process it */
691  
-DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
  696
+DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, unsigned length)
692 697
 {
693  
-	int i = 0;
  698
+	unsigned i = 0, o;
694 699
 	int q = 0;
695  
-	int curanswer, o;
  700
+	int curanswer;
696 701
 	ResourceRecord rr;
697 702
  	unsigned short ptr;
698 703
 
@@ -790,17 +795,31 @@ DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
790 795
 
791 796
 	switch (rr.type)
792 797
 	{
  798
+		/*
  799
+		 * CNAME and PTR are compressed.  We need to decompress them.
  800
+		 */
793 801
 		case DNS_QUERY_CNAME:
794  
-			/* CNAME and PTR have the same processing code */
795 802
 		case DNS_QUERY_PTR:
796 803
 			o = 0;
797 804
 			q = 0;
798 805
 			while (q == 0 && i < length && o + 256 < 1023)
799 806
 			{
  807
+				/* DN label found (byte over 63) */
800 808
 				if (header.payload[i] > 63)
801 809
 				{
802 810
 					memcpy(&ptr,&header.payload[i],2);
803  
-					i = ntohs(ptr) - 0xC000 - 12;
  811
+
  812
+					i = ntohs(ptr);
  813
+
  814
+					/* check that highest two bits are set. if not, we've been had */
  815
+					if (!(i & DN_COMP_BITMASK))
  816
+						return std::make_pair((unsigned char *) NULL, "DN label decompression header is bogus");
  817
+
  818
+					/* mask away the two highest bits. */
  819
+					i &= ~DN_COMP_BITMASK;
  820
+
  821
+					/* and decrease length by 12 bytes. */
  822
+					i =- 12;
804 823
 				}
805 824
 				else
806 825
 				{
@@ -813,7 +832,11 @@ DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
813 832
 						res[o] = 0;
814 833
 						if (o != 0)
815 834
 							res[o++] = '.';
816  
-						memcpy(&res[o],&header.payload[i + 1],header.payload[i]);
  835
+
  836
+						if (o + header.payload[i] > sizeof(DNSHeader))
  837
+							return std::make_pair((unsigned char *) NULL, "DN label decompression is impossible -- malformed/hostile packet?");
  838
+
  839
+						memcpy(&res[o], &header.payload[i + 1], header.payload[i]);
817 840
 						o += header.payload[i];
818 841
 						i += header.payload[i] + 1;
819 842
 					}
@@ -822,16 +845,21 @@ DNSInfo DNSRequest::ResultIsReady(DNSHeader &header, int length)
822 845
 			res[o] = 0;
823 846
 		break;
824 847
 		case DNS_QUERY_AAAA:
  848
+			if (rr.rdlength != sizeof(struct in6_addr))
  849
+				return std::make_pair((unsigned char *) NULL, "rr.rdlength is larger than 16 bytes for an ipv6 entry -- malformed/hostile packet?");
  850
+
825 851
 			memcpy(res,&header.payload[i],rr.rdlength);
826 852
 			res[rr.rdlength] = 0;
827 853
 		break;
828 854
 		case DNS_QUERY_A:
  855
+			if (rr.rdlength != sizeof(struct in_addr))
  856
+				return std::make_pair((unsigned char *) NULL, "rr.rdlength is larger than 4 bytes for an ipv4 entry -- malformed/hostile packet?");
  857
+
829 858
 			memcpy(res,&header.payload[i],rr.rdlength);
830 859
 			res[rr.rdlength] = 0;
831 860
 		break;
832 861
 		default:
833  
-			memcpy(res,&header.payload[i],rr.rdlength);
834  
-			res[rr.rdlength] = 0;
  862
+			return std::make_pair((unsigned char *) NULL, "don't know how to handle undefined type (" + ConvToStr(rr.type) + ") -- rejecting");
835 863
 		break;
836 864
 	}
837 865
 	return std::make_pair(res,"No error");
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.