1 /*------------------------------------------------------------------------------
2 *
3 * Copyright (c) 2011-2021, EURid vzw. All rights reserved.
4 * The YADIFA TM software product is provided under the BSD 3-clause license:
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of EURid nor the names of its contributors may be
16 * used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 *------------------------------------------------------------------------------
32 *
33 */
34
35 /** @defgroup nsec3 NSEC3 functions
36 * @ingroup dnsdbdnssec
37 * @brief
38 *
39 *
40 *
41 * @{
42 */
43 /*------------------------------------------------------------------------------
44 *
45 * USE INCLUDES */
46 #include "dnsdb/dnsdb-config.h"
47 #include <stdio.h>
48 #include <stdlib.h>
49
50 /*
51 * RFC 5155
52 *
53 * Server Response to a Run-Time Collision
54 *
55 * If the hash of a non-existing QNAME collides with the owner name of
56 * an existing NSEC3 RR, then the server will be unable to return a
57 * response that proves that QNAME does not exist. In this case, the
58 * server MUST return a response with an RCODE of 2 (server failure).
59 *
60 * Note that with the hash algorithm specified in this document, SHA-1,
61 * such collisions are highly unlikely.
62 *
63 */
64
65 #include "dnsdb/zdb_types.h"
66
67 #if !ZDB_HAS_NSEC3_SUPPORT
68 #error nsec3.c should not be compiled when ZDB_HAS_NSEC3_SUPPORT == 0
69 #endif
70
71 #include <dnscore/dnsname.h>
72 #include <dnscore/base32hex.h>
73 #include <dnscore/rfc.h>
74 #include <dnscore/ptr_vector.h>
75 #include <dnscore/logger.h>
76 #include <dnscore/dnskey-signature.h>
77
78 #include "dnsdb/zdb_zone.h"
79 #include "dnsdb/zdb_zone_label_iterator.h"
80 #include "dnsdb/zdb_record.h"
81 #include "dnsdb/nsec3.h"
82 #include "dnsdb/nsec_common.h"
83 #include "dnsdb/nsec3_owner.h"
84 #include "dnsdb/rrsig.h"
85 #include "dnsdb/dynupdate-diff.h"
86 #include "dnsdb/dynupdate-message.h"
87 #include "dnsdb/zdb-zone-path-provider.h"
88
89 #define NSEC3_ZONE_LABEL_UPDATE_CHAIN_LINKS_DEBUG 0
90
91 #if NSEC3_UPDATE_ZONE_DEBUG
92 #pragma message("NSEC3_UPDATE_ZONE_DEBUG enabled, disable this for release builds")
93 #endif
94
95 #define MODULE_MSG_HANDLE g_dnssec_logger
96 extern logger_handle *g_dnssec_logger;
97
98 #define N3IRRVDT_TAG 0x544456525249334e
99 #define RRVDATA_TAG 0x41544144565252
100
101 /**
102 * used by nsec3_label_link
103 *
104 * It will find if the label has got a matching NSEC3 record (by digest)
105 * If so, it will link to it.
106 */
107
108 static nsec3_zone_item *
nsec3_label_link_seeknode(nsec3_zone * n3,const u8 * fqdn,s32 fqdn_len,u8 * digest)109 nsec3_label_link_seeknode(nsec3_zone* n3, const u8 *fqdn, s32 fqdn_len, u8 *digest)
110 {
111 nsec3_compute_digest_from_fqdn_with_len(n3, fqdn, fqdn_len, digest, FALSE);
112
113 #if NSEC3_UPDATE_ZONE_DEBUG
114 log_debug("nsec3: seeking node for %{dnsname} with %{digest32h}", fqdn, digest);
115 #endif
116
117 nsec3_zone_item *self = nsec3_find(&n3->items, digest);
118
119 return self;
120 }
121
122 /**
123 * used by nsec3_label_link
124 *
125 * It will find if the *.label has got a matching NSEC3 record (by digest)
126 * If so, it will link to it.
127 */
128
129 static nsec3_zone_item *
nsec3_label_link_seekstar(nsec3_zone * n3,const u8 * fqdn,s32 fqdn_len,u8 * digest)130 nsec3_label_link_seekstar(nsec3_zone* n3, const u8 *fqdn, s32 fqdn_len, u8 *digest)
131 {
132 nsec3_compute_digest_from_fqdn_with_len(n3, fqdn, fqdn_len, digest, TRUE);
133
134 #if NSEC3_UPDATE_ZONE_DEBUG
135 log_debug("nsec3: seeking star for %{dnsname} with %{digest32h}", fqdn, digest);
136 #endif
137
138 nsec3_zone_item* star = nsec3_find_interval_start(&n3->items, digest);
139
140 return star;
141 }
142
143 /*
144 * This destroy all the NSEC3 structures from the zone, starting from the NSEC3PARAM.
145 * The zdb_rr_label are also affected by the call.
146 */
147
148 void
nsec3_destroy_zone(zdb_zone * zone)149 nsec3_destroy_zone(zdb_zone *zone)
150 {
151 // Note that from the 'transaction' update, the dnssec zone collections have to be read without checking for the NSEC3 flag
152
153 while(zone->nsec.nsec3 != NULL)
154 {
155 #if DEBUG
156 nsec3_zone *n3 = zone->nsec.nsec3;
157 #endif
158 nsec3_zone_destroy(zone, zone->nsec.nsec3);
159 #if DEBUG
160 yassert(n3 != zone->nsec.nsec3);
161 #endif
162 }
163 }
164
165 /******************************************************************************
166 *
167 * NSEC3 - queries
168 *
169 *****************************************************************************/
170
171 /**
172 * @brief Finds the provable resource record label matching a path of labels starting from another rr label
173 *
174 * Finds the resource record label matching a path of labels starting from another rr label
175 * Typically the starting label is a zone cut.
176 * The starting point MUST be provable (ie: the apex in NSEC and in NSEC3 zones)
177 *
178 * @param[in] apex the starting label
179 * @param[in] path a stack of labels
180 * @param[in] path_index the index of the top of the stack
181 *
182 * @return the matching label or NULL if it has not been found
183 */
184
185 /* NSEC3: Zone possible */
186 static int
nsec3_get_closest_provable_encloser_match(const void * label,const dictionary_node * node)187 nsec3_get_closest_provable_encloser_match(const void *label, const dictionary_node *node)
188 {
189 zdb_rr_label* rr_label = (zdb_rr_label*) node;
190 return dnslabel_equals(rr_label->name, label);
191 }
192
193 /**
194 *
195 * Finds what is the closest provable encloser for a label in a zone
196 *
197 * @param apex
198 * @param sections
199 * @param sections_topp
200 * @return
201 */
202
203 const zdb_rr_label*
nsec3_get_closest_provable_encloser_optin(const zdb_rr_label * apex,const_dnslabel_vector_reference sections,s32 * sections_topp)204 nsec3_get_closest_provable_encloser_optin(const zdb_rr_label *apex, const_dnslabel_vector_reference sections, s32 *sections_topp)
205 {
206 yassert((apex != NULL) && (sections != NULL) && (sections_topp != NULL));
207
208 s32 index = *sections_topp;
209 const zdb_rr_label* rr_label = apex; /* the zone cut */
210
211 const zdb_rr_label* provable = apex;
212
213 /*
214 * the apex is already known, so we don't loop for it
215 */
216
217 index--;
218
219 /* look into the sub level*/
220
221 while(index >= 0)
222 {
223 const u8* label = sections[index];
224 hashcode hash = hash_dnslabel(label);
225
226 rr_label = (zdb_rr_label*) dictionary_find(&rr_label->sub, hash, label, nsec3_get_closest_provable_encloser_match);
227
228 if(rr_label == NULL)
229 {
230 index++;
231 break;
232 }
233
234 if(zdb_rr_label_flag_matches(rr_label, ZDB_RR_LABEL_N3COVERED))
235 {
236 provable = rr_label;
237 *sections_topp = index;
238 }
239
240 index--;
241 }
242
243 return provable;
244 }
245
246 const zdb_rr_label*
nsec3_get_closest_provable_encloser_optout(const zdb_rr_label * apex,const_dnslabel_vector_reference sections,s32 * sections_topp)247 nsec3_get_closest_provable_encloser_optout(const zdb_rr_label *apex, const_dnslabel_vector_reference sections, s32 *sections_topp)
248 {
249 yassert(apex != NULL && sections != NULL && sections_topp != NULL);
250
251 s32 index = *sections_topp;
252 const zdb_rr_label* rr_label = apex; /* the zone cut */
253
254 const zdb_rr_label* provable = apex;
255
256 /*
257 * the apex is already known, so we don't loop for it
258 */
259
260 index--;
261
262 /* look into the sub level*/
263
264 while(index >= 0)
265 {
266 const u8* label = sections[index];
267 hashcode hash = hash_dnslabel(label);
268
269 rr_label = (zdb_rr_label*) dictionary_find(&rr_label->sub, hash, label, nsec3_get_closest_provable_encloser_match);
270
271 if(rr_label == NULL)
272 {
273 index++;
274 break;
275 }
276
277 if(zdb_rr_label_flag_matches(rr_label, ZDB_RR_LABEL_N3OCOVERED))
278 {
279 provable = rr_label;
280 *sections_topp = index;
281 }
282 /*
283 else if(zdb_rr_label_flag_matches(rr_label, ZDB_RR_LABEL_GOT_WILD))
284 {
285 dictionary_iterator iter;
286 dictionary_iterator_init(&rr_label->sub, &iter);
287 if(dictionary_iterator_hasnext(&iter))
288 {
289 provable = *(zdb_rr_label**)dictionary_iterator_next(&iter);
290 *sections_topp = index - 1;
291 break;
292 }
293 }
294 */
295
296 index--;
297 }
298
299 return provable;
300 }
301
302 void
nsec3_get_wild_match_and_closest_provable_encloser_optin(const zdb_rr_label * apex,const_dnslabel_vector_reference sections,s32 sections_top,const zdb_rr_label ** wild_matchp,s32 * wild_topp,const zdb_rr_label ** provable_matchp,s32 * provable_topp)303 nsec3_get_wild_match_and_closest_provable_encloser_optin(const zdb_rr_label *apex, const_dnslabel_vector_reference sections, s32 sections_top,
304 const zdb_rr_label** wild_matchp, s32 *wild_topp,
305 const zdb_rr_label** provable_matchp, s32 *provable_topp)
306 {
307 yassert(apex != NULL && sections != NULL && wild_matchp != NULL && wild_topp != NULL && provable_matchp != NULL && provable_topp != NULL);
308
309 s32 index = sections_top;
310 const zdb_rr_label* rr_label = apex; /* the zone cut */
311 *wild_matchp = NULL;
312 *provable_matchp = apex;
313 *provable_topp = sections_top;
314
315 /*
316 * the apex is already known, so we don't loop for it
317 */
318
319 index--;
320
321 /* look into the sub level*/
322
323 while(index >= 0)
324 {
325 const u8* label = sections[index];
326 hashcode hash = hash_dnslabel(label);
327
328 rr_label = (zdb_rr_label*) dictionary_find(&rr_label->sub, hash, label, nsec3_get_closest_provable_encloser_match);
329
330 if(rr_label == NULL)
331 {
332 break;
333 }
334
335 if(zdb_rr_label_flag_matches(rr_label, ZDB_RR_LABEL_GOT_WILD))
336 {
337 dictionary_iterator iter;
338 dictionary_iterator_init(&rr_label->sub, &iter);
339 if(dictionary_iterator_hasnext(&iter))
340 {
341 *wild_matchp = *(zdb_rr_label**)dictionary_iterator_next(&iter);
342 *wild_topp = index - 1;
343 }
344 }
345 if(zdb_rr_label_flag_matches(rr_label, ZDB_RR_LABEL_N3COVERED))
346 {
347 *provable_matchp = rr_label;
348 *provable_topp = index;
349 }
350 }
351 }
352
353 void
nsec3_get_wild_match_and_closest_provable_encloser_optout(const zdb_rr_label * apex,const_dnslabel_vector_reference sections,s32 sections_top,const zdb_rr_label ** wild_matchp,s32 * wild_topp,const zdb_rr_label ** provable_matchp,s32 * provable_topp)354 nsec3_get_wild_match_and_closest_provable_encloser_optout(const zdb_rr_label *apex, const_dnslabel_vector_reference sections, s32 sections_top,
355 const zdb_rr_label** wild_matchp, s32 *wild_topp,
356 const zdb_rr_label** provable_matchp, s32 *provable_topp
357 )
358 {
359 yassert(apex != NULL && sections != NULL && wild_matchp != NULL && wild_topp != NULL && provable_matchp != NULL && provable_topp != NULL);
360
361 s32 index = sections_top;
362 const zdb_rr_label* rr_label = apex; /* the zone cut */
363 *wild_matchp = NULL;
364 *provable_matchp = apex;
365 *provable_topp = sections_top;
366
367 --index;
368
369 /* look into the sub level*/
370
371 while(index >= 0)
372 {
373 const u8* label = sections[index];
374 hashcode hash = hash_dnslabel(label);
375
376 rr_label = (zdb_rr_label*) dictionary_find(&rr_label->sub, hash, label, nsec3_get_closest_provable_encloser_match);
377
378 if(rr_label == NULL)
379 {
380 break;
381 }
382
383 if(zdb_rr_label_flag_matches(rr_label, ZDB_RR_LABEL_GOT_WILD))
384 {
385 dictionary_iterator iter;
386 dictionary_iterator_init(&rr_label->sub, &iter);
387 if(dictionary_iterator_hasnext(&iter))
388 {
389 *wild_matchp = *(zdb_rr_label**)dictionary_iterator_next(&iter);
390 *wild_topp = index - 1;
391 }
392 }
393 if(zdb_rr_label_flag_matches(rr_label, ZDB_RR_LABEL_N3OCOVERED))
394 {
395 *provable_matchp = rr_label;
396 *provable_topp = index;
397 }
398
399 index--;
400 }
401 }
402
403 /**
404 * Computes the closest closer proof for a name in a zone
405 * Results are returned in 3 pointers
406 * The last one of them can be set NULL if the information is not needed.
407 *
408 * @param zone
409 * @param qname the fqdn of the query
410 * @param apex_index the index of the apex in qname
411 * @param encloser_nsec3p will point to the encloser
412 * @param closest_provable_encloser_nsec3p will point to the closest provable encloser
413 * @param wild_closest_provable_encloser_nsec3p will point to the *.closest provable encloser
414 *
415 */
416
417 void
nsec3_wild_closest_encloser_proof(const zdb_zone * zone,const dnsname_vector * qname,s32 apex_index,const nsec3_zone_item ** wild_encloser_nsec3p,const nsec3_zone_item ** closest_provable_encloser_nsec3p,const nsec3_zone_item ** qname_encloser_nsec3p)418 nsec3_wild_closest_encloser_proof(
419 const zdb_zone *zone,
420 const dnsname_vector *qname, s32 apex_index,
421 const nsec3_zone_item **wild_encloser_nsec3p,
422 const nsec3_zone_item **closest_provable_encloser_nsec3p,
423 const nsec3_zone_item **qname_encloser_nsec3p
424 )
425 {
426 u8 tmp_fqdn[MAX_DOMAIN_LENGTH + 1];
427 u8 digest[64 + 1];
428 digest[0] = SHA_DIGEST_LENGTH;
429
430 // wild_closest_provable_encloser_nsec3p can be NULL
431
432 const_dnslabel_vector_reference qname_sections = qname->labels;
433 s32 closest_encloser_index_limit = qname->size - apex_index + 1; /* not "+1'" because it starts at the apex */
434
435 const nsec3_zone* n3 = zone->nsec.nsec3;
436
437 #if DEBUG
438 if((n3 == NULL) || (n3->items == NULL))
439 {
440 log_err("zone %{dnsname} has invalid NSEC3 data");
441 return;
442 }
443 #endif
444
445 if(closest_encloser_index_limit > 0)
446 {
447 const zdb_rr_label* wild_match;
448 const zdb_rr_label* provable_match;
449 s32 wild_top;
450 s32 provable_top;
451
452 if((zdb_zone_get_flags(zone) & ZDB_ZONE_HAS_OPTOUT_COVERAGE) != 0)
453 {
454 nsec3_get_wild_match_and_closest_provable_encloser_optout(zone->apex, qname_sections, closest_encloser_index_limit,
455 &wild_match, &wild_top,
456 &provable_match, &provable_top);
457 }
458 else
459 {
460 nsec3_get_wild_match_and_closest_provable_encloser_optin(zone->apex, qname_sections, closest_encloser_index_limit,
461 &wild_match, &wild_top,
462 &provable_match, &provable_top);
463 }
464
465 /* Get ZONE NSEC3PARAM */
466 u16 iterations = nsec3_zone_get_iterations(n3);
467 u8 salt_len = NSEC3_ZONE_SALT_LEN(n3);
468 const u8* salt = NSEC3_ZONE_SALT(n3);
469
470 nsec3_hash_function* const digestname = nsec3_hash_get_function(NSEC3_ZONE_ALGORITHM(n3)); /// @note 20150917 edf -- do not use nsec3_compute_digest_from_fqdn_with_len
471
472 /** @note log_* cannot be used here (except yassert because if that one logs it will abort anyway ...) */
473
474 //dnsname_vector_sub_to_dnsname(qname, closest_encloser_index_limit , );
475
476 const nsec3_zone_item *wild_closest_provable_encloser_nsec3 = NULL;
477 const nsec3_zone_item *qname_encloser_nsec3 = NULL;
478
479 if((wild_match != NULL) && zdb_rr_label_nsec3_linked(wild_match))
480 {
481 wild_closest_provable_encloser_nsec3 = nsec3_label_extension_self(wild_match->nsec.nsec3);
482
483 // add the interval for the fqdn at the * level
484
485 dnsname_vector_sub_to_dnsname(qname, wild_top, tmp_fqdn);
486 digestname(tmp_fqdn, dnsname_len(tmp_fqdn), salt, salt_len, iterations, &digest[1], FALSE);
487 qname_encloser_nsec3 = nsec3_find_interval_start(&n3->items, digest);
488
489 if(qname_encloser_nsec3 != wild_closest_provable_encloser_nsec3)
490 {
491 *qname_encloser_nsec3p = qname_encloser_nsec3;
492 }
493 }
494
495 *wild_encloser_nsec3p = wild_closest_provable_encloser_nsec3;
496
497 const nsec3_zone_item *closest_provable_encloser_nsec3;
498
499 if(zdb_rr_label_nsec3_linked(provable_match))
500 {
501 closest_provable_encloser_nsec3 = nsec3_label_extension_self(provable_match->nsec.nsec3);
502 }
503 else
504 {
505 digestname(tmp_fqdn, dnsname_len(tmp_fqdn), salt, salt_len, iterations, &digest[1], FALSE);
506 closest_provable_encloser_nsec3 = nsec3_find_interval_start(&n3->items, digest);
507 }
508
509 if((closest_provable_encloser_nsec3 != wild_closest_provable_encloser_nsec3) && (closest_provable_encloser_nsec3 != qname_encloser_nsec3))
510 {
511 *closest_provable_encloser_nsec3p = closest_provable_encloser_nsec3;
512 }
513 else
514 {
515 *closest_provable_encloser_nsec3p = NULL;
516 }
517 }
518 else // the closest is the item itself ...
519 {
520 *wild_encloser_nsec3p = nsec3_label_extension_self(zone->apex->nsec.nsec3);
521 *closest_provable_encloser_nsec3p = NULL;
522 }
523 }
524
525 /**
526 * Computes the closest closer proof for a name in a zone
527 * Results are returned in 3 pointers
528 * The last one of them can be set NULL if the information is not needed.
529 *
530 * @param zone
531 * @param qname the fqdn of the query
532 * @param apex_index the index of the apex in qname
533 * @param encloser_nsec3p will point to the encloser
534 * @param closest_provable_encloser_nsec3p will point to the closest provable encloser
535 * @param wild_closest_provable_encloser_nsec3p will point to the *.closest provable encloser
536 *
537 */
538
539 void
nsec3_closest_encloser_proof(const zdb_zone * zone,const dnsname_vector * qname,s32 apex_index,const nsec3_zone_item ** encloser_nsec3p,const nsec3_zone_item ** closest_provable_encloser_nsec3p,const nsec3_zone_item ** wild_closest_provable_encloser_nsec3p)540 nsec3_closest_encloser_proof(
541 const zdb_zone *zone,
542 const dnsname_vector *qname, s32 apex_index,
543 const nsec3_zone_item **encloser_nsec3p,
544 const nsec3_zone_item **closest_provable_encloser_nsec3p,
545 const nsec3_zone_item **wild_closest_provable_encloser_nsec3p
546 )
547 {
548 u8 closest_provable_encloser[MAX_DOMAIN_LENGTH+1];
549 u8 encloser[MAX_DOMAIN_LENGTH+1];
550 u8 digest[64 + 1];
551 digest[0] = SHA_DIGEST_LENGTH;
552
553 yassert(encloser_nsec3p != NULL);
554 yassert(closest_provable_encloser_nsec3p != NULL);
555 // wild_closest_provable_encloser_nsec3p can be NULL
556
557 const_dnslabel_vector_reference qname_sections = qname->labels;
558 s32 closest_encloser_index_limit = qname->size - apex_index + 1; /* not "+1'" because it starts at the apex */
559
560 const nsec3_zone* n3 = zone->nsec.nsec3;
561
562 #if DEBUG
563 if((n3 == NULL) || (n3->items == NULL))
564 {
565 log_err("zone %{dnsname} has invalid NSEC3 data");
566 return;
567 }
568 #endif
569
570 if(closest_encloser_index_limit > 0)
571 {
572 const zdb_rr_label* closest_provable_encloser_label = ((zdb_zone_get_flags(zone) & ZDB_ZONE_HAS_OPTOUT_COVERAGE) != 0)?
573 nsec3_get_closest_provable_encloser_optout(zone->apex, qname_sections, &closest_encloser_index_limit):
574 nsec3_get_closest_provable_encloser_optin(zone->apex, qname_sections, &closest_encloser_index_limit);
575
576 //log_debug("closest_provable_encloser_label: %{dnslabel}: %{digest32h}", closest_provable_encloser_label->name, closest_provable_encloser_label->nsec.nsec3->self->digest);
577 //log_debug("*.closest_provable_encloser_label: %{dnslabel}: %{digest32h}", closest_provable_encloser_label->name, closest_provable_encloser_label->nsec.nsec3->star->digest);
578
579 /*
580 * Convert from closest_encloser_label_bottom to name.size into a dnslabel
581 */
582
583 /* Get ZONE NSEC3PARAM */
584 u16 iterations = nsec3_zone_get_iterations(n3);
585 u8 salt_len = NSEC3_ZONE_SALT_LEN(n3);
586 const u8* salt = NSEC3_ZONE_SALT(n3);
587 const nsec3_zone_item* encloser_nsec3 = NULL;
588
589 nsec3_hash_function* const digestname = nsec3_hash_get_function(NSEC3_ZONE_ALGORITHM(n3)); /// @note 20150917 edf -- do not use nsec3_compute_digest_from_fqdn_with_len
590
591 /** @note log_* cannot be used here (except yassert because if that one logs it will abort anyway ...) */
592
593 // encloser_nsec3p
594
595 if(closest_encloser_index_limit > 0) // if the closest encloser is itself, we should not be here
596 {
597 yassert(closest_provable_encloser_label != NULL);
598 dnsname_vector_sub_to_dnsname(qname, closest_encloser_index_limit - 1, encloser);
599 digestname(encloser, dnsname_len(encloser), salt, salt_len, iterations, &digest[1], FALSE);
600 encloser_nsec3 = nsec3_zone_item_find_encloser_start(n3, digest);
601 *encloser_nsec3p = encloser_nsec3;
602 }
603 else
604 {
605 *encloser_nsec3p = NULL; // the closest is itself (ie: missing type)
606 }
607
608 // closest_provable_encloser_nsec3p
609
610 dnsname_vector_sub_to_dnsname(qname, closest_encloser_index_limit , closest_provable_encloser);
611
612 const nsec3_zone_item* closest_provable_encloser_nsec3;
613
614 if(!zdb_rr_label_nsec3_linked(closest_provable_encloser_label))
615 {
616 digestname(closest_provable_encloser, dnsname_len(closest_provable_encloser), salt, salt_len, iterations, &digest[1], FALSE);
617 closest_provable_encloser_nsec3 = nsec3_find(&n3->items, digest);
618 }
619 else
620 {
621 closest_provable_encloser_nsec3 = nsec3_label_extension_self(closest_provable_encloser_label->nsec.nsec3);
622 }
623 if(closest_provable_encloser_nsec3 == encloser_nsec3)
624 {
625 closest_provable_encloser_nsec3 = NULL;
626 }
627
628 *closest_provable_encloser_nsec3p = closest_provable_encloser_nsec3;
629
630 if(wild_closest_provable_encloser_nsec3p != NULL)
631 {
632 if(closest_provable_encloser_nsec3 == NULL)
633 {
634 dnsname_vector_sub_to_dnsname(qname, closest_encloser_index_limit , closest_provable_encloser);
635 }
636
637 const nsec3_zone_item *wild_closest_provable_encloser_nsec3;
638
639 if(!zdb_rr_label_nsec3_linked(closest_provable_encloser_label))
640 {
641 digestname(closest_provable_encloser, dnsname_len(closest_provable_encloser), salt, salt_len, iterations, &digest[1], TRUE);
642 wild_closest_provable_encloser_nsec3 = nsec3_find_interval_start(&n3->items, digest);
643 }
644 else
645 {
646 wild_closest_provable_encloser_nsec3 = nsec3_label_extension_star(closest_provable_encloser_label->nsec.nsec3);
647 }
648
649 if(wild_closest_provable_encloser_nsec3 == encloser_nsec3)
650 {
651 wild_closest_provable_encloser_nsec3 = NULL;
652 }
653 else if(wild_closest_provable_encloser_nsec3 == closest_provable_encloser_nsec3)
654 {
655 wild_closest_provable_encloser_nsec3 = NULL;
656 }
657
658 *wild_closest_provable_encloser_nsec3p = wild_closest_provable_encloser_nsec3;
659 }
660 }
661 else // the closest is the item itself ...
662 {
663 *encloser_nsec3p = nsec3_label_extension_self(zone->apex->nsec.nsec3);
664 *closest_provable_encloser_nsec3p = nsec3_label_extension_self(zone->apex->nsec.nsec3);
665 if(wild_closest_provable_encloser_nsec3p != NULL)
666 {
667 *wild_closest_provable_encloser_nsec3p = nsec3_label_extension_self(zone->apex->nsec.nsec3);
668 }
669 }
670 }
671
672 #if NSEC3_UPDATE_ZONE_DEBUG
673
674 /**
675 * This is an internal integrity check
676 *
677 * For all owners of the NSEC3 record (aka nsec3_zone_item aka nsec3_node)
678 * Check the label is not under a delegation (log debug only)
679 * Check the label points back to the NSEC3 record
680 *
681 * @param item the NSEC3 record
682 * @param param_index_base the index of the chain of the NSEC3 record
683 */
684
685 void
nsec3_check_item(nsec3_zone_item * item,u32 param_index_base)686 nsec3_check_item(nsec3_zone_item *item, u32 param_index_base)
687 {
688 yassert(item != NULL);
689
690 u16 n = nsec3_owner_count(item);
691
692 if(n == 0)
693 {
694 log_err("nsec3_check: %{digest32h} has no owner", item->digest);
695 logger_flush();
696 abort();
697 }
698
699 for(u16 i = 0; i < n; i++)
700 {
701 zdb_rr_label *label = nsec3_item_owner_get(item, i);
702
703 yassert(label != NULL && label->nsec.nsec3 != NULL);
704
705 if(ZDB_LABEL_UNDERDELEGATION(label))
706 {
707 log_err("nsec3_check: %{digest32h} label nsec3 reference under a delegation (%{dnslabel})", item->digest, label);
708 }
709
710 nsec3_label_extension *n3le = label->nsec.nsec3;
711
712 u32 param_index = param_index_base;
713 while(param_index > 0)
714 {
715 yassert(n3le != NULL);
716
717
718
719 n3le = nsec3_label_extension_next(n3le);
720
721 param_index--;
722 }
723
724 yassert(n3le != NULL);
725
726
727 // the nsec3 structure reference to the item linked to the label does not links back to the item
728 #if 0 /* fix */
729 #else
730 yassert(n3le->_self == item);
731 #endif
732 }
733
734 n = nsec3_star_count(item);
735
736 for(u16 i = 0; i < n; i++)
737 {
738 zdb_rr_label *label = nsec3_item_star_get(item, i);
739
740 if(!((label != NULL) && (label->nsec.nsec3 != NULL)))
741 {
742 log_err("nsec3_check: %{digest32h} (#self=%d/#star=%d) corrupted", item->digest, item->rc, item->sc);
743 }
744
745 yassert(label != NULL && label->nsec.nsec3 != NULL);
746
747 if(ZDB_LABEL_UNDERDELEGATION(label))
748 {
749 log_err("nsec3_check: %{digest32h} *.label nsec3 reference under a delegation (%{dnslabel})", item->digest, label);
750 }
751
752 nsec3_label_extension *n3le = label->nsec.nsec3;
753
754 u32 param_index = param_index_base;
755 while(param_index > 0)
756 {
757 yassert(n3le != NULL);
758
759
760
761 n3le = nsec3_label_extension_next(n3le);
762
763 param_index--;
764 }
765
766 yassert(n3le != NULL);
767
768
769
770 if(nsec3_label_extension_star(n3le) != item)
771 {
772 if(nsec3_label_extension_star(n3le) != NULL)
773 {
774 log_err("nsec3_check: %{digest32h} (#self=%d/#star=%d) failing %{dnslabel} expected %{digest32h}", item->digest, item->rc, item->sc, label->name, nsec3_label_extension_star(n3le)->digest);
775 }
776 else
777 {
778 log_err("nsec3_check: %{digest32h} (#self=%d/#star=%d) *.%{dnslabel} is NULL", item->digest, item->rc, item->sc, label->name, nsec3_label_extension_star(n3le)->digest);
779 }
780 }
781
782 if(nsec3_label_extension_self(n3le) == NULL)
783 {
784 log_err("nsec3_check: %{digest32h} (#self=%d/#star=%d) failing %{dnslabel}: no self", item->digest, item->rc, item->sc, label->name);
785 }
786
787 if(nsec3_label_extension_star(n3le) != item)
788 {
789 log_err("nsec3_check: %{digest32h} *.label nsec3 reference does not point back to the nsec3 item (%{dnslabel})", item->digest, label);
790 }
791 if(nsec3_label_extension_self(n3le) == NULL)
792 {
793 log_err("nsec3_check: %{digest32h} *.label nsec3 reference self is NULL (%{dnslabel})", item->digest, label);
794 }
795 }
796 }
797
798 /**
799 * This is an internal integrity check
800 *
801 * Checks all NSEC3 links to their owners back and forth.
802 *
803 * @param zone
804 */
805
806 void
nsec3_check(zdb_zone * zone)807 nsec3_check(zdb_zone *zone)
808 {
809 log_debug("nsec3_check: %{dnsname}, from the NSEC3's reference", zone->origin);
810
811 const nsec3_zone *n3 = zone->nsec.nsec3;
812
813 if(n3 == NULL)
814 {
815 log_debug("nsec3_check: %{dnsname}: no NSEC3", zone->origin);
816
817 return;
818 }
819
820 /*
821 * For each node, check if the owners and stars are coherent
822 */
823
824 u32 param_index = 0;
825
826 while(n3 != NULL)
827 {
828 nsec3_iterator n3iter;
829 nsec3_iterator_init(&n3->items, &n3iter);
830 while(nsec3_iterator_hasnext(&n3iter))
831 {
832 nsec3_zone_item* item = nsec3_iterator_next_node(&n3iter);
833
834 nsec3_check_item(item, param_index);
835 }
836
837 param_index++;
838
839 n3 = n3->next;
840 }
841
842 log_debug("nsec3_check: %{dnsname}: from the label's reference", zone->origin);
843
844 zdb_zone_label_iterator label_iterator;
845 u8 fqdn[MAX_DOMAIN_LENGTH + 1];
846
847 zdb_zone_label_iterator_init(&label_iterator, zone);
848
849 while(zdb_zone_label_iterator_hasnext(&label_iterator))
850 {
851 zdb_zone_label_iterator_nextname(&label_iterator, fqdn);
852 zdb_rr_label* label = zdb_zone_label_iterator_next(&label_iterator);
853 nsec3_label_extension *n3le = label->nsec.nsec3;
854
855 while(n3le != NULL)
856 {
857 if(n3le->_self != NULL)
858 {
859 int found = 0;
860
861 for(s32 i = 0; i < n3le->_self->rc; ++i)
862 {
863 zdb_rr_label* self = nsec3_item_owner_get(n3le->_self, i);
864 if(self == label)
865 {
866 ++found;
867 }
868 }
869
870 if(found == 0)
871 {
872 log_err("nsec3_check: %{dnsname}: %{dnsname} => %{digest32h} is one way", zone->origin, fqdn, n3le->_self->digest);
873 }
874 else if(found > 1)
875 {
876 log_err("nsec3_check: %{dnsname}: %{dnsname} => %{digest32h} is referenced back multiple times", zone->origin, fqdn, n3le->_self->digest);
877 }
878 }
879
880 if(n3le->_star != NULL)
881 {
882 int found = 0;
883
884 for(s32 i = 0; i < n3le->_star->sc; ++i)
885 {
886 zdb_rr_label* star = nsec3_item_star_get(n3le->_star, i);
887 if(star == label)
888 {
889 ++found;
890 }
891 }
892
893 if(found == 0)
894 {
895 log_err("nsec3_check: %{dnsname}: *.%{dnsname} => %{digest32h} is one way", zone->origin, fqdn, n3le->_star->digest);
896 }
897 else if(found > 1)
898 {
899 log_err("nsec3_check: %{dnsname}: *.%{dnsname} => %{digest32h} is referenced back multiple times", zone->origin, fqdn, n3le->_star->digest);
900 }
901 }
902
903 n3le = n3le->_next;
904 }
905 }
906
907 log_debug("nsec3_check: %{dnsname} : done", zone->origin);
908 }
909
910 #endif
911
912 void
nsec3_compute_digest_from_fqdn_with_len(const nsec3_zone * n3,const u8 * fqdn,u32 fqdn_len,u8 * digest,bool isstar)913 nsec3_compute_digest_from_fqdn_with_len(const nsec3_zone *n3, const u8 *fqdn, u32 fqdn_len, u8 *digest, bool isstar)
914 {
915 digest[0] = nsec3_hash_len(NSEC3_ZONE_ALGORITHM(n3));
916
917 nsec3_hash_get_function(NSEC3_ZONE_ALGORITHM(n3))(
918 fqdn,
919 fqdn_len,
920 NSEC3_ZONE_SALT(n3),
921 NSEC3_ZONE_SALT_LEN(n3),
922 nsec3_zone_get_iterations(n3),
923 &digest[1],
924 isstar);
925 }
926
927 void
nsec3_zone_label_detach(zdb_rr_label * label)928 nsec3_zone_label_detach(zdb_rr_label *label)
929 {
930 yassert((label != NULL) && zdb_rr_label_flag_isset(label, (ZDB_RR_LABEL_NSEC3|ZDB_RR_LABEL_NSEC3_OPTOUT)));
931
932 nsec3_label_extension *n3le = label->nsec.nsec3;
933
934 while(n3le != NULL)
935 {
936 // remove
937 if(nsec3_label_extension_self(n3le) != NULL)
938 {
939 #if DEBUG
940 nsec3_node *node_self = nsec3_label_extension_self(n3le);
941 #endif
942 nsec3_item_remove_owner(nsec3_label_extension_self(n3le), label);
943 #if DEBUG
944 log_debug1("nsec3_zone_label_detach(%{dnslabel}@%p) : nsec3 rc = %i", nsec3_owner_count(node_self));
945 #endif
946 }
947 if(nsec3_label_extension_star(n3le) != NULL)
948 {
949 nsec3_item_remove_star(nsec3_label_extension_star(n3le), label);
950 }
951 zdb_rr_label_flag_and(label, ~(ZDB_RR_LABEL_NSEC3|ZDB_RR_LABEL_NSEC3_OPTOUT));
952 label->nsec.nsec3 = NULL;
953
954
955 nsec3_label_extension *tmp = n3le;
956 n3le = nsec3_label_extension_next(n3le);
957
958 nsec3_label_extension_free(tmp);
959 }
960
961 label->nsec.nsec3 = NULL;
962 }
963
964 ya_result
nsec3_get_next_digest_from_rdata(const u8 * rdata,u32 rdata_size,u8 * digest,u32 digest_size)965 nsec3_get_next_digest_from_rdata(const u8 *rdata, u32 rdata_size, u8 *digest, u32 digest_size)
966 {
967 if((NSEC3_RDATA_ALGORITHM(rdata) == NSEC3_DIGEST_ALGORITHM_SHA1) && (rdata_size > 5 + 21))
968 {
969 u32 salt_size = rdata[4];
970 u32 hash_size = rdata[5 + salt_size];
971 if((hash_size < digest_size) && (hash_size + salt_size + 5 < rdata_size))
972 {
973 memcpy(digest, &rdata[5 + salt_size], hash_size + 1);
974 return hash_size + 1;
975 }
976 }
977
978 return ERROR;
979 }
980
981 // frees from back to front
982
nsec3_zone_label_extension_remove(zdb_rr_label * label,nsec3_label_extension * n3le)983 static inline void nsec3_zone_label_extension_remove(zdb_rr_label *label, nsec3_label_extension *n3le)
984 {
985 if(nsec3_label_extension_next(n3le) != NULL)
986 {
987 nsec3_zone_label_extension_remove(label, nsec3_label_extension_next(n3le));
988 nsec3_label_extension_set_next(n3le, NULL);
989 }
990
991 // remove
992 if(nsec3_label_extension_self(n3le) != NULL)
993 {
994 nsec3_item_remove_owner(nsec3_label_extension_self(n3le), label);
995 }
996
997 if(nsec3_label_extension_star(n3le) != NULL)
998 {
999 nsec3_item_remove_star(nsec3_label_extension_star(n3le), label);
1000 }
1001
1002 nsec3_label_extension_free(n3le);
1003 }
1004
1005 void
nsec3_zone_label_update_chain_links(nsec3_zone * n3,zdb_rr_label * label,int count,u16 coverage_mask,const u8 * fqdn)1006 nsec3_zone_label_update_chain_links(nsec3_zone *n3, zdb_rr_label* label, int count, u16 coverage_mask, const u8 *fqdn)
1007 {
1008 nsec3_label_extension *n3le = label->nsec.nsec3;
1009 u8 digest[1 + MAX_DIGEST_LENGTH];
1010
1011 #if NSEC3_ZONE_LABEL_UPDATE_CHAIN_LINKS_DEBUG
1012 log_info("link: %{dnsname} (DEBUG)", fqdn);
1013 #endif
1014
1015 // coverage_mask tells what coverage is expected in the zone. It has only one bit set.
1016
1017 bool should_be_covered = zdb_rr_label_flag_isset(label, coverage_mask);
1018 u8 expected_optout_flag_value = (coverage_mask & ZDB_RR_LABEL_N3OCOVERED)?1:0;
1019
1020 if(should_be_covered) // should be covered
1021 {
1022 #if NSEC3_ZONE_LABEL_UPDATE_CHAIN_LINKS_DEBUG
1023 log_info("link: %{dnsname}: should be covered (DEBUG)", fqdn);
1024 #endif
1025
1026 if(n3le == NULL) // has no extension
1027 {
1028 // add the extension list
1029
1030 n3le = nsec3_label_extension_alloc_list(count);
1031
1032 label->nsec.nsec3 = n3le;
1033
1034 if(zdb_rr_label_flag_isset(label, ZDB_RR_LABEL_N3OCOVERED))
1035 {
1036 zdb_rr_label_flag_or(label, ZDB_RR_LABEL_NSEC3|ZDB_RR_LABEL_NSEC3_OPTOUT);
1037 }
1038 else
1039 {
1040 zdb_rr_label_flag_or(label, ZDB_RR_LABEL_NSEC3);
1041 }
1042 }
1043
1044 do
1045 {
1046 // are links missing ?
1047
1048 if((nsec3_label_extension_self(n3le) == NULL) || (nsec3_label_extension_star(n3le) == NULL))
1049 {
1050 // compute the digest(s) and link
1051
1052 s32 fqdn_len = dnsname_len(fqdn);
1053
1054 if(nsec3_label_extension_self(n3le) == NULL)
1055 {
1056 nsec3_zone_item *self = nsec3_label_link_seeknode(n3, fqdn, fqdn_len, digest);
1057 if(self != NULL)
1058 {
1059 if(self->flags != expected_optout_flag_value)
1060 {
1061 log_warn("%{dnsname} NSEC3 coverage flag doesn't match expected value", fqdn);
1062 }
1063
1064 nsec3_item_add_owner(self, label);
1065 nsec3_label_extension_set_self(n3le, self);
1066 #if NSEC3_ZONE_LABEL_UPDATE_CHAIN_LINKS_DEBUG
1067 log_info("link: %{dnsname}: self node %{digest32h} bound (DEBUG)", fqdn, digest);
1068 #endif
1069 #if HAS_SUPERDUMP
1070 nsec3_superdump_integrity_check_label_nsec3_self_points_back(label,0);
1071 nsec3_superdump_integrity_check_nsec3_owner_self_points_back(self,0);
1072 #endif
1073 }
1074 #if NSEC3_ZONE_LABEL_UPDATE_CHAIN_LINKS_DEBUG
1075 else
1076 {
1077 log_info("link: %{dnsname}: self node %{digest32h} not found (DEBUG)", fqdn, digest);
1078 }
1079 #endif
1080 }
1081
1082 if(nsec3_label_extension_star(n3le) == NULL)
1083 {
1084 nsec3_zone_item *star = nsec3_label_link_seekstar(n3, fqdn, fqdn_len, digest);
1085 if(star != NULL)
1086 {
1087 //nsec3_superdump_integrity_check_label_nsec3_star_points_back(label,0);
1088 nsec3_item_add_star(star, label);
1089 nsec3_label_extension_set_star(n3le, star);
1090 #if NSEC3_ZONE_LABEL_UPDATE_CHAIN_LINKS_DEBUG
1091 log_info("link: %{dnsname}: star node %{digest32h} bound (DEBUG)", fqdn, digest);
1092 #endif
1093 #if HAS_SUPERDUMP
1094 nsec3_superdump_integrity_check_label_nsec3_star_points_back(label,0);
1095 nsec3_superdump_integrity_check_nsec3_owner_star_points_back(star,0);
1096 #endif
1097 }
1098
1099 #if NSEC3_ZONE_LABEL_UPDATE_CHAIN_LINKS_DEBUG
1100 else
1101 {
1102 log_info("link: %{dnsname}: star node %{digest32h} not found (DEBUG)", fqdn, digest);
1103 }
1104 #endif
1105 }
1106 }
1107
1108 n3 = n3->next;
1109 nsec3_label_extension *next = nsec3_label_extension_next(n3le);
1110
1111 if((next == NULL) && (n3 != NULL))
1112 {
1113 // add
1114 nsec3_label_extension_set_next(n3le, nsec3_label_extension_alloc_list(1));
1115 }
1116 else if((next != NULL) && (n3 == NULL))
1117 {
1118 // extensions beyond the chain
1119
1120 nsec3_label_extension_set_next(n3le, NULL);
1121 nsec3_zone_label_extension_remove(label, next);
1122 next = NULL;
1123 }
1124
1125 n3le = next;
1126 }
1127 while(n3le != NULL);
1128 }
1129 else // should not be covered
1130 {
1131 #if NSEC3_ZONE_LABEL_UPDATE_CHAIN_LINKS_DEBUG
1132 log_info("link: %{dnsname}: should not be covered (DEBUG)", fqdn);
1133 #endif
1134 if(n3le != NULL)
1135 {
1136 nsec3_zone_label_extension_remove(label, n3le);
1137
1138 zdb_rr_label_flag_and(label, ~(ZDB_RR_LABEL_NSEC3|ZDB_RR_LABEL_NSEC3_OPTOUT));
1139 label->nsec.nsec3 = NULL;
1140 }
1141 }
1142 }
1143
1144 /**
1145 * Updates links for the first NSEC3 chain of the zone
1146 * Only links to existing NSEC3 records.
1147 * Only links label with an extension and self/wild set to NULL
1148 *
1149 * @param zone
1150 */
1151
1152 void
nsec3_zone_update_chain0_links(zdb_zone * zone)1153 nsec3_zone_update_chain0_links(zdb_zone *zone)
1154 {
1155 nsec3_zone *n3 = zone->nsec.nsec3;
1156
1157 if(n3 == NULL)
1158 {
1159 return;
1160 }
1161
1162 u16 coverage_mask;
1163 u8 maintain_mode = zone_get_maintain_mode(zone);
1164 if(maintain_mode & ZDB_ZONE_HAS_OPTOUT_COVERAGE)
1165 {
1166 coverage_mask = ZDB_RR_LABEL_N3OCOVERED;
1167 }
1168 else
1169 {
1170 coverage_mask = ZDB_RR_LABEL_N3COVERED;
1171 }
1172
1173 log_debug("nsec3_zone_update_chain0_links(%{dnsname}) maintain_mode=%x", zone->origin, maintain_mode);
1174
1175 int n3_count = 1;
1176 //u16 structure_mask = (maintain_mode == ZDB_ZONE_MAINTAIN_NSEC3_OPTOUT)?ZDB_RR_LABEL_NSEC3_OPTOUT:((maintain_mode == ZDB_ZONE_MAINTAIN_NSEC3)?ZDB_RR_LABEL_NSEC3:0);
1177
1178 {
1179 const nsec3_zone *n3 = zone->nsec.nsec3->next;
1180
1181 while(n3 != NULL)
1182 {
1183 ++n3_count;
1184 n3 = n3->next;
1185 }
1186 }
1187
1188 zdb_zone_label_iterator label_iterator;
1189 u8 fqdn[MAX_DOMAIN_LENGTH + 1];
1190 #if 1
1191 #else
1192 u8 digest[1 + MAX_DIGEST_LENGTH];
1193 #endif
1194
1195 zdb_zone_label_iterator_init(&label_iterator, zone);
1196
1197 while(zdb_zone_label_iterator_hasnext(&label_iterator))
1198 {
1199 zdb_zone_label_iterator_nextname(&label_iterator, fqdn);
1200 zdb_rr_label* label = zdb_zone_label_iterator_next(&label_iterator);
1201 #if 1
1202 #else
1203 nsec3_label_extension *n3le = label->nsec.nsec3;
1204 #endif
1205 #if 1
1206 nsec3_zone_label_update_chain_links(zone->nsec.nsec3, label, n3_count, coverage_mask, fqdn);
1207 #else
1208 if(zdb_rr_label_flag_isset(label, coverage_mask))
1209 {
1210 if(n3le == NULL)
1211 {
1212 n3le = nsec3_label_extension_alloc();
1213 ZEROMEMORY(n3le, sizeof(nsec3_label_extension));
1214 label->nsec.nsec3 = n3le;
1215 if(zdb_rr_label_flag_isset(label, ZDB_RR_LABEL_N3OCOVERED))
1216 {
1217 zdb_rr_label_flag_or(label, ZDB_RR_LABEL_NSEC3|ZDB_RR_LABEL_NSEC3_OPTOUT);
1218 }
1219 else
1220 {
1221 zdb_rr_label_flag_or(label, ZDB_RR_LABEL_NSEC3);
1222 }
1223 }
1224
1225 if(nsec3_label_extension_self(n3le) == NULL || nsec3_label_extension_star(n3le) == NULL)
1226 {
1227 s32 fqdn_len = dnsname_len(fqdn);
1228
1229 if(nsec3_label_extension_self(n3le) == NULL)
1230 {
1231 nsec3_zone_item *self = nsec3_label_link_seeknode(n3, fqdn, fqdn_len, digest);
1232 if(self != NULL)
1233 {
1234 nsec3_item_add_owner(self, label);
1235 nsec3_label_extension_set_self(n3le, self);
1236 #if HAS_SUPERDUMP
1237 nsec3_superdump_integrity_check_label_nsec3_self_points_back(label,0);
1238 nsec3_superdump_integrity_check_nsec3_owner_self_points_back(self,0);
1239 #endif
1240 }
1241 }
1242
1243 if(nsec3_label_extension_star(n3le) == NULL)
1244 {
1245 nsec3_zone_item *star = nsec3_label_link_seekstar(n3, fqdn, fqdn_len, digest);
1246 if(star != NULL)
1247 {
1248 //nsec3_superdump_integrity_check_label_nsec3_star_points_back(label,0);
1249 nsec3_item_add_star(star, label);
1250 nsec3_label_extension_set_star(n3le, star);
1251 #if HAS_SUPERDUMP
1252 nsec3_superdump_integrity_check_label_nsec3_star_points_back(label,0);
1253 nsec3_superdump_integrity_check_nsec3_owner_star_points_back(star,0);
1254 #endif
1255 }
1256 }
1257 }
1258 }
1259 else
1260 {
1261 if(n3le != NULL)
1262 {
1263 // remove
1264 if(nsec3_label_extension_self(n3le) != NULL)
1265 {
1266 nsec3_item_remove_owner(nsec3_label_extension_self(n3le), label);
1267 }
1268 if(nsec3_label_extension_star(n3le) != NULL)
1269 {
1270 nsec3_item_remove_star(nsec3_label_extension_star(n3le), label);
1271 }
1272 nsec3_label_extension_free(n3le);
1273 zdb_rr_label_flag_and(label->flags, ~(ZDB_RR_LABEL_NSEC3|ZDB_RR_LABEL_NSEC3_OPTOUT));
1274 label->nsec.nsec3 = NULL;
1275 }
1276 }
1277 #endif
1278 }
1279 }
1280
1281 #if ZDB_HAS_MASTER_SUPPORT
1282 /**
1283 * Sets the NSEC3 maintenance status for a specific chain.
1284 * Marks the zone using private records.
1285 *
1286 * The zone must be double-locked.
1287 *
1288 * @param zone
1289 * @param secondary_lock the secondary lock owner
1290 * @param algorithm
1291 * @param optout
1292 * @param salt
1293 * @param salt_len
1294 * @param iterations
1295 * @param status
1296 * @return
1297 */
1298
1299 ya_result
nsec3_zone_set_status(zdb_zone * zone,u8 secondary_lock,u8 algorithm,u8 optout,u16 iterations,const u8 * salt,u8 salt_len,u8 status)1300 nsec3_zone_set_status(zdb_zone *zone, u8 secondary_lock, u8 algorithm, u8 optout, u16 iterations, const u8 *salt, u8 salt_len, u8 status)
1301 {
1302 dynupdate_message dmsg;
1303 packet_unpack_reader_data reader;
1304 dynupdate_message_init(&dmsg, zone->origin, CLASS_IN);
1305
1306 u8 prev_status = 0;
1307 #ifndef WIN32
1308 u8 nsec3paramadd_rdata[5 + salt_len + 1];
1309 #else
1310 u8 nsec3paramadd_rdata[5 + 255 + 1];
1311 #endif
1312 nsec3paramadd_rdata[0] = algorithm;
1313 nsec3paramadd_rdata[1] = optout;
1314 SET_U16_AT(nsec3paramadd_rdata[2], htons(iterations));
1315 nsec3paramadd_rdata[4] = salt_len;
1316 memcpy(&nsec3paramadd_rdata[5], salt, salt_len);
1317 nsec3paramadd_rdata[5 + salt_len] = status;
1318
1319 // look for the matching record
1320 if(nsec3_zone_get_status(zone, algorithm, optout, iterations, salt, salt_len, &prev_status) == 1)
1321 {
1322 // if the record exists, remove it and add it
1323 nsec3paramadd_rdata[5 + salt_len] = prev_status;
1324 if(prev_status == status)
1325 {
1326 // already set
1327
1328 return SUCCESS;
1329 }
1330 dynupdate_message_del_record(&dmsg, zone->origin, TYPE_NSEC3CHAINSTATE, 0, 6 + salt_len, nsec3paramadd_rdata);
1331 nsec3paramadd_rdata[5 + salt_len] = status;
1332 }
1333
1334 dynupdate_message_add_record(&dmsg, zone->origin, TYPE_NSEC3CHAINSTATE, 0, 6 + salt_len, nsec3paramadd_rdata);
1335
1336 dynupdate_message_set_reader(&dmsg, &reader);
1337 u16 count = dynupdate_message_get_count(&dmsg);
1338
1339 packet_reader_skip(&reader, DNS_HEADER_LENGTH); // checked below
1340 packet_reader_skip_fqdn(&reader); // checked below
1341 packet_reader_skip(&reader, 4); // checked below
1342
1343 ya_result ret;
1344
1345 if(!packet_reader_eof(&reader))
1346 {
1347 #if ZDB_HAS_DNSSEC_SUPPORT && ZDB_HAS_RRSIG_MANAGEMENT_SUPPORT
1348 if(zone_get_maintain_mode(zone) == 0)
1349 {
1350 zone_set_maintain_mode(zone, ZDB_ZONE_MAINTAIN_NSEC3_OPTOUT);
1351 }
1352 #endif
1353
1354 ret = dynupdate_diff(zone, &reader, count, secondary_lock, DYNUPDATE_DIFF_RUN);
1355
1356 dynupdate_message_finalize(&dmsg);
1357
1358 if(ret == ZDB_JOURNAL_MUST_SAFEGUARD_CONTINUITY)
1359 {
1360 // trigger a background store of the zone
1361
1362 zdb_zone_info_background_store_zone(zone->origin);
1363 }
1364 }
1365 else
1366 {
1367 ret = MAKE_DNSMSG_ERROR(RCODE_FORMERR);
1368 }
1369
1370 return ret;
1371 }
1372
1373 #endif
1374
1375 /**
1376 * Gets the NSEC3 maintenance status for a specific chain.
1377 * Get the information from the zone using private records.
1378 *
1379 * The zone must be locked.
1380 *
1381 * @param zone
1382 * @param algorithm
1383 * @param optout
1384 * @param salt
1385 * @param salt_len
1386 * @param iterations
1387 * @param status
1388 * @return
1389 */
1390
nsec3_zone_get_status(zdb_zone * zone,u8 algorithm,u8 optout,u16 iterations,const u8 * salt,u8 salt_len,u8 * statusp)1391 ya_result nsec3_zone_get_status(zdb_zone *zone, u8 algorithm, u8 optout, u16 iterations, const u8 *salt, u8 salt_len, u8 *statusp)
1392 {
1393 // get the TYPE_NSEC3PARAMADD record set
1394 // search for a record matching the chain
1395 zdb_packed_ttlrdata *rrset = zdb_record_find(&zone->apex->resource_record_set, TYPE_NSEC3CHAINSTATE);
1396 while(rrset != NULL)
1397 {
1398 if(rrset->rdata_size == 6 + salt_len)
1399 {
1400 if(rrset->rdata_start[0] == algorithm)
1401 {
1402 if(rrset->rdata_start[1] == optout)
1403 {
1404 if(GET_U16_AT(rrset->rdata_start[2]) == htons(iterations))
1405 {
1406 if(rrset->rdata_start[4] == salt_len)
1407 {
1408 if(memcmp(&rrset->rdata_start[5], salt, salt_len) == 0)
1409 {
1410 *statusp = rrset->rdata_start[5 + salt_len];
1411 return 1;
1412 }
1413 }
1414 }
1415 }
1416 }
1417 rrset = rrset->next;
1418 }
1419 }
1420
1421 return 0;
1422 }
1423
1424 /**
1425 * Gets the NSEC3 maintenance status for a specific chain.
1426 * Get the information from the zone using private records.
1427 *
1428 * The zone must be locked.
1429 *
1430 * @param zone
1431 * @param rdata
1432 * @param rdata_size
1433 * @param statusp
1434 * @return
1435 */
1436
1437 ya_result
nsec3_zone_get_status_from_rdata(zdb_zone * zone,const u8 * rdata,u16 rdata_size,u8 * statusp)1438 nsec3_zone_get_status_from_rdata(zdb_zone *zone, const u8* rdata, u16 rdata_size, u8 *statusp)
1439 {
1440 // get the TYPE_NSEC3PARAMADD record set
1441 // search for a record matching the chain
1442 zdb_packed_ttlrdata *rrset = zdb_record_find(&zone->apex->resource_record_set, TYPE_NSEC3CHAINSTATE);
1443 while(rrset != NULL)
1444 {
1445 if(rrset->rdata_size == rdata_size + 1)
1446 {
1447 if(rrset->rdata_start[0] == rdata[0])
1448 {
1449 if(GET_U16_AT(rrset->rdata_start[2]) == GET_U16_AT(rdata[2]))
1450 {
1451 if(rrset->rdata_start[4] == rdata[4])
1452 {
1453 if(memcmp(&rrset->rdata_start[5], &rdata[5], rdata[4]) == 0)
1454 {
1455 *statusp = rrset->rdata_start[5 + rdata[4]];
1456 return 1;
1457 }
1458 }
1459 }
1460 }
1461 rrset = rrset->next;
1462 }
1463 }
1464
1465 return 0;
1466 }
1467
1468 /**
1469 * Returns the number of known chains in the zone.
1470 * Inactive chains are also counted.
1471 * Zone must be locked.
1472 *
1473 * @param zone
1474 * @return
1475 */
1476
1477 int
nsec3_zone_get_chain_count(zdb_zone * zone)1478 nsec3_zone_get_chain_count(zdb_zone *zone)
1479 {
1480 int ret = 0;
1481 nsec3_zone *n3 = zone->nsec.nsec3;
1482 while(n3 != NULL)
1483 {
1484 ++ret;
1485 n3 = n3->next;
1486 }
1487 return ret;
1488 }
1489
1490 /**
1491 * Returns pointers to the chains from the zone.
1492 * Inactive chains are also counted.
1493 * Zone must be locked.
1494 *
1495 * @param zone
1496 * @param n3p
1497 * @param max_count
1498 * @return
1499 */
1500
1501 int
nsec3_zone_get_chains(zdb_zone * zone,nsec3_zone ** n3p,int max_count)1502 nsec3_zone_get_chains(zdb_zone *zone, nsec3_zone **n3p, int max_count)
1503 {
1504 int ret = 0;
1505 nsec3_zone *n3 = zone->nsec.nsec3;
1506 while(n3 != NULL)
1507 {
1508 *n3p++ = n3;
1509 if(++ret == max_count)
1510 {
1511 break;
1512 }
1513 n3 = n3->next;
1514 }
1515 return ret;
1516 }
1517
1518 struct nsec3_item_rrv_data_s
1519 {
1520 nsec3_zone *n3;
1521 nsec3_node *item;
1522 const u8 *origin;
1523 u8 *rdata;
1524 u16 rdata_size;
1525 u16 rdata_buffer_size;
1526 s32 ttl;
1527 u8 fqdn[256];
1528 };
1529
1530 typedef struct nsec3_item_rrv_data_s nsec3_item_rrv_data_t;
1531
1532 static void
nsec3_item_resource_record_view_data_item_set(nsec3_item_rrv_data_t * rrv_data,nsec3_node * item)1533 nsec3_item_resource_record_view_data_item_set(nsec3_item_rrv_data_t *rrv_data, nsec3_node *item)
1534 {
1535 u32 required_size = nsec3_zone_item_rdata_size(rrv_data->n3, item);
1536 if(rrv_data->rdata_buffer_size < required_size)
1537 {
1538 free(rrv_data->rdata);
1539 rrv_data->rdata_buffer_size = (required_size + 128) & ~127;
1540 MALLOC_OBJECT_ARRAY_OR_DIE(rrv_data->rdata, u8, rrv_data->rdata_buffer_size, RRVDATA_TAG);
1541 }
1542
1543 rrv_data->rdata_size = nsec3_zone_item_to_rdata(rrv_data->n3, item, rrv_data->rdata, rrv_data->rdata_buffer_size);
1544 u32 b32_len = base32hex_encode(NSEC3_NODE_DIGEST_PTR(item), NSEC3_NODE_DIGEST_SIZE(item), (char*)&rrv_data->fqdn[1]);
1545 rrv_data->fqdn[0] = b32_len;
1546 dnsname_copy(&rrv_data->fqdn[b32_len + 1], rrv_data->origin);
1547 rrv_data->item = item;
1548 }
1549
1550 static const u8 *
nsec3_item_rrv_get_fqdn(void * data,const void * p)1551 nsec3_item_rrv_get_fqdn(void *data, const void* p)
1552 {
1553 nsec3_item_rrv_data_t *rrv_data = (nsec3_item_rrv_data_t*)data;
1554 if(rrv_data->item != (nsec3_node*)p) nsec3_item_resource_record_view_data_item_set(rrv_data, (nsec3_node*)p);
1555 return rrv_data->fqdn;
1556 }
1557
1558 static u16
nsec3_item_rrv_get_type(void * data,const void * p)1559 nsec3_item_rrv_get_type(void *data, const void* p)
1560 {
1561 (void)data;
1562 (void)p;
1563 return TYPE_NSEC3;
1564 }
1565
1566 static u16
nsec3_item_rrv_get_class(void * data,const void * p)1567 nsec3_item_rrv_get_class(void *data, const void* p)
1568 {
1569 (void)data;
1570 (void)p;
1571 return CLASS_IN;
1572 }
1573
1574 static s32
nsec3_item_rrv_get_ttl(void * data,const void * p)1575 nsec3_item_rrv_get_ttl(void *data, const void* p)
1576 {
1577 (void)p;
1578 nsec3_item_rrv_data_t *rrv_data = (nsec3_item_rrv_data_t*)data;
1579 return rrv_data->ttl;
1580 }
1581
1582 static u16
nsec3_item_rrv_get_rdata_size(void * data,const void * p)1583 nsec3_item_rrv_get_rdata_size(void *data, const void* p)
1584 {
1585 nsec3_item_rrv_data_t *rrv_data = (nsec3_item_rrv_data_t*)data;
1586 if(rrv_data->item != (nsec3_node*)p) nsec3_item_resource_record_view_data_item_set(rrv_data, (nsec3_node*)p);
1587 return rrv_data->rdata_size;
1588 }
1589
1590 static const u8 *
nsec3_item_rrv_get_rdata(void * data,const void * p)1591 nsec3_item_rrv_get_rdata(void *data, const void* p)
1592 {
1593 nsec3_item_rrv_data_t *rrv_data = (nsec3_item_rrv_data_t*)data;
1594 if(rrv_data->item != (nsec3_node*)p) nsec3_item_resource_record_view_data_item_set(rrv_data, (nsec3_node*)p);
1595 return rrv_data->rdata;
1596 }
1597
1598 static void *
nsec3_item_rrv_new_instance(void * data,const u8 * fqdn,u16 rtype,u16 rclass,s32 ttl,u16 rdata_size,const u8 * rdata)1599 nsec3_item_rrv_new_instance(void *data, const u8 *fqdn, u16 rtype, u16 rclass, s32 ttl, u16 rdata_size, const u8 *rdata)
1600 {
1601 (void)data;
1602 (void)fqdn;
1603 (void)rtype;
1604 (void)rclass;
1605 yassert(rtype == TYPE_RRSIG);
1606 struct zdb_packed_ttlrdata *ttlrdata;
1607 ZDB_RECORD_ZALLOC(ttlrdata, ttl, rdata_size, rdata);
1608 return ttlrdata;
1609 }
1610
1611 static const struct resource_record_view_vtbl nsec3_item_rrv_vtbl =
1612 {
1613 nsec3_item_rrv_get_fqdn,
1614 nsec3_item_rrv_get_type,
1615 nsec3_item_rrv_get_class,
1616 nsec3_item_rrv_get_ttl,
1617 nsec3_item_rrv_get_rdata_size,
1618 nsec3_item_rrv_get_rdata,
1619 nsec3_item_rrv_new_instance
1620 };
1621
1622 void
nsec3_item_resource_record_view_init(resource_record_view * rrv)1623 nsec3_item_resource_record_view_init(resource_record_view *rrv)
1624 {
1625 ZALLOC_OBJECT_OR_DIE(rrv->data, nsec3_item_rrv_data_t, N3IRRVDT_TAG);
1626 ZEROMEMORY(rrv->data, sizeof(nsec3_item_rrv_data_t));
1627 rrv->vtbl = &nsec3_item_rrv_vtbl;
1628 }
1629
1630 void
nsec3_item_resource_record_view_origin_set(struct resource_record_view * rrv,const u8 * origin)1631 nsec3_item_resource_record_view_origin_set(struct resource_record_view *rrv, const u8 *origin)
1632 {
1633 yassert(rrv->vtbl == &nsec3_item_rrv_vtbl);
1634 nsec3_item_rrv_data_t *rrv_data = (nsec3_item_rrv_data_t*)rrv->data;
1635 rrv_data->origin = origin;
1636 }
1637
1638 void
nsec3_item_resource_record_view_nsec3_zone_set(struct resource_record_view * rrv,nsec3_zone * n3)1639 nsec3_item_resource_record_view_nsec3_zone_set(struct resource_record_view *rrv, nsec3_zone *n3)
1640 {
1641 yassert(rrv->vtbl == &nsec3_item_rrv_vtbl);
1642 nsec3_item_rrv_data_t *rrv_data = (nsec3_item_rrv_data_t*)rrv->data;
1643 rrv_data->n3 = n3;
1644 }
1645
1646 void
nsec3_item_resource_record_view_ttl_set(resource_record_view * rrv,s32 ttl)1647 nsec3_item_resource_record_view_ttl_set(resource_record_view *rrv, s32 ttl)
1648 {
1649 yassert(rrv->vtbl == &nsec3_item_rrv_vtbl);
1650 nsec3_item_rrv_data_t *rrv_data = (nsec3_item_rrv_data_t*)rrv->data;
1651 rrv_data->ttl = ttl;
1652 }
1653
1654 void
nsec3_item_resource_record_finalize(resource_record_view * rrv)1655 nsec3_item_resource_record_finalize(resource_record_view *rrv)
1656 {
1657 yassert(rrv->vtbl == &nsec3_item_rrv_vtbl);
1658 ZFREE_OBJECT_OF_TYPE(rrv->data, nsec3_item_rrv_data_t);
1659 rrv->vtbl = NULL;
1660 }
1661
1662 void
nsec3_superdump(zdb_zone * zone)1663 nsec3_superdump(zdb_zone *zone)
1664 {
1665 (void)zone;
1666 }
1667
1668 /** @} */
1669