/src/bind9/lib/dns/update.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) Internet Systems Consortium, Inc. ("ISC") |
3 | | * |
4 | | * SPDX-License-Identifier: MPL-2.0 |
5 | | * |
6 | | * This Source Code Form is subject to the terms of the Mozilla Public |
7 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
8 | | * file, you can obtain one at https://mozilla.org/MPL/2.0/. |
9 | | * |
10 | | * See the COPYRIGHT file distributed with this work for additional |
11 | | * information regarding copyright ownership. |
12 | | */ |
13 | | |
14 | | #include <inttypes.h> |
15 | | #include <stdbool.h> |
16 | | #include <time.h> |
17 | | |
18 | | #include <isc/log.h> |
19 | | #include <isc/magic.h> |
20 | | #include <isc/mem.h> |
21 | | #include <isc/netaddr.h> |
22 | | #include <isc/random.h> |
23 | | #include <isc/result.h> |
24 | | #include <isc/serial.h> |
25 | | #include <isc/stats.h> |
26 | | #include <isc/stdtime.h> |
27 | | #include <isc/string.h> |
28 | | #include <isc/time.h> |
29 | | #include <isc/util.h> |
30 | | |
31 | | #include <dns/db.h> |
32 | | #include <dns/dbiterator.h> |
33 | | #include <dns/diff.h> |
34 | | #include <dns/dnssec.h> |
35 | | #include <dns/fixedname.h> |
36 | | #include <dns/journal.h> |
37 | | #include <dns/kasp.h> |
38 | | #include <dns/keyvalues.h> |
39 | | #include <dns/message.h> |
40 | | #include <dns/nsec.h> |
41 | | #include <dns/nsec3.h> |
42 | | #include <dns/private.h> |
43 | | #include <dns/rdataclass.h> |
44 | | #include <dns/rdataset.h> |
45 | | #include <dns/rdatasetiter.h> |
46 | | #include <dns/rdatastruct.h> |
47 | | #include <dns/rdatatype.h> |
48 | | #include <dns/skr.h> |
49 | | #include <dns/soa.h> |
50 | | #include <dns/ssu.h> |
51 | | #include <dns/stats.h> |
52 | | #include <dns/tsig.h> |
53 | | #include <dns/update.h> |
54 | | #include <dns/view.h> |
55 | | #include <dns/zone.h> |
56 | | #include <dns/zt.h> |
57 | | |
58 | | /**************************************************************************/ |
59 | | |
60 | 0 | #define STATE_MAGIC ISC_MAGIC('S', 'T', 'T', 'E') |
61 | | #define DNS_STATE_VALID(state) ISC_MAGIC_VALID(state, STATE_MAGIC) |
62 | | |
63 | | /*% |
64 | | * Log level for tracing dynamic update protocol requests. |
65 | | */ |
66 | | #define LOGLEVEL_PROTOCOL ISC_LOG_INFO |
67 | | |
68 | | /*% |
69 | | * Log level for low-level debug tracing. |
70 | | */ |
71 | | #define LOGLEVEL_DEBUG ISC_LOG_DEBUG(8) |
72 | | |
73 | | /**************************************************************************/ |
74 | | |
75 | | typedef struct rr rr_t; |
76 | | |
77 | | struct rr { |
78 | | /* dns_name_t name; */ |
79 | | uint32_t ttl; |
80 | | dns_rdata_t rdata; |
81 | | }; |
82 | | |
83 | | typedef struct update_event update_event_t; |
84 | | |
85 | | /**************************************************************************/ |
86 | | |
87 | | static void |
88 | | update_log(dns_update_log_t *callback, dns_zone_t *zone, int level, |
89 | | const char *fmt, ...) ISC_FORMAT_PRINTF(4, 5); |
90 | | |
91 | | static void |
92 | | update_log(dns_update_log_t *callback, dns_zone_t *zone, int level, |
93 | 0 | const char *fmt, ...) { |
94 | 0 | va_list ap; |
95 | 0 | char message[4096]; |
96 | |
|
97 | 0 | if (callback == NULL) { |
98 | 0 | return; |
99 | 0 | } |
100 | | |
101 | 0 | if (!isc_log_wouldlog(level)) { |
102 | 0 | return; |
103 | 0 | } |
104 | | |
105 | 0 | va_start(ap, fmt); |
106 | 0 | vsnprintf(message, sizeof(message), fmt, ap); |
107 | 0 | va_end(ap); |
108 | |
|
109 | 0 | (callback->func)(callback->arg, zone, level, message); |
110 | 0 | } |
111 | | |
112 | | /*% |
113 | | * Update a single RR in version 'ver' of 'db' and log the |
114 | | * update in 'diff'. |
115 | | * |
116 | | * Ensures: |
117 | | * \li '*tuple' == NULL. Either the tuple is freed, or its |
118 | | * ownership has been transferred to the diff. |
119 | | */ |
120 | | static isc_result_t |
121 | | do_one_tuple(dns_difftuple_t **tuple, dns_db_t *db, dns_dbversion_t *ver, |
122 | 0 | dns_diff_t *diff) { |
123 | 0 | dns_diff_t temp_diff; |
124 | 0 | isc_result_t result; |
125 | | |
126 | | /* |
127 | | * Create a singleton diff. |
128 | | */ |
129 | 0 | dns_diff_init(diff->mctx, &temp_diff); |
130 | 0 | ISC_LIST_APPEND(temp_diff.tuples, *tuple, link); |
131 | | |
132 | | /* |
133 | | * Apply it to the database. |
134 | | */ |
135 | 0 | result = dns_diff_apply(&temp_diff, db, ver); |
136 | 0 | ISC_LIST_UNLINK(temp_diff.tuples, *tuple, link); |
137 | 0 | if (result != ISC_R_SUCCESS) { |
138 | 0 | dns_difftuple_free(tuple); |
139 | 0 | return result; |
140 | 0 | } |
141 | | |
142 | | /* |
143 | | * Merge it into the current pending journal entry. |
144 | | */ |
145 | 0 | dns_diff_appendminimal(diff, tuple); |
146 | | |
147 | | /* |
148 | | * Do not clear temp_diff. |
149 | | */ |
150 | 0 | return ISC_R_SUCCESS; |
151 | 0 | } |
152 | | |
153 | | static isc_result_t |
154 | | update_one_rr(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff, |
155 | | dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl, |
156 | 0 | dns_rdata_t *rdata) { |
157 | 0 | dns_difftuple_t *tuple = NULL; |
158 | 0 | dns_difftuple_create(diff->mctx, op, name, ttl, rdata, &tuple); |
159 | 0 | return do_one_tuple(&tuple, db, ver, diff); |
160 | 0 | } |
161 | | |
162 | | /**************************************************************************/ |
163 | | /* |
164 | | * Callback-style iteration over rdatasets and rdatas. |
165 | | * |
166 | | * foreach_rrset() can be used to iterate over the RRsets |
167 | | * of a name and call a callback function with each |
168 | | * one. Similarly, foreach_rr() can be used to iterate |
169 | | * over the individual RRs at name, optionally restricted |
170 | | * to RRs of a given type. |
171 | | * |
172 | | * The callback functions are called "actions" and take |
173 | | * two arguments: a void pointer for passing arbitrary |
174 | | * context information, and a pointer to the current RRset |
175 | | * or RR. By convention, their names end in "_action". |
176 | | */ |
177 | | |
178 | | /* |
179 | | * XXXRTH We might want to make this public somewhere in libdns. |
180 | | */ |
181 | | |
182 | | /*% |
183 | | * Function type for foreach_rrset() iterator actions. |
184 | | */ |
185 | | typedef isc_result_t |
186 | | rrset_func(void *data, dns_rdataset_t *rrset); |
187 | | |
188 | | /*% |
189 | | * Function type for foreach_rr() iterator actions. |
190 | | */ |
191 | | typedef isc_result_t |
192 | | rr_func(void *data, rr_t *rr); |
193 | | |
194 | | /*% |
195 | | * Internal context struct for foreach_node_rr(). |
196 | | */ |
197 | | typedef struct { |
198 | | rr_func *rr_action; |
199 | | void *rr_action_data; |
200 | | } foreach_node_rr_ctx_t; |
201 | | |
202 | | /*% |
203 | | * Internal helper function for foreach_node_rr(). |
204 | | */ |
205 | | static isc_result_t |
206 | 0 | foreach_node_rr_action(void *data, dns_rdataset_t *rdataset) { |
207 | 0 | foreach_node_rr_ctx_t *ctx = data; |
208 | 0 | DNS_RDATASET_FOREACH(rdataset) { |
209 | 0 | rr_t rr = { 0, DNS_RDATA_INIT }; |
210 | |
|
211 | 0 | dns_rdataset_current(rdataset, &rr.rdata); |
212 | 0 | rr.ttl = rdataset->ttl; |
213 | 0 | RETERR((*ctx->rr_action)(ctx->rr_action_data, &rr)); |
214 | 0 | } |
215 | | |
216 | 0 | return ISC_R_SUCCESS; |
217 | 0 | } |
218 | | |
219 | | /*% |
220 | | * For each rdataset of 'name' in 'ver' of 'db', call 'action' |
221 | | * with the rdataset and 'action_data' as arguments. If the name |
222 | | * does not exist, do nothing. |
223 | | * |
224 | | * If 'action' returns an error, abort iteration and return the error. |
225 | | */ |
226 | | static isc_result_t |
227 | | foreach_rrset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, |
228 | 0 | rrset_func *action, void *action_data) { |
229 | 0 | isc_result_t result; |
230 | 0 | dns_dbnode_t *node = NULL; |
231 | 0 | dns_rdatasetiter_t *iter = NULL; |
232 | |
|
233 | 0 | result = dns_db_findnode(db, name, false, &node); |
234 | 0 | if (result == ISC_R_NOTFOUND) { |
235 | 0 | return ISC_R_SUCCESS; |
236 | 0 | } |
237 | 0 | if (result != ISC_R_SUCCESS) { |
238 | 0 | return result; |
239 | 0 | } |
240 | | |
241 | 0 | result = dns_db_allrdatasets(db, node, ver, 0, (isc_stdtime_t)0, &iter); |
242 | 0 | if (result != ISC_R_SUCCESS) { |
243 | 0 | goto cleanup_node; |
244 | 0 | } |
245 | | |
246 | 0 | DNS_RDATASETITER_FOREACH(iter) { |
247 | 0 | dns_rdataset_t rdataset = DNS_RDATASET_INIT; |
248 | |
|
249 | 0 | dns_rdatasetiter_current(iter, &rdataset); |
250 | |
|
251 | 0 | result = (*action)(action_data, &rdataset); |
252 | |
|
253 | 0 | dns_rdataset_disassociate(&rdataset); |
254 | 0 | if (result != ISC_R_SUCCESS) { |
255 | 0 | break; |
256 | 0 | } |
257 | 0 | } |
258 | 0 | dns_rdatasetiter_destroy(&iter); |
259 | |
|
260 | 0 | cleanup_node: |
261 | 0 | dns_db_detachnode(&node); |
262 | |
|
263 | 0 | return result; |
264 | 0 | } |
265 | | |
266 | | /*% |
267 | | * For each RR of 'name' in 'ver' of 'db', call 'action' |
268 | | * with the RR and 'action_data' as arguments. If the name |
269 | | * does not exist, do nothing. |
270 | | * |
271 | | * If 'action' returns an error, abort iteration |
272 | | * and return the error. |
273 | | */ |
274 | | static isc_result_t |
275 | | foreach_node_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, |
276 | 0 | rr_func *rr_action, void *rr_action_data) { |
277 | 0 | foreach_node_rr_ctx_t ctx; |
278 | 0 | ctx.rr_action = rr_action; |
279 | 0 | ctx.rr_action_data = rr_action_data; |
280 | 0 | return foreach_rrset(db, ver, name, foreach_node_rr_action, &ctx); |
281 | 0 | } |
282 | | |
283 | | /*% |
284 | | * For each of the RRs specified by 'db', 'ver', 'name', 'type', |
285 | | * (which can be dns_rdatatype_any to match any type), and 'covers', call |
286 | | * 'action' with the RR and 'action_data' as arguments. If the name |
287 | | * does not exist, or if no RRset of the given type exists at the name, |
288 | | * do nothing. |
289 | | * |
290 | | * If 'action' returns an error, abort iteration and return the error. |
291 | | */ |
292 | | static isc_result_t |
293 | | foreach_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, |
294 | | dns_rdatatype_t type, dns_rdatatype_t covers, rr_func *rr_action, |
295 | 0 | void *rr_action_data) { |
296 | 0 | isc_result_t result; |
297 | 0 | dns_dbnode_t *node; |
298 | 0 | dns_rdataset_t rdataset; |
299 | |
|
300 | 0 | if (type == dns_rdatatype_any) { |
301 | 0 | return foreach_node_rr(db, ver, name, rr_action, |
302 | 0 | rr_action_data); |
303 | 0 | } |
304 | | |
305 | 0 | node = NULL; |
306 | 0 | if (type == dns_rdatatype_nsec3 || |
307 | 0 | (type == dns_rdatatype_rrsig && covers == dns_rdatatype_nsec3)) |
308 | 0 | { |
309 | 0 | result = dns_db_findnsec3node(db, name, false, &node); |
310 | 0 | } else { |
311 | 0 | result = dns_db_findnode(db, name, false, &node); |
312 | 0 | } |
313 | 0 | if (result == ISC_R_NOTFOUND) { |
314 | 0 | return ISC_R_SUCCESS; |
315 | 0 | } |
316 | 0 | if (result != ISC_R_SUCCESS) { |
317 | 0 | return result; |
318 | 0 | } |
319 | | |
320 | 0 | dns_rdataset_init(&rdataset); |
321 | 0 | result = dns_db_findrdataset(db, node, ver, type, covers, |
322 | 0 | (isc_stdtime_t)0, &rdataset, NULL); |
323 | 0 | if (result == ISC_R_NOTFOUND) { |
324 | 0 | result = ISC_R_SUCCESS; |
325 | 0 | goto cleanup_node; |
326 | 0 | } |
327 | 0 | if (result != ISC_R_SUCCESS) { |
328 | 0 | goto cleanup_node; |
329 | 0 | } |
330 | | |
331 | 0 | DNS_RDATASET_FOREACH(&rdataset) { |
332 | 0 | rr_t rr = { 0, DNS_RDATA_INIT }; |
333 | 0 | dns_rdataset_current(&rdataset, &rr.rdata); |
334 | 0 | rr.ttl = rdataset.ttl; |
335 | 0 | result = (*rr_action)(rr_action_data, &rr); |
336 | 0 | if (result != ISC_R_SUCCESS) { |
337 | 0 | goto cleanup_rdataset; |
338 | 0 | } |
339 | 0 | } |
340 | | |
341 | 0 | result = ISC_R_SUCCESS; |
342 | |
|
343 | 0 | cleanup_rdataset: |
344 | 0 | dns_rdataset_disassociate(&rdataset); |
345 | 0 | cleanup_node: |
346 | 0 | dns_db_detachnode(&node); |
347 | |
|
348 | 0 | return result; |
349 | 0 | } |
350 | | |
351 | | /**************************************************************************/ |
352 | | /* |
353 | | * Various tests on the database contents (for prerequisites, etc). |
354 | | */ |
355 | | |
356 | | /*% |
357 | | * Function type for predicate functions that compare a database RR 'db_rr' |
358 | | * against an update RR 'update_rr'. |
359 | | */ |
360 | | typedef bool |
361 | | rr_predicate(dns_rdata_t *update_rr, dns_rdata_t *db_rr); |
362 | | |
363 | | /*% |
364 | | * Helper function for rrset_exists(). |
365 | | */ |
366 | | static isc_result_t |
367 | 0 | rrset_exists_action(void *data, rr_t *rr) { |
368 | 0 | UNUSED(data); |
369 | 0 | UNUSED(rr); |
370 | 0 | return ISC_R_EXISTS; |
371 | 0 | } |
372 | | |
373 | | /*% |
374 | | * Utility macro for RR existence checking functions. |
375 | | * |
376 | | * If the variable 'result' has the value ISC_R_EXISTS or |
377 | | * ISC_R_SUCCESS, set *exists to true or false, |
378 | | * respectively, and return success. |
379 | | * |
380 | | * If 'result' has any other value, there was a failure. |
381 | | * Return the failure result code and do not set *exists. |
382 | | * |
383 | | * This would be more readable as "do { if ... } while(0)", |
384 | | * but that form generates tons of warnings on Solaris 2.6. |
385 | | */ |
386 | | #define RETURN_EXISTENCE_FLAG \ |
387 | 0 | return ((result == ISC_R_EXISTS) \ |
388 | 0 | ? (*exists = true, ISC_R_SUCCESS) \ |
389 | 0 | : ((result == ISC_R_SUCCESS) \ |
390 | 0 | ? (*exists = false, ISC_R_SUCCESS) \ |
391 | 0 | : result)) |
392 | | |
393 | | /*% |
394 | | * Set '*exists' to true iff an rrset of the given type exists, |
395 | | * to false otherwise. |
396 | | */ |
397 | | static isc_result_t |
398 | | rrset_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, |
399 | 0 | dns_rdatatype_t type, dns_rdatatype_t covers, bool *exists) { |
400 | 0 | isc_result_t result; |
401 | 0 | result = foreach_rr(db, ver, name, type, covers, rrset_exists_action, |
402 | 0 | NULL); |
403 | 0 | RETURN_EXISTENCE_FLAG; |
404 | 0 | } |
405 | | |
406 | | /*% |
407 | | * Set '*visible' to true if the RRset exists and is part of the |
408 | | * visible zone. Otherwise '*visible' is set to false unless a |
409 | | * error occurs. |
410 | | */ |
411 | | static isc_result_t |
412 | | rrset_visible(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, |
413 | 0 | dns_rdatatype_t type, bool *visible) { |
414 | 0 | isc_result_t result; |
415 | 0 | dns_fixedname_t fixed; |
416 | |
|
417 | 0 | dns_fixedname_init(&fixed); |
418 | 0 | result = dns_db_find(db, name, ver, type, DNS_DBFIND_NOWILD, |
419 | 0 | (isc_stdtime_t)0, NULL, dns_fixedname_name(&fixed), |
420 | 0 | NULL, NULL); |
421 | 0 | switch (result) { |
422 | 0 | case ISC_R_SUCCESS: |
423 | 0 | *visible = true; |
424 | 0 | break; |
425 | | /* |
426 | | * Glue, obscured, deleted or replaced records. |
427 | | */ |
428 | 0 | case DNS_R_DELEGATION: |
429 | 0 | case DNS_R_DNAME: |
430 | 0 | case DNS_R_CNAME: |
431 | 0 | case DNS_R_NXDOMAIN: |
432 | 0 | case DNS_R_NXRRSET: |
433 | 0 | case DNS_R_EMPTYNAME: |
434 | 0 | case DNS_R_COVERINGNSEC: |
435 | 0 | *visible = false; |
436 | 0 | result = ISC_R_SUCCESS; |
437 | 0 | break; |
438 | 0 | default: |
439 | 0 | *visible = false; /* silence false compiler warning */ |
440 | 0 | break; |
441 | 0 | } |
442 | 0 | return result; |
443 | 0 | } |
444 | | |
445 | | /*% |
446 | | * Context struct and helper function for name_exists(). |
447 | | */ |
448 | | |
449 | | static isc_result_t |
450 | 0 | name_exists_action(void *data, dns_rdataset_t *rrset) { |
451 | 0 | UNUSED(data); |
452 | 0 | UNUSED(rrset); |
453 | 0 | return ISC_R_EXISTS; |
454 | 0 | } |
455 | | |
456 | | /*% |
457 | | * Set '*exists' to true iff the given name exists, to false otherwise. |
458 | | */ |
459 | | static isc_result_t |
460 | | name_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, |
461 | 0 | bool *exists) { |
462 | 0 | isc_result_t result; |
463 | 0 | result = foreach_rrset(db, ver, name, name_exists_action, NULL); |
464 | 0 | RETURN_EXISTENCE_FLAG; |
465 | 0 | } |
466 | | |
467 | | /**************************************************************************/ |
468 | | /* |
469 | | * Checking of "RRset exists (value dependent)" prerequisites. |
470 | | * |
471 | | * In the RFC2136 section 3.2.5, this is the pseudocode involving |
472 | | * a variable called "temp", a mapping of <name, type> tuples to rrsets. |
473 | | * |
474 | | * Here, we represent the "temp" data structure as (non-minimal) "dns_diff_t" |
475 | | * where each tuple has op==DNS_DIFFOP_EXISTS. |
476 | | */ |
477 | | |
478 | | /*% |
479 | | * A comparison function defining the sorting order for the entries |
480 | | * in the "temp" data structure. The major sort key is the owner name, |
481 | | * followed by the type and rdata. |
482 | | */ |
483 | | static int |
484 | 0 | temp_order(const void *av, const void *bv) { |
485 | 0 | dns_difftuple_t const *const *ap = av; |
486 | 0 | dns_difftuple_t const *const *bp = bv; |
487 | 0 | dns_difftuple_t const *a = *ap; |
488 | 0 | dns_difftuple_t const *b = *bp; |
489 | 0 | int r; |
490 | 0 | r = dns_name_compare(&a->name, &b->name); |
491 | 0 | if (r != 0) { |
492 | 0 | return r; |
493 | 0 | } |
494 | 0 | r = (b->rdata.type - a->rdata.type); |
495 | 0 | if (r != 0) { |
496 | 0 | return r; |
497 | 0 | } |
498 | 0 | r = dns_rdata_casecompare(&a->rdata, &b->rdata); |
499 | 0 | return r; |
500 | 0 | } |
501 | | |
502 | | /**************************************************************************/ |
503 | | /* |
504 | | * Conditional deletion of RRs. |
505 | | */ |
506 | | |
507 | | /*% |
508 | | * Context structure for delete_if(). |
509 | | */ |
510 | | |
511 | | typedef struct { |
512 | | rr_predicate *predicate; |
513 | | dns_db_t *db; |
514 | | dns_dbversion_t *ver; |
515 | | dns_diff_t *diff; |
516 | | dns_name_t *name; |
517 | | dns_rdata_t *update_rr; |
518 | | } conditional_delete_ctx_t; |
519 | | |
520 | | /*% |
521 | | * Predicate functions for delete_if(). |
522 | | */ |
523 | | |
524 | | /*% |
525 | | * Return true always. |
526 | | */ |
527 | | static bool |
528 | 0 | true_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { |
529 | 0 | UNUSED(update_rr); |
530 | 0 | UNUSED(db_rr); |
531 | 0 | return true; |
532 | 0 | } |
533 | | |
534 | | /*% |
535 | | * Return true if the record is a RRSIG. |
536 | | */ |
537 | | static bool |
538 | 0 | rrsig_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) { |
539 | 0 | UNUSED(update_rr); |
540 | 0 | return (db_rr->type == dns_rdatatype_rrsig) ? true : false; |
541 | 0 | } |
542 | | |
543 | | /*% |
544 | | * Internal helper function for delete_if(). |
545 | | */ |
546 | | static isc_result_t |
547 | 0 | delete_if_action(void *data, rr_t *rr) { |
548 | 0 | conditional_delete_ctx_t *ctx = data; |
549 | 0 | if ((*ctx->predicate)(ctx->update_rr, &rr->rdata)) { |
550 | 0 | isc_result_t result; |
551 | 0 | result = update_one_rr(ctx->db, ctx->ver, ctx->diff, |
552 | 0 | DNS_DIFFOP_DEL, ctx->name, rr->ttl, |
553 | 0 | &rr->rdata); |
554 | 0 | return result; |
555 | 0 | } else { |
556 | 0 | return ISC_R_SUCCESS; |
557 | 0 | } |
558 | 0 | } |
559 | | |
560 | | /*% |
561 | | * Conditionally delete RRs. Apply 'predicate' to the RRs |
562 | | * specified by 'db', 'ver', 'name', and 'type' (which can |
563 | | * be dns_rdatatype_any to match any type). Delete those |
564 | | * RRs for which the predicate returns true, and log the |
565 | | * deletions in 'diff'. |
566 | | */ |
567 | | static isc_result_t |
568 | | delete_if(rr_predicate *predicate, dns_db_t *db, dns_dbversion_t *ver, |
569 | | dns_name_t *name, dns_rdatatype_t type, dns_rdatatype_t covers, |
570 | 0 | dns_rdata_t *update_rr, dns_diff_t *diff) { |
571 | 0 | conditional_delete_ctx_t ctx; |
572 | 0 | ctx.predicate = predicate; |
573 | 0 | ctx.db = db; |
574 | 0 | ctx.ver = ver; |
575 | 0 | ctx.diff = diff; |
576 | 0 | ctx.name = name; |
577 | 0 | ctx.update_rr = update_rr; |
578 | 0 | return foreach_rr(db, ver, name, type, covers, delete_if_action, &ctx); |
579 | 0 | } |
580 | | |
581 | | /**************************************************************************/ |
582 | | /* |
583 | | * Incremental updating of NSECs and RRSIGs. |
584 | | */ |
585 | | |
586 | | /*% |
587 | | * We abuse the dns_diff_t type to represent a set of domain names |
588 | | * affected by the update. |
589 | | */ |
590 | | static void |
591 | 0 | namelist_append_name(dns_diff_t *list, dns_name_t *name) { |
592 | 0 | dns_difftuple_t *tuple = NULL; |
593 | 0 | static dns_rdata_t dummy_rdata = DNS_RDATA_INIT; |
594 | |
|
595 | 0 | dns_difftuple_create(list->mctx, DNS_DIFFOP_EXISTS, name, 0, |
596 | 0 | &dummy_rdata, &tuple); |
597 | 0 | dns_diff_append(list, &tuple); |
598 | 0 | } |
599 | | |
600 | | static isc_result_t |
601 | | namelist_append_subdomain(dns_db_t *db, dns_name_t *name, |
602 | 0 | dns_diff_t *affected) { |
603 | 0 | isc_result_t result; |
604 | 0 | dns_fixedname_t fixedname; |
605 | 0 | dns_name_t *child; |
606 | 0 | dns_dbiterator_t *dbit = NULL; |
607 | |
|
608 | 0 | child = dns_fixedname_initname(&fixedname); |
609 | |
|
610 | 0 | CHECK(dns_db_createiterator(db, DNS_DB_NONSEC3, &dbit)); |
611 | |
|
612 | 0 | for (result = dns_dbiterator_seek(dbit, name); result == ISC_R_SUCCESS; |
613 | 0 | result = dns_dbiterator_next(dbit)) |
614 | 0 | { |
615 | 0 | dns_dbnode_t *node = NULL; |
616 | 0 | CHECK(dns_dbiterator_current(dbit, &node, child)); |
617 | 0 | dns_db_detachnode(&node); |
618 | 0 | if (!dns_name_issubdomain(child, name)) { |
619 | 0 | break; |
620 | 0 | } |
621 | 0 | namelist_append_name(affected, child); |
622 | 0 | } |
623 | 0 | if (result == ISC_R_NOMORE) { |
624 | 0 | result = ISC_R_SUCCESS; |
625 | 0 | } |
626 | 0 | cleanup: |
627 | 0 | if (dbit != NULL) { |
628 | 0 | dns_dbiterator_destroy(&dbit); |
629 | 0 | } |
630 | 0 | return result; |
631 | 0 | } |
632 | | |
633 | | /*% |
634 | | * Helper function for non_nsec_rrset_exists(). |
635 | | */ |
636 | | static isc_result_t |
637 | 0 | is_non_nsec_action(void *data, dns_rdataset_t *rrset) { |
638 | 0 | UNUSED(data); |
639 | 0 | if (!(dns_rdatatype_isnsec(rrset->type) || |
640 | 0 | (rrset->type == dns_rdatatype_rrsig && |
641 | 0 | dns_rdatatype_isnsec(rrset->covers)))) |
642 | 0 | { |
643 | 0 | return ISC_R_EXISTS; |
644 | 0 | } |
645 | 0 | return ISC_R_SUCCESS; |
646 | 0 | } |
647 | | |
648 | | /*% |
649 | | * Check whether there is an rrset other than a NSEC or RRSIG NSEC, |
650 | | * i.e., anything that justifies the continued existence of a name |
651 | | * after a secure update. |
652 | | * |
653 | | * If such an rrset exists, set '*exists' to true. |
654 | | * Otherwise, set it to false. |
655 | | */ |
656 | | static isc_result_t |
657 | | non_nsec_rrset_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, |
658 | 0 | bool *exists) { |
659 | 0 | isc_result_t result; |
660 | 0 | result = foreach_rrset(db, ver, name, is_non_nsec_action, NULL); |
661 | 0 | RETURN_EXISTENCE_FLAG; |
662 | 0 | } |
663 | | |
664 | | /*% |
665 | | * A comparison function for sorting dns_diff_t:s by name. |
666 | | */ |
667 | | static int |
668 | 0 | name_order(const void *av, const void *bv) { |
669 | 0 | dns_difftuple_t const *const *ap = av; |
670 | 0 | dns_difftuple_t const *const *bp = bv; |
671 | 0 | dns_difftuple_t const *a = *ap; |
672 | 0 | dns_difftuple_t const *b = *bp; |
673 | 0 | return dns_name_compare(&a->name, &b->name); |
674 | 0 | } |
675 | | |
676 | | static isc_result_t |
677 | 0 | uniqify_name_list(dns_diff_t *list) { |
678 | 0 | isc_result_t result; |
679 | |
|
680 | 0 | CHECK(dns_diff_sort(list, name_order)); |
681 | |
|
682 | 0 | dns_name_t *curr_name = NULL; |
683 | 0 | ISC_LIST_FOREACH(list->tuples, p, link) { |
684 | 0 | if (curr_name == NULL || !dns_name_equal(curr_name, &p->name)) { |
685 | 0 | curr_name = &(p->name); |
686 | 0 | } else { |
687 | 0 | ISC_LIST_UNLINK(list->tuples, p, link); |
688 | 0 | dns_difftuple_free(&p); |
689 | 0 | } |
690 | 0 | } |
691 | 0 | cleanup: |
692 | 0 | return result; |
693 | 0 | } |
694 | | |
695 | | static isc_result_t |
696 | | is_active(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, bool *flag, |
697 | 0 | bool *cut, bool *unsecure) { |
698 | 0 | isc_result_t result; |
699 | 0 | dns_fixedname_t foundname; |
700 | 0 | dns_fixedname_init(&foundname); |
701 | 0 | result = dns_db_find(db, name, ver, dns_rdatatype_any, |
702 | 0 | DNS_DBFIND_GLUEOK | DNS_DBFIND_NOWILD, |
703 | 0 | (isc_stdtime_t)0, NULL, |
704 | 0 | dns_fixedname_name(&foundname), NULL, NULL); |
705 | 0 | if (result == ISC_R_SUCCESS || result == DNS_R_EMPTYNAME) { |
706 | 0 | *flag = true; |
707 | 0 | *cut = false; |
708 | 0 | SET_IF_NOT_NULL(unsecure, false); |
709 | 0 | return ISC_R_SUCCESS; |
710 | 0 | } else if (result == DNS_R_ZONECUT) { |
711 | 0 | *flag = true; |
712 | 0 | *cut = true; |
713 | 0 | if (unsecure != NULL) { |
714 | | /* |
715 | | * We are at the zonecut. Check to see if there |
716 | | * is a DS RRset. |
717 | | */ |
718 | 0 | if (dns_db_find(db, name, ver, dns_rdatatype_ds, 0, |
719 | 0 | (isc_stdtime_t)0, NULL, |
720 | 0 | dns_fixedname_name(&foundname), NULL, |
721 | 0 | NULL) == DNS_R_NXRRSET) |
722 | 0 | { |
723 | 0 | *unsecure = true; |
724 | 0 | } else { |
725 | 0 | *unsecure = false; |
726 | 0 | } |
727 | 0 | } |
728 | 0 | return ISC_R_SUCCESS; |
729 | 0 | } else if (result == DNS_R_GLUE || result == DNS_R_DNAME || |
730 | 0 | result == DNS_R_DELEGATION || result == DNS_R_NXDOMAIN) |
731 | 0 | { |
732 | 0 | *flag = false; |
733 | 0 | *cut = false; |
734 | 0 | SET_IF_NOT_NULL(unsecure, false); |
735 | 0 | return ISC_R_SUCCESS; |
736 | 0 | } else { |
737 | | /* |
738 | | * Silence compiler. |
739 | | */ |
740 | 0 | *flag = false; |
741 | 0 | *cut = false; |
742 | 0 | SET_IF_NOT_NULL(unsecure, false); |
743 | 0 | return result; |
744 | 0 | } |
745 | 0 | } |
746 | | |
747 | | /*% |
748 | | * Find the next/previous name that has a NSEC record. |
749 | | * In other words, skip empty database nodes and names that |
750 | | * have had their NSECs removed because they are obscured by |
751 | | * a zone cut. |
752 | | */ |
753 | | static isc_result_t |
754 | | next_active(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, |
755 | | dns_dbversion_t *ver, dns_name_t *oldname, dns_name_t *newname, |
756 | 0 | bool forward) { |
757 | 0 | isc_result_t result; |
758 | 0 | dns_dbiterator_t *dbit = NULL; |
759 | 0 | bool has_nsec = false; |
760 | 0 | unsigned int wraps = 0; |
761 | 0 | bool secure = dns_db_issecure(db); |
762 | |
|
763 | 0 | CHECK(dns_db_createiterator(db, 0, &dbit)); |
764 | |
|
765 | 0 | CHECK(dns_dbiterator_seek(dbit, oldname)); |
766 | 0 | do { |
767 | 0 | dns_dbnode_t *node = NULL; |
768 | |
|
769 | 0 | if (forward) { |
770 | 0 | result = dns_dbiterator_next(dbit); |
771 | 0 | } else { |
772 | 0 | result = dns_dbiterator_prev(dbit); |
773 | 0 | } |
774 | 0 | if (result == ISC_R_NOMORE) { |
775 | | /* |
776 | | * Wrap around. |
777 | | */ |
778 | 0 | if (forward) { |
779 | 0 | CHECK(dns_dbiterator_first(dbit)); |
780 | 0 | } else { |
781 | 0 | CHECK(dns_dbiterator_last(dbit)); |
782 | 0 | } |
783 | 0 | wraps++; |
784 | 0 | if (wraps == 2) { |
785 | 0 | update_log(log, zone, ISC_LOG_ERROR, |
786 | 0 | "secure zone with no NSECs"); |
787 | 0 | CLEANUP(DNS_R_BADZONE); |
788 | 0 | } |
789 | 0 | } |
790 | 0 | CHECK(dns_dbiterator_current(dbit, &node, newname)); |
791 | 0 | dns_db_detachnode(&node); |
792 | | |
793 | | /* |
794 | | * The iterator may hold the tree lock, and |
795 | | * rrset_exists() calls dns_db_findnode() which |
796 | | * may try to reacquire it. To avoid deadlock |
797 | | * we must pause the iterator first. |
798 | | */ |
799 | 0 | CHECK(dns_dbiterator_pause(dbit)); |
800 | 0 | if (secure) { |
801 | 0 | CHECK(rrset_exists(db, ver, newname, dns_rdatatype_nsec, |
802 | 0 | 0, &has_nsec)); |
803 | 0 | } else { |
804 | 0 | dns_fixedname_t ffound; |
805 | 0 | dns_name_t *found; |
806 | 0 | found = dns_fixedname_initname(&ffound); |
807 | 0 | result = dns_db_find( |
808 | 0 | db, newname, ver, dns_rdatatype_soa, |
809 | 0 | DNS_DBFIND_NOWILD, 0, NULL, found, NULL, NULL); |
810 | 0 | if (result == ISC_R_SUCCESS || |
811 | 0 | result == DNS_R_EMPTYNAME || |
812 | 0 | result == DNS_R_NXRRSET || result == DNS_R_CNAME || |
813 | 0 | (result == DNS_R_DELEGATION && |
814 | 0 | dns_name_equal(newname, found))) |
815 | 0 | { |
816 | 0 | has_nsec = true; |
817 | 0 | result = ISC_R_SUCCESS; |
818 | 0 | } else if (result != DNS_R_NXDOMAIN) { |
819 | 0 | break; |
820 | 0 | } |
821 | 0 | } |
822 | 0 | } while (!has_nsec); |
823 | 0 | cleanup: |
824 | 0 | if (dbit != NULL) { |
825 | 0 | dns_dbiterator_destroy(&dbit); |
826 | 0 | } |
827 | |
|
828 | 0 | return result; |
829 | 0 | } |
830 | | |
831 | | /*% |
832 | | * Add a NSEC record for "name", recording the change in "diff". |
833 | | * The existing NSEC is removed. |
834 | | */ |
835 | | static isc_result_t |
836 | | add_nsec(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, |
837 | | dns_dbversion_t *ver, dns_name_t *name, dns_ttl_t nsecttl, |
838 | 0 | dns_diff_t *diff) { |
839 | 0 | isc_result_t result; |
840 | 0 | dns_dbnode_t *node = NULL; |
841 | 0 | unsigned char buffer[DNS_NSEC_BUFFERSIZE]; |
842 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
843 | 0 | dns_difftuple_t *tuple = NULL; |
844 | 0 | dns_fixedname_t fixedname; |
845 | 0 | dns_name_t *target; |
846 | |
|
847 | 0 | target = dns_fixedname_initname(&fixedname); |
848 | | |
849 | | /* |
850 | | * Find the successor name, aka NSEC target. |
851 | | */ |
852 | 0 | CHECK(next_active(log, zone, db, ver, name, target, true)); |
853 | | |
854 | | /* |
855 | | * Create the NSEC RDATA. |
856 | | */ |
857 | 0 | CHECK(dns_db_findnode(db, name, false, &node)); |
858 | 0 | dns_rdata_init(&rdata); |
859 | 0 | CHECK(dns_nsec_buildrdata(db, ver, node, target, buffer, &rdata)); |
860 | 0 | dns_db_detachnode(&node); |
861 | | |
862 | | /* |
863 | | * Delete the old NSEC and record the change. |
864 | | */ |
865 | 0 | CHECK(delete_if(true_p, db, ver, name, dns_rdatatype_nsec, 0, NULL, |
866 | 0 | diff)); |
867 | | /* |
868 | | * Add the new NSEC and record the change. |
869 | | */ |
870 | 0 | dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, nsecttl, &rdata, |
871 | 0 | &tuple); |
872 | 0 | CHECK(do_one_tuple(&tuple, db, ver, diff)); |
873 | 0 | INSIST(tuple == NULL); |
874 | |
|
875 | 0 | cleanup: |
876 | 0 | if (node != NULL) { |
877 | 0 | dns_db_detachnode(&node); |
878 | 0 | } |
879 | 0 | return result; |
880 | 0 | } |
881 | | |
882 | | /*% |
883 | | * Add a placeholder NSEC record for "name", recording the change in "diff". |
884 | | */ |
885 | | static isc_result_t |
886 | | add_placeholder_nsec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, |
887 | 0 | dns_diff_t *diff) { |
888 | 0 | isc_result_t result; |
889 | 0 | dns_difftuple_t *tuple = NULL; |
890 | 0 | isc_region_t r; |
891 | 0 | unsigned char data[1] = { 0 }; /* The root domain, no bits. */ |
892 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
893 | |
|
894 | 0 | r.base = data; |
895 | 0 | r.length = sizeof(data); |
896 | 0 | dns_rdata_fromregion(&rdata, dns_db_class(db), dns_rdatatype_nsec, &r); |
897 | 0 | dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, 0, &rdata, |
898 | 0 | &tuple); |
899 | 0 | CHECK(do_one_tuple(&tuple, db, ver, diff)); |
900 | 0 | cleanup: |
901 | 0 | return result; |
902 | 0 | } |
903 | | |
904 | | static isc_result_t |
905 | | find_zone_keys(dns_zone_t *zone, isc_mem_t *mctx, unsigned int maxkeys, |
906 | 0 | dst_key_t **keys, unsigned int *nkeys) { |
907 | 0 | dns_dnsseckeylist_t keylist; |
908 | 0 | unsigned int count = 0; |
909 | 0 | isc_result_t result; |
910 | 0 | isc_stdtime_t now = isc_stdtime_now(); |
911 | 0 | dns_kasp_t *kasp; |
912 | 0 | dns_keystorelist_t *keystores; |
913 | 0 | const char *keydir; |
914 | |
|
915 | 0 | ISC_LIST_INIT(keylist); |
916 | |
|
917 | 0 | kasp = dns_zone_getkasp(zone); |
918 | 0 | keydir = dns_zone_getkeydirectory(zone); |
919 | 0 | keystores = dns_zone_getkeystores(zone); |
920 | |
|
921 | 0 | dns_zone_lock_keyfiles(zone); |
922 | 0 | result = dns_dnssec_findmatchingkeys(dns_zone_getorigin(zone), kasp, |
923 | 0 | keydir, keystores, now, false, |
924 | 0 | mctx, &keylist); |
925 | 0 | dns_zone_unlock_keyfiles(zone); |
926 | |
|
927 | 0 | if (result != ISC_R_SUCCESS) { |
928 | 0 | *nkeys = 0; |
929 | 0 | return result; |
930 | 0 | } |
931 | | |
932 | | /* Add new 'dnskeys' to 'keys' */ |
933 | 0 | ISC_LIST_FOREACH(keylist, k, link) { |
934 | 0 | if (count >= maxkeys) { |
935 | 0 | ISC_LIST_UNLINK(keylist, k, link); |
936 | 0 | dns_dnsseckey_destroy(mctx, &k); |
937 | 0 | result = ISC_R_NOSPACE; |
938 | 0 | break; |
939 | 0 | } |
940 | | |
941 | | /* Detect inactive keys */ |
942 | 0 | if (!dns_dnssec_keyactive(k->key, now)) { |
943 | 0 | dst_key_setinactive(k->key, true); |
944 | 0 | } |
945 | |
|
946 | 0 | keys[count] = k->key; |
947 | 0 | k->key = NULL; |
948 | 0 | count++; |
949 | |
|
950 | 0 | ISC_LIST_UNLINK(keylist, k, link); |
951 | 0 | dns_dnsseckey_destroy(mctx, &k); |
952 | 0 | } |
953 | |
|
954 | 0 | *nkeys = count; |
955 | 0 | return result; |
956 | 0 | } |
957 | | |
958 | | /*% |
959 | | * Add RRSIG records for an RRset, recording the change in "diff". |
960 | | */ |
961 | | static isc_result_t |
962 | | add_sigs(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, |
963 | | dns_dbversion_t *ver, dns_name_t *name, dns_rdatatype_t type, |
964 | | dns_diff_t *diff, dst_key_t **keys, unsigned int nkeys, |
965 | 0 | isc_stdtime_t now, isc_stdtime_t inception, isc_stdtime_t expire) { |
966 | 0 | isc_result_t result; |
967 | 0 | dns_dbnode_t *node = NULL; |
968 | 0 | dns_kasp_t *kasp = dns_zone_getkasp(zone); |
969 | 0 | dns_rdataset_t rdataset; |
970 | 0 | dns_rdata_t sig_rdata = DNS_RDATA_INIT; |
971 | 0 | dns_stats_t *dnssecsignstats = dns_zone_getdnssecsignstats(zone); |
972 | 0 | isc_buffer_t buffer; |
973 | 0 | unsigned char data[1024]; /* XXX */ |
974 | 0 | unsigned int i; |
975 | 0 | bool added_sig = false; |
976 | 0 | bool use_kasp = false; |
977 | 0 | bool offlineksk = false; |
978 | 0 | isc_mem_t *mctx = diff->mctx; |
979 | |
|
980 | 0 | if (kasp != NULL) { |
981 | 0 | use_kasp = true; |
982 | 0 | offlineksk = dns_kasp_offlineksk(kasp); |
983 | 0 | } |
984 | |
|
985 | 0 | dns_rdataset_init(&rdataset); |
986 | 0 | isc_buffer_init(&buffer, data, sizeof(data)); |
987 | | |
988 | | /* Get the rdataset to sign. */ |
989 | 0 | if (type == dns_rdatatype_nsec3) { |
990 | 0 | CHECK(dns_db_findnsec3node(db, name, false, &node)); |
991 | 0 | } else { |
992 | 0 | CHECK(dns_db_findnode(db, name, false, &node)); |
993 | 0 | } |
994 | 0 | CHECK(dns_db_findrdataset(db, node, ver, type, 0, (isc_stdtime_t)0, |
995 | 0 | &rdataset, NULL)); |
996 | 0 | dns_db_detachnode(&node); |
997 | |
|
998 | 0 | #define REVOKE(x) ((dst_key_flags(x) & DNS_KEYFLAG_REVOKE) != 0) |
999 | 0 | #define KSK(x) ((dst_key_flags(x) & DNS_KEYFLAG_KSK) != 0) |
1000 | 0 | #define ID(x) dst_key_id(x) |
1001 | 0 | #define ALG(x) dst_key_alg(x) |
1002 | | |
1003 | | /* |
1004 | | * If we are honoring KSK flags then we need to check that we |
1005 | | * have both KSK and non-KSK keys that are not revoked per |
1006 | | * algorithm. |
1007 | | */ |
1008 | 0 | for (i = 0; i < nkeys; i++) { |
1009 | 0 | bool both = false; |
1010 | | |
1011 | | /* Don't add signatures for offline or inactive keys */ |
1012 | 0 | if (!dst_key_isprivate(keys[i]) && !offlineksk) { |
1013 | 0 | continue; |
1014 | 0 | } |
1015 | 0 | if (dst_key_inactive(keys[i]) && !offlineksk) { |
1016 | 0 | continue; |
1017 | 0 | } |
1018 | | |
1019 | 0 | if (use_kasp) { |
1020 | | /* |
1021 | | * A dnssec-policy is found. Check what RRsets this |
1022 | | * key should sign. |
1023 | | */ |
1024 | 0 | isc_stdtime_t when; |
1025 | 0 | isc_result_t kresult; |
1026 | 0 | bool ksk = false; |
1027 | 0 | bool zsk = false; |
1028 | |
|
1029 | 0 | kresult = dst_key_getbool(keys[i], DST_BOOL_KSK, &ksk); |
1030 | 0 | if (kresult != ISC_R_SUCCESS) { |
1031 | 0 | if (KSK(keys[i])) { |
1032 | 0 | ksk = true; |
1033 | 0 | } |
1034 | 0 | } |
1035 | 0 | kresult = dst_key_getbool(keys[i], DST_BOOL_ZSK, &zsk); |
1036 | 0 | if (kresult != ISC_R_SUCCESS) { |
1037 | 0 | if (!KSK(keys[i])) { |
1038 | 0 | zsk = true; |
1039 | 0 | } |
1040 | 0 | } |
1041 | |
|
1042 | 0 | if (!dst_key_isprivate(keys[i]) && offlineksk && zsk) { |
1043 | 0 | continue; |
1044 | 0 | } |
1045 | 0 | if (dst_key_inactive(keys[i]) && offlineksk && zsk) { |
1046 | 0 | continue; |
1047 | 0 | } |
1048 | | |
1049 | 0 | if (dns_rdatatype_iskeymaterial(type)) { |
1050 | | /* |
1051 | | * DNSKEY RRset is signed with KSK. |
1052 | | * CDS and CDNSKEY RRsets too (RFC 7344, 4.1). |
1053 | | */ |
1054 | 0 | if (!ksk) { |
1055 | 0 | continue; |
1056 | 0 | } |
1057 | 0 | } else if (!zsk) { |
1058 | | /* |
1059 | | * Other RRsets are signed with ZSK. |
1060 | | */ |
1061 | 0 | continue; |
1062 | 0 | } else if (zsk && |
1063 | 0 | !dst_key_is_signing(keys[i], DST_BOOL_ZSK, |
1064 | 0 | now, &when)) |
1065 | 0 | { |
1066 | | /* |
1067 | | * This key is not active for zone-signing. |
1068 | | */ |
1069 | 0 | continue; |
1070 | 0 | } |
1071 | 0 | } else if (!REVOKE(keys[i])) { |
1072 | | /* |
1073 | | * Don't consider inactive keys, however the KSK may be |
1074 | | * temporary offline, so do consider KSKs which private |
1075 | | * key files are unavailable. |
1076 | | */ |
1077 | 0 | both = dst_key_have_ksk_and_zsk( |
1078 | 0 | keys, nkeys, i, false, KSK(keys[i]), |
1079 | 0 | !KSK(keys[i]), NULL, NULL); |
1080 | 0 | if (both) { |
1081 | | /* |
1082 | | * CDS and CDNSKEY are signed with KSK (RFC |
1083 | | * 7344, 4.1). |
1084 | | */ |
1085 | 0 | if (dns_rdatatype_iskeymaterial(type)) { |
1086 | 0 | if (!KSK(keys[i])) { |
1087 | 0 | continue; |
1088 | 0 | } |
1089 | 0 | } else if (KSK(keys[i])) { |
1090 | 0 | continue; |
1091 | 0 | } |
1092 | 0 | } |
1093 | 0 | } |
1094 | | |
1095 | | /* |
1096 | | * If this key is revoked, it may only sign the DNSKEY RRset. |
1097 | | */ |
1098 | 0 | if (REVOKE(keys[i]) && type != dns_rdatatype_dnskey) { |
1099 | 0 | continue; |
1100 | 0 | } |
1101 | | |
1102 | | /* Calculate the signature, creating a RRSIG RDATA. */ |
1103 | 0 | if (offlineksk && dns_rdatatype_iskeymaterial(type)) { |
1104 | | /* Look up the signature in the SKR bundle */ |
1105 | 0 | dns_skrbundle_t *bundle = dns_zone_getskrbundle(zone); |
1106 | 0 | if (bundle == NULL) { |
1107 | 0 | CLEANUP(DNS_R_NOSKRBUNDLE); |
1108 | 0 | } |
1109 | 0 | CHECK(dns_skrbundle_getsig(bundle, keys[i], type, |
1110 | 0 | &sig_rdata)); |
1111 | 0 | } else { |
1112 | 0 | CHECK(dns_dnssec_sign(name, &rdataset, keys[i], |
1113 | 0 | &inception, &expire, mctx, |
1114 | 0 | &buffer, &sig_rdata)); |
1115 | 0 | } |
1116 | | |
1117 | | /* Update the database and journal with the RRSIG. */ |
1118 | | /* XXX inefficient - will cause dataset merging */ |
1119 | 0 | CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADDRESIGN, name, |
1120 | 0 | rdataset.ttl, &sig_rdata)); |
1121 | 0 | dns_rdata_reset(&sig_rdata); |
1122 | 0 | isc_buffer_init(&buffer, data, sizeof(data)); |
1123 | 0 | added_sig = true; |
1124 | | /* Update DNSSEC sign statistics. */ |
1125 | 0 | if (dnssecsignstats != NULL) { |
1126 | 0 | dns_dnssecsignstats_increment(dnssecsignstats, |
1127 | 0 | ID(keys[i]), |
1128 | 0 | (uint8_t)ALG(keys[i]), |
1129 | 0 | dns_dnssecsignstats_sign); |
1130 | 0 | } |
1131 | 0 | } |
1132 | 0 | if (!added_sig) { |
1133 | 0 | update_log(log, zone, ISC_LOG_ERROR, |
1134 | 0 | "found no active private keys, " |
1135 | 0 | "unable to generate any signatures"); |
1136 | 0 | result = ISC_R_NOTFOUND; |
1137 | 0 | } |
1138 | |
|
1139 | 0 | cleanup: |
1140 | 0 | dns_rdataset_cleanup(&rdataset); |
1141 | 0 | if (node != NULL) { |
1142 | 0 | dns_db_detachnode(&node); |
1143 | 0 | } |
1144 | 0 | return result; |
1145 | 0 | } |
1146 | | |
1147 | | /* |
1148 | | * Delete expired RRsigs and any RRsigs we are about to re-sign. |
1149 | | * See also zone.c:del_sigs(). |
1150 | | */ |
1151 | | static isc_result_t |
1152 | | del_keysigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name, |
1153 | 0 | dns_diff_t *diff, dst_key_t **keys, unsigned int nkeys) { |
1154 | 0 | isc_result_t result; |
1155 | 0 | dns_dbnode_t *node = NULL; |
1156 | 0 | dns_rdataset_t rdataset; |
1157 | 0 | unsigned int i; |
1158 | 0 | dns_rdata_rrsig_t rrsig; |
1159 | 0 | bool found; |
1160 | |
|
1161 | 0 | dns_rdataset_init(&rdataset); |
1162 | |
|
1163 | 0 | result = dns_db_findnode(db, name, false, &node); |
1164 | 0 | if (result == ISC_R_NOTFOUND) { |
1165 | 0 | return ISC_R_SUCCESS; |
1166 | 0 | } |
1167 | 0 | CHECK(result); |
1168 | |
|
1169 | 0 | result = dns_db_findrdataset(db, node, ver, dns_rdatatype_rrsig, |
1170 | 0 | dns_rdatatype_dnskey, (isc_stdtime_t)0, |
1171 | 0 | &rdataset, NULL); |
1172 | 0 | dns_db_detachnode(&node); |
1173 | |
|
1174 | 0 | if (result == ISC_R_NOTFOUND) { |
1175 | 0 | return ISC_R_SUCCESS; |
1176 | 0 | } |
1177 | 0 | CHECK(result); |
1178 | |
|
1179 | 0 | DNS_RDATASET_FOREACH(&rdataset) { |
1180 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
1181 | 0 | dns_rdataset_current(&rdataset, &rdata); |
1182 | 0 | result = dns_rdata_tostruct(&rdata, &rrsig, NULL); |
1183 | 0 | RUNTIME_CHECK(result == ISC_R_SUCCESS); |
1184 | 0 | found = false; |
1185 | 0 | for (i = 0; i < nkeys; i++) { |
1186 | 0 | if (rrsig.keyid == dst_key_id(keys[i])) { |
1187 | 0 | found = true; |
1188 | 0 | if (!dst_key_isprivate(keys[i]) && |
1189 | 0 | !dst_key_inactive(keys[i])) |
1190 | 0 | { |
1191 | | /* |
1192 | | * The re-signing code in zone.c |
1193 | | * will mark this as offline. |
1194 | | * Just skip the record for now. |
1195 | | */ |
1196 | 0 | break; |
1197 | 0 | } |
1198 | 0 | result = update_one_rr(db, ver, diff, |
1199 | 0 | DNS_DIFFOP_DEL, name, |
1200 | 0 | rdataset.ttl, &rdata); |
1201 | 0 | break; |
1202 | 0 | } |
1203 | 0 | } |
1204 | | /* |
1205 | | * If there is not a matching DNSKEY then delete the RRSIG. |
1206 | | */ |
1207 | 0 | if (!found) { |
1208 | 0 | result = update_one_rr(db, ver, diff, DNS_DIFFOP_DEL, |
1209 | 0 | name, rdataset.ttl, &rdata); |
1210 | 0 | } |
1211 | 0 | dns_rdata_reset(&rdata); |
1212 | 0 | if (result != ISC_R_SUCCESS) { |
1213 | 0 | break; |
1214 | 0 | } |
1215 | 0 | } |
1216 | 0 | dns_rdataset_disassociate(&rdataset); |
1217 | |
|
1218 | 0 | cleanup: |
1219 | 0 | if (node != NULL) { |
1220 | 0 | dns_db_detachnode(&node); |
1221 | 0 | } |
1222 | 0 | return result; |
1223 | 0 | } |
1224 | | |
1225 | | static isc_result_t |
1226 | | add_exposed_sigs(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, |
1227 | | dns_dbversion_t *ver, dns_name_t *name, bool cut, |
1228 | | dns_diff_t *diff, dst_key_t **keys, unsigned int nkeys, |
1229 | | isc_stdtime_t now, isc_stdtime_t inception, |
1230 | 0 | isc_stdtime_t expire, unsigned int *sigs) { |
1231 | 0 | isc_result_t result; |
1232 | 0 | dns_dbnode_t *node; |
1233 | 0 | dns_rdatasetiter_t *iter; |
1234 | |
|
1235 | 0 | node = NULL; |
1236 | 0 | result = dns_db_findnode(db, name, false, &node); |
1237 | 0 | if (result == ISC_R_NOTFOUND) { |
1238 | 0 | return ISC_R_SUCCESS; |
1239 | 0 | } |
1240 | 0 | if (result != ISC_R_SUCCESS) { |
1241 | 0 | return result; |
1242 | 0 | } |
1243 | | |
1244 | 0 | iter = NULL; |
1245 | 0 | result = dns_db_allrdatasets(db, node, ver, 0, (isc_stdtime_t)0, &iter); |
1246 | 0 | if (result != ISC_R_SUCCESS) { |
1247 | 0 | goto cleanup_node; |
1248 | 0 | } |
1249 | | |
1250 | 0 | DNS_RDATASETITER_FOREACH(iter) { |
1251 | 0 | dns_rdataset_t rdataset = DNS_RDATASET_INIT; |
1252 | 0 | dns_rdatatype_t type; |
1253 | 0 | bool flag; |
1254 | |
|
1255 | 0 | dns_rdatasetiter_current(iter, &rdataset); |
1256 | 0 | type = rdataset.type; |
1257 | 0 | dns_rdataset_disassociate(&rdataset); |
1258 | | |
1259 | | /* |
1260 | | * We don't need to sign unsigned NSEC records at the cut |
1261 | | * as they are handled elsewhere. |
1262 | | */ |
1263 | 0 | if ((type == dns_rdatatype_rrsig) || |
1264 | 0 | (cut && type != dns_rdatatype_ds)) |
1265 | 0 | { |
1266 | 0 | continue; |
1267 | 0 | } |
1268 | 0 | result = rrset_exists(db, ver, name, dns_rdatatype_rrsig, type, |
1269 | 0 | &flag); |
1270 | 0 | if (result != ISC_R_SUCCESS) { |
1271 | 0 | break; |
1272 | 0 | } |
1273 | 0 | if (flag) { |
1274 | 0 | continue; |
1275 | 0 | } |
1276 | 0 | result = add_sigs(log, zone, db, ver, name, type, diff, keys, |
1277 | 0 | nkeys, now, inception, expire); |
1278 | 0 | if (result != ISC_R_SUCCESS) { |
1279 | 0 | break; |
1280 | 0 | } |
1281 | 0 | (*sigs)++; |
1282 | 0 | } |
1283 | 0 | dns_rdatasetiter_destroy(&iter); |
1284 | |
|
1285 | 0 | cleanup_node: |
1286 | 0 | dns_db_detachnode(&node); |
1287 | |
|
1288 | 0 | return result; |
1289 | 0 | } |
1290 | | |
1291 | | /*% |
1292 | | * Update RRSIG, NSEC and NSEC3 records affected by an update. The original |
1293 | | * update, including the SOA serial update but excluding the RRSIG & NSEC |
1294 | | * changes, is in "diff" and has already been applied to "newver" of "db". |
1295 | | * The database version prior to the update is "oldver". |
1296 | | * |
1297 | | * The necessary RRSIG, NSEC and NSEC3 changes will be applied to "newver" |
1298 | | * and added (as a minimal diff) to "diff". |
1299 | | * |
1300 | | * The RRSIGs generated will be valid for 'sigvalidityinterval' seconds. |
1301 | | */ |
1302 | | isc_result_t |
1303 | | dns_update_signatures(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, |
1304 | | dns_dbversion_t *oldver, dns_dbversion_t *newver, |
1305 | 0 | dns_diff_t *diff, uint32_t sigvalidityinterval) { |
1306 | 0 | return dns_update_signaturesinc(log, zone, db, oldver, newver, diff, |
1307 | 0 | sigvalidityinterval, NULL); |
1308 | 0 | } |
1309 | | |
1310 | | struct dns_update_state { |
1311 | | unsigned int magic; |
1312 | | dns_diff_t diffnames; |
1313 | | dns_diff_t affected; |
1314 | | dns_diff_t sig_diff; |
1315 | | dns_diff_t nsec_diff; |
1316 | | dns_diff_t nsec_mindiff; |
1317 | | dns_diff_t work; |
1318 | | dst_key_t *zone_keys[DNS_MAXZONEKEYS]; |
1319 | | unsigned int nkeys; |
1320 | | isc_stdtime_t now, inception, expire, soaexpire, keyexpire; |
1321 | | dns_ttl_t nsecttl; |
1322 | | bool build_nsec3; |
1323 | | enum { |
1324 | | sign_updates, |
1325 | | remove_orphaned, |
1326 | | build_chain, |
1327 | | process_nsec, |
1328 | | sign_nsec, |
1329 | | update_nsec3, |
1330 | | process_nsec3, |
1331 | | sign_nsec3 |
1332 | | } state; |
1333 | | }; |
1334 | | |
1335 | | static uint32_t |
1336 | 0 | dns__jitter_expire(dns_zone_t *zone) { |
1337 | | /* Spread out signatures over time */ |
1338 | 0 | isc_stdtime_t jitter = DEFAULT_JITTER; |
1339 | 0 | isc_stdtime_t sigvalidity = dns_zone_getsigvalidityinterval(zone); |
1340 | 0 | dns_kasp_t *kasp = dns_zone_getkasp(zone); |
1341 | |
|
1342 | 0 | if (kasp != NULL) { |
1343 | 0 | jitter = dns_kasp_sigjitter(kasp); |
1344 | 0 | sigvalidity = dns_kasp_sigvalidity(kasp); |
1345 | 0 | INSIST(jitter <= sigvalidity); |
1346 | 0 | } |
1347 | |
|
1348 | 0 | if (jitter > sigvalidity) { |
1349 | 0 | jitter = sigvalidity; |
1350 | 0 | } |
1351 | |
|
1352 | 0 | if (sigvalidity >= 3600U) { |
1353 | 0 | if (sigvalidity > 7200U) { |
1354 | 0 | sigvalidity -= isc_random_uniform(jitter); |
1355 | 0 | } else { |
1356 | 0 | sigvalidity -= isc_random_uniform(1200); |
1357 | 0 | } |
1358 | 0 | } |
1359 | 0 | return sigvalidity; |
1360 | 0 | } |
1361 | | |
1362 | | isc_result_t |
1363 | | dns_update_signaturesinc(dns_update_log_t *log, dns_zone_t *zone, dns_db_t *db, |
1364 | | dns_dbversion_t *oldver, dns_dbversion_t *newver, |
1365 | | dns_diff_t *diff, uint32_t sigvalidityinterval, |
1366 | 0 | dns_update_state_t **statep) { |
1367 | 0 | isc_result_t result = ISC_R_SUCCESS; |
1368 | 0 | dns_update_state_t mystate, *state = NULL; |
1369 | 0 | dns_difftuple_t *tuple = NULL; |
1370 | 0 | bool flag, build_nsec; |
1371 | 0 | unsigned int i; |
1372 | 0 | dns_rdata_soa_t soa; |
1373 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
1374 | 0 | dns_rdataset_t rdataset; |
1375 | 0 | dns_dbnode_t *node = NULL; |
1376 | 0 | bool unsecure; |
1377 | 0 | bool cut; |
1378 | 0 | dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone); |
1379 | 0 | unsigned int sigs = 0; |
1380 | 0 | unsigned int maxsigs = dns_zone_getsignatures(zone); |
1381 | |
|
1382 | 0 | if (statep == NULL || *statep == NULL) { |
1383 | 0 | if (statep == NULL) { |
1384 | 0 | state = &mystate; |
1385 | 0 | } else { |
1386 | 0 | state = isc_mem_get(diff->mctx, sizeof(*state)); |
1387 | 0 | } |
1388 | |
|
1389 | 0 | dns_diff_init(diff->mctx, &state->diffnames); |
1390 | 0 | dns_diff_init(diff->mctx, &state->affected); |
1391 | 0 | dns_diff_init(diff->mctx, &state->sig_diff); |
1392 | 0 | dns_diff_init(diff->mctx, &state->nsec_diff); |
1393 | 0 | dns_diff_init(diff->mctx, &state->nsec_mindiff); |
1394 | 0 | dns_diff_init(diff->mctx, &state->work); |
1395 | 0 | state->nkeys = 0; |
1396 | 0 | state->build_nsec3 = false; |
1397 | |
|
1398 | 0 | result = find_zone_keys(zone, diff->mctx, DNS_MAXZONEKEYS, |
1399 | 0 | state->zone_keys, &state->nkeys); |
1400 | 0 | if (result == ISC_R_NOSPACE) { |
1401 | 0 | update_log(log, zone, ISC_LOG_ERROR, |
1402 | 0 | "too many zone keys for secure " |
1403 | 0 | "dynamic update"); |
1404 | 0 | } else if (result != ISC_R_SUCCESS) { |
1405 | 0 | update_log(log, zone, ISC_LOG_ERROR, |
1406 | 0 | "could not get zone keys for secure " |
1407 | 0 | "dynamic update"); |
1408 | 0 | goto cleanup; |
1409 | 0 | } |
1410 | | |
1411 | 0 | state->now = isc_stdtime_now(); |
1412 | 0 | state->inception = state->now - 3600; /* Allow for some clock |
1413 | | skew. */ |
1414 | 0 | state->expire = state->now + dns__jitter_expire(zone); |
1415 | 0 | state->soaexpire = state->now + sigvalidityinterval; |
1416 | 0 | state->keyexpire = dns_zone_getkeyvalidityinterval(zone); |
1417 | 0 | if (state->keyexpire == 0) { |
1418 | 0 | state->keyexpire = state->expire; |
1419 | 0 | } else { |
1420 | 0 | state->keyexpire += state->now; |
1421 | 0 | } |
1422 | | |
1423 | | /* |
1424 | | * Calculate the NSEC/NSEC3 TTL as a minimum of the SOA TTL and |
1425 | | * MINIMUM field. |
1426 | | */ |
1427 | 0 | CHECK(dns_db_findnode(db, dns_db_origin(db), false, &node)); |
1428 | 0 | dns_rdataset_init(&rdataset); |
1429 | 0 | CHECK(dns_db_findrdataset(db, node, newver, dns_rdatatype_soa, |
1430 | 0 | 0, (isc_stdtime_t)0, &rdataset, |
1431 | 0 | NULL)); |
1432 | 0 | CHECK(dns_rdataset_first(&rdataset)); |
1433 | 0 | dns_rdataset_current(&rdataset, &rdata); |
1434 | 0 | CHECK(dns_rdata_tostruct(&rdata, &soa, NULL)); |
1435 | 0 | state->nsecttl = ISC_MIN(rdataset.ttl, soa.minimum); |
1436 | 0 | dns_rdataset_disassociate(&rdataset); |
1437 | 0 | dns_db_detachnode(&node); |
1438 | | |
1439 | | /* |
1440 | | * Find all RRsets directly affected by the update, and |
1441 | | * update their RRSIGs. Also build a list of names affected |
1442 | | * by the update in "diffnames". |
1443 | | */ |
1444 | 0 | CHECK(dns_diff_sort(diff, temp_order)); |
1445 | 0 | state->state = sign_updates; |
1446 | 0 | state->magic = STATE_MAGIC; |
1447 | 0 | SET_IF_NOT_NULL(statep, state); |
1448 | 0 | } else { |
1449 | 0 | REQUIRE(DNS_STATE_VALID(*statep)); |
1450 | 0 | state = *statep; |
1451 | 0 | } |
1452 | | |
1453 | 0 | next_state: |
1454 | 0 | switch (state->state) { |
1455 | 0 | case sign_updates: |
1456 | 0 | tuple = ISC_LIST_HEAD(diff->tuples); |
1457 | 0 | while (tuple != NULL) { |
1458 | 0 | dns_name_t *name = &tuple->name; |
1459 | 0 | dns_difftuple_t *next = NULL; |
1460 | | |
1461 | | /* |
1462 | | * Now "name" is a new, unique name affected by the |
1463 | | * update. |
1464 | | */ |
1465 | 0 | namelist_append_name(&state->diffnames, name); |
1466 | |
|
1467 | 0 | while (tuple != NULL && |
1468 | 0 | dns_name_equal(&tuple->name, name)) |
1469 | 0 | { |
1470 | 0 | dns_rdatatype_t type; |
1471 | 0 | type = tuple->rdata.type; |
1472 | | |
1473 | | /* |
1474 | | * Now "name" and "type" denote a new unique |
1475 | | * RRset affected by the update. |
1476 | | */ |
1477 | | |
1478 | | /* Don't sign RRSIGs. */ |
1479 | 0 | if (type == dns_rdatatype_rrsig) { |
1480 | 0 | goto skip; |
1481 | 0 | } |
1482 | | |
1483 | | /* |
1484 | | * Delete all old RRSIGs covering this type, |
1485 | | * since they are all invalid when the signed |
1486 | | * RRset has changed. We may not be able to |
1487 | | * recreate all of them - tough. |
1488 | | * Special case changes to the zone's DNSKEY |
1489 | | * records to support offline KSKs. |
1490 | | */ |
1491 | 0 | if (type == dns_rdatatype_dnskey) { |
1492 | 0 | del_keysigs(db, newver, name, |
1493 | 0 | &state->sig_diff, |
1494 | 0 | state->zone_keys, |
1495 | 0 | state->nkeys); |
1496 | 0 | } else { |
1497 | 0 | CHECK(delete_if( |
1498 | 0 | true_p, db, newver, name, |
1499 | 0 | dns_rdatatype_rrsig, type, NULL, |
1500 | 0 | &state->sig_diff)); |
1501 | 0 | } |
1502 | | |
1503 | | /* |
1504 | | * If this RRset is still visible after the |
1505 | | * update, add a new signature for it. |
1506 | | */ |
1507 | 0 | CHECK(rrset_visible(db, newver, name, type, |
1508 | 0 | &flag)); |
1509 | 0 | if (flag) { |
1510 | 0 | isc_stdtime_t exp; |
1511 | 0 | if (dns_rdatatype_iskeymaterial(type)) { |
1512 | 0 | exp = state->keyexpire; |
1513 | 0 | } else if (type == dns_rdatatype_soa) { |
1514 | 0 | exp = state->soaexpire; |
1515 | 0 | } else { |
1516 | 0 | exp = state->expire; |
1517 | 0 | } |
1518 | |
|
1519 | 0 | CHECK(add_sigs(log, zone, db, newver, |
1520 | 0 | name, type, |
1521 | 0 | &state->sig_diff, |
1522 | 0 | state->zone_keys, |
1523 | 0 | state->nkeys, state->now, |
1524 | 0 | state->inception, exp)); |
1525 | 0 | sigs++; |
1526 | 0 | } |
1527 | 0 | skip: |
1528 | | /* Skip any other updates to the same RRset. */ |
1529 | 0 | while (tuple != NULL && |
1530 | 0 | dns_name_equal(&tuple->name, name) && |
1531 | 0 | tuple->rdata.type == type) |
1532 | 0 | { |
1533 | 0 | next = ISC_LIST_NEXT(tuple, link); |
1534 | 0 | ISC_LIST_UNLINK(diff->tuples, tuple, |
1535 | 0 | link); |
1536 | 0 | ISC_LIST_APPEND(state->work.tuples, |
1537 | 0 | tuple, link); |
1538 | 0 | tuple = next; |
1539 | 0 | } |
1540 | 0 | } |
1541 | 0 | if (state != &mystate && sigs > maxsigs) { |
1542 | 0 | return DNS_R_CONTINUE; |
1543 | 0 | } |
1544 | 0 | } |
1545 | 0 | ISC_LIST_APPENDLIST(diff->tuples, state->work.tuples, link); |
1546 | |
|
1547 | 0 | update_log(log, zone, ISC_LOG_DEBUG(3), |
1548 | 0 | "updated data signatures"); |
1549 | 0 | FALLTHROUGH; |
1550 | 0 | case remove_orphaned: |
1551 | 0 | state->state = remove_orphaned; |
1552 | | |
1553 | | /* Remove orphaned NSECs and RRSIG NSECs. */ |
1554 | 0 | ISC_LIST_FOREACH(state->diffnames.tuples, t, link) { |
1555 | 0 | CHECK(non_nsec_rrset_exists(db, newver, &t->name, |
1556 | 0 | &flag)); |
1557 | 0 | if (!flag) { |
1558 | 0 | CHECK(delete_if(true_p, db, newver, &t->name, |
1559 | 0 | dns_rdatatype_any, 0, NULL, |
1560 | 0 | &state->sig_diff)); |
1561 | 0 | } |
1562 | 0 | } |
1563 | 0 | update_log(log, zone, ISC_LOG_DEBUG(3), |
1564 | 0 | "removed any orphaned NSEC records"); |
1565 | | |
1566 | | /* |
1567 | | * See if we need to build NSEC or NSEC3 chains. |
1568 | | */ |
1569 | 0 | CHECK(dns_private_chains(db, newver, privatetype, &build_nsec, |
1570 | 0 | &state->build_nsec3)); |
1571 | 0 | if (!build_nsec) { |
1572 | 0 | state->state = update_nsec3; |
1573 | 0 | goto next_state; |
1574 | 0 | } |
1575 | | |
1576 | 0 | update_log(log, zone, ISC_LOG_DEBUG(3), |
1577 | 0 | "rebuilding NSEC chain"); |
1578 | |
|
1579 | 0 | FALLTHROUGH; |
1580 | 0 | case build_chain: |
1581 | 0 | state->state = build_chain; |
1582 | | /* |
1583 | | * When a name is created or deleted, its predecessor needs to |
1584 | | * have its NSEC updated. |
1585 | | */ |
1586 | 0 | ISC_LIST_FOREACH(state->diffnames.tuples, t, link) { |
1587 | 0 | bool existed, exists; |
1588 | 0 | dns_fixedname_t fixedname; |
1589 | 0 | dns_name_t *prevname; |
1590 | |
|
1591 | 0 | prevname = dns_fixedname_initname(&fixedname); |
1592 | |
|
1593 | 0 | if (oldver != NULL) { |
1594 | 0 | CHECK(name_exists(db, oldver, &t->name, |
1595 | 0 | &existed)); |
1596 | 0 | } else { |
1597 | 0 | existed = false; |
1598 | 0 | } |
1599 | 0 | CHECK(name_exists(db, newver, &t->name, &exists)); |
1600 | 0 | if (exists == existed) { |
1601 | 0 | continue; |
1602 | 0 | } |
1603 | | |
1604 | | /* |
1605 | | * Find the predecessor. |
1606 | | * When names become obscured or unobscured in this |
1607 | | * update transaction, we may find the wrong |
1608 | | * predecessor because the NSECs have not yet been |
1609 | | * updated to reflect the delegation change. This |
1610 | | * should not matter because in this case, the correct |
1611 | | * predecessor is either the delegation node or a |
1612 | | * newly unobscured node, and those nodes are on the |
1613 | | * "affected" list in any case. |
1614 | | */ |
1615 | 0 | CHECK(next_active(log, zone, db, newver, &t->name, |
1616 | 0 | prevname, false)); |
1617 | 0 | namelist_append_name(&state->affected, prevname); |
1618 | 0 | } |
1619 | | |
1620 | | /* |
1621 | | * Find names potentially affected by delegation changes |
1622 | | * (obscured by adding an NS or DNAME, or unobscured by |
1623 | | * removing one). |
1624 | | */ |
1625 | 0 | ISC_LIST_FOREACH(state->diffnames.tuples, t, link) { |
1626 | 0 | bool ns_existed, dname_existed; |
1627 | 0 | bool ns_exists, dname_exists; |
1628 | |
|
1629 | 0 | if (oldver != NULL) { |
1630 | 0 | CHECK(rrset_exists(db, oldver, &t->name, |
1631 | 0 | dns_rdatatype_ns, 0, |
1632 | 0 | &ns_existed)); |
1633 | 0 | } else { |
1634 | 0 | ns_existed = false; |
1635 | 0 | } |
1636 | 0 | if (oldver != NULL) { |
1637 | 0 | CHECK(rrset_exists(db, oldver, &t->name, |
1638 | 0 | dns_rdatatype_dname, 0, |
1639 | 0 | &dname_existed)); |
1640 | 0 | } else { |
1641 | 0 | dname_existed = false; |
1642 | 0 | } |
1643 | 0 | CHECK(rrset_exists(db, newver, &t->name, |
1644 | 0 | dns_rdatatype_ns, 0, &ns_exists)); |
1645 | 0 | CHECK(rrset_exists(db, newver, &t->name, |
1646 | 0 | dns_rdatatype_dname, 0, |
1647 | 0 | &dname_exists)); |
1648 | 0 | if ((ns_exists || dname_exists) == |
1649 | 0 | (ns_existed || dname_existed)) |
1650 | 0 | { |
1651 | 0 | continue; |
1652 | 0 | } |
1653 | | /* |
1654 | | * There was a delegation change. Mark all subdomains |
1655 | | * of t->name as potentially needing a NSEC update. |
1656 | | */ |
1657 | 0 | CHECK(namelist_append_subdomain(db, &t->name, |
1658 | 0 | &state->affected)); |
1659 | 0 | } |
1660 | 0 | ISC_LIST_APPENDLIST(state->affected.tuples, |
1661 | 0 | state->diffnames.tuples, link); |
1662 | 0 | INSIST(ISC_LIST_EMPTY(state->diffnames.tuples)); |
1663 | |
|
1664 | 0 | CHECK(uniqify_name_list(&state->affected)); |
1665 | |
|
1666 | 0 | FALLTHROUGH; |
1667 | 0 | case process_nsec: |
1668 | 0 | state->state = process_nsec; |
1669 | | |
1670 | | /* |
1671 | | * Determine which names should have NSECs, and delete/create |
1672 | | * NSECs to make it so. We don't know the final NSEC targets |
1673 | | * yet, so we just create placeholder NSECs with arbitrary |
1674 | | * contents to indicate that their respective owner names |
1675 | | * should be part of the NSEC chain. |
1676 | | */ |
1677 | 0 | ISC_LIST_FOREACH(state->affected.tuples, t, link) { |
1678 | 0 | bool exists; |
1679 | 0 | dns_name_t *name = &t->name; |
1680 | |
|
1681 | 0 | CHECK(name_exists(db, newver, name, &exists)); |
1682 | 0 | if (!exists) { |
1683 | 0 | goto unlink; |
1684 | 0 | } |
1685 | 0 | CHECK(is_active(db, newver, name, &flag, &cut, NULL)); |
1686 | 0 | if (!flag) { |
1687 | | /* |
1688 | | * This name is obscured. Delete any |
1689 | | * existing NSEC record. |
1690 | | */ |
1691 | 0 | CHECK(delete_if(true_p, db, newver, name, |
1692 | 0 | dns_rdatatype_nsec, 0, NULL, |
1693 | 0 | &state->nsec_diff)); |
1694 | 0 | CHECK(delete_if(rrsig_p, db, newver, name, |
1695 | 0 | dns_rdatatype_any, 0, NULL, |
1696 | 0 | diff)); |
1697 | 0 | } else { |
1698 | | /* |
1699 | | * This name is not obscured. It needs to have |
1700 | | * a NSEC unless it is the at the origin, in |
1701 | | * which case it should already exist if there |
1702 | | * is a complete NSEC chain and if there isn't |
1703 | | * a complete NSEC chain we don't want to add |
1704 | | * one as that would signal that there is a |
1705 | | * complete NSEC chain. |
1706 | | */ |
1707 | 0 | if (!dns_name_equal(name, dns_db_origin(db))) { |
1708 | 0 | CHECK(rrset_exists(db, newver, name, |
1709 | 0 | dns_rdatatype_nsec, |
1710 | 0 | 0, &flag)); |
1711 | 0 | if (!flag) { |
1712 | 0 | CHECK(add_placeholder_nsec( |
1713 | 0 | db, newver, name, |
1714 | 0 | diff)); |
1715 | 0 | } |
1716 | 0 | } |
1717 | 0 | CHECK(add_exposed_sigs( |
1718 | 0 | log, zone, db, newver, name, cut, |
1719 | 0 | &state->sig_diff, state->zone_keys, |
1720 | 0 | state->nkeys, state->now, |
1721 | 0 | state->inception, state->expire, |
1722 | 0 | &sigs)); |
1723 | 0 | } |
1724 | 0 | unlink: |
1725 | 0 | ISC_LIST_UNLINK(state->affected.tuples, t, link); |
1726 | 0 | ISC_LIST_APPEND(state->work.tuples, t, link); |
1727 | 0 | if (state != &mystate && sigs > maxsigs) { |
1728 | 0 | return DNS_R_CONTINUE; |
1729 | 0 | } |
1730 | 0 | } |
1731 | 0 | ISC_LIST_APPENDLIST(state->affected.tuples, state->work.tuples, |
1732 | 0 | link); |
1733 | | |
1734 | | /* |
1735 | | * Now we know which names are part of the NSEC chain. |
1736 | | * Make them all point at their correct targets. |
1737 | | */ |
1738 | 0 | ISC_LIST_FOREACH(state->affected.tuples, t, link) { |
1739 | 0 | CHECK(rrset_exists(db, newver, &t->name, |
1740 | 0 | dns_rdatatype_nsec, 0, &flag)); |
1741 | 0 | if (flag) { |
1742 | | /* |
1743 | | * There is a NSEC, but we don't know if it |
1744 | | * is correct. Delete it and create a correct |
1745 | | * one to be sure. If the update was |
1746 | | * unnecessary, the diff minimization |
1747 | | * will take care of eliminating it from the |
1748 | | * journal, IXFRs, etc. |
1749 | | * |
1750 | | * The RRSIG bit should always be set in the |
1751 | | * NSECs we generate, because they will all |
1752 | | * get RRSIG NSECs. |
1753 | | * (XXX what if the zone keys are missing?). |
1754 | | * Because the RRSIG NSECs have not necessarily |
1755 | | * been created yet, the correctness of the |
1756 | | * bit mask relies on the assumption that NSECs |
1757 | | * are only created if there is other data, and |
1758 | | * if there is other data, there are other |
1759 | | * RRSIGs. |
1760 | | */ |
1761 | 0 | CHECK(add_nsec(log, zone, db, newver, &t->name, |
1762 | 0 | state->nsecttl, |
1763 | 0 | &state->nsec_diff)); |
1764 | 0 | } |
1765 | 0 | } |
1766 | | |
1767 | | /* |
1768 | | * Minimize the set of NSEC updates so that we don't |
1769 | | * have to regenerate the RRSIG NSECs for NSECs that were |
1770 | | * replaced with identical ones. |
1771 | | */ |
1772 | 0 | ISC_LIST_FOREACH(state->nsec_diff.tuples, t, link) { |
1773 | 0 | ISC_LIST_UNLINK(state->nsec_diff.tuples, t, link); |
1774 | 0 | dns_diff_appendminimal(&state->nsec_mindiff, &t); |
1775 | 0 | } |
1776 | |
|
1777 | 0 | update_log(log, zone, ISC_LOG_DEBUG(3), |
1778 | 0 | "signing rebuilt NSEC chain"); |
1779 | |
|
1780 | 0 | FALLTHROUGH; |
1781 | 0 | case sign_nsec: |
1782 | 0 | state->state = sign_nsec; |
1783 | | /* Update RRSIG NSECs. */ |
1784 | 0 | ISC_LIST_FOREACH(state->nsec_mindiff.tuples, t, link) { |
1785 | 0 | if (t->op == DNS_DIFFOP_DEL) { |
1786 | 0 | CHECK(delete_if(true_p, db, newver, &t->name, |
1787 | 0 | dns_rdatatype_rrsig, |
1788 | 0 | dns_rdatatype_nsec, NULL, |
1789 | 0 | &state->sig_diff)); |
1790 | 0 | } else if (t->op == DNS_DIFFOP_ADD) { |
1791 | 0 | CHECK(add_sigs(log, zone, db, newver, &t->name, |
1792 | 0 | dns_rdatatype_nsec, |
1793 | 0 | &state->sig_diff, |
1794 | 0 | state->zone_keys, state->nkeys, |
1795 | 0 | state->now, state->inception, |
1796 | 0 | state->expire)); |
1797 | 0 | sigs++; |
1798 | 0 | } else { |
1799 | 0 | UNREACHABLE(); |
1800 | 0 | } |
1801 | 0 | ISC_LIST_UNLINK(state->nsec_mindiff.tuples, t, link); |
1802 | 0 | ISC_LIST_APPEND(state->work.tuples, t, link); |
1803 | 0 | if (state != &mystate && sigs > maxsigs) { |
1804 | 0 | return DNS_R_CONTINUE; |
1805 | 0 | } |
1806 | 0 | } |
1807 | 0 | ISC_LIST_APPENDLIST(state->nsec_mindiff.tuples, |
1808 | 0 | state->work.tuples, link); |
1809 | 0 | FALLTHROUGH; |
1810 | 0 | case update_nsec3: |
1811 | 0 | state->state = update_nsec3; |
1812 | | |
1813 | | /* Record our changes for the journal. */ |
1814 | 0 | ISC_LIST_FOREACH(state->sig_diff.tuples, t, link) { |
1815 | 0 | ISC_LIST_UNLINK(state->sig_diff.tuples, t, link); |
1816 | 0 | dns_diff_appendminimal(diff, &t); |
1817 | 0 | } |
1818 | 0 | ISC_LIST_FOREACH(state->nsec_mindiff.tuples, t, link) { |
1819 | 0 | ISC_LIST_UNLINK(state->nsec_mindiff.tuples, t, link); |
1820 | 0 | dns_diff_appendminimal(diff, &t); |
1821 | 0 | } |
1822 | |
|
1823 | 0 | INSIST(ISC_LIST_EMPTY(state->sig_diff.tuples)); |
1824 | 0 | INSIST(ISC_LIST_EMPTY(state->nsec_diff.tuples)); |
1825 | 0 | INSIST(ISC_LIST_EMPTY(state->nsec_mindiff.tuples)); |
1826 | |
|
1827 | 0 | if (!state->build_nsec3) { |
1828 | 0 | update_log(log, zone, ISC_LOG_DEBUG(3), |
1829 | 0 | "no NSEC3 chains to rebuild"); |
1830 | 0 | goto cleanup; |
1831 | 0 | } |
1832 | | |
1833 | 0 | update_log(log, zone, ISC_LOG_DEBUG(3), |
1834 | 0 | "rebuilding NSEC3 chains"); |
1835 | |
|
1836 | 0 | dns_diff_clear(&state->diffnames); |
1837 | 0 | dns_diff_clear(&state->affected); |
1838 | |
|
1839 | 0 | CHECK(dns_diff_sort(diff, temp_order)); |
1840 | | |
1841 | | /* |
1842 | | * Find names potentially affected by delegation changes |
1843 | | * (obscured by adding an NS or DNAME, or unobscured by |
1844 | | * removing one). |
1845 | | */ |
1846 | 0 | tuple = ISC_LIST_HEAD(diff->tuples); |
1847 | 0 | while (tuple != NULL) { |
1848 | 0 | dns_name_t *name = &tuple->name; |
1849 | |
|
1850 | 0 | bool ns_existed, dname_existed; |
1851 | 0 | bool ns_exists, dname_exists; |
1852 | 0 | bool exists, existed; |
1853 | |
|
1854 | 0 | if (tuple->rdata.type == dns_rdatatype_nsec || |
1855 | 0 | tuple->rdata.type == dns_rdatatype_rrsig) |
1856 | 0 | { |
1857 | 0 | tuple = ISC_LIST_NEXT(tuple, link); |
1858 | 0 | continue; |
1859 | 0 | } |
1860 | | |
1861 | 0 | namelist_append_name(&state->affected, name); |
1862 | |
|
1863 | 0 | if (oldver != NULL) { |
1864 | 0 | CHECK(rrset_exists(db, oldver, name, |
1865 | 0 | dns_rdatatype_ns, 0, |
1866 | 0 | &ns_existed)); |
1867 | 0 | } else { |
1868 | 0 | ns_existed = false; |
1869 | 0 | } |
1870 | 0 | if (oldver != NULL) { |
1871 | 0 | CHECK(rrset_exists(db, oldver, name, |
1872 | 0 | dns_rdatatype_dname, 0, |
1873 | 0 | &dname_existed)); |
1874 | 0 | } else { |
1875 | 0 | dname_existed = false; |
1876 | 0 | } |
1877 | 0 | CHECK(rrset_exists(db, newver, name, dns_rdatatype_ns, |
1878 | 0 | 0, &ns_exists)); |
1879 | 0 | CHECK(rrset_exists(db, newver, name, |
1880 | 0 | dns_rdatatype_dname, 0, |
1881 | 0 | &dname_exists)); |
1882 | |
|
1883 | 0 | exists = ns_exists || dname_exists; |
1884 | 0 | existed = ns_existed || dname_existed; |
1885 | 0 | if (exists == existed) { |
1886 | 0 | goto nextname; |
1887 | 0 | } |
1888 | | /* |
1889 | | * There was a delegation change. Mark all subdomains |
1890 | | * of tuple->name as potentially needing a NSEC3 update. |
1891 | | */ |
1892 | 0 | CHECK(namelist_append_subdomain(db, name, |
1893 | 0 | &state->affected)); |
1894 | |
|
1895 | 0 | nextname: |
1896 | 0 | while (tuple != NULL && |
1897 | 0 | dns_name_equal(&tuple->name, name)) |
1898 | 0 | { |
1899 | 0 | tuple = ISC_LIST_NEXT(tuple, link); |
1900 | 0 | } |
1901 | 0 | } |
1902 | | |
1903 | 0 | FALLTHROUGH; |
1904 | 0 | case process_nsec3: |
1905 | 0 | state->state = process_nsec3; |
1906 | 0 | ISC_LIST_FOREACH(state->affected.tuples, t, link) { |
1907 | 0 | dns_name_t *name = &t->name; |
1908 | |
|
1909 | 0 | unsecure = false; /* Silence compiler warning. */ |
1910 | 0 | CHECK(is_active(db, newver, name, &flag, &cut, |
1911 | 0 | &unsecure)); |
1912 | |
|
1913 | 0 | if (!flag) { |
1914 | 0 | CHECK(delete_if(rrsig_p, db, newver, name, |
1915 | 0 | dns_rdatatype_any, 0, NULL, |
1916 | 0 | diff)); |
1917 | 0 | CHECK(dns_nsec3_delnsec3sx(db, newver, name, |
1918 | 0 | privatetype, |
1919 | 0 | &state->nsec_diff)); |
1920 | 0 | } else { |
1921 | 0 | CHECK(add_exposed_sigs( |
1922 | 0 | log, zone, db, newver, name, cut, |
1923 | 0 | &state->sig_diff, state->zone_keys, |
1924 | 0 | state->nkeys, state->now, |
1925 | 0 | state->inception, state->expire, |
1926 | 0 | &sigs)); |
1927 | 0 | CHECK(dns_nsec3_addnsec3sx( |
1928 | 0 | db, newver, name, state->nsecttl, |
1929 | 0 | unsecure, privatetype, |
1930 | 0 | &state->nsec_diff)); |
1931 | 0 | } |
1932 | 0 | ISC_LIST_UNLINK(state->affected.tuples, t, link); |
1933 | 0 | ISC_LIST_APPEND(state->work.tuples, t, link); |
1934 | 0 | if (state != &mystate && sigs > maxsigs) { |
1935 | 0 | return DNS_R_CONTINUE; |
1936 | 0 | } |
1937 | 0 | } |
1938 | 0 | ISC_LIST_APPENDLIST(state->affected.tuples, state->work.tuples, |
1939 | 0 | link); |
1940 | | |
1941 | | /* |
1942 | | * Minimize the set of NSEC3 updates so that we don't |
1943 | | * have to regenerate the RRSIG NSEC3s for NSEC3s that were |
1944 | | * replaced with identical ones. |
1945 | | */ |
1946 | 0 | ISC_LIST_FOREACH(state->nsec_diff.tuples, t, link) { |
1947 | 0 | ISC_LIST_UNLINK(state->nsec_diff.tuples, t, link); |
1948 | 0 | dns_diff_appendminimal(&state->nsec_mindiff, &t); |
1949 | 0 | } |
1950 | |
|
1951 | 0 | update_log(log, zone, ISC_LOG_DEBUG(3), |
1952 | 0 | "signing rebuilt NSEC3 chain"); |
1953 | |
|
1954 | 0 | FALLTHROUGH; |
1955 | 0 | case sign_nsec3: |
1956 | 0 | state->state = sign_nsec3; |
1957 | | /* Update RRSIG NSEC3s. */ |
1958 | 0 | ISC_LIST_FOREACH(state->nsec_mindiff.tuples, t, link) { |
1959 | 0 | if (t->op == DNS_DIFFOP_DEL) { |
1960 | 0 | CHECK(delete_if(true_p, db, newver, &t->name, |
1961 | 0 | dns_rdatatype_rrsig, |
1962 | 0 | dns_rdatatype_nsec3, NULL, |
1963 | 0 | &state->sig_diff)); |
1964 | 0 | } else if (t->op == DNS_DIFFOP_ADD) { |
1965 | 0 | CHECK(add_sigs(log, zone, db, newver, &t->name, |
1966 | 0 | dns_rdatatype_nsec3, |
1967 | 0 | &state->sig_diff, |
1968 | 0 | state->zone_keys, state->nkeys, |
1969 | 0 | state->now, state->inception, |
1970 | 0 | state->expire)); |
1971 | 0 | sigs++; |
1972 | 0 | } else { |
1973 | 0 | UNREACHABLE(); |
1974 | 0 | } |
1975 | 0 | ISC_LIST_UNLINK(state->nsec_mindiff.tuples, t, link); |
1976 | 0 | ISC_LIST_APPEND(state->work.tuples, t, link); |
1977 | 0 | if (state != &mystate && sigs > maxsigs) { |
1978 | 0 | return DNS_R_CONTINUE; |
1979 | 0 | } |
1980 | 0 | } |
1981 | 0 | ISC_LIST_APPENDLIST(state->nsec_mindiff.tuples, |
1982 | 0 | state->work.tuples, link); |
1983 | | |
1984 | | /* Record our changes for the journal. */ |
1985 | 0 | ISC_LIST_FOREACH(state->sig_diff.tuples, t, link) { |
1986 | 0 | ISC_LIST_UNLINK(state->sig_diff.tuples, t, link); |
1987 | 0 | dns_diff_appendminimal(diff, &t); |
1988 | 0 | } |
1989 | 0 | ISC_LIST_FOREACH(state->nsec_mindiff.tuples, t, link) { |
1990 | 0 | ISC_LIST_UNLINK(state->nsec_mindiff.tuples, t, link); |
1991 | 0 | dns_diff_appendminimal(diff, &t); |
1992 | 0 | } |
1993 | |
|
1994 | 0 | INSIST(ISC_LIST_EMPTY(state->sig_diff.tuples)); |
1995 | 0 | INSIST(ISC_LIST_EMPTY(state->nsec_diff.tuples)); |
1996 | 0 | INSIST(ISC_LIST_EMPTY(state->nsec_mindiff.tuples)); |
1997 | 0 | break; |
1998 | 0 | default: |
1999 | 0 | UNREACHABLE(); |
2000 | 0 | } |
2001 | | |
2002 | 0 | cleanup: |
2003 | 0 | if (node != NULL) { |
2004 | 0 | dns_db_detachnode(&node); |
2005 | 0 | } |
2006 | |
|
2007 | 0 | dns_diff_clear(&state->sig_diff); |
2008 | 0 | dns_diff_clear(&state->nsec_diff); |
2009 | 0 | dns_diff_clear(&state->nsec_mindiff); |
2010 | |
|
2011 | 0 | dns_diff_clear(&state->affected); |
2012 | 0 | dns_diff_clear(&state->diffnames); |
2013 | 0 | dns_diff_clear(&state->work); |
2014 | |
|
2015 | 0 | for (i = 0; i < state->nkeys; i++) { |
2016 | 0 | dst_key_free(&state->zone_keys[i]); |
2017 | 0 | } |
2018 | |
|
2019 | 0 | if (state != &mystate) { |
2020 | 0 | *statep = NULL; |
2021 | 0 | state->magic = 0; |
2022 | 0 | isc_mem_put(diff->mctx, state, sizeof(*state)); |
2023 | 0 | } |
2024 | |
|
2025 | 0 | return result; |
2026 | 0 | } |
2027 | | |
2028 | | static isc_stdtime_t |
2029 | 0 | epoch_to_yyyymmdd(time_t when) { |
2030 | 0 | struct tm t, *tm = localtime_r(&when, &t); |
2031 | 0 | if (tm == NULL) { |
2032 | 0 | return 0; |
2033 | 0 | } |
2034 | 0 | return ((tm->tm_year + 1900) * 10000) + ((tm->tm_mon + 1) * 100) + |
2035 | 0 | tm->tm_mday; |
2036 | 0 | } |
2037 | | |
2038 | | static uint32_t |
2039 | 0 | dns__update_soaserial(uint32_t serial, dns_updatemethod_t method) { |
2040 | 0 | isc_stdtime_t now; |
2041 | |
|
2042 | 0 | switch (method) { |
2043 | 0 | case dns_updatemethod_none: |
2044 | 0 | return serial; |
2045 | 0 | case dns_updatemethod_unixtime: |
2046 | 0 | now = isc_stdtime_now(); |
2047 | 0 | return now; |
2048 | 0 | case dns_updatemethod_date: |
2049 | 0 | now = isc_stdtime_now(); |
2050 | 0 | return epoch_to_yyyymmdd((time_t)now) * 100; |
2051 | 0 | case dns_updatemethod_increment: |
2052 | | /* RFC1982 */ |
2053 | 0 | serial = (serial + 1) & 0xFFFFFFFF; |
2054 | 0 | if (serial == 0) { |
2055 | 0 | return 1; |
2056 | 0 | } |
2057 | 0 | return serial; |
2058 | 0 | default: |
2059 | 0 | UNREACHABLE(); |
2060 | 0 | } |
2061 | 0 | } |
2062 | | |
2063 | | uint32_t |
2064 | | dns_update_soaserial(uint32_t serial, dns_updatemethod_t method, |
2065 | 0 | dns_updatemethod_t *used) { |
2066 | 0 | uint32_t new_serial = dns__update_soaserial(serial, method); |
2067 | 0 | switch (method) { |
2068 | 0 | case dns_updatemethod_none: |
2069 | 0 | case dns_updatemethod_increment: |
2070 | 0 | break; |
2071 | 0 | case dns_updatemethod_unixtime: |
2072 | 0 | case dns_updatemethod_date: |
2073 | 0 | if (!(new_serial != 0 && isc_serial_gt(new_serial, serial))) { |
2074 | | /* |
2075 | | * If the new date serial following YYYYMMDD00 is equal |
2076 | | * to or smaller than the current serial, but YYYYMMDD99 |
2077 | | * would be larger, pretend we have used the |
2078 | | * "dns_updatemethod_date" method. |
2079 | | */ |
2080 | 0 | if (method == dns_updatemethod_unixtime || |
2081 | 0 | !isc_serial_gt(new_serial + 99, serial)) |
2082 | 0 | { |
2083 | 0 | method = dns_updatemethod_increment; |
2084 | 0 | } |
2085 | 0 | new_serial = dns__update_soaserial( |
2086 | 0 | serial, dns_updatemethod_increment); |
2087 | 0 | } |
2088 | 0 | break; |
2089 | 0 | default: |
2090 | 0 | UNREACHABLE(); |
2091 | 0 | } |
2092 | | |
2093 | 0 | SET_IF_NOT_NULL(used, method); |
2094 | |
|
2095 | 0 | return new_serial; |
2096 | 0 | } |