1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
7  *
8  * See the COPYRIGHT file distributed with this work for additional
9  * information regarding copyright ownership.
10  */
11 
12 /*! \file */
13 
14 #include <stdbool.h>
15 
16 #include <isc/log.h>
17 #include <isc/result.h>
18 #include <isc/string.h>
19 #include <isc/util.h>
20 
21 #include <dns/db.h>
22 #include <dns/nsec.h>
23 #include <dns/rdata.h>
24 #include <dns/rdatalist.h>
25 #include <dns/rdataset.h>
26 #include <dns/rdatasetiter.h>
27 #include <dns/rdatastruct.h>
28 
29 #include <dst/dst.h>
30 
31 #define RETERR(x)                            \
32 	do {                                 \
33 		result = (x);                \
34 		if (result != ISC_R_SUCCESS) \
35 			goto failure;        \
36 	} while (0)
37 
38 void
dns_nsec_setbit(unsigned char * array,unsigned int type,unsigned int bit)39 dns_nsec_setbit(unsigned char *array, unsigned int type, unsigned int bit) {
40 	unsigned int shift, mask;
41 
42 	shift = 7 - (type % 8);
43 	mask = 1 << shift;
44 
45 	if (bit != 0) {
46 		array[type / 8] |= mask;
47 	} else {
48 		array[type / 8] &= (~mask & 0xFF);
49 	}
50 }
51 
52 bool
dns_nsec_isset(const unsigned char * array,unsigned int type)53 dns_nsec_isset(const unsigned char *array, unsigned int type) {
54 	unsigned int byte, shift, mask;
55 
56 	byte = array[type / 8];
57 	shift = 7 - (type % 8);
58 	mask = 1 << shift;
59 
60 	return ((byte & mask) != 0);
61 }
62 
63 unsigned int
dns_nsec_compressbitmap(unsigned char * map,const unsigned char * raw,unsigned int max_type)64 dns_nsec_compressbitmap(unsigned char *map, const unsigned char *raw,
65 			unsigned int max_type) {
66 	unsigned char *start = map;
67 	unsigned int window;
68 	int octet;
69 
70 	if (raw == NULL) {
71 		return (0);
72 	}
73 
74 	for (window = 0; window < 256; window++) {
75 		if (window * 256 > max_type) {
76 			break;
77 		}
78 		for (octet = 31; octet >= 0; octet--) {
79 			if (*(raw + octet) != 0) {
80 				break;
81 			}
82 		}
83 		if (octet < 0) {
84 			raw += 32;
85 			continue;
86 		}
87 		*map++ = window;
88 		*map++ = octet + 1;
89 		/*
90 		 * Note: potential overlapping move.
91 		 */
92 		memmove(map, raw, octet + 1);
93 		map += octet + 1;
94 		raw += 32;
95 	}
96 	return ((unsigned int)(map - start));
97 }
98 
99 isc_result_t
dns_nsec_buildrdata(dns_db_t * db,dns_dbversion_t * version,dns_dbnode_t * node,const dns_name_t * target,unsigned char * buffer,dns_rdata_t * rdata)100 dns_nsec_buildrdata(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node,
101 		    const dns_name_t *target, unsigned char *buffer,
102 		    dns_rdata_t *rdata) {
103 	isc_result_t result;
104 	dns_rdataset_t rdataset;
105 	isc_region_t r;
106 	unsigned int i;
107 
108 	unsigned char *nsec_bits, *bm;
109 	unsigned int max_type;
110 	dns_rdatasetiter_t *rdsiter;
111 
112 	REQUIRE(target != NULL);
113 
114 	memset(buffer, 0, DNS_NSEC_BUFFERSIZE);
115 	dns_name_toregion(target, &r);
116 	memmove(buffer, r.base, r.length);
117 	r.base = buffer;
118 	/*
119 	 * Use the end of the space for a raw bitmap leaving enough
120 	 * space for the window identifiers and length octets.
121 	 */
122 	bm = r.base + r.length + 512;
123 	nsec_bits = r.base + r.length;
124 	dns_nsec_setbit(bm, dns_rdatatype_rrsig, 1);
125 	dns_nsec_setbit(bm, dns_rdatatype_nsec, 1);
126 	max_type = dns_rdatatype_nsec;
127 	dns_rdataset_init(&rdataset);
128 	rdsiter = NULL;
129 	result = dns_db_allrdatasets(db, node, version, 0, &rdsiter);
130 	if (result != ISC_R_SUCCESS) {
131 		return (result);
132 	}
133 	for (result = dns_rdatasetiter_first(rdsiter); result == ISC_R_SUCCESS;
134 	     result = dns_rdatasetiter_next(rdsiter))
135 	{
136 		dns_rdatasetiter_current(rdsiter, &rdataset);
137 		if (rdataset.type != dns_rdatatype_nsec &&
138 		    rdataset.type != dns_rdatatype_nsec3 &&
139 		    rdataset.type != dns_rdatatype_rrsig)
140 		{
141 			if (rdataset.type > max_type) {
142 				max_type = rdataset.type;
143 			}
144 			dns_nsec_setbit(bm, rdataset.type, 1);
145 		}
146 		dns_rdataset_disassociate(&rdataset);
147 	}
148 
149 	/*
150 	 * At zone cuts, deny the existence of glue in the parent zone.
151 	 */
152 	if (dns_nsec_isset(bm, dns_rdatatype_ns) &&
153 	    !dns_nsec_isset(bm, dns_rdatatype_soa))
154 	{
155 		for (i = 0; i <= max_type; i++) {
156 			if (dns_nsec_isset(bm, i) &&
157 			    !dns_rdatatype_iszonecutauth((dns_rdatatype_t)i)) {
158 				dns_nsec_setbit(bm, i, 0);
159 			}
160 		}
161 	}
162 
163 	dns_rdatasetiter_destroy(&rdsiter);
164 	if (result != ISC_R_NOMORE) {
165 		return (result);
166 	}
167 
168 	nsec_bits += dns_nsec_compressbitmap(nsec_bits, bm, max_type);
169 
170 	r.length = (unsigned int)(nsec_bits - r.base);
171 	INSIST(r.length <= DNS_NSEC_BUFFERSIZE);
172 	dns_rdata_fromregion(rdata, dns_db_class(db), dns_rdatatype_nsec, &r);
173 
174 	return (ISC_R_SUCCESS);
175 }
176 
177 isc_result_t
dns_nsec_build(dns_db_t * db,dns_dbversion_t * version,dns_dbnode_t * node,const dns_name_t * target,dns_ttl_t ttl)178 dns_nsec_build(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node,
179 	       const dns_name_t *target, dns_ttl_t ttl) {
180 	isc_result_t result;
181 	dns_rdata_t rdata = DNS_RDATA_INIT;
182 	unsigned char data[DNS_NSEC_BUFFERSIZE];
183 	dns_rdatalist_t rdatalist;
184 	dns_rdataset_t rdataset;
185 
186 	dns_rdataset_init(&rdataset);
187 	dns_rdata_init(&rdata);
188 
189 	RETERR(dns_nsec_buildrdata(db, version, node, target, data, &rdata));
190 
191 	dns_rdatalist_init(&rdatalist);
192 	rdatalist.rdclass = dns_db_class(db);
193 	rdatalist.type = dns_rdatatype_nsec;
194 	rdatalist.ttl = ttl;
195 	ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
196 	RETERR(dns_rdatalist_tordataset(&rdatalist, &rdataset));
197 	result = dns_db_addrdataset(db, node, version, 0, &rdataset, 0, NULL);
198 	if (result == DNS_R_UNCHANGED) {
199 		result = ISC_R_SUCCESS;
200 	}
201 
202 failure:
203 	if (dns_rdataset_isassociated(&rdataset)) {
204 		dns_rdataset_disassociate(&rdataset);
205 	}
206 	return (result);
207 }
208 
209 bool
dns_nsec_typepresent(dns_rdata_t * nsec,dns_rdatatype_t type)210 dns_nsec_typepresent(dns_rdata_t *nsec, dns_rdatatype_t type) {
211 	dns_rdata_nsec_t nsecstruct;
212 	isc_result_t result;
213 	bool present;
214 	unsigned int i, len, window;
215 
216 	REQUIRE(nsec != NULL);
217 	REQUIRE(nsec->type == dns_rdatatype_nsec);
218 
219 	/* This should never fail */
220 	result = dns_rdata_tostruct(nsec, &nsecstruct, NULL);
221 	INSIST(result == ISC_R_SUCCESS);
222 
223 	present = false;
224 	for (i = 0; i < nsecstruct.len; i += len) {
225 		INSIST(i + 2 <= nsecstruct.len);
226 		window = nsecstruct.typebits[i];
227 		len = nsecstruct.typebits[i + 1];
228 		INSIST(len > 0 && len <= 32);
229 		i += 2;
230 		INSIST(i + len <= nsecstruct.len);
231 		if (window * 256 > type) {
232 			break;
233 		}
234 		if ((window + 1) * 256 <= type) {
235 			continue;
236 		}
237 		if (type < (window * 256) + len * 8) {
238 			present = dns_nsec_isset(&nsecstruct.typebits[i],
239 						 type % 256);
240 		}
241 		break;
242 	}
243 	dns_rdata_freestruct(&nsecstruct);
244 	return (present);
245 }
246 
247 isc_result_t
dns_nsec_nseconly(dns_db_t * db,dns_dbversion_t * version,bool * answer)248 dns_nsec_nseconly(dns_db_t *db, dns_dbversion_t *version, bool *answer) {
249 	dns_dbnode_t *node = NULL;
250 	dns_rdataset_t rdataset;
251 	dns_rdata_dnskey_t dnskey;
252 	isc_result_t result;
253 
254 	REQUIRE(answer != NULL);
255 
256 	dns_rdataset_init(&rdataset);
257 
258 	result = dns_db_getoriginnode(db, &node);
259 	if (result != ISC_R_SUCCESS) {
260 		return (result);
261 	}
262 
263 	result = dns_db_findrdataset(db, node, version, dns_rdatatype_dnskey, 0,
264 				     0, &rdataset, NULL);
265 	dns_db_detachnode(db, &node);
266 
267 	if (result == ISC_R_NOTFOUND) {
268 		*answer = false;
269 	}
270 	if (result != ISC_R_SUCCESS) {
271 		return (result);
272 	}
273 	for (result = dns_rdataset_first(&rdataset); result == ISC_R_SUCCESS;
274 	     result = dns_rdataset_next(&rdataset))
275 	{
276 		dns_rdata_t rdata = DNS_RDATA_INIT;
277 
278 		dns_rdataset_current(&rdataset, &rdata);
279 		result = dns_rdata_tostruct(&rdata, &dnskey, NULL);
280 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
281 
282 		if (dnskey.algorithm == DST_ALG_RSAMD5 ||
283 		    dnskey.algorithm == DST_ALG_RSASHA1) {
284 			break;
285 		}
286 	}
287 	dns_rdataset_disassociate(&rdataset);
288 	if (result == ISC_R_SUCCESS) {
289 		*answer = true;
290 	}
291 	if (result == ISC_R_NOMORE) {
292 		*answer = false;
293 		result = ISC_R_SUCCESS;
294 	}
295 	return (result);
296 }
297 
298 /*%
299  * Return ISC_R_SUCCESS if we can determine that the name doesn't exist
300  * or we can determine whether there is data or not at the name.
301  * If the name does not exist return the wildcard name.
302  *
303  * Return ISC_R_IGNORE when the NSEC is not the appropriate one.
304  */
305 isc_result_t
dns_nsec_noexistnodata(dns_rdatatype_t type,const dns_name_t * name,const dns_name_t * nsecname,dns_rdataset_t * nsecset,bool * exists,bool * data,dns_name_t * wild,dns_nseclog_t logit,void * arg)306 dns_nsec_noexistnodata(dns_rdatatype_t type, const dns_name_t *name,
307 		       const dns_name_t *nsecname, dns_rdataset_t *nsecset,
308 		       bool *exists, bool *data, dns_name_t *wild,
309 		       dns_nseclog_t logit, void *arg) {
310 	int order;
311 	dns_rdata_t rdata = DNS_RDATA_INIT;
312 	isc_result_t result;
313 	dns_namereln_t relation;
314 	unsigned int olabels, nlabels, labels;
315 	dns_rdata_nsec_t nsec;
316 	bool atparent;
317 	bool ns;
318 	bool soa;
319 
320 	REQUIRE(exists != NULL);
321 	REQUIRE(data != NULL);
322 	REQUIRE(nsecset != NULL && nsecset->type == dns_rdatatype_nsec);
323 
324 	result = dns_rdataset_first(nsecset);
325 	if (result != ISC_R_SUCCESS) {
326 		(*logit)(arg, ISC_LOG_DEBUG(3), "failure processing NSEC set");
327 		return (result);
328 	}
329 	dns_rdataset_current(nsecset, &rdata);
330 
331 	(*logit)(arg, ISC_LOG_DEBUG(3), "looking for relevant NSEC");
332 	relation = dns_name_fullcompare(name, nsecname, &order, &olabels);
333 
334 	if (order < 0) {
335 		/*
336 		 * The name is not within the NSEC range.
337 		 */
338 		(*logit)(arg, ISC_LOG_DEBUG(3),
339 			 "NSEC does not cover name, before NSEC");
340 		return (ISC_R_IGNORE);
341 	}
342 
343 	if (order == 0) {
344 		/*
345 		 * The names are the same.   If we are validating "."
346 		 * then atparent should not be set as there is no parent.
347 		 */
348 		atparent = (olabels != 1) && dns_rdatatype_atparent(type);
349 		ns = dns_nsec_typepresent(&rdata, dns_rdatatype_ns);
350 		soa = dns_nsec_typepresent(&rdata, dns_rdatatype_soa);
351 		if (ns && !soa) {
352 			if (!atparent) {
353 				/*
354 				 * This NSEC record is from somewhere higher in
355 				 * the DNS, and at the parent of a delegation.
356 				 * It can not be legitimately used here.
357 				 */
358 				(*logit)(arg, ISC_LOG_DEBUG(3),
359 					 "ignoring parent nsec");
360 				return (ISC_R_IGNORE);
361 			}
362 		} else if (atparent && ns && soa) {
363 			/*
364 			 * This NSEC record is from the child.
365 			 * It can not be legitimately used here.
366 			 */
367 			(*logit)(arg, ISC_LOG_DEBUG(3), "ignoring child nsec");
368 			return (ISC_R_IGNORE);
369 		}
370 		if (type == dns_rdatatype_cname || type == dns_rdatatype_nxt ||
371 		    type == dns_rdatatype_nsec || type == dns_rdatatype_key ||
372 		    !dns_nsec_typepresent(&rdata, dns_rdatatype_cname))
373 		{
374 			*exists = true;
375 			*data = dns_nsec_typepresent(&rdata, type);
376 			(*logit)(arg, ISC_LOG_DEBUG(3),
377 				 "nsec proves name exists (owner) data=%d",
378 				 *data);
379 			return (ISC_R_SUCCESS);
380 		}
381 		(*logit)(arg, ISC_LOG_DEBUG(3), "NSEC proves CNAME exists");
382 		return (ISC_R_IGNORE);
383 	}
384 
385 	if (relation == dns_namereln_subdomain &&
386 	    dns_nsec_typepresent(&rdata, dns_rdatatype_ns) &&
387 	    !dns_nsec_typepresent(&rdata, dns_rdatatype_soa))
388 	{
389 		/*
390 		 * This NSEC record is from somewhere higher in
391 		 * the DNS, and at the parent of a delegation or
392 		 * at a DNAME.
393 		 * It can not be legitimately used here.
394 		 */
395 		(*logit)(arg, ISC_LOG_DEBUG(3), "ignoring parent nsec");
396 		return (ISC_R_IGNORE);
397 	}
398 
399 	if (relation == dns_namereln_subdomain &&
400 	    dns_nsec_typepresent(&rdata, dns_rdatatype_dname))
401 	{
402 		(*logit)(arg, ISC_LOG_DEBUG(3), "nsec proves covered by dname");
403 		*exists = false;
404 		return (DNS_R_DNAME);
405 	}
406 
407 	result = dns_rdata_tostruct(&rdata, &nsec, NULL);
408 	if (result != ISC_R_SUCCESS) {
409 		return (result);
410 	}
411 	relation = dns_name_fullcompare(&nsec.next, name, &order, &nlabels);
412 	if (order == 0) {
413 		dns_rdata_freestruct(&nsec);
414 		(*logit)(arg, ISC_LOG_DEBUG(3),
415 			 "ignoring nsec matches next name");
416 		return (ISC_R_IGNORE);
417 	}
418 
419 	if (order < 0 && !dns_name_issubdomain(nsecname, &nsec.next)) {
420 		/*
421 		 * The name is not within the NSEC range.
422 		 */
423 		dns_rdata_freestruct(&nsec);
424 		(*logit)(arg, ISC_LOG_DEBUG(3),
425 			 "ignoring nsec because name is past end of range");
426 		return (ISC_R_IGNORE);
427 	}
428 
429 	if (order > 0 && relation == dns_namereln_subdomain) {
430 		(*logit)(arg, ISC_LOG_DEBUG(3),
431 			 "nsec proves name exist (empty)");
432 		dns_rdata_freestruct(&nsec);
433 		*exists = true;
434 		*data = false;
435 		return (ISC_R_SUCCESS);
436 	}
437 	if (wild != NULL) {
438 		dns_name_t common;
439 		dns_name_init(&common, NULL);
440 		if (olabels > nlabels) {
441 			labels = dns_name_countlabels(nsecname);
442 			dns_name_getlabelsequence(nsecname, labels - olabels,
443 						  olabels, &common);
444 		} else {
445 			labels = dns_name_countlabels(&nsec.next);
446 			dns_name_getlabelsequence(&nsec.next, labels - nlabels,
447 						  nlabels, &common);
448 		}
449 		result = dns_name_concatenate(dns_wildcardname, &common, wild,
450 					      NULL);
451 		if (result != ISC_R_SUCCESS) {
452 			dns_rdata_freestruct(&nsec);
453 			(*logit)(arg, ISC_LOG_DEBUG(3),
454 				 "failure generating wildcard name");
455 			return (result);
456 		}
457 	}
458 	dns_rdata_freestruct(&nsec);
459 	(*logit)(arg, ISC_LOG_DEBUG(3), "nsec range ok");
460 	*exists = false;
461 	return (ISC_R_SUCCESS);
462 }
463