Browse code

dnsmasq : Fix CVE-2017-15107

Change-Id: I30c71ab486ff6450d0b7bced4f234268cd73434c
Reviewed-on: http://photon-jenkins.eng.vmware.com:8082/4782
Tested-by: gerrit-photon <photon-checkins@vmware.com>
Reviewed-by: Anish Swaminathan <anishs@vmware.com>

Xiaolin Li authored on 2018/02/14 05:46:35
Showing 2 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,210 @@
0
+From 4fe6744a220eddd3f1749b40cac3dfc510787de6 Mon Sep 17 00:00:00 2001
1
+From: Simon Kelley <simon@thekelleys.org.uk>
2
+Date: Fri, 19 Jan 2018 12:26:08 +0000
3
+Subject: [PATCH] DNSSEC fix for wildcard NSEC records. CVE-2017-15107
4
+ applies.
5
+
6
+It's OK for NSEC records to be expanded from wildcards,
7
+but in that case, the proof of non-existence is only valid
8
+starting at the wildcard name, *.<domain> NOT the name expanded
9
+from the wildcard. Without this check it's possible for an
10
+attacker to craft an NSEC which wrongly proves non-existence
11
+in a domain which includes a wildcard for NSEC.
12
+---
13
+ CHANGELOG    |   12 +++++-
14
+ src/dnssec.c |  117 +++++++++++++++++++++++++++++++++++++++++++++++++++-------
15
+ 2 files changed, 114 insertions(+), 15 deletions(-)
16
+
17
+ version 2.78
18
+         Fix logic of appending ".<layer>" to PXE basename. Thanks to Chris
19
+diff --git a/src/dnssec.c b/src/dnssec.c
20
+index eb6c11c..a54a0b4 100644
21
+--- a/src/dnssec.c
22
+@@ -103,15 +103,17 @@ static void from_wire(char *name)
23
+ static int count_labels(char *name)
24
+ {
25
+   int i;
26
+-
27
++  char *p;
28
++  
29
+   if (*name == 0)
30
+     return 0;
31
+ 
32
+-  for (i = 0; *name; name++)
33
+-    if (*name == '.')
34
++  for (p = name, i = 0; *p; p++)
35
++    if (*p == '.')
36
+       i++;
37
+ 
38
+-  return i+1;
39
++  /* Don't count empty first label. */
40
++  return *name == '.' ? i : i+1;
41
+ }
42
+ 
43
+ /* Implement RFC1982 wrapped compare for 32-bit numbers */
44
+@@ -1094,8 +1096,8 @@ static int hostname_cmp(const char *a, const char *b)
45
+     }
46
+ }
47
+ 
48
+-static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsigned char **nsecs, int nsec_count,
49
+-				    char *workspace1, char *workspace2, char *name, int type, int *nons)
50
++static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsigned char **nsecs, unsigned char **labels, int nsec_count,
51
++				    char *workspace1_in, char *workspace2, char *name, int type, int *nons)
52
+ {
53
+   int i, rc, rdlen;
54
+   unsigned char *p, *psave;
55
+@@ -1108,6 +1110,9 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
56
+   /* Find NSEC record that proves name doesn't exist */
57
+   for (i = 0; i < nsec_count; i++)
58
+     {
59
++      char *workspace1 = workspace1_in;
60
++      int sig_labels, name_labels;
61
++
62
+       p = nsecs[i];
63
+       if (!extract_name(header, plen, &p, workspace1, 1, 10))
64
+ 	return 0;
65
+@@ -1116,7 +1121,27 @@ static int prove_non_existence_nsec(struct dns_header *header, size_t plen, unsi
66
+       psave = p;
67
+       if (!extract_name(header, plen, &p, workspace2, 1, 10))
68
+ 	return 0;
69
+-      
70
++
71
++      /* If NSEC comes from wildcard expansion, use original wildcard
72
++	 as name for computation. */
73
++      sig_labels = *labels[i];
74
++      name_labels = count_labels(workspace1);
75
++
76
++      if (sig_labels < name_labels)
77
++	{
78
++	  int k;
79
++	  for (k = name_labels - sig_labels; k != 0; k--)
80
++	    {
81
++	      while (*workspace1 != '.' && *workspace1 != 0)
82
++		workspace1++;
83
++	      if (k != 1 && *workspace1 == '.')
84
++		workspace1++;
85
++	    }
86
++	  
87
++	  workspace1--;
88
++	  *workspace1 = '*';
89
++	}
90
++	  
91
+       rc = hostname_cmp(workspace1, name);
92
+       
93
+       if (rc == 0)
94
+@@ -1514,24 +1539,26 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
95
+ 
96
+ static int prove_non_existence(struct dns_header *header, size_t plen, char *keyname, char *name, int qtype, int qclass, char *wildname, int *nons)
97
+ {
98
+-  static unsigned char **nsecset = NULL;
99
+-  static int nsecset_sz = 0;
100
++  static unsigned char **nsecset = NULL, **rrsig_labels = NULL;
101
++  static int nsecset_sz = 0, rrsig_labels_sz = 0;
102
+   
103
+   int type_found = 0;
104
+-  unsigned char *p = skip_questions(header, plen);
105
++  unsigned char *auth_start, *p = skip_questions(header, plen);
106
+   int type, class, rdlen, i, nsecs_found;
107
+   
108
+   /* Move to NS section */
109
+   if (!p || !(p = skip_section(p, ntohs(header->ancount), header, plen)))
110
+     return 0;
111
++
112
++  auth_start = p;
113
+   
114
+   for (nsecs_found = 0, i = ntohs(header->nscount); i != 0; i--)
115
+     {
116
+       unsigned char *pstart = p;
117
+       
118
+-      if (!(p = skip_name(p, header, plen, 10)))
119
++      if (!extract_name(header, plen, &p, daemon->workspacename, 1, 10))
120
+ 	return 0;
121
+-      
122
++	  
123
+       GETSHORT(type, p); 
124
+       GETSHORT(class, p);
125
+       p += 4; /* TTL */
126
+@@ -1548,7 +1575,69 @@ static int prove_non_existence(struct dns_header *header, size_t plen, char *key
127
+ 	  if (!expand_workspace(&nsecset, &nsecset_sz, nsecs_found))
128
+ 	    return 0; 
129
+ 	  
130
+-	  nsecset[nsecs_found++] = pstart;
131
++	  if (type == T_NSEC)
132
++	    {
133
++	      /* If we're looking for NSECs, find the corresponding SIGs, to 
134
++		 extract the labels value, which we need in case the NSECs
135
++		 are the result of wildcard expansion.
136
++		 Note that the NSEC may not have been validated yet
137
++		 so if there are multiple SIGs, make sure the label value
138
++		 is the same in all, to avoid be duped by a rogue one.
139
++		 If there are no SIGs, that's an error */
140
++	      unsigned char *p1 = auth_start;
141
++	      int res, j, rdlen1, type1, class1;
142
++	      
143
++	      if (!expand_workspace(&rrsig_labels, &rrsig_labels_sz, nsecs_found))
144
++		return 0;
145
++	      
146
++	      rrsig_labels[nsecs_found] = NULL;
147
++	      
148
++	      for (j = ntohs(header->nscount); j != 0; j--)
149
++		{
150
++		  if (!(res = extract_name(header, plen, &p1, daemon->workspacename, 0, 10)))
151
++		    return 0;
152
++
153
++		   GETSHORT(type1, p1); 
154
++		   GETSHORT(class1, p1);
155
++		   p1 += 4; /* TTL */
156
++		   GETSHORT(rdlen1, p1);
157
++
158
++		   if (!CHECK_LEN(header, p1, plen, rdlen1))
159
++		     return 0;
160
++		   
161
++		   if (res == 1 && class1 == qclass && type1 == T_RRSIG)
162
++		     {
163
++		       int type_covered;
164
++		       unsigned char *psav = p1;
165
++		       
166
++		       if (rdlen < 18)
167
++			 return 0; /* bad packet */
168
++
169
++		       GETSHORT(type_covered, p1);
170
++
171
++		       if (type_covered == T_NSEC)
172
++			 {
173
++			   p1++; /* algo */
174
++			   
175
++			   /* labels field must be the same in every SIG we find. */
176
++			   if (!rrsig_labels[nsecs_found])
177
++			     rrsig_labels[nsecs_found] = p1;
178
++			   else if (*rrsig_labels[nsecs_found] != *p1) /* algo */
179
++			     return 0;
180
++			   }
181
++		       p1 = psav;
182
++		     }
183
++		   
184
++		   if (!ADD_RDLEN(header, p1, plen, rdlen1))
185
++		     return 0;
186
++		}
187
++
188
++	      /* Must have found at least one sig. */
189
++	      if (!rrsig_labels[nsecs_found])
190
++		return 0;
191
++	    }
192
++
193
++	  nsecset[nsecs_found++] = pstart;   
194
+ 	}
195
+       
196
+       if (!ADD_RDLEN(header, p, plen, rdlen))
197
+@@ -1556,7 +1645,7 @@ static int prove_non_existence(struct dns_header *header, size_t plen, char *key
198
+     }
199
+   
200
+   if (type_found == T_NSEC)
201
+-    return prove_non_existence_nsec(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, nons);
202
++    return prove_non_existence_nsec(header, plen, nsecset, rrsig_labels, nsecs_found, daemon->workspacename, keyname, name, qtype, nons);
203
+   else if (type_found == T_NSEC3)
204
+     return prove_non_existence_nsec3(header, plen, nsecset, nsecs_found, daemon->workspacename, keyname, name, qtype, wildname, nons);
205
+   else
206
+-- 
207
+1.7.10.4
208
+
... ...
@@ -1,16 +1,17 @@
1
-Summary:	DNS proxy with integrated DHCP server
2
-Name:		dnsmasq
3
-Version:	2.76
4
-Release:	4%{?dist}
5
-License:	GPLv2 or GPLv3
6
-Group:		System Environment/Daemons
7
-URL:		http://www.thekelleys.org.uk/dnsmasq/
8
-Source:	        %{name}-%{version}.tar.xz
9
-%define sha1 dnsmasq=db42d7297dc0a05d51588baa2f298ebb42fcef99
10
-Vendor:		VMware, Inc.
11
-Distribution:	Photon
12
-Patch0:		dnsmasq.patch
13
-Patch1:		CVE-2017-13704.patch
1
+Summary:        DNS proxy with integrated DHCP server
2
+Name:           dnsmasq
3
+Version:        2.76
4
+Release:        5%{?dist}
5
+License:        GPLv2 or GPLv3
6
+Group:          System Environment/Daemons
7
+URL:            http://www.thekelleys.org.uk/dnsmasq/
8
+Source:         %{name}-%{version}.tar.xz
9
+%define sha1    dnsmasq=db42d7297dc0a05d51588baa2f298ebb42fcef99
10
+Vendor:         VMware, Inc.
11
+Distribution:   Photon
12
+Patch0:         dnsmasq.patch
13
+Patch1:         CVE-2017-13704.patch
14
+Patch2:         CVE-2017-15107.patch
14 15
 
15 16
 %description
16 17
 Dnsmasq a lightweight, caching DNS proxy with integrated DHCP server.
... ...
@@ -19,6 +20,7 @@ Dnsmasq a lightweight, caching DNS proxy with integrated DHCP server.
19 19
 %setup -q
20 20
 %patch0 -p1
21 21
 %patch1 -p1
22
+%patch2 -p1
22 23
 
23 24
 %build
24 25
 make %{?_smp_mflags}
... ...
@@ -70,6 +72,8 @@ rm -rf %{buildroot}
70 70
 %config  /usr/share/dnsmasq/trust-anchors.conf
71 71
 
72 72
 %changelog
73
+*   Tue Feb 13 2018 Xiaolin Li <xiaolinl@vmware.com> 2.76-5
74
+-   Fix CVE-2017-15107
73 75
 *   Mon Nov 13 2017 Vinay Kulkarni <kulkarniv@vmware.com> 2.76-4
74 76
 -   Always restart dnsmasq service on exit
75 77
 *   Wed Oct 11 2017 Alexey Makhalov <amakhalov@vmware.com> 2.76-3