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