/src/bind9/lib/dns/dnssec.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 | | /*! \file */ |
15 | | |
16 | | #include <ctype.h> |
17 | | #include <inttypes.h> |
18 | | #include <stdbool.h> |
19 | | #include <stdlib.h> |
20 | | |
21 | | #include <isc/buffer.h> |
22 | | #include <isc/dir.h> |
23 | | #include <isc/mem.h> |
24 | | #include <isc/result.h> |
25 | | #include <isc/serial.h> |
26 | | #include <isc/string.h> |
27 | | #include <isc/util.h> |
28 | | |
29 | | #include <dns/db.h> |
30 | | #include <dns/diff.h> |
31 | | #include <dns/dnssec.h> |
32 | | #include <dns/fixedname.h> |
33 | | #include <dns/kasp.h> |
34 | | #include <dns/keyvalues.h> |
35 | | #include <dns/log.h> |
36 | | #include <dns/message.h> |
37 | | #include <dns/rdata.h> |
38 | | #include <dns/rdatalist.h> |
39 | | #include <dns/rdataset.h> |
40 | | #include <dns/rdatastruct.h> |
41 | | #include <dns/stats.h> |
42 | | #include <dns/tsig.h> /* for DNS_TSIG_FUDGE */ |
43 | | |
44 | | isc_stats_t *dns_dnssec_stats; |
45 | | |
46 | 123 | #define is_response(msg) ((msg->flags & DNS_MESSAGEFLAG_QR) != 0) |
47 | | |
48 | | #define RETERR(x) \ |
49 | 298 | do { \ |
50 | 298 | result = (x); \ |
51 | 298 | if (result != ISC_R_SUCCESS) \ |
52 | 298 | goto failure; \ |
53 | 298 | } while (0) |
54 | | |
55 | | #define TYPE_SIGN 0 |
56 | | #define TYPE_VERIFY 1 |
57 | | |
58 | | static isc_result_t |
59 | | digest_callback(void *arg, isc_region_t *data); |
60 | | |
61 | | static int |
62 | | rdata_compare_wrapper(const void *rdata1, const void *rdata2); |
63 | | |
64 | | static isc_result_t |
65 | | rdataset_to_sortedarray(dns_rdataset_t *set, isc_mem_t *mctx, |
66 | | dns_rdata_t **rdata, int *nrdata); |
67 | | |
68 | | static isc_result_t |
69 | 0 | digest_callback(void *arg, isc_region_t *data) { |
70 | 0 | dst_context_t *ctx = arg; |
71 | |
|
72 | 0 | return (dst_context_adddata(ctx, data)); |
73 | 0 | } |
74 | | |
75 | | static void |
76 | 0 | inc_stat(isc_statscounter_t counter) { |
77 | 0 | if (dns_dnssec_stats != NULL) { |
78 | 0 | isc_stats_increment(dns_dnssec_stats, counter); |
79 | 0 | } |
80 | 0 | } |
81 | | |
82 | | /* |
83 | | * Make qsort happy. |
84 | | */ |
85 | | static int |
86 | 0 | rdata_compare_wrapper(const void *rdata1, const void *rdata2) { |
87 | 0 | return (dns_rdata_compare((const dns_rdata_t *)rdata1, |
88 | 0 | (const dns_rdata_t *)rdata2)); |
89 | 0 | } |
90 | | |
91 | | /* |
92 | | * Sort the rdataset into an array. |
93 | | */ |
94 | | static isc_result_t |
95 | | rdataset_to_sortedarray(dns_rdataset_t *set, isc_mem_t *mctx, |
96 | 0 | dns_rdata_t **rdata, int *nrdata) { |
97 | 0 | isc_result_t ret; |
98 | 0 | int i = 0, n; |
99 | 0 | dns_rdata_t *data; |
100 | 0 | dns_rdataset_t rdataset; |
101 | |
|
102 | 0 | n = dns_rdataset_count(set); |
103 | |
|
104 | 0 | data = isc_mem_get(mctx, n * sizeof(dns_rdata_t)); |
105 | |
|
106 | 0 | dns_rdataset_init(&rdataset); |
107 | 0 | dns_rdataset_clone(set, &rdataset); |
108 | 0 | ret = dns_rdataset_first(&rdataset); |
109 | 0 | if (ret != ISC_R_SUCCESS) { |
110 | 0 | dns_rdataset_disassociate(&rdataset); |
111 | 0 | isc_mem_put(mctx, data, n * sizeof(dns_rdata_t)); |
112 | 0 | return (ret); |
113 | 0 | } |
114 | | |
115 | | /* |
116 | | * Put them in the array. |
117 | | */ |
118 | 0 | do { |
119 | 0 | dns_rdata_init(&data[i]); |
120 | 0 | dns_rdataset_current(&rdataset, &data[i++]); |
121 | 0 | } while (dns_rdataset_next(&rdataset) == ISC_R_SUCCESS); |
122 | | |
123 | | /* |
124 | | * Sort the array. |
125 | | */ |
126 | 0 | qsort(data, n, sizeof(dns_rdata_t), rdata_compare_wrapper); |
127 | 0 | *rdata = data; |
128 | 0 | *nrdata = n; |
129 | 0 | dns_rdataset_disassociate(&rdataset); |
130 | 0 | return (ISC_R_SUCCESS); |
131 | 0 | } |
132 | | |
133 | | isc_result_t |
134 | | dns_dnssec_keyfromrdata(const dns_name_t *name, const dns_rdata_t *rdata, |
135 | 0 | isc_mem_t *mctx, dst_key_t **key) { |
136 | 0 | isc_buffer_t b; |
137 | 0 | isc_region_t r; |
138 | |
|
139 | 0 | INSIST(name != NULL); |
140 | 0 | INSIST(rdata != NULL); |
141 | 0 | INSIST(mctx != NULL); |
142 | 0 | INSIST(key != NULL); |
143 | 0 | INSIST(*key == NULL); |
144 | 0 | REQUIRE(rdata->type == dns_rdatatype_key || |
145 | 0 | rdata->type == dns_rdatatype_dnskey); |
146 | |
|
147 | 0 | dns_rdata_toregion(rdata, &r); |
148 | 0 | isc_buffer_init(&b, r.base, r.length); |
149 | 0 | isc_buffer_add(&b, r.length); |
150 | 0 | return (dst_key_fromdns(name, rdata->rdclass, &b, mctx, key)); |
151 | 0 | } |
152 | | |
153 | | static isc_result_t |
154 | | digest_sig(dst_context_t *ctx, bool downcase, dns_rdata_t *sigrdata, |
155 | 0 | dns_rdata_rrsig_t *rrsig) { |
156 | 0 | isc_region_t r; |
157 | 0 | isc_result_t ret; |
158 | 0 | dns_fixedname_t fname; |
159 | |
|
160 | 0 | dns_rdata_toregion(sigrdata, &r); |
161 | 0 | INSIST(r.length >= 19); |
162 | |
|
163 | 0 | r.length = 18; |
164 | 0 | ret = dst_context_adddata(ctx, &r); |
165 | 0 | if (ret != ISC_R_SUCCESS) { |
166 | 0 | return (ret); |
167 | 0 | } |
168 | 0 | if (downcase) { |
169 | 0 | dns_fixedname_init(&fname); |
170 | |
|
171 | 0 | RUNTIME_CHECK(dns_name_downcase(&rrsig->signer, |
172 | 0 | dns_fixedname_name(&fname), |
173 | 0 | NULL) == ISC_R_SUCCESS); |
174 | 0 | dns_name_toregion(dns_fixedname_name(&fname), &r); |
175 | 0 | } else { |
176 | 0 | dns_name_toregion(&rrsig->signer, &r); |
177 | 0 | } |
178 | |
|
179 | 0 | return (dst_context_adddata(ctx, &r)); |
180 | 0 | } |
181 | | |
182 | | isc_result_t |
183 | | dns_dnssec_sign(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, |
184 | | isc_stdtime_t *inception, isc_stdtime_t *expire, |
185 | 0 | isc_mem_t *mctx, isc_buffer_t *buffer, dns_rdata_t *sigrdata) { |
186 | 0 | dns_rdata_rrsig_t sig; |
187 | 0 | dns_rdata_t tmpsigrdata; |
188 | 0 | dns_rdata_t *rdatas; |
189 | 0 | int nrdatas, i; |
190 | 0 | isc_buffer_t sigbuf, envbuf; |
191 | 0 | isc_region_t r; |
192 | 0 | dst_context_t *ctx = NULL; |
193 | 0 | isc_result_t ret; |
194 | 0 | isc_buffer_t *databuf = NULL; |
195 | 0 | char data[256 + 8]; |
196 | 0 | uint32_t flags; |
197 | 0 | unsigned int sigsize; |
198 | 0 | dns_fixedname_t fnewname; |
199 | 0 | dns_fixedname_t fsigner; |
200 | |
|
201 | 0 | REQUIRE(name != NULL); |
202 | 0 | REQUIRE(dns_name_countlabels(name) <= 255); |
203 | 0 | REQUIRE(set != NULL); |
204 | 0 | REQUIRE(key != NULL); |
205 | 0 | REQUIRE(inception != NULL); |
206 | 0 | REQUIRE(expire != NULL); |
207 | 0 | REQUIRE(mctx != NULL); |
208 | 0 | REQUIRE(sigrdata != NULL); |
209 | |
|
210 | 0 | if (*inception >= *expire) { |
211 | 0 | return (DNS_R_INVALIDTIME); |
212 | 0 | } |
213 | | |
214 | | /* |
215 | | * Is the key allowed to sign data? |
216 | | */ |
217 | 0 | flags = dst_key_flags(key); |
218 | 0 | if ((flags & DNS_KEYTYPE_NOAUTH) != 0) { |
219 | 0 | return (DNS_R_KEYUNAUTHORIZED); |
220 | 0 | } |
221 | 0 | if ((flags & DNS_KEYFLAG_OWNERMASK) != DNS_KEYOWNER_ZONE) { |
222 | 0 | return (DNS_R_KEYUNAUTHORIZED); |
223 | 0 | } |
224 | | |
225 | 0 | sig.mctx = mctx; |
226 | 0 | sig.common.rdclass = set->rdclass; |
227 | 0 | sig.common.rdtype = dns_rdatatype_rrsig; |
228 | 0 | ISC_LINK_INIT(&sig.common, link); |
229 | | |
230 | | /* |
231 | | * Downcase signer. |
232 | | */ |
233 | 0 | dns_name_init(&sig.signer, NULL); |
234 | 0 | dns_fixedname_init(&fsigner); |
235 | 0 | RUNTIME_CHECK(dns_name_downcase(dst_key_name(key), |
236 | 0 | dns_fixedname_name(&fsigner), |
237 | 0 | NULL) == ISC_R_SUCCESS); |
238 | 0 | dns_name_clone(dns_fixedname_name(&fsigner), &sig.signer); |
239 | |
|
240 | 0 | sig.covered = set->type; |
241 | 0 | sig.algorithm = dst_key_alg(key); |
242 | 0 | sig.labels = dns_name_countlabels(name) - 1; |
243 | 0 | if (dns_name_iswildcard(name)) { |
244 | 0 | sig.labels--; |
245 | 0 | } |
246 | 0 | sig.originalttl = set->ttl; |
247 | 0 | sig.timesigned = *inception; |
248 | 0 | sig.timeexpire = *expire; |
249 | 0 | sig.keyid = dst_key_id(key); |
250 | 0 | ret = dst_key_sigsize(key, &sigsize); |
251 | 0 | if (ret != ISC_R_SUCCESS) { |
252 | 0 | return (ret); |
253 | 0 | } |
254 | 0 | sig.siglen = sigsize; |
255 | | /* |
256 | | * The actual contents of sig.signature are not important yet, since |
257 | | * they're not used in digest_sig(). |
258 | | */ |
259 | 0 | sig.signature = isc_mem_get(mctx, sig.siglen); |
260 | |
|
261 | 0 | isc_buffer_allocate(mctx, &databuf, sigsize + 256 + 18); |
262 | |
|
263 | 0 | dns_rdata_init(&tmpsigrdata); |
264 | 0 | ret = dns_rdata_fromstruct(&tmpsigrdata, sig.common.rdclass, |
265 | 0 | sig.common.rdtype, &sig, databuf); |
266 | 0 | if (ret != ISC_R_SUCCESS) { |
267 | 0 | goto cleanup_databuf; |
268 | 0 | } |
269 | | |
270 | 0 | ret = dst_context_create(key, mctx, DNS_LOGCATEGORY_DNSSEC, true, 0, |
271 | 0 | &ctx); |
272 | 0 | if (ret != ISC_R_SUCCESS) { |
273 | 0 | goto cleanup_databuf; |
274 | 0 | } |
275 | | |
276 | | /* |
277 | | * Digest the SIG rdata. |
278 | | */ |
279 | 0 | ret = digest_sig(ctx, false, &tmpsigrdata, &sig); |
280 | 0 | if (ret != ISC_R_SUCCESS) { |
281 | 0 | goto cleanup_context; |
282 | 0 | } |
283 | | |
284 | 0 | dns_fixedname_init(&fnewname); |
285 | 0 | RUNTIME_CHECK(dns_name_downcase(name, dns_fixedname_name(&fnewname), |
286 | 0 | NULL) == ISC_R_SUCCESS); |
287 | 0 | dns_name_toregion(dns_fixedname_name(&fnewname), &r); |
288 | | |
289 | | /* |
290 | | * Create an envelope for each rdata: <name|type|class|ttl>. |
291 | | */ |
292 | 0 | isc_buffer_init(&envbuf, data, sizeof(data)); |
293 | 0 | memmove(data, r.base, r.length); |
294 | 0 | isc_buffer_add(&envbuf, r.length); |
295 | 0 | isc_buffer_putuint16(&envbuf, set->type); |
296 | 0 | isc_buffer_putuint16(&envbuf, set->rdclass); |
297 | 0 | isc_buffer_putuint32(&envbuf, set->ttl); |
298 | |
|
299 | 0 | ret = rdataset_to_sortedarray(set, mctx, &rdatas, &nrdatas); |
300 | 0 | if (ret != ISC_R_SUCCESS) { |
301 | 0 | goto cleanup_context; |
302 | 0 | } |
303 | 0 | isc_buffer_usedregion(&envbuf, &r); |
304 | |
|
305 | 0 | for (i = 0; i < nrdatas; i++) { |
306 | 0 | uint16_t len; |
307 | 0 | isc_buffer_t lenbuf; |
308 | 0 | isc_region_t lenr; |
309 | | |
310 | | /* |
311 | | * Skip duplicates. |
312 | | */ |
313 | 0 | if (i > 0 && dns_rdata_compare(&rdatas[i], &rdatas[i - 1]) == 0) |
314 | 0 | { |
315 | 0 | continue; |
316 | 0 | } |
317 | | |
318 | | /* |
319 | | * Digest the envelope. |
320 | | */ |
321 | 0 | ret = dst_context_adddata(ctx, &r); |
322 | 0 | if (ret != ISC_R_SUCCESS) { |
323 | 0 | goto cleanup_array; |
324 | 0 | } |
325 | | |
326 | | /* |
327 | | * Digest the length of the rdata. |
328 | | */ |
329 | 0 | isc_buffer_init(&lenbuf, &len, sizeof(len)); |
330 | 0 | INSIST(rdatas[i].length < 65536); |
331 | 0 | isc_buffer_putuint16(&lenbuf, (uint16_t)rdatas[i].length); |
332 | 0 | isc_buffer_usedregion(&lenbuf, &lenr); |
333 | 0 | ret = dst_context_adddata(ctx, &lenr); |
334 | 0 | if (ret != ISC_R_SUCCESS) { |
335 | 0 | goto cleanup_array; |
336 | 0 | } |
337 | | |
338 | | /* |
339 | | * Digest the rdata. |
340 | | */ |
341 | 0 | ret = dns_rdata_digest(&rdatas[i], digest_callback, ctx); |
342 | 0 | if (ret != ISC_R_SUCCESS) { |
343 | 0 | goto cleanup_array; |
344 | 0 | } |
345 | 0 | } |
346 | | |
347 | 0 | isc_buffer_init(&sigbuf, sig.signature, sig.siglen); |
348 | 0 | ret = dst_context_sign(ctx, &sigbuf); |
349 | 0 | if (ret != ISC_R_SUCCESS) { |
350 | 0 | goto cleanup_array; |
351 | 0 | } |
352 | 0 | isc_buffer_usedregion(&sigbuf, &r); |
353 | 0 | if (r.length != sig.siglen) { |
354 | 0 | ret = ISC_R_NOSPACE; |
355 | 0 | goto cleanup_array; |
356 | 0 | } |
357 | | |
358 | 0 | ret = dns_rdata_fromstruct(sigrdata, sig.common.rdclass, |
359 | 0 | sig.common.rdtype, &sig, buffer); |
360 | |
|
361 | 0 | cleanup_array: |
362 | 0 | isc_mem_put(mctx, rdatas, nrdatas * sizeof(dns_rdata_t)); |
363 | 0 | cleanup_context: |
364 | 0 | dst_context_destroy(&ctx); |
365 | 0 | cleanup_databuf: |
366 | 0 | isc_buffer_free(&databuf); |
367 | 0 | isc_mem_put(mctx, sig.signature, sig.siglen); |
368 | |
|
369 | 0 | return (ret); |
370 | 0 | } |
371 | | |
372 | | isc_result_t |
373 | | dns_dnssec_verify(const dns_name_t *name, dns_rdataset_t *set, dst_key_t *key, |
374 | | bool ignoretime, unsigned int maxbits, isc_mem_t *mctx, |
375 | 0 | dns_rdata_t *sigrdata, dns_name_t *wild) { |
376 | 0 | dns_rdata_rrsig_t sig; |
377 | 0 | dns_fixedname_t fnewname; |
378 | 0 | isc_region_t r; |
379 | 0 | isc_buffer_t envbuf; |
380 | 0 | dns_rdata_t *rdatas; |
381 | 0 | int nrdatas, i; |
382 | 0 | isc_stdtime_t now; |
383 | 0 | isc_result_t ret; |
384 | 0 | unsigned char data[300]; |
385 | 0 | dst_context_t *ctx = NULL; |
386 | 0 | int labels = 0; |
387 | 0 | uint32_t flags; |
388 | 0 | bool downcase = false; |
389 | |
|
390 | 0 | REQUIRE(name != NULL); |
391 | 0 | REQUIRE(set != NULL); |
392 | 0 | REQUIRE(key != NULL); |
393 | 0 | REQUIRE(mctx != NULL); |
394 | 0 | REQUIRE(sigrdata != NULL && sigrdata->type == dns_rdatatype_rrsig); |
395 | |
|
396 | 0 | ret = dns_rdata_tostruct(sigrdata, &sig, NULL); |
397 | 0 | if (ret != ISC_R_SUCCESS) { |
398 | 0 | return (ret); |
399 | 0 | } |
400 | | |
401 | 0 | if (set->type != sig.covered) { |
402 | 0 | return (DNS_R_SIGINVALID); |
403 | 0 | } |
404 | | |
405 | 0 | if (isc_serial_lt(sig.timeexpire, sig.timesigned)) { |
406 | 0 | inc_stat(dns_dnssecstats_fail); |
407 | 0 | return (DNS_R_SIGINVALID); |
408 | 0 | } |
409 | | |
410 | 0 | if (!ignoretime) { |
411 | 0 | now = isc_stdtime_now(); |
412 | | |
413 | | /* |
414 | | * Is SIG temporally valid? |
415 | | */ |
416 | 0 | if (isc_serial_lt((uint32_t)now, sig.timesigned)) { |
417 | 0 | inc_stat(dns_dnssecstats_fail); |
418 | 0 | return (DNS_R_SIGFUTURE); |
419 | 0 | } else if (isc_serial_lt(sig.timeexpire, (uint32_t)now)) { |
420 | 0 | inc_stat(dns_dnssecstats_fail); |
421 | 0 | return (DNS_R_SIGEXPIRED); |
422 | 0 | } |
423 | 0 | } |
424 | | |
425 | | /* |
426 | | * NS, SOA and DNSSKEY records are signed by their owner. |
427 | | * DS records are signed by the parent. |
428 | | */ |
429 | 0 | switch (set->type) { |
430 | 0 | case dns_rdatatype_ns: |
431 | 0 | case dns_rdatatype_soa: |
432 | 0 | case dns_rdatatype_dnskey: |
433 | 0 | if (!dns_name_equal(name, &sig.signer)) { |
434 | 0 | inc_stat(dns_dnssecstats_fail); |
435 | 0 | return (DNS_R_SIGINVALID); |
436 | 0 | } |
437 | 0 | break; |
438 | 0 | case dns_rdatatype_ds: |
439 | 0 | if (dns_name_equal(name, &sig.signer)) { |
440 | 0 | inc_stat(dns_dnssecstats_fail); |
441 | 0 | return (DNS_R_SIGINVALID); |
442 | 0 | } |
443 | 0 | FALLTHROUGH; |
444 | 0 | default: |
445 | 0 | if (!dns_name_issubdomain(name, &sig.signer)) { |
446 | 0 | inc_stat(dns_dnssecstats_fail); |
447 | 0 | return (DNS_R_SIGINVALID); |
448 | 0 | } |
449 | 0 | break; |
450 | 0 | } |
451 | | |
452 | | /* |
453 | | * Is the key allowed to sign data? |
454 | | */ |
455 | 0 | flags = dst_key_flags(key); |
456 | 0 | if ((flags & DNS_KEYTYPE_NOAUTH) != 0) { |
457 | 0 | inc_stat(dns_dnssecstats_fail); |
458 | 0 | return (DNS_R_KEYUNAUTHORIZED); |
459 | 0 | } |
460 | 0 | if ((flags & DNS_KEYFLAG_OWNERMASK) != DNS_KEYOWNER_ZONE) { |
461 | 0 | inc_stat(dns_dnssecstats_fail); |
462 | 0 | return (DNS_R_KEYUNAUTHORIZED); |
463 | 0 | } |
464 | | |
465 | 0 | again: |
466 | 0 | ret = dst_context_create(key, mctx, DNS_LOGCATEGORY_DNSSEC, false, |
467 | 0 | maxbits, &ctx); |
468 | 0 | if (ret != ISC_R_SUCCESS) { |
469 | 0 | goto cleanup_struct; |
470 | 0 | } |
471 | | |
472 | | /* |
473 | | * Digest the SIG rdata (not including the signature). |
474 | | */ |
475 | 0 | ret = digest_sig(ctx, downcase, sigrdata, &sig); |
476 | 0 | if (ret != ISC_R_SUCCESS) { |
477 | 0 | goto cleanup_context; |
478 | 0 | } |
479 | | |
480 | | /* |
481 | | * If the name is an expanded wildcard, use the wildcard name. |
482 | | */ |
483 | 0 | dns_fixedname_init(&fnewname); |
484 | 0 | labels = dns_name_countlabels(name) - 1; |
485 | 0 | RUNTIME_CHECK(dns_name_downcase(name, dns_fixedname_name(&fnewname), |
486 | 0 | NULL) == ISC_R_SUCCESS); |
487 | 0 | if (labels - sig.labels > 0) { |
488 | 0 | dns_name_split(dns_fixedname_name(&fnewname), sig.labels + 1, |
489 | 0 | NULL, dns_fixedname_name(&fnewname)); |
490 | 0 | } |
491 | |
|
492 | 0 | dns_name_toregion(dns_fixedname_name(&fnewname), &r); |
493 | | |
494 | | /* |
495 | | * Create an envelope for each rdata: <name|type|class|ttl>. |
496 | | */ |
497 | 0 | isc_buffer_init(&envbuf, data, sizeof(data)); |
498 | 0 | if (labels - sig.labels > 0) { |
499 | 0 | isc_buffer_putuint8(&envbuf, 1); |
500 | 0 | isc_buffer_putuint8(&envbuf, '*'); |
501 | 0 | memmove(data + 2, r.base, r.length); |
502 | 0 | } else { |
503 | 0 | memmove(data, r.base, r.length); |
504 | 0 | } |
505 | 0 | isc_buffer_add(&envbuf, r.length); |
506 | 0 | isc_buffer_putuint16(&envbuf, set->type); |
507 | 0 | isc_buffer_putuint16(&envbuf, set->rdclass); |
508 | 0 | isc_buffer_putuint32(&envbuf, sig.originalttl); |
509 | |
|
510 | 0 | ret = rdataset_to_sortedarray(set, mctx, &rdatas, &nrdatas); |
511 | 0 | if (ret != ISC_R_SUCCESS) { |
512 | 0 | goto cleanup_context; |
513 | 0 | } |
514 | | |
515 | 0 | isc_buffer_usedregion(&envbuf, &r); |
516 | |
|
517 | 0 | for (i = 0; i < nrdatas; i++) { |
518 | 0 | uint16_t len; |
519 | 0 | isc_buffer_t lenbuf; |
520 | 0 | isc_region_t lenr; |
521 | | |
522 | | /* |
523 | | * Skip duplicates. |
524 | | */ |
525 | 0 | if (i > 0 && dns_rdata_compare(&rdatas[i], &rdatas[i - 1]) == 0) |
526 | 0 | { |
527 | 0 | continue; |
528 | 0 | } |
529 | | |
530 | | /* |
531 | | * Digest the envelope. |
532 | | */ |
533 | 0 | ret = dst_context_adddata(ctx, &r); |
534 | 0 | if (ret != ISC_R_SUCCESS) { |
535 | 0 | goto cleanup_array; |
536 | 0 | } |
537 | | |
538 | | /* |
539 | | * Digest the rdata length. |
540 | | */ |
541 | 0 | isc_buffer_init(&lenbuf, &len, sizeof(len)); |
542 | 0 | INSIST(rdatas[i].length < 65536); |
543 | 0 | isc_buffer_putuint16(&lenbuf, (uint16_t)rdatas[i].length); |
544 | 0 | isc_buffer_usedregion(&lenbuf, &lenr); |
545 | | |
546 | | /* |
547 | | * Digest the rdata. |
548 | | */ |
549 | 0 | ret = dst_context_adddata(ctx, &lenr); |
550 | 0 | if (ret != ISC_R_SUCCESS) { |
551 | 0 | goto cleanup_array; |
552 | 0 | } |
553 | 0 | ret = dns_rdata_digest(&rdatas[i], digest_callback, ctx); |
554 | 0 | if (ret != ISC_R_SUCCESS) { |
555 | 0 | goto cleanup_array; |
556 | 0 | } |
557 | 0 | } |
558 | | |
559 | 0 | r.base = sig.signature; |
560 | 0 | r.length = sig.siglen; |
561 | 0 | ret = dst_context_verify2(ctx, maxbits, &r); |
562 | 0 | if (ret == ISC_R_SUCCESS && downcase) { |
563 | 0 | char namebuf[DNS_NAME_FORMATSIZE]; |
564 | 0 | dns_name_format(&sig.signer, namebuf, sizeof(namebuf)); |
565 | 0 | isc_log_write(dns_lctx, DNS_LOGCATEGORY_DNSSEC, |
566 | 0 | DNS_LOGMODULE_DNSSEC, ISC_LOG_DEBUG(1), |
567 | 0 | "successfully validated after lower casing " |
568 | 0 | "signer '%s'", |
569 | 0 | namebuf); |
570 | 0 | inc_stat(dns_dnssecstats_downcase); |
571 | 0 | } else if (ret == ISC_R_SUCCESS) { |
572 | 0 | inc_stat(dns_dnssecstats_asis); |
573 | 0 | } |
574 | |
|
575 | 0 | cleanup_array: |
576 | 0 | isc_mem_put(mctx, rdatas, nrdatas * sizeof(dns_rdata_t)); |
577 | 0 | cleanup_context: |
578 | 0 | dst_context_destroy(&ctx); |
579 | 0 | if (ret == DST_R_VERIFYFAILURE && !downcase) { |
580 | 0 | downcase = true; |
581 | 0 | goto again; |
582 | 0 | } |
583 | 0 | cleanup_struct: |
584 | 0 | dns_rdata_freestruct(&sig); |
585 | |
|
586 | 0 | if (ret == DST_R_VERIFYFAILURE) { |
587 | 0 | ret = DNS_R_SIGINVALID; |
588 | 0 | } |
589 | |
|
590 | 0 | if (ret != ISC_R_SUCCESS) { |
591 | 0 | inc_stat(dns_dnssecstats_fail); |
592 | 0 | } |
593 | |
|
594 | 0 | if (ret == ISC_R_SUCCESS && labels - sig.labels > 0) { |
595 | 0 | if (wild != NULL) { |
596 | 0 | RUNTIME_CHECK(dns_name_concatenate( |
597 | 0 | dns_wildcardname, |
598 | 0 | dns_fixedname_name(&fnewname), |
599 | 0 | wild, NULL) == ISC_R_SUCCESS); |
600 | 0 | } |
601 | 0 | inc_stat(dns_dnssecstats_wildcard); |
602 | 0 | ret = DNS_R_FROMWILDCARD; |
603 | 0 | } |
604 | 0 | return (ret); |
605 | 0 | } |
606 | | |
607 | | bool |
608 | 0 | dns_dnssec_keyactive(dst_key_t *key, isc_stdtime_t now) { |
609 | 0 | isc_result_t result; |
610 | 0 | isc_stdtime_t publish, active, revoke, remove; |
611 | 0 | bool hint_publish, hint_zsign, hint_ksign, hint_revoke, hint_remove; |
612 | 0 | int major, minor; |
613 | 0 | bool ksk = false, zsk = false; |
614 | 0 | isc_result_t ret; |
615 | | |
616 | | /* Is this an old-style key? */ |
617 | 0 | result = dst_key_getprivateformat(key, &major, &minor); |
618 | 0 | RUNTIME_CHECK(result == ISC_R_SUCCESS); |
619 | | |
620 | | /* Is this a KSK? */ |
621 | 0 | ret = dst_key_getbool(key, DST_BOOL_KSK, &ksk); |
622 | 0 | if (ret != ISC_R_SUCCESS) { |
623 | 0 | ksk = ((dst_key_flags(key) & DNS_KEYFLAG_KSK) != 0); |
624 | 0 | } |
625 | 0 | ret = dst_key_getbool(key, DST_BOOL_ZSK, &zsk); |
626 | 0 | if (ret != ISC_R_SUCCESS) { |
627 | 0 | zsk = ((dst_key_flags(key) & DNS_KEYFLAG_KSK) == 0); |
628 | 0 | } |
629 | | |
630 | | /* |
631 | | * Smart signing started with key format 1.3; prior to that, all |
632 | | * keys are assumed active. |
633 | | */ |
634 | 0 | if (major == 1 && minor <= 2) { |
635 | 0 | return (true); |
636 | 0 | } |
637 | | |
638 | 0 | hint_publish = dst_key_is_published(key, now, &publish); |
639 | 0 | hint_zsign = dst_key_is_signing(key, DST_BOOL_ZSK, now, &active); |
640 | 0 | hint_ksign = dst_key_is_signing(key, DST_BOOL_KSK, now, &active); |
641 | 0 | hint_revoke = dst_key_is_revoked(key, now, &revoke); |
642 | 0 | hint_remove = dst_key_is_removed(key, now, &remove); |
643 | |
|
644 | 0 | if (hint_remove) { |
645 | 0 | return (false); |
646 | 0 | } |
647 | 0 | if (hint_publish && hint_revoke) { |
648 | 0 | return (true); |
649 | 0 | } |
650 | 0 | if (hint_zsign && zsk) { |
651 | 0 | return (true); |
652 | 0 | } |
653 | 0 | if (hint_ksign && ksk) { |
654 | 0 | return (true); |
655 | 0 | } |
656 | 0 | return (false); |
657 | 0 | } |
658 | | |
659 | | /*%< |
660 | | * Indicate whether a key is scheduled to to have CDS/CDNSKEY records |
661 | | * published now. |
662 | | * |
663 | | * Returns true if. |
664 | | * - kasp says the DS record should be published (e.g. the DS state is in |
665 | | * RUMOURED or OMNIPRESENT state). |
666 | | * Or: |
667 | | * - SyncPublish is set and in the past, AND |
668 | | * - SyncDelete is unset or in the future |
669 | | */ |
670 | | static bool |
671 | 0 | syncpublish(dst_key_t *key, isc_stdtime_t now) { |
672 | 0 | isc_result_t result; |
673 | 0 | isc_stdtime_t when; |
674 | 0 | dst_key_state_t state; |
675 | 0 | int major, minor; |
676 | 0 | bool publish; |
677 | | |
678 | | /* |
679 | | * Is this an old-style key? |
680 | | */ |
681 | 0 | result = dst_key_getprivateformat(key, &major, &minor); |
682 | 0 | RUNTIME_CHECK(result == ISC_R_SUCCESS); |
683 | | |
684 | | /* |
685 | | * Smart signing started with key format 1.3 |
686 | | */ |
687 | 0 | if (major == 1 && minor <= 2) { |
688 | 0 | return (false); |
689 | 0 | } |
690 | | |
691 | | /* Check kasp state first. */ |
692 | 0 | result = dst_key_getstate(key, DST_KEY_DS, &state); |
693 | 0 | if (result == ISC_R_SUCCESS) { |
694 | 0 | return (state == DST_KEY_STATE_RUMOURED || |
695 | 0 | state == DST_KEY_STATE_OMNIPRESENT); |
696 | 0 | } |
697 | | |
698 | | /* If no kasp state, check timings. */ |
699 | 0 | publish = false; |
700 | 0 | result = dst_key_gettime(key, DST_TIME_SYNCPUBLISH, &when); |
701 | 0 | if (result == ISC_R_SUCCESS && when <= now) { |
702 | 0 | publish = true; |
703 | 0 | } |
704 | 0 | result = dst_key_gettime(key, DST_TIME_SYNCDELETE, &when); |
705 | 0 | if (result == ISC_R_SUCCESS && when < now) { |
706 | 0 | publish = false; |
707 | 0 | } |
708 | 0 | return (publish); |
709 | 0 | } |
710 | | |
711 | | /*%< |
712 | | * Indicate whether a key is scheduled to to have CDS/CDNSKEY records |
713 | | * deleted now. |
714 | | * |
715 | | * Returns true if: |
716 | | * - kasp says the DS record should be unpublished (e.g. the DS state is in |
717 | | * UNRETENTIVE or HIDDEN state). |
718 | | * Or: |
719 | | * - SyncDelete is set and in the past. |
720 | | */ |
721 | | static bool |
722 | 0 | syncdelete(dst_key_t *key, isc_stdtime_t now) { |
723 | 0 | isc_result_t result; |
724 | 0 | isc_stdtime_t when; |
725 | 0 | dst_key_state_t state; |
726 | 0 | int major, minor; |
727 | | |
728 | | /* |
729 | | * Is this an old-style key? |
730 | | */ |
731 | 0 | result = dst_key_getprivateformat(key, &major, &minor); |
732 | 0 | RUNTIME_CHECK(result == ISC_R_SUCCESS); |
733 | | |
734 | | /* |
735 | | * Smart signing started with key format 1.3. |
736 | | */ |
737 | 0 | if (major == 1 && minor <= 2) { |
738 | 0 | return (false); |
739 | 0 | } |
740 | | |
741 | | /* Check kasp state first. */ |
742 | 0 | result = dst_key_getstate(key, DST_KEY_DS, &state); |
743 | 0 | if (result == ISC_R_SUCCESS) { |
744 | 0 | return (state == DST_KEY_STATE_UNRETENTIVE || |
745 | 0 | state == DST_KEY_STATE_HIDDEN); |
746 | 0 | } |
747 | | |
748 | | /* If no kasp state, check timings. */ |
749 | 0 | result = dst_key_gettime(key, DST_TIME_SYNCDELETE, &when); |
750 | 0 | if (result != ISC_R_SUCCESS) { |
751 | 0 | return (false); |
752 | 0 | } |
753 | 0 | if (when <= now) { |
754 | 0 | return (true); |
755 | 0 | } |
756 | 0 | return (false); |
757 | 0 | } |
758 | | |
759 | | #define is_zone_key(key) \ |
760 | 0 | ((dst_key_flags(key) & DNS_KEYFLAG_OWNERMASK) == DNS_KEYOWNER_ZONE) |
761 | | |
762 | | isc_result_t |
763 | | dns_dnssec_findzonekeys(dns_db_t *db, dns_dbversion_t *ver, dns_dbnode_t *node, |
764 | | const dns_name_t *name, const char *directory, |
765 | | isc_stdtime_t now, isc_mem_t *mctx, |
766 | | unsigned int maxkeys, dst_key_t **keys, |
767 | 0 | unsigned int *nkeys) { |
768 | 0 | dns_rdataset_t rdataset; |
769 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
770 | 0 | isc_result_t result; |
771 | 0 | dst_key_t *pubkey = NULL; |
772 | 0 | unsigned int count = 0; |
773 | |
|
774 | 0 | REQUIRE(nkeys != NULL); |
775 | 0 | REQUIRE(keys != NULL); |
776 | |
|
777 | 0 | *nkeys = 0; |
778 | 0 | memset(keys, 0, sizeof(*keys) * maxkeys); |
779 | 0 | dns_rdataset_init(&rdataset); |
780 | 0 | RETERR(dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey, 0, 0, |
781 | 0 | &rdataset, NULL)); |
782 | 0 | RETERR(dns_rdataset_first(&rdataset)); |
783 | 0 | while (result == ISC_R_SUCCESS && count < maxkeys) { |
784 | 0 | pubkey = NULL; |
785 | 0 | dns_rdataset_current(&rdataset, &rdata); |
786 | 0 | RETERR(dns_dnssec_keyfromrdata(name, &rdata, mctx, &pubkey)); |
787 | 0 | dst_key_setttl(pubkey, rdataset.ttl); |
788 | |
|
789 | 0 | if (!is_zone_key(pubkey) || |
790 | 0 | (dst_key_flags(pubkey) & DNS_KEYTYPE_NOAUTH) != 0) |
791 | 0 | { |
792 | 0 | goto next; |
793 | 0 | } |
794 | | /* Corrupted .key file? */ |
795 | 0 | if (!dns_name_equal(name, dst_key_name(pubkey))) { |
796 | 0 | goto next; |
797 | 0 | } |
798 | 0 | keys[count] = NULL; |
799 | 0 | result = dst_key_fromfile( |
800 | 0 | dst_key_name(pubkey), dst_key_id(pubkey), |
801 | 0 | dst_key_alg(pubkey), |
802 | 0 | DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | DST_TYPE_STATE, |
803 | 0 | directory, mctx, &keys[count]); |
804 | | |
805 | | /* |
806 | | * If the key was revoked and the private file |
807 | | * doesn't exist, maybe it was revoked internally |
808 | | * by named. Try loading the unrevoked version. |
809 | | */ |
810 | 0 | if (result == ISC_R_FILENOTFOUND) { |
811 | 0 | uint32_t flags; |
812 | 0 | flags = dst_key_flags(pubkey); |
813 | 0 | if ((flags & DNS_KEYFLAG_REVOKE) != 0) { |
814 | 0 | dst_key_setflags(pubkey, |
815 | 0 | flags & ~DNS_KEYFLAG_REVOKE); |
816 | 0 | result = dst_key_fromfile( |
817 | 0 | dst_key_name(pubkey), |
818 | 0 | dst_key_id(pubkey), dst_key_alg(pubkey), |
819 | 0 | DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | |
820 | 0 | DST_TYPE_STATE, |
821 | 0 | directory, mctx, &keys[count]); |
822 | 0 | if (result == ISC_R_SUCCESS && |
823 | 0 | dst_key_pubcompare(pubkey, keys[count], |
824 | 0 | false)) |
825 | 0 | { |
826 | 0 | dst_key_setflags(keys[count], flags); |
827 | 0 | } |
828 | 0 | dst_key_setflags(pubkey, flags); |
829 | 0 | } |
830 | 0 | } |
831 | |
|
832 | 0 | if (result != ISC_R_SUCCESS) { |
833 | 0 | char filename[DNS_NAME_FORMATSIZE + |
834 | 0 | DNS_SECALG_FORMATSIZE + |
835 | 0 | sizeof("key file for //65535")]; |
836 | 0 | isc_result_t result2; |
837 | 0 | isc_buffer_t buf; |
838 | |
|
839 | 0 | isc_buffer_init(&buf, filename, NAME_MAX); |
840 | 0 | result2 = dst_key_getfilename( |
841 | 0 | dst_key_name(pubkey), dst_key_id(pubkey), |
842 | 0 | dst_key_alg(pubkey), |
843 | 0 | (DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | |
844 | 0 | DST_TYPE_STATE), |
845 | 0 | directory, mctx, &buf); |
846 | 0 | if (result2 != ISC_R_SUCCESS) { |
847 | 0 | char namebuf[DNS_NAME_FORMATSIZE]; |
848 | 0 | char algbuf[DNS_SECALG_FORMATSIZE]; |
849 | |
|
850 | 0 | dns_name_format(dst_key_name(pubkey), namebuf, |
851 | 0 | sizeof(namebuf)); |
852 | 0 | dns_secalg_format(dst_key_alg(pubkey), algbuf, |
853 | 0 | sizeof(algbuf)); |
854 | 0 | snprintf(filename, sizeof(filename) - 1, |
855 | 0 | "key file for %s/%s/%d", namebuf, |
856 | 0 | algbuf, dst_key_id(pubkey)); |
857 | 0 | } |
858 | |
|
859 | 0 | isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, |
860 | 0 | DNS_LOGMODULE_DNSSEC, ISC_LOG_WARNING, |
861 | 0 | "dns_dnssec_findzonekeys2: error " |
862 | 0 | "reading %s: %s", |
863 | 0 | filename, isc_result_totext(result)); |
864 | 0 | } |
865 | |
|
866 | 0 | if (result == ISC_R_FILENOTFOUND || result == ISC_R_NOPERM) { |
867 | 0 | keys[count] = pubkey; |
868 | 0 | pubkey = NULL; |
869 | 0 | count++; |
870 | 0 | goto next; |
871 | 0 | } |
872 | | |
873 | 0 | if (result != ISC_R_SUCCESS) { |
874 | 0 | goto failure; |
875 | 0 | } |
876 | | |
877 | | /* |
878 | | * If a key is marked inactive, skip it |
879 | | */ |
880 | 0 | if (!dns_dnssec_keyactive(keys[count], now)) { |
881 | 0 | dst_key_setinactive(pubkey, true); |
882 | 0 | dst_key_free(&keys[count]); |
883 | 0 | keys[count] = pubkey; |
884 | 0 | pubkey = NULL; |
885 | 0 | count++; |
886 | 0 | goto next; |
887 | 0 | } |
888 | | |
889 | | /* |
890 | | * Whatever the key's default TTL may have |
891 | | * been, the rdataset TTL takes priority. |
892 | | */ |
893 | 0 | dst_key_setttl(keys[count], rdataset.ttl); |
894 | |
|
895 | 0 | if ((dst_key_flags(keys[count]) & DNS_KEYTYPE_NOAUTH) != 0) { |
896 | | /* We should never get here. */ |
897 | 0 | dst_key_free(&keys[count]); |
898 | 0 | goto next; |
899 | 0 | } |
900 | 0 | count++; |
901 | 0 | next: |
902 | 0 | if (pubkey != NULL) { |
903 | 0 | dst_key_free(&pubkey); |
904 | 0 | } |
905 | 0 | dns_rdata_reset(&rdata); |
906 | 0 | result = dns_rdataset_next(&rdataset); |
907 | 0 | } |
908 | 0 | if (result != ISC_R_NOMORE) { |
909 | 0 | goto failure; |
910 | 0 | } |
911 | 0 | if (count == 0) { |
912 | 0 | result = ISC_R_NOTFOUND; |
913 | 0 | } else { |
914 | 0 | result = ISC_R_SUCCESS; |
915 | 0 | } |
916 | |
|
917 | 0 | failure: |
918 | 0 | if (dns_rdataset_isassociated(&rdataset)) { |
919 | 0 | dns_rdataset_disassociate(&rdataset); |
920 | 0 | } |
921 | 0 | if (pubkey != NULL) { |
922 | 0 | dst_key_free(&pubkey); |
923 | 0 | } |
924 | 0 | if (result != ISC_R_SUCCESS) { |
925 | 0 | while (count > 0) { |
926 | 0 | dst_key_free(&keys[--count]); |
927 | 0 | } |
928 | 0 | } |
929 | 0 | *nkeys = count; |
930 | 0 | return (result); |
931 | 0 | } |
932 | | |
933 | | isc_result_t |
934 | 0 | dns_dnssec_signmessage(dns_message_t *msg, dst_key_t *key) { |
935 | 0 | dns_rdata_sig_t sig; /* SIG(0) */ |
936 | 0 | unsigned char data[512]; |
937 | 0 | unsigned char header[DNS_MESSAGE_HEADERLEN]; |
938 | 0 | isc_buffer_t headerbuf, databuf, sigbuf; |
939 | 0 | unsigned int sigsize; |
940 | 0 | isc_buffer_t *dynbuf = NULL; |
941 | 0 | dns_rdata_t *rdata; |
942 | 0 | dns_rdatalist_t *datalist; |
943 | 0 | dns_rdataset_t *dataset; |
944 | 0 | isc_region_t r; |
945 | 0 | isc_stdtime_t now; |
946 | 0 | dst_context_t *ctx = NULL; |
947 | 0 | isc_mem_t *mctx; |
948 | 0 | isc_result_t result; |
949 | |
|
950 | 0 | REQUIRE(msg != NULL); |
951 | 0 | REQUIRE(key != NULL); |
952 | |
|
953 | 0 | if (is_response(msg)) { |
954 | 0 | REQUIRE(msg->query.base != NULL); |
955 | 0 | } |
956 | |
|
957 | 0 | mctx = msg->mctx; |
958 | |
|
959 | 0 | memset(&sig, 0, sizeof(sig)); |
960 | |
|
961 | 0 | sig.mctx = mctx; |
962 | 0 | sig.common.rdclass = dns_rdataclass_any; |
963 | 0 | sig.common.rdtype = dns_rdatatype_sig; /* SIG(0) */ |
964 | 0 | ISC_LINK_INIT(&sig.common, link); |
965 | |
|
966 | 0 | sig.covered = 0; |
967 | 0 | sig.algorithm = dst_key_alg(key); |
968 | 0 | sig.labels = 0; /* the root name */ |
969 | 0 | sig.originalttl = 0; |
970 | |
|
971 | 0 | if (msg->fuzzing) { |
972 | 0 | now = msg->fuzztime; |
973 | 0 | } else { |
974 | 0 | now = isc_stdtime_now(); |
975 | 0 | } |
976 | 0 | sig.timesigned = now - DNS_TSIG_FUDGE; |
977 | 0 | sig.timeexpire = now + DNS_TSIG_FUDGE; |
978 | |
|
979 | 0 | sig.keyid = dst_key_id(key); |
980 | |
|
981 | 0 | dns_name_init(&sig.signer, NULL); |
982 | 0 | dns_name_clone(dst_key_name(key), &sig.signer); |
983 | |
|
984 | 0 | sig.siglen = 0; |
985 | 0 | sig.signature = NULL; |
986 | |
|
987 | 0 | isc_buffer_init(&databuf, data, sizeof(data)); |
988 | |
|
989 | 0 | RETERR(dst_context_create(key, mctx, DNS_LOGCATEGORY_DNSSEC, true, 0, |
990 | 0 | &ctx)); |
991 | | |
992 | | /* |
993 | | * Digest the fields of the SIG - we can cheat and use |
994 | | * dns_rdata_fromstruct. Since siglen is 0, the digested data |
995 | | * is identical to dns format. |
996 | | */ |
997 | 0 | RETERR(dns_rdata_fromstruct(NULL, dns_rdataclass_any, |
998 | 0 | dns_rdatatype_sig /* SIG(0) */, &sig, |
999 | 0 | &databuf)); |
1000 | 0 | isc_buffer_usedregion(&databuf, &r); |
1001 | 0 | RETERR(dst_context_adddata(ctx, &r)); |
1002 | | |
1003 | | /* |
1004 | | * If this is a response, digest the query. |
1005 | | */ |
1006 | 0 | if (is_response(msg)) { |
1007 | 0 | RETERR(dst_context_adddata(ctx, &msg->query)); |
1008 | 0 | } |
1009 | | |
1010 | | /* |
1011 | | * Digest the header. |
1012 | | */ |
1013 | 0 | isc_buffer_init(&headerbuf, header, sizeof(header)); |
1014 | 0 | dns_message_renderheader(msg, &headerbuf); |
1015 | 0 | isc_buffer_usedregion(&headerbuf, &r); |
1016 | 0 | RETERR(dst_context_adddata(ctx, &r)); |
1017 | | |
1018 | | /* |
1019 | | * Digest the remainder of the message. |
1020 | | */ |
1021 | 0 | isc_buffer_usedregion(msg->buffer, &r); |
1022 | 0 | isc_region_consume(&r, DNS_MESSAGE_HEADERLEN); |
1023 | 0 | RETERR(dst_context_adddata(ctx, &r)); |
1024 | | |
1025 | 0 | RETERR(dst_key_sigsize(key, &sigsize)); |
1026 | 0 | sig.siglen = sigsize; |
1027 | 0 | sig.signature = isc_mem_get(mctx, sig.siglen); |
1028 | |
|
1029 | 0 | isc_buffer_init(&sigbuf, sig.signature, sig.siglen); |
1030 | 0 | RETERR(dst_context_sign(ctx, &sigbuf)); |
1031 | 0 | dst_context_destroy(&ctx); |
1032 | |
|
1033 | 0 | rdata = NULL; |
1034 | 0 | dns_message_gettemprdata(msg, &rdata); |
1035 | 0 | isc_buffer_allocate(msg->mctx, &dynbuf, 1024); |
1036 | 0 | RETERR(dns_rdata_fromstruct(rdata, dns_rdataclass_any, |
1037 | 0 | dns_rdatatype_sig /* SIG(0) */, &sig, |
1038 | 0 | dynbuf)); |
1039 | | |
1040 | 0 | isc_mem_put(mctx, sig.signature, sig.siglen); |
1041 | |
|
1042 | 0 | dns_message_takebuffer(msg, &dynbuf); |
1043 | |
|
1044 | 0 | datalist = NULL; |
1045 | 0 | dns_message_gettemprdatalist(msg, &datalist); |
1046 | 0 | datalist->rdclass = dns_rdataclass_any; |
1047 | 0 | datalist->type = dns_rdatatype_sig; /* SIG(0) */ |
1048 | 0 | ISC_LIST_APPEND(datalist->rdata, rdata, link); |
1049 | 0 | dataset = NULL; |
1050 | 0 | dns_message_gettemprdataset(msg, &dataset); |
1051 | 0 | dns_rdatalist_tordataset(datalist, dataset); |
1052 | 0 | msg->sig0 = dataset; |
1053 | |
|
1054 | 0 | return (ISC_R_SUCCESS); |
1055 | | |
1056 | 0 | failure: |
1057 | 0 | if (dynbuf != NULL) { |
1058 | 0 | isc_buffer_free(&dynbuf); |
1059 | 0 | } |
1060 | 0 | if (sig.signature != NULL) { |
1061 | 0 | isc_mem_put(mctx, sig.signature, sig.siglen); |
1062 | 0 | } |
1063 | 0 | if (ctx != NULL) { |
1064 | 0 | dst_context_destroy(&ctx); |
1065 | 0 | } |
1066 | |
|
1067 | 0 | return (result); |
1068 | 0 | } |
1069 | | |
1070 | | isc_result_t |
1071 | | dns_dnssec_verifymessage(isc_buffer_t *source, dns_message_t *msg, |
1072 | 96 | dst_key_t *key) { |
1073 | 96 | dns_rdata_sig_t sig; /* SIG(0) */ |
1074 | 96 | unsigned char header[DNS_MESSAGE_HEADERLEN]; |
1075 | 96 | dns_rdata_t rdata = DNS_RDATA_INIT; |
1076 | 96 | isc_region_t r, source_r, sig_r, header_r; |
1077 | 96 | isc_stdtime_t now; |
1078 | 96 | dst_context_t *ctx = NULL; |
1079 | 96 | isc_mem_t *mctx; |
1080 | 96 | isc_result_t result; |
1081 | 96 | uint16_t addcount, addcount_n; |
1082 | 96 | bool signeedsfree = false; |
1083 | | |
1084 | 96 | REQUIRE(source != NULL); |
1085 | 96 | REQUIRE(msg != NULL); |
1086 | 96 | REQUIRE(key != NULL); |
1087 | | |
1088 | 96 | mctx = msg->mctx; |
1089 | | |
1090 | 96 | msg->verify_attempted = 1; |
1091 | 96 | msg->verified_sig = 0; |
1092 | 96 | msg->sig0status = dns_tsigerror_badsig; |
1093 | | |
1094 | 96 | if (is_response(msg)) { |
1095 | 1 | if (msg->query.base == NULL) { |
1096 | 1 | return (DNS_R_UNEXPECTEDTSIG); |
1097 | 1 | } |
1098 | 1 | } |
1099 | | |
1100 | 95 | isc_buffer_usedregion(source, &source_r); |
1101 | | |
1102 | 95 | RETERR(dns_rdataset_first(msg->sig0)); |
1103 | 95 | dns_rdataset_current(msg->sig0, &rdata); |
1104 | | |
1105 | 95 | RETERR(dns_rdata_tostruct(&rdata, &sig, NULL)); |
1106 | 95 | signeedsfree = true; |
1107 | | |
1108 | 95 | if (sig.labels != 0) { |
1109 | 6 | result = DNS_R_SIGINVALID; |
1110 | 6 | goto failure; |
1111 | 6 | } |
1112 | | |
1113 | 89 | if (isc_serial_lt(sig.timeexpire, sig.timesigned)) { |
1114 | 30 | result = DNS_R_SIGINVALID; |
1115 | 30 | msg->sig0status = dns_tsigerror_badtime; |
1116 | 30 | goto failure; |
1117 | 30 | } |
1118 | | |
1119 | 59 | if (msg->fuzzing) { |
1120 | 59 | now = msg->fuzztime; |
1121 | 59 | } else { |
1122 | 0 | now = isc_stdtime_now(); |
1123 | 0 | } |
1124 | | |
1125 | 59 | if (isc_serial_lt((uint32_t)now, sig.timesigned)) { |
1126 | 18 | result = DNS_R_SIGFUTURE; |
1127 | 18 | msg->sig0status = dns_tsigerror_badtime; |
1128 | 18 | goto failure; |
1129 | 41 | } else if (isc_serial_lt(sig.timeexpire, (uint32_t)now)) { |
1130 | 14 | result = DNS_R_SIGEXPIRED; |
1131 | 14 | msg->sig0status = dns_tsigerror_badtime; |
1132 | 14 | goto failure; |
1133 | 14 | } |
1134 | | |
1135 | 27 | if (!dns_name_equal(dst_key_name(key), &sig.signer)) { |
1136 | 0 | result = DNS_R_SIGINVALID; |
1137 | 0 | msg->sig0status = dns_tsigerror_badkey; |
1138 | 0 | goto failure; |
1139 | 0 | } |
1140 | | |
1141 | 27 | RETERR(dst_context_create(key, mctx, DNS_LOGCATEGORY_DNSSEC, false, 0, |
1142 | 27 | &ctx)); |
1143 | | |
1144 | | /* |
1145 | | * Digest the SIG(0) record, except for the signature. |
1146 | | */ |
1147 | 27 | dns_rdata_toregion(&rdata, &r); |
1148 | 27 | r.length -= sig.siglen; |
1149 | 27 | RETERR(dst_context_adddata(ctx, &r)); |
1150 | | |
1151 | | /* |
1152 | | * If this is a response, digest the query. |
1153 | | */ |
1154 | 27 | if (is_response(msg)) { |
1155 | 0 | RETERR(dst_context_adddata(ctx, &msg->query)); |
1156 | 0 | } |
1157 | | |
1158 | | /* |
1159 | | * Extract the header. |
1160 | | */ |
1161 | 27 | memmove(header, source_r.base, DNS_MESSAGE_HEADERLEN); |
1162 | | |
1163 | | /* |
1164 | | * Decrement the additional field counter. |
1165 | | */ |
1166 | 27 | memmove(&addcount, &header[DNS_MESSAGE_HEADERLEN - 2], 2); |
1167 | 27 | addcount_n = ntohs(addcount); |
1168 | 27 | addcount = htons((uint16_t)(addcount_n - 1)); |
1169 | 27 | memmove(&header[DNS_MESSAGE_HEADERLEN - 2], &addcount, 2); |
1170 | | |
1171 | | /* |
1172 | | * Digest the modified header. |
1173 | | */ |
1174 | 27 | header_r.base = (unsigned char *)header; |
1175 | 27 | header_r.length = DNS_MESSAGE_HEADERLEN; |
1176 | 27 | RETERR(dst_context_adddata(ctx, &header_r)); |
1177 | | |
1178 | | /* |
1179 | | * Digest all non-SIG(0) records. |
1180 | | */ |
1181 | 27 | r.base = source_r.base + DNS_MESSAGE_HEADERLEN; |
1182 | 27 | r.length = msg->sigstart - DNS_MESSAGE_HEADERLEN; |
1183 | 27 | RETERR(dst_context_adddata(ctx, &r)); |
1184 | | |
1185 | 27 | sig_r.base = sig.signature; |
1186 | 27 | sig_r.length = sig.siglen; |
1187 | 27 | result = dst_context_verify(ctx, &sig_r); |
1188 | 27 | if (result != ISC_R_SUCCESS) { |
1189 | 27 | msg->sig0status = dns_tsigerror_badsig; |
1190 | 27 | goto failure; |
1191 | 27 | } |
1192 | | |
1193 | 0 | msg->verified_sig = 1; |
1194 | 0 | msg->sig0status = dns_rcode_noerror; |
1195 | |
|
1196 | 0 | dst_context_destroy(&ctx); |
1197 | 0 | dns_rdata_freestruct(&sig); |
1198 | |
|
1199 | 0 | return (ISC_R_SUCCESS); |
1200 | | |
1201 | 95 | failure: |
1202 | 95 | if (signeedsfree) { |
1203 | 95 | dns_rdata_freestruct(&sig); |
1204 | 95 | } |
1205 | 95 | if (ctx != NULL) { |
1206 | 27 | dst_context_destroy(&ctx); |
1207 | 27 | } |
1208 | | |
1209 | 95 | return (result); |
1210 | 27 | } |
1211 | | |
1212 | | /*% |
1213 | | * Does this key ('rdata') self sign the rrset ('rdataset')? |
1214 | | */ |
1215 | | bool |
1216 | | dns_dnssec_selfsigns(dns_rdata_t *rdata, const dns_name_t *name, |
1217 | | dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, |
1218 | 0 | bool ignoretime, isc_mem_t *mctx) { |
1219 | 0 | INSIST(rdataset->type == dns_rdatatype_key || |
1220 | 0 | rdataset->type == dns_rdatatype_dnskey); |
1221 | 0 | if (rdataset->type == dns_rdatatype_key) { |
1222 | 0 | INSIST(sigrdataset->type == dns_rdatatype_sig); |
1223 | 0 | INSIST(sigrdataset->covers == dns_rdatatype_key); |
1224 | 0 | } else { |
1225 | 0 | INSIST(sigrdataset->type == dns_rdatatype_rrsig); |
1226 | 0 | INSIST(sigrdataset->covers == dns_rdatatype_dnskey); |
1227 | 0 | } |
1228 | |
|
1229 | 0 | return (dns_dnssec_signs(rdata, name, rdataset, sigrdataset, ignoretime, |
1230 | 0 | mctx)); |
1231 | 0 | } |
1232 | | |
1233 | | bool |
1234 | | dns_dnssec_signs(dns_rdata_t *rdata, const dns_name_t *name, |
1235 | | dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset, |
1236 | 0 | bool ignoretime, isc_mem_t *mctx) { |
1237 | 0 | dst_key_t *dstkey = NULL; |
1238 | 0 | dns_keytag_t keytag; |
1239 | 0 | dns_rdata_dnskey_t key; |
1240 | 0 | dns_rdata_rrsig_t sig; |
1241 | 0 | dns_rdata_t sigrdata = DNS_RDATA_INIT; |
1242 | 0 | isc_result_t result; |
1243 | |
|
1244 | 0 | INSIST(sigrdataset->type == dns_rdatatype_rrsig); |
1245 | 0 | if (sigrdataset->covers != rdataset->type) { |
1246 | 0 | return (false); |
1247 | 0 | } |
1248 | | |
1249 | 0 | result = dns_dnssec_keyfromrdata(name, rdata, mctx, &dstkey); |
1250 | 0 | if (result != ISC_R_SUCCESS) { |
1251 | 0 | return (false); |
1252 | 0 | } |
1253 | 0 | result = dns_rdata_tostruct(rdata, &key, NULL); |
1254 | 0 | RUNTIME_CHECK(result == ISC_R_SUCCESS); |
1255 | |
|
1256 | 0 | keytag = dst_key_id(dstkey); |
1257 | 0 | for (result = dns_rdataset_first(sigrdataset); result == ISC_R_SUCCESS; |
1258 | 0 | result = dns_rdataset_next(sigrdataset)) |
1259 | 0 | { |
1260 | 0 | dns_rdata_reset(&sigrdata); |
1261 | 0 | dns_rdataset_current(sigrdataset, &sigrdata); |
1262 | 0 | result = dns_rdata_tostruct(&sigrdata, &sig, NULL); |
1263 | 0 | RUNTIME_CHECK(result == ISC_R_SUCCESS); |
1264 | |
|
1265 | 0 | if (sig.algorithm == key.algorithm && sig.keyid == keytag) { |
1266 | 0 | result = dns_dnssec_verify(name, rdataset, dstkey, |
1267 | 0 | ignoretime, 0, mctx, |
1268 | 0 | &sigrdata, NULL); |
1269 | 0 | if (result == ISC_R_SUCCESS) { |
1270 | 0 | dst_key_free(&dstkey); |
1271 | 0 | return (true); |
1272 | 0 | } |
1273 | 0 | } |
1274 | 0 | } |
1275 | 0 | dst_key_free(&dstkey); |
1276 | 0 | return (false); |
1277 | 0 | } |
1278 | | |
1279 | | void |
1280 | | dns_dnsseckey_create(isc_mem_t *mctx, dst_key_t **dstkey, |
1281 | 0 | dns_dnsseckey_t **dkp) { |
1282 | 0 | isc_result_t result; |
1283 | 0 | dns_dnsseckey_t *dk; |
1284 | 0 | int major, minor; |
1285 | |
|
1286 | 0 | REQUIRE(dkp != NULL && *dkp == NULL); |
1287 | 0 | dk = isc_mem_get(mctx, sizeof(dns_dnsseckey_t)); |
1288 | |
|
1289 | 0 | dk->key = *dstkey; |
1290 | 0 | *dstkey = NULL; |
1291 | 0 | dk->force_publish = false; |
1292 | 0 | dk->force_sign = false; |
1293 | 0 | dk->hint_publish = false; |
1294 | 0 | dk->hint_sign = false; |
1295 | 0 | dk->hint_revoke = false; |
1296 | 0 | dk->hint_remove = false; |
1297 | 0 | dk->first_sign = false; |
1298 | 0 | dk->is_active = false; |
1299 | 0 | dk->purge = false; |
1300 | 0 | dk->prepublish = 0; |
1301 | 0 | dk->source = dns_keysource_unknown; |
1302 | 0 | dk->index = 0; |
1303 | | |
1304 | | /* KSK or ZSK? */ |
1305 | 0 | result = dst_key_getbool(dk->key, DST_BOOL_KSK, &dk->ksk); |
1306 | 0 | if (result != ISC_R_SUCCESS) { |
1307 | 0 | dk->ksk = ((dst_key_flags(dk->key) & DNS_KEYFLAG_KSK) != 0); |
1308 | 0 | } |
1309 | 0 | result = dst_key_getbool(dk->key, DST_BOOL_ZSK, &dk->zsk); |
1310 | 0 | if (result != ISC_R_SUCCESS) { |
1311 | 0 | dk->zsk = ((dst_key_flags(dk->key) & DNS_KEYFLAG_KSK) == 0); |
1312 | 0 | } |
1313 | | |
1314 | | /* Is this an old-style key? */ |
1315 | 0 | result = dst_key_getprivateformat(dk->key, &major, &minor); |
1316 | 0 | INSIST(result == ISC_R_SUCCESS); |
1317 | | |
1318 | | /* Smart signing started with key format 1.3 */ |
1319 | 0 | dk->legacy = (major == 1 && minor <= 2); |
1320 | |
|
1321 | 0 | ISC_LINK_INIT(dk, link); |
1322 | 0 | *dkp = dk; |
1323 | 0 | } |
1324 | | |
1325 | | void |
1326 | 0 | dns_dnsseckey_destroy(isc_mem_t *mctx, dns_dnsseckey_t **dkp) { |
1327 | 0 | dns_dnsseckey_t *dk; |
1328 | |
|
1329 | 0 | REQUIRE(dkp != NULL && *dkp != NULL); |
1330 | 0 | dk = *dkp; |
1331 | 0 | *dkp = NULL; |
1332 | 0 | if (dk->key != NULL) { |
1333 | 0 | dst_key_free(&dk->key); |
1334 | 0 | } |
1335 | 0 | isc_mem_put(mctx, dk, sizeof(dns_dnsseckey_t)); |
1336 | 0 | } |
1337 | | |
1338 | | void |
1339 | 0 | dns_dnssec_get_hints(dns_dnsseckey_t *key, isc_stdtime_t now) { |
1340 | 0 | isc_stdtime_t publish = 0, active = 0, revoke = 0, remove = 0; |
1341 | |
|
1342 | 0 | REQUIRE(key != NULL && key->key != NULL); |
1343 | |
|
1344 | 0 | key->hint_publish = dst_key_is_published(key->key, now, &publish); |
1345 | 0 | key->hint_sign = dst_key_is_signing(key->key, DST_BOOL_ZSK, now, |
1346 | 0 | &active); |
1347 | 0 | key->hint_revoke = dst_key_is_revoked(key->key, now, &revoke); |
1348 | 0 | key->hint_remove = dst_key_is_removed(key->key, now, &remove); |
1349 | | |
1350 | | /* |
1351 | | * Activation date is set (maybe in the future), but publication date |
1352 | | * isn't. Most likely the user wants to publish now and activate later. |
1353 | | * Most likely because this is true for most rollovers, except for: |
1354 | | * 1. The unpopular ZSK Double-RRSIG method. |
1355 | | * 2. When introducing a new algorithm. |
1356 | | * These two cases are rare enough that we will set hint_publish |
1357 | | * anyway when hint_sign is set, because BIND 9 natively does not |
1358 | | * support the ZSK Double-RRSIG method, and when introducing a new |
1359 | | * algorithm, we strive to publish its signatures and DNSKEY records |
1360 | | * at the same time. |
1361 | | */ |
1362 | 0 | if (key->hint_sign && publish == 0) { |
1363 | 0 | key->hint_publish = true; |
1364 | 0 | } |
1365 | | |
1366 | | /* |
1367 | | * If activation date is in the future, make note of how far off. |
1368 | | */ |
1369 | 0 | if (key->hint_publish && active > now) { |
1370 | 0 | key->prepublish = active - now; |
1371 | 0 | } |
1372 | | |
1373 | | /* |
1374 | | * Metadata says revoke. If the key is published, we *have to* sign |
1375 | | * with it per RFC5011 -- even if it was not active before. |
1376 | | * |
1377 | | * If it hasn't already been done, we should also revoke it now. |
1378 | | */ |
1379 | 0 | if (key->hint_publish && key->hint_revoke) { |
1380 | 0 | uint32_t flags; |
1381 | 0 | key->hint_sign = true; |
1382 | 0 | flags = dst_key_flags(key->key); |
1383 | 0 | if ((flags & DNS_KEYFLAG_REVOKE) == 0) { |
1384 | 0 | flags |= DNS_KEYFLAG_REVOKE; |
1385 | 0 | dst_key_setflags(key->key, flags); |
1386 | 0 | } |
1387 | 0 | } |
1388 | | |
1389 | | /* |
1390 | | * Metadata says delete, so don't publish this key or sign with it |
1391 | | * (note that signatures of a removed key may still be reused). |
1392 | | */ |
1393 | 0 | if (key->hint_remove) { |
1394 | 0 | key->hint_publish = false; |
1395 | 0 | key->hint_sign = false; |
1396 | 0 | } |
1397 | 0 | } |
1398 | | |
1399 | | /*% |
1400 | | * Get a list of DNSSEC keys from the key repository. |
1401 | | */ |
1402 | | isc_result_t |
1403 | | dns_dnssec_findmatchingkeys(const dns_name_t *origin, const char *directory, |
1404 | | isc_stdtime_t now, isc_mem_t *mctx, |
1405 | 0 | dns_dnsseckeylist_t *keylist) { |
1406 | 0 | isc_result_t result = ISC_R_SUCCESS; |
1407 | 0 | bool dir_open = false; |
1408 | 0 | dns_dnsseckeylist_t list; |
1409 | 0 | isc_dir_t dir; |
1410 | 0 | dns_dnsseckey_t *key = NULL; |
1411 | 0 | dst_key_t *dstkey = NULL; |
1412 | 0 | char namebuf[DNS_NAME_FORMATSIZE]; |
1413 | 0 | isc_buffer_t b; |
1414 | 0 | unsigned int len, i, alg; |
1415 | |
|
1416 | 0 | REQUIRE(keylist != NULL); |
1417 | 0 | ISC_LIST_INIT(list); |
1418 | 0 | isc_dir_init(&dir); |
1419 | |
|
1420 | 0 | isc_buffer_init(&b, namebuf, sizeof(namebuf) - 1); |
1421 | 0 | RETERR(dns_name_tofilenametext(origin, false, &b)); |
1422 | 0 | len = isc_buffer_usedlength(&b); |
1423 | 0 | namebuf[len] = '\0'; |
1424 | |
|
1425 | 0 | if (directory == NULL) { |
1426 | 0 | directory = "."; |
1427 | 0 | } |
1428 | 0 | RETERR(isc_dir_open(&dir, directory)); |
1429 | 0 | dir_open = true; |
1430 | |
|
1431 | 0 | while (isc_dir_read(&dir) == ISC_R_SUCCESS) { |
1432 | 0 | if (dir.entry.name[0] != 'K' || dir.entry.length < len + 1 || |
1433 | 0 | dir.entry.name[len + 1] != '+' || |
1434 | 0 | strncasecmp(dir.entry.name + 1, namebuf, len) != 0) |
1435 | 0 | { |
1436 | 0 | continue; |
1437 | 0 | } |
1438 | | |
1439 | 0 | alg = 0; |
1440 | 0 | for (i = len + 1 + 1; i < dir.entry.length; i++) { |
1441 | 0 | if (!isdigit((unsigned char)dir.entry.name[i])) { |
1442 | 0 | break; |
1443 | 0 | } |
1444 | 0 | alg *= 10; |
1445 | 0 | alg += dir.entry.name[i] - '0'; |
1446 | 0 | } |
1447 | | |
1448 | | /* |
1449 | | * Did we not read exactly 3 digits? |
1450 | | * Did we overflow? |
1451 | | * Did we correctly terminate? |
1452 | | */ |
1453 | 0 | if (i != len + 1 + 1 + 3 || i >= dir.entry.length || |
1454 | 0 | dir.entry.name[i] != '+') |
1455 | 0 | { |
1456 | 0 | continue; |
1457 | 0 | } |
1458 | | |
1459 | 0 | for (i++; i < dir.entry.length; i++) { |
1460 | 0 | if (!isdigit((unsigned char)dir.entry.name[i])) { |
1461 | 0 | break; |
1462 | 0 | } |
1463 | 0 | } |
1464 | | |
1465 | | /* |
1466 | | * Did we not read exactly 5 more digits? |
1467 | | * Did we overflow? |
1468 | | * Did we correctly terminate? |
1469 | | */ |
1470 | 0 | if (i != len + 1 + 1 + 3 + 1 + 5 || i >= dir.entry.length || |
1471 | 0 | strcmp(dir.entry.name + i, ".private") != 0) |
1472 | 0 | { |
1473 | 0 | continue; |
1474 | 0 | } |
1475 | | |
1476 | 0 | dstkey = NULL; |
1477 | 0 | result = dst_key_fromnamedfile( |
1478 | 0 | dir.entry.name, directory, |
1479 | 0 | DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | DST_TYPE_STATE, |
1480 | 0 | mctx, &dstkey); |
1481 | |
|
1482 | 0 | switch (alg) { |
1483 | 0 | case DST_ALG_HMACMD5: |
1484 | 0 | case DST_ALG_HMACSHA1: |
1485 | 0 | case DST_ALG_HMACSHA224: |
1486 | 0 | case DST_ALG_HMACSHA256: |
1487 | 0 | case DST_ALG_HMACSHA384: |
1488 | 0 | case DST_ALG_HMACSHA512: |
1489 | 0 | if (result == DST_R_BADKEYTYPE) { |
1490 | 0 | continue; |
1491 | 0 | } |
1492 | 0 | } |
1493 | | |
1494 | 0 | if (result != ISC_R_SUCCESS) { |
1495 | 0 | isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, |
1496 | 0 | DNS_LOGMODULE_DNSSEC, ISC_LOG_WARNING, |
1497 | 0 | "dns_dnssec_findmatchingkeys: " |
1498 | 0 | "error reading key file %s: %s", |
1499 | 0 | dir.entry.name, |
1500 | 0 | isc_result_totext(result)); |
1501 | 0 | continue; |
1502 | 0 | } |
1503 | | |
1504 | 0 | dns_dnsseckey_create(mctx, &dstkey, &key); |
1505 | 0 | key->source = dns_keysource_repository; |
1506 | 0 | dns_dnssec_get_hints(key, now); |
1507 | |
|
1508 | 0 | if (key->legacy) { |
1509 | 0 | dns_dnsseckey_destroy(mctx, &key); |
1510 | 0 | } else { |
1511 | 0 | ISC_LIST_APPEND(list, key, link); |
1512 | 0 | key = NULL; |
1513 | 0 | } |
1514 | 0 | } |
1515 | | |
1516 | 0 | if (!ISC_LIST_EMPTY(list)) { |
1517 | 0 | result = ISC_R_SUCCESS; |
1518 | 0 | ISC_LIST_APPENDLIST(*keylist, list, link); |
1519 | 0 | } else { |
1520 | 0 | result = ISC_R_NOTFOUND; |
1521 | 0 | } |
1522 | |
|
1523 | 0 | failure: |
1524 | 0 | if (dir_open) { |
1525 | 0 | isc_dir_close(&dir); |
1526 | 0 | } |
1527 | 0 | INSIST(key == NULL); |
1528 | 0 | while ((key = ISC_LIST_HEAD(list)) != NULL) { |
1529 | 0 | ISC_LIST_UNLINK(list, key, link); |
1530 | 0 | INSIST(key->key != NULL); |
1531 | 0 | dst_key_free(&key->key); |
1532 | 0 | dns_dnsseckey_destroy(mctx, &key); |
1533 | 0 | } |
1534 | 0 | if (dstkey != NULL) { |
1535 | 0 | dst_key_free(&dstkey); |
1536 | 0 | } |
1537 | 0 | return (result); |
1538 | 0 | } |
1539 | | |
1540 | | /*% |
1541 | | * Add 'newkey' to 'keylist' if it's not already there. |
1542 | | * |
1543 | | * If 'savekeys' is true, then we need to preserve all |
1544 | | * the keys in the keyset, regardless of whether they have |
1545 | | * metadata indicating they should be deactivated or removed. |
1546 | | */ |
1547 | | static void |
1548 | | addkey(dns_dnsseckeylist_t *keylist, dst_key_t **newkey, bool savekeys, |
1549 | 0 | isc_mem_t *mctx) { |
1550 | 0 | dns_dnsseckey_t *key = NULL; |
1551 | | |
1552 | | /* Skip duplicates */ |
1553 | 0 | for (key = ISC_LIST_HEAD(*keylist); key != NULL; |
1554 | 0 | key = ISC_LIST_NEXT(key, link)) |
1555 | 0 | { |
1556 | 0 | if (dst_key_id(key->key) == dst_key_id(*newkey) && |
1557 | 0 | dst_key_alg(key->key) == dst_key_alg(*newkey) && |
1558 | 0 | dns_name_equal(dst_key_name(key->key), |
1559 | 0 | dst_key_name(*newkey))) |
1560 | 0 | { |
1561 | 0 | break; |
1562 | 0 | } |
1563 | 0 | } |
1564 | |
|
1565 | 0 | if (key != NULL) { |
1566 | | /* |
1567 | | * Found a match. If the old key was only public and the |
1568 | | * new key is private, replace the old one; otherwise |
1569 | | * leave it. But either way, mark the key as having |
1570 | | * been found in the zone. |
1571 | | */ |
1572 | 0 | if (dst_key_isprivate(key->key)) { |
1573 | 0 | dst_key_free(newkey); |
1574 | 0 | } else if (dst_key_isprivate(*newkey)) { |
1575 | 0 | dst_key_free(&key->key); |
1576 | 0 | key->key = *newkey; |
1577 | 0 | } |
1578 | |
|
1579 | 0 | key->source = dns_keysource_zoneapex; |
1580 | 0 | return; |
1581 | 0 | } |
1582 | | |
1583 | 0 | dns_dnsseckey_create(mctx, newkey, &key); |
1584 | 0 | if (key->legacy || savekeys) { |
1585 | 0 | key->force_publish = true; |
1586 | 0 | key->force_sign = dst_key_isprivate(key->key); |
1587 | 0 | } |
1588 | 0 | key->source = dns_keysource_zoneapex; |
1589 | 0 | ISC_LIST_APPEND(*keylist, key, link); |
1590 | 0 | *newkey = NULL; |
1591 | 0 | } |
1592 | | |
1593 | | /*% |
1594 | | * Mark all keys which signed the DNSKEY/SOA RRsets as "active", |
1595 | | * for future reference. |
1596 | | */ |
1597 | | static isc_result_t |
1598 | 0 | mark_active_keys(dns_dnsseckeylist_t *keylist, dns_rdataset_t *rrsigs) { |
1599 | 0 | isc_result_t result = ISC_R_SUCCESS; |
1600 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
1601 | 0 | dns_rdataset_t sigs; |
1602 | 0 | dns_dnsseckey_t *key; |
1603 | |
|
1604 | 0 | REQUIRE(rrsigs != NULL && dns_rdataset_isassociated(rrsigs)); |
1605 | |
|
1606 | 0 | dns_rdataset_init(&sigs); |
1607 | 0 | dns_rdataset_clone(rrsigs, &sigs); |
1608 | 0 | for (key = ISC_LIST_HEAD(*keylist); key != NULL; |
1609 | 0 | key = ISC_LIST_NEXT(key, link)) |
1610 | 0 | { |
1611 | 0 | uint16_t keyid, sigid; |
1612 | 0 | dns_secalg_t keyalg, sigalg; |
1613 | 0 | keyid = dst_key_id(key->key); |
1614 | 0 | keyalg = dst_key_alg(key->key); |
1615 | |
|
1616 | 0 | for (result = dns_rdataset_first(&sigs); |
1617 | 0 | result == ISC_R_SUCCESS; result = dns_rdataset_next(&sigs)) |
1618 | 0 | { |
1619 | 0 | dns_rdata_rrsig_t sig; |
1620 | |
|
1621 | 0 | dns_rdata_reset(&rdata); |
1622 | 0 | dns_rdataset_current(&sigs, &rdata); |
1623 | 0 | result = dns_rdata_tostruct(&rdata, &sig, NULL); |
1624 | 0 | RUNTIME_CHECK(result == ISC_R_SUCCESS); |
1625 | 0 | sigalg = sig.algorithm; |
1626 | 0 | sigid = sig.keyid; |
1627 | 0 | if (keyid == sigid && keyalg == sigalg) { |
1628 | 0 | key->is_active = true; |
1629 | 0 | break; |
1630 | 0 | } |
1631 | 0 | } |
1632 | 0 | } |
1633 | |
|
1634 | 0 | if (result == ISC_R_NOMORE) { |
1635 | 0 | result = ISC_R_SUCCESS; |
1636 | 0 | } |
1637 | |
|
1638 | 0 | if (dns_rdataset_isassociated(&sigs)) { |
1639 | 0 | dns_rdataset_disassociate(&sigs); |
1640 | 0 | } |
1641 | 0 | return (result); |
1642 | 0 | } |
1643 | | |
1644 | | /*% |
1645 | | * Add the contents of a DNSKEY rdataset 'keyset' to 'keylist'. |
1646 | | */ |
1647 | | isc_result_t |
1648 | | dns_dnssec_keylistfromrdataset(const dns_name_t *origin, const char *directory, |
1649 | | isc_mem_t *mctx, dns_rdataset_t *keyset, |
1650 | | dns_rdataset_t *keysigs, dns_rdataset_t *soasigs, |
1651 | | bool savekeys, bool publickey, |
1652 | 0 | dns_dnsseckeylist_t *keylist) { |
1653 | 0 | dns_rdataset_t keys; |
1654 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
1655 | 0 | dst_key_t *dnskey = NULL, *pubkey = NULL, *privkey = NULL; |
1656 | 0 | isc_result_t result; |
1657 | |
|
1658 | 0 | REQUIRE(keyset != NULL && dns_rdataset_isassociated(keyset)); |
1659 | |
|
1660 | 0 | dns_rdataset_init(&keys); |
1661 | |
|
1662 | 0 | dns_rdataset_clone(keyset, &keys); |
1663 | 0 | for (result = dns_rdataset_first(&keys); result == ISC_R_SUCCESS; |
1664 | 0 | result = dns_rdataset_next(&keys)) |
1665 | 0 | { |
1666 | 0 | dns_rdata_reset(&rdata); |
1667 | 0 | dns_rdataset_current(&keys, &rdata); |
1668 | |
|
1669 | 0 | REQUIRE(rdata.type == dns_rdatatype_key || |
1670 | 0 | rdata.type == dns_rdatatype_dnskey); |
1671 | 0 | REQUIRE(rdata.length > 3); |
1672 | | |
1673 | | /* Skip unsupported algorithms */ |
1674 | 0 | if (!dst_algorithm_supported(rdata.data[3])) { |
1675 | 0 | goto skip; |
1676 | 0 | } |
1677 | | |
1678 | 0 | RETERR(dns_dnssec_keyfromrdata(origin, &rdata, mctx, &dnskey)); |
1679 | 0 | dst_key_setttl(dnskey, keys.ttl); |
1680 | |
|
1681 | 0 | if (!is_zone_key(dnskey) || |
1682 | 0 | (dst_key_flags(dnskey) & DNS_KEYTYPE_NOAUTH) != 0) |
1683 | 0 | { |
1684 | 0 | goto skip; |
1685 | 0 | } |
1686 | | |
1687 | | /* Corrupted .key file? */ |
1688 | 0 | if (!dns_name_equal(origin, dst_key_name(dnskey))) { |
1689 | 0 | goto skip; |
1690 | 0 | } |
1691 | | |
1692 | 0 | if (publickey) { |
1693 | 0 | addkey(keylist, &dnskey, savekeys, mctx); |
1694 | 0 | goto skip; |
1695 | 0 | } |
1696 | | |
1697 | | /* Try to read the public key. */ |
1698 | 0 | result = dst_key_fromfile( |
1699 | 0 | dst_key_name(dnskey), dst_key_id(dnskey), |
1700 | 0 | dst_key_alg(dnskey), (DST_TYPE_PUBLIC | DST_TYPE_STATE), |
1701 | 0 | directory, mctx, &pubkey); |
1702 | 0 | if (result == ISC_R_FILENOTFOUND || result == ISC_R_NOPERM) { |
1703 | 0 | result = ISC_R_SUCCESS; |
1704 | 0 | } |
1705 | 0 | RETERR(result); |
1706 | | |
1707 | | /* Now read the private key. */ |
1708 | 0 | result = dst_key_fromfile( |
1709 | 0 | dst_key_name(dnskey), dst_key_id(dnskey), |
1710 | 0 | dst_key_alg(dnskey), |
1711 | 0 | (DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | DST_TYPE_STATE), |
1712 | 0 | directory, mctx, &privkey); |
1713 | | |
1714 | | /* |
1715 | | * If the key was revoked and the private file |
1716 | | * doesn't exist, maybe it was revoked internally |
1717 | | * by named. Try loading the unrevoked version. |
1718 | | */ |
1719 | 0 | if (result == ISC_R_FILENOTFOUND) { |
1720 | 0 | uint32_t flags; |
1721 | 0 | flags = dst_key_flags(dnskey); |
1722 | 0 | if ((flags & DNS_KEYFLAG_REVOKE) != 0) { |
1723 | 0 | dst_key_setflags(dnskey, |
1724 | 0 | flags & ~DNS_KEYFLAG_REVOKE); |
1725 | 0 | result = dst_key_fromfile( |
1726 | 0 | dst_key_name(dnskey), |
1727 | 0 | dst_key_id(dnskey), dst_key_alg(dnskey), |
1728 | 0 | (DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | |
1729 | 0 | DST_TYPE_STATE), |
1730 | 0 | directory, mctx, &privkey); |
1731 | 0 | if (result == ISC_R_SUCCESS && |
1732 | 0 | dst_key_pubcompare(dnskey, privkey, false)) |
1733 | 0 | { |
1734 | 0 | dst_key_setflags(privkey, flags); |
1735 | 0 | } |
1736 | 0 | dst_key_setflags(dnskey, flags); |
1737 | 0 | } |
1738 | 0 | } |
1739 | |
|
1740 | 0 | if (result != ISC_R_SUCCESS) { |
1741 | 0 | char filename[DNS_NAME_FORMATSIZE + |
1742 | 0 | DNS_SECALG_FORMATSIZE + |
1743 | 0 | sizeof("key file for //65535")]; |
1744 | 0 | isc_result_t result2; |
1745 | 0 | isc_buffer_t buf; |
1746 | |
|
1747 | 0 | isc_buffer_init(&buf, filename, NAME_MAX); |
1748 | 0 | result2 = dst_key_getfilename( |
1749 | 0 | dst_key_name(dnskey), dst_key_id(dnskey), |
1750 | 0 | dst_key_alg(dnskey), |
1751 | 0 | (DST_TYPE_PUBLIC | DST_TYPE_PRIVATE | |
1752 | 0 | DST_TYPE_STATE), |
1753 | 0 | directory, mctx, &buf); |
1754 | 0 | if (result2 != ISC_R_SUCCESS) { |
1755 | 0 | char namebuf[DNS_NAME_FORMATSIZE]; |
1756 | 0 | char algbuf[DNS_SECALG_FORMATSIZE]; |
1757 | |
|
1758 | 0 | dns_name_format(dst_key_name(dnskey), namebuf, |
1759 | 0 | sizeof(namebuf)); |
1760 | 0 | dns_secalg_format(dst_key_alg(dnskey), algbuf, |
1761 | 0 | sizeof(algbuf)); |
1762 | 0 | snprintf(filename, sizeof(filename) - 1, |
1763 | 0 | "key file for %s/%s/%d", namebuf, |
1764 | 0 | algbuf, dst_key_id(dnskey)); |
1765 | 0 | } |
1766 | |
|
1767 | 0 | isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, |
1768 | 0 | DNS_LOGMODULE_DNSSEC, ISC_LOG_WARNING, |
1769 | 0 | "dns_dnssec_keylistfromrdataset: error " |
1770 | 0 | "reading %s: %s", |
1771 | 0 | filename, isc_result_totext(result)); |
1772 | 0 | } |
1773 | |
|
1774 | 0 | if (result == ISC_R_FILENOTFOUND || result == ISC_R_NOPERM) { |
1775 | 0 | if (pubkey != NULL) { |
1776 | 0 | addkey(keylist, &pubkey, savekeys, mctx); |
1777 | 0 | } else { |
1778 | 0 | addkey(keylist, &dnskey, savekeys, mctx); |
1779 | 0 | } |
1780 | 0 | goto skip; |
1781 | 0 | } |
1782 | 0 | RETERR(result); |
1783 | | |
1784 | | /* This should never happen. */ |
1785 | 0 | if ((dst_key_flags(privkey) & DNS_KEYTYPE_NOAUTH) != 0) { |
1786 | 0 | goto skip; |
1787 | 0 | } |
1788 | | |
1789 | | /* |
1790 | | * Whatever the key's default TTL may have |
1791 | | * been, the rdataset TTL takes priority. |
1792 | | */ |
1793 | 0 | dst_key_setttl(privkey, dst_key_getttl(dnskey)); |
1794 | |
|
1795 | 0 | addkey(keylist, &privkey, savekeys, mctx); |
1796 | 0 | skip: |
1797 | 0 | if (dnskey != NULL) { |
1798 | 0 | dst_key_free(&dnskey); |
1799 | 0 | } |
1800 | 0 | if (pubkey != NULL) { |
1801 | 0 | dst_key_free(&pubkey); |
1802 | 0 | } |
1803 | 0 | if (privkey != NULL) { |
1804 | 0 | dst_key_free(&privkey); |
1805 | 0 | } |
1806 | 0 | } |
1807 | | |
1808 | 0 | if (result != ISC_R_NOMORE) { |
1809 | 0 | RETERR(result); |
1810 | 0 | } |
1811 | | |
1812 | 0 | if (keysigs != NULL && dns_rdataset_isassociated(keysigs)) { |
1813 | 0 | RETERR(mark_active_keys(keylist, keysigs)); |
1814 | 0 | } |
1815 | | |
1816 | 0 | if (soasigs != NULL && dns_rdataset_isassociated(soasigs)) { |
1817 | 0 | RETERR(mark_active_keys(keylist, soasigs)); |
1818 | 0 | } |
1819 | | |
1820 | 0 | result = ISC_R_SUCCESS; |
1821 | |
|
1822 | 0 | failure: |
1823 | 0 | if (dns_rdataset_isassociated(&keys)) { |
1824 | 0 | dns_rdataset_disassociate(&keys); |
1825 | 0 | } |
1826 | 0 | if (dnskey != NULL) { |
1827 | 0 | dst_key_free(&dnskey); |
1828 | 0 | } |
1829 | 0 | if (pubkey != NULL) { |
1830 | 0 | dst_key_free(&pubkey); |
1831 | 0 | } |
1832 | 0 | if (privkey != NULL) { |
1833 | 0 | dst_key_free(&privkey); |
1834 | 0 | } |
1835 | 0 | return (result); |
1836 | 0 | } |
1837 | | |
1838 | | isc_result_t |
1839 | | dns_dnssec_make_dnskey(dst_key_t *key, unsigned char *buf, int bufsize, |
1840 | 0 | dns_rdata_t *target) { |
1841 | 0 | isc_result_t result; |
1842 | 0 | isc_buffer_t b; |
1843 | 0 | isc_region_t r; |
1844 | |
|
1845 | 0 | isc_buffer_init(&b, buf, bufsize); |
1846 | 0 | result = dst_key_todns(key, &b); |
1847 | 0 | if (result != ISC_R_SUCCESS) { |
1848 | 0 | return (result); |
1849 | 0 | } |
1850 | | |
1851 | 0 | dns_rdata_reset(target); |
1852 | 0 | isc_buffer_usedregion(&b, &r); |
1853 | 0 | dns_rdata_fromregion(target, dst_key_class(key), dns_rdatatype_dnskey, |
1854 | 0 | &r); |
1855 | 0 | return (ISC_R_SUCCESS); |
1856 | 0 | } |
1857 | | |
1858 | | static isc_result_t |
1859 | | addrdata(dns_rdata_t *rdata, dns_diff_t *diff, const dns_name_t *origin, |
1860 | 0 | dns_ttl_t ttl, isc_mem_t *mctx) { |
1861 | 0 | isc_result_t result; |
1862 | 0 | dns_difftuple_t *tuple = NULL; |
1863 | |
|
1864 | 0 | RETERR(dns_difftuple_create(mctx, DNS_DIFFOP_ADD, origin, ttl, rdata, |
1865 | 0 | &tuple)); |
1866 | 0 | dns_diff_appendminimal(diff, &tuple); |
1867 | |
|
1868 | 0 | failure: |
1869 | 0 | return (result); |
1870 | 0 | } |
1871 | | |
1872 | | static isc_result_t |
1873 | | delrdata(dns_rdata_t *rdata, dns_diff_t *diff, const dns_name_t *origin, |
1874 | 0 | dns_ttl_t ttl, isc_mem_t *mctx) { |
1875 | 0 | isc_result_t result; |
1876 | 0 | dns_difftuple_t *tuple = NULL; |
1877 | |
|
1878 | 0 | RETERR(dns_difftuple_create(mctx, DNS_DIFFOP_DEL, origin, ttl, rdata, |
1879 | 0 | &tuple)); |
1880 | 0 | dns_diff_appendminimal(diff, &tuple); |
1881 | |
|
1882 | 0 | failure: |
1883 | 0 | return (result); |
1884 | 0 | } |
1885 | | |
1886 | | static isc_result_t |
1887 | | publish_key(dns_diff_t *diff, dns_dnsseckey_t *key, const dns_name_t *origin, |
1888 | | dns_ttl_t ttl, isc_mem_t *mctx, |
1889 | 0 | void (*report)(const char *, ...) ISC_FORMAT_PRINTF(1, 2)) { |
1890 | 0 | isc_result_t result; |
1891 | 0 | unsigned char buf[DST_KEY_MAXSIZE]; |
1892 | 0 | char keystr[DST_KEY_FORMATSIZE]; |
1893 | 0 | dns_rdata_t dnskey = DNS_RDATA_INIT; |
1894 | |
|
1895 | 0 | dns_rdata_reset(&dnskey); |
1896 | 0 | RETERR(dns_dnssec_make_dnskey(key->key, buf, sizeof(buf), &dnskey)); |
1897 | 0 | dst_key_format(key->key, keystr, sizeof(keystr)); |
1898 | |
|
1899 | 0 | report("Fetching %s (%s) from key %s.", keystr, |
1900 | 0 | key->ksk ? (key->zsk ? "CSK" : "KSK") : "ZSK", |
1901 | 0 | key->source == dns_keysource_user ? "file" : "repository"); |
1902 | |
|
1903 | 0 | if (key->prepublish && ttl > key->prepublish) { |
1904 | 0 | isc_stdtime_t now; |
1905 | |
|
1906 | 0 | report("Key %s: Delaying activation to match the DNSKEY TTL " |
1907 | 0 | "(%u).", |
1908 | 0 | keystr, ttl); |
1909 | |
|
1910 | 0 | now = isc_stdtime_now(); |
1911 | 0 | dst_key_settime(key->key, DST_TIME_ACTIVATE, now + ttl); |
1912 | 0 | } |
1913 | | |
1914 | | /* publish key */ |
1915 | 0 | result = addrdata(&dnskey, diff, origin, ttl, mctx); |
1916 | |
|
1917 | 0 | failure: |
1918 | 0 | return (result); |
1919 | 0 | } |
1920 | | |
1921 | | static isc_result_t |
1922 | | remove_key(dns_diff_t *diff, dns_dnsseckey_t *key, const dns_name_t *origin, |
1923 | | dns_ttl_t ttl, isc_mem_t *mctx, const char *reason, |
1924 | 0 | void (*report)(const char *, ...) ISC_FORMAT_PRINTF(1, 2)) { |
1925 | 0 | isc_result_t result; |
1926 | 0 | unsigned char buf[DST_KEY_MAXSIZE]; |
1927 | 0 | dns_rdata_t dnskey = DNS_RDATA_INIT; |
1928 | 0 | char alg[80]; |
1929 | 0 | char namebuf[DNS_NAME_FORMATSIZE]; |
1930 | |
|
1931 | 0 | dns_secalg_format(dst_key_alg(key->key), alg, sizeof(alg)); |
1932 | 0 | dns_name_format(dst_key_name(key->key), namebuf, sizeof(namebuf)); |
1933 | 0 | report("Removing %s key %s/%d/%s from DNSKEY RRset.", reason, namebuf, |
1934 | 0 | dst_key_id(key->key), alg); |
1935 | |
|
1936 | 0 | RETERR(dns_dnssec_make_dnskey(key->key, buf, sizeof(buf), &dnskey)); |
1937 | 0 | result = delrdata(&dnskey, diff, origin, ttl, mctx); |
1938 | |
|
1939 | 0 | failure: |
1940 | 0 | return (result); |
1941 | 0 | } |
1942 | | |
1943 | | static bool |
1944 | 0 | exists(dns_rdataset_t *rdataset, dns_rdata_t *rdata) { |
1945 | 0 | isc_result_t result; |
1946 | 0 | dns_rdataset_t trdataset; |
1947 | |
|
1948 | 0 | dns_rdataset_init(&trdataset); |
1949 | 0 | dns_rdataset_clone(rdataset, &trdataset); |
1950 | 0 | for (result = dns_rdataset_first(&trdataset); result == ISC_R_SUCCESS; |
1951 | 0 | result = dns_rdataset_next(&trdataset)) |
1952 | 0 | { |
1953 | 0 | dns_rdata_t current = DNS_RDATA_INIT; |
1954 | |
|
1955 | 0 | dns_rdataset_current(&trdataset, ¤t); |
1956 | 0 | if (dns_rdata_compare(rdata, ¤t) == 0) { |
1957 | 0 | dns_rdataset_disassociate(&trdataset); |
1958 | 0 | return (true); |
1959 | 0 | } |
1960 | 0 | } |
1961 | 0 | dns_rdataset_disassociate(&trdataset); |
1962 | 0 | return (false); |
1963 | 0 | } |
1964 | | |
1965 | | static isc_result_t |
1966 | | add_cds(dns_dnsseckey_t *key, dns_rdata_t *keyrdata, const char *keystr, |
1967 | | dns_rdataset_t *cds, unsigned int digesttype, dns_ttl_t ttl, |
1968 | 0 | dns_diff_t *diff, isc_mem_t *mctx) { |
1969 | 0 | isc_result_t r = ISC_R_SUCCESS; |
1970 | 0 | unsigned char dsbuf[DNS_DS_BUFFERSIZE]; |
1971 | 0 | dns_rdata_t cdsrdata = DNS_RDATA_INIT; |
1972 | 0 | dns_name_t *origin = dst_key_name(key->key); |
1973 | |
|
1974 | 0 | r = dns_ds_buildrdata(origin, keyrdata, digesttype, dsbuf, &cdsrdata); |
1975 | 0 | if (r != ISC_R_SUCCESS) { |
1976 | 0 | char algbuf[DNS_DSDIGEST_FORMATSIZE]; |
1977 | 0 | dns_dsdigest_format(digesttype, algbuf, |
1978 | 0 | DNS_DSDIGEST_FORMATSIZE); |
1979 | 0 | isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, |
1980 | 0 | DNS_LOGMODULE_DNSSEC, ISC_LOG_ERROR, |
1981 | 0 | "build rdata CDS (%s) for key %s failed", algbuf, |
1982 | 0 | keystr); |
1983 | 0 | return (r); |
1984 | 0 | } |
1985 | | |
1986 | 0 | cdsrdata.type = dns_rdatatype_cds; |
1987 | 0 | if (!dns_rdataset_isassociated(cds) || !exists(cds, &cdsrdata)) { |
1988 | 0 | char algbuf[DNS_DSDIGEST_FORMATSIZE]; |
1989 | 0 | dns_dsdigest_format(digesttype, algbuf, |
1990 | 0 | DNS_DSDIGEST_FORMATSIZE); |
1991 | 0 | isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, |
1992 | 0 | DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO, |
1993 | 0 | "CDS (%s) for key %s is now published", algbuf, |
1994 | 0 | keystr); |
1995 | 0 | r = addrdata(&cdsrdata, diff, origin, ttl, mctx); |
1996 | 0 | } |
1997 | 0 | return (r); |
1998 | 0 | } |
1999 | | |
2000 | | static isc_result_t |
2001 | | delete_cds(dns_dnsseckey_t *key, dns_rdata_t *keyrdata, const char *keystr, |
2002 | | dns_rdataset_t *cds, unsigned int digesttype, dns_diff_t *diff, |
2003 | 0 | isc_mem_t *mctx) { |
2004 | 0 | isc_result_t r = ISC_R_SUCCESS; |
2005 | 0 | unsigned char dsbuf[DNS_DS_BUFFERSIZE]; |
2006 | 0 | dns_rdata_t cdsrdata = DNS_RDATA_INIT; |
2007 | 0 | dns_name_t *origin = dst_key_name(key->key); |
2008 | |
|
2009 | 0 | r = dns_ds_buildrdata(origin, keyrdata, digesttype, dsbuf, &cdsrdata); |
2010 | 0 | if (r != ISC_R_SUCCESS) { |
2011 | 0 | return (r); |
2012 | 0 | } |
2013 | | |
2014 | 0 | cdsrdata.type = dns_rdatatype_cds; |
2015 | 0 | if (exists(cds, &cdsrdata)) { |
2016 | 0 | char algbuf[DNS_DSDIGEST_FORMATSIZE]; |
2017 | 0 | dns_dsdigest_format(digesttype, algbuf, |
2018 | 0 | DNS_DSDIGEST_FORMATSIZE); |
2019 | 0 | isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, |
2020 | 0 | DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO, |
2021 | 0 | "CDS (%s) for key %s is now deleted", algbuf, |
2022 | 0 | keystr); |
2023 | 0 | r = delrdata(&cdsrdata, diff, origin, cds->ttl, mctx); |
2024 | 0 | } |
2025 | 0 | return (r); |
2026 | 0 | } |
2027 | | |
2028 | | isc_result_t |
2029 | | dns_dnssec_syncupdate(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *rmkeys, |
2030 | | dns_rdataset_t *cds, dns_rdataset_t *cdnskey, |
2031 | | isc_stdtime_t now, dns_kasp_digestlist_t *digests, |
2032 | | bool gencdnskey, dns_ttl_t ttl, dns_diff_t *diff, |
2033 | 0 | isc_mem_t *mctx) { |
2034 | 0 | unsigned char keybuf[DST_KEY_MAXSIZE]; |
2035 | 0 | isc_result_t result; |
2036 | 0 | dns_dnsseckey_t *key; |
2037 | |
|
2038 | 0 | REQUIRE(digests != NULL); |
2039 | 0 | REQUIRE(keys != NULL); |
2040 | 0 | REQUIRE(rmkeys != NULL); |
2041 | |
|
2042 | 0 | for (key = ISC_LIST_HEAD(*keys); key != NULL; |
2043 | 0 | key = ISC_LIST_NEXT(key, link)) |
2044 | 0 | { |
2045 | 0 | dns_rdata_t cdnskeyrdata = DNS_RDATA_INIT; |
2046 | 0 | dns_name_t *origin = dst_key_name(key->key); |
2047 | |
|
2048 | 0 | RETERR(dns_dnssec_make_dnskey(key->key, keybuf, sizeof(keybuf), |
2049 | 0 | &cdnskeyrdata)); |
2050 | 0 | cdnskeyrdata.type = dns_rdatatype_cdnskey; |
2051 | |
|
2052 | 0 | if (syncpublish(key->key, now)) { |
2053 | 0 | char keystr[DST_KEY_FORMATSIZE]; |
2054 | 0 | dst_key_format(key->key, keystr, sizeof(keystr)); |
2055 | |
|
2056 | 0 | for (dns_kasp_digest_t *alg = ISC_LIST_HEAD(*digests); |
2057 | 0 | alg != NULL; alg = ISC_LIST_NEXT(alg, link)) |
2058 | 0 | { |
2059 | 0 | RETERR(add_cds(key, &cdnskeyrdata, |
2060 | 0 | (const char *)keystr, cds, |
2061 | 0 | alg->digest, ttl, diff, mctx)); |
2062 | 0 | } |
2063 | | |
2064 | 0 | if (gencdnskey && |
2065 | 0 | (!dns_rdataset_isassociated(cdnskey) || |
2066 | 0 | !exists(cdnskey, &cdnskeyrdata))) |
2067 | 0 | { |
2068 | 0 | isc_log_write( |
2069 | 0 | dns_lctx, DNS_LOGCATEGORY_GENERAL, |
2070 | 0 | DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO, |
2071 | 0 | "CDNSKEY for key %s is now published", |
2072 | 0 | keystr); |
2073 | 0 | RETERR(addrdata(&cdnskeyrdata, diff, origin, |
2074 | 0 | ttl, mctx)); |
2075 | 0 | } |
2076 | 0 | } |
2077 | | |
2078 | 0 | if (syncdelete(key->key, now)) { |
2079 | 0 | char keystr[DST_KEY_FORMATSIZE]; |
2080 | 0 | dst_key_format(key->key, keystr, sizeof(keystr)); |
2081 | |
|
2082 | 0 | if (dns_rdataset_isassociated(cds)) { |
2083 | | /* Delete all possible CDS records */ |
2084 | 0 | delete_cds(key, &cdnskeyrdata, |
2085 | 0 | (const char *)keystr, cds, |
2086 | 0 | DNS_DSDIGEST_SHA1, diff, mctx); |
2087 | 0 | delete_cds(key, &cdnskeyrdata, |
2088 | 0 | (const char *)keystr, cds, |
2089 | 0 | DNS_DSDIGEST_SHA256, diff, mctx); |
2090 | 0 | delete_cds(key, &cdnskeyrdata, |
2091 | 0 | (const char *)keystr, cds, |
2092 | 0 | DNS_DSDIGEST_SHA384, diff, mctx); |
2093 | 0 | } |
2094 | |
|
2095 | 0 | if (dns_rdataset_isassociated(cdnskey)) { |
2096 | 0 | if (exists(cdnskey, &cdnskeyrdata)) { |
2097 | 0 | isc_log_write(dns_lctx, |
2098 | 0 | DNS_LOGCATEGORY_GENERAL, |
2099 | 0 | DNS_LOGMODULE_DNSSEC, |
2100 | 0 | ISC_LOG_INFO, |
2101 | 0 | "CDNSKEY for key %s is " |
2102 | 0 | "now deleted", |
2103 | 0 | keystr); |
2104 | 0 | RETERR(delrdata(&cdnskeyrdata, diff, |
2105 | 0 | origin, cdnskey->ttl, |
2106 | 0 | mctx)); |
2107 | 0 | } |
2108 | 0 | } |
2109 | 0 | } |
2110 | 0 | } |
2111 | | |
2112 | 0 | if (!dns_rdataset_isassociated(cds) && |
2113 | 0 | !dns_rdataset_isassociated(cdnskey)) |
2114 | 0 | { |
2115 | 0 | return (ISC_R_SUCCESS); |
2116 | 0 | } |
2117 | | |
2118 | | /* |
2119 | | * Unconditionally remove CDS/DNSKEY records for removed keys. |
2120 | | */ |
2121 | 0 | for (key = ISC_LIST_HEAD(*rmkeys); key != NULL; |
2122 | 0 | key = ISC_LIST_NEXT(key, link)) |
2123 | 0 | { |
2124 | 0 | dns_rdata_t cdnskeyrdata = DNS_RDATA_INIT; |
2125 | 0 | dns_name_t *origin = dst_key_name(key->key); |
2126 | |
|
2127 | 0 | char keystr[DST_KEY_FORMATSIZE]; |
2128 | 0 | dst_key_format(key->key, keystr, sizeof(keystr)); |
2129 | |
|
2130 | 0 | RETERR(dns_dnssec_make_dnskey(key->key, keybuf, sizeof(keybuf), |
2131 | 0 | &cdnskeyrdata)); |
2132 | | |
2133 | 0 | if (dns_rdataset_isassociated(cds)) { |
2134 | 0 | delete_cds(key, &cdnskeyrdata, (const char *)keystr, |
2135 | 0 | cds, DNS_DSDIGEST_SHA1, diff, mctx); |
2136 | 0 | delete_cds(key, &cdnskeyrdata, (const char *)keystr, |
2137 | 0 | cds, DNS_DSDIGEST_SHA256, diff, mctx); |
2138 | 0 | delete_cds(key, &cdnskeyrdata, (const char *)keystr, |
2139 | 0 | cds, DNS_DSDIGEST_SHA384, diff, mctx); |
2140 | 0 | } |
2141 | |
|
2142 | 0 | if (dns_rdataset_isassociated(cdnskey)) { |
2143 | 0 | if (exists(cdnskey, &cdnskeyrdata)) { |
2144 | 0 | isc_log_write( |
2145 | 0 | dns_lctx, DNS_LOGCATEGORY_GENERAL, |
2146 | 0 | DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO, |
2147 | 0 | "CDNSKEY for key %s is now deleted", |
2148 | 0 | keystr); |
2149 | 0 | RETERR(delrdata(&cdnskeyrdata, diff, origin, |
2150 | 0 | cdnskey->ttl, mctx)); |
2151 | 0 | } |
2152 | 0 | } |
2153 | 0 | } |
2154 | | |
2155 | 0 | result = ISC_R_SUCCESS; |
2156 | |
|
2157 | 0 | failure: |
2158 | 0 | return (result); |
2159 | 0 | } |
2160 | | |
2161 | | isc_result_t |
2162 | | dns_dnssec_syncdelete(dns_rdataset_t *cds, dns_rdataset_t *cdnskey, |
2163 | | dns_name_t *origin, dns_rdataclass_t zclass, |
2164 | | dns_ttl_t ttl, dns_diff_t *diff, isc_mem_t *mctx, |
2165 | 0 | bool expect_cds_delete, bool expect_cdnskey_delete) { |
2166 | 0 | unsigned char dsbuf[5] = { 0, 0, 0, 0, 0 }; /* CDS DELETE rdata */ |
2167 | 0 | unsigned char keybuf[5] = { 0, 0, 3, 0, 0 }; /* CDNSKEY DELETE rdata */ |
2168 | 0 | char namebuf[DNS_NAME_FORMATSIZE]; |
2169 | 0 | dns_rdata_t cds_delete = DNS_RDATA_INIT; |
2170 | 0 | dns_rdata_t cdnskey_delete = DNS_RDATA_INIT; |
2171 | 0 | isc_region_t r; |
2172 | 0 | isc_result_t result; |
2173 | |
|
2174 | 0 | r.base = keybuf; |
2175 | 0 | r.length = sizeof(keybuf); |
2176 | 0 | dns_rdata_fromregion(&cdnskey_delete, zclass, dns_rdatatype_cdnskey, |
2177 | 0 | &r); |
2178 | |
|
2179 | 0 | r.base = dsbuf; |
2180 | 0 | r.length = sizeof(dsbuf); |
2181 | 0 | dns_rdata_fromregion(&cds_delete, zclass, dns_rdatatype_cds, &r); |
2182 | |
|
2183 | 0 | dns_name_format(origin, namebuf, sizeof(namebuf)); |
2184 | |
|
2185 | 0 | if (expect_cds_delete) { |
2186 | 0 | if (!dns_rdataset_isassociated(cds) || |
2187 | 0 | !exists(cds, &cds_delete)) |
2188 | 0 | { |
2189 | 0 | isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, |
2190 | 0 | DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO, |
2191 | 0 | "CDS (DELETE) for zone %s is now " |
2192 | 0 | "published", |
2193 | 0 | namebuf); |
2194 | 0 | RETERR(addrdata(&cds_delete, diff, origin, ttl, mctx)); |
2195 | 0 | } |
2196 | 0 | } else { |
2197 | 0 | if (dns_rdataset_isassociated(cds) && exists(cds, &cds_delete)) |
2198 | 0 | { |
2199 | 0 | isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, |
2200 | 0 | DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO, |
2201 | 0 | "CDS (DELETE) for zone %s is now " |
2202 | 0 | "deleted", |
2203 | 0 | namebuf); |
2204 | 0 | RETERR(delrdata(&cds_delete, diff, origin, cds->ttl, |
2205 | 0 | mctx)); |
2206 | 0 | } |
2207 | 0 | } |
2208 | | |
2209 | 0 | if (expect_cdnskey_delete) { |
2210 | 0 | if (!dns_rdataset_isassociated(cdnskey) || |
2211 | 0 | !exists(cdnskey, &cdnskey_delete)) |
2212 | 0 | { |
2213 | 0 | isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, |
2214 | 0 | DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO, |
2215 | 0 | "CDNSKEY (DELETE) for zone %s is now " |
2216 | 0 | "published", |
2217 | 0 | namebuf); |
2218 | 0 | RETERR(addrdata(&cdnskey_delete, diff, origin, ttl, |
2219 | 0 | mctx)); |
2220 | 0 | } |
2221 | 0 | } else { |
2222 | 0 | if (dns_rdataset_isassociated(cdnskey) && |
2223 | 0 | exists(cdnskey, &cdnskey_delete)) |
2224 | 0 | { |
2225 | 0 | isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, |
2226 | 0 | DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO, |
2227 | 0 | "CDNSKEY (DELETE) for zone %s is now " |
2228 | 0 | "deleted", |
2229 | 0 | namebuf); |
2230 | 0 | RETERR(delrdata(&cdnskey_delete, diff, origin, |
2231 | 0 | cdnskey->ttl, mctx)); |
2232 | 0 | } |
2233 | 0 | } |
2234 | | |
2235 | 0 | result = ISC_R_SUCCESS; |
2236 | |
|
2237 | 0 | failure: |
2238 | 0 | return (result); |
2239 | 0 | } |
2240 | | |
2241 | | /* |
2242 | | * Update 'keys' with information from 'newkeys'. |
2243 | | * |
2244 | | * If 'removed' is not NULL, any keys that are being removed from |
2245 | | * the zone will be added to the list for post-removal processing. |
2246 | | */ |
2247 | | isc_result_t |
2248 | | dns_dnssec_updatekeys(dns_dnsseckeylist_t *keys, dns_dnsseckeylist_t *newkeys, |
2249 | | dns_dnsseckeylist_t *removed, const dns_name_t *origin, |
2250 | | dns_ttl_t hint_ttl, dns_diff_t *diff, isc_mem_t *mctx, |
2251 | | void (*report)(const char *, ...) |
2252 | 0 | ISC_FORMAT_PRINTF(1, 2)) { |
2253 | 0 | isc_result_t result; |
2254 | 0 | dns_dnsseckey_t *key, *key1, *key2, *next; |
2255 | 0 | bool found_ttl = false; |
2256 | 0 | dns_ttl_t ttl = hint_ttl; |
2257 | | |
2258 | | /* |
2259 | | * First, look through the existing key list to find keys |
2260 | | * supplied from the command line which are not in the zone. |
2261 | | * Update the zone to include them. |
2262 | | * |
2263 | | * Also, if there are keys published in the zone already, |
2264 | | * use their TTL for all subsequent published keys. |
2265 | | */ |
2266 | 0 | for (key = ISC_LIST_HEAD(*keys); key != NULL; |
2267 | 0 | key = ISC_LIST_NEXT(key, link)) |
2268 | 0 | { |
2269 | 0 | if (key->source == dns_keysource_user && |
2270 | 0 | (key->hint_publish || key->force_publish)) |
2271 | 0 | { |
2272 | 0 | RETERR(publish_key(diff, key, origin, ttl, mctx, |
2273 | 0 | report)); |
2274 | 0 | } |
2275 | 0 | if (key->source == dns_keysource_zoneapex) { |
2276 | 0 | ttl = dst_key_getttl(key->key); |
2277 | 0 | found_ttl = true; |
2278 | 0 | } |
2279 | 0 | } |
2280 | | |
2281 | | /* |
2282 | | * If there were no existing keys, use the smallest nonzero |
2283 | | * TTL of the keys found in the repository. |
2284 | | */ |
2285 | 0 | if (!found_ttl && !ISC_LIST_EMPTY(*newkeys)) { |
2286 | 0 | dns_ttl_t shortest = 0; |
2287 | |
|
2288 | 0 | for (key = ISC_LIST_HEAD(*newkeys); key != NULL; |
2289 | 0 | key = ISC_LIST_NEXT(key, link)) |
2290 | 0 | { |
2291 | 0 | dns_ttl_t thisttl = dst_key_getttl(key->key); |
2292 | 0 | if (thisttl != 0 && |
2293 | 0 | (shortest == 0 || thisttl < shortest)) |
2294 | 0 | { |
2295 | 0 | shortest = thisttl; |
2296 | 0 | } |
2297 | 0 | } |
2298 | |
|
2299 | 0 | if (shortest != 0) { |
2300 | 0 | ttl = shortest; |
2301 | 0 | } |
2302 | 0 | } |
2303 | | |
2304 | | /* |
2305 | | * Second, scan the list of newly found keys looking for matches |
2306 | | * with known keys, and update accordingly. |
2307 | | */ |
2308 | 0 | for (key1 = ISC_LIST_HEAD(*newkeys); key1 != NULL; key1 = next) { |
2309 | 0 | bool key_revoked = false; |
2310 | 0 | char keystr1[DST_KEY_FORMATSIZE]; |
2311 | 0 | char keystr2[DST_KEY_FORMATSIZE]; |
2312 | |
|
2313 | 0 | next = ISC_LIST_NEXT(key1, link); |
2314 | |
|
2315 | 0 | for (key2 = ISC_LIST_HEAD(*keys); key2 != NULL; |
2316 | 0 | key2 = ISC_LIST_NEXT(key2, link)) |
2317 | 0 | { |
2318 | 0 | int f1 = dst_key_flags(key1->key); |
2319 | 0 | int f2 = dst_key_flags(key2->key); |
2320 | 0 | int nr1 = f1 & ~DNS_KEYFLAG_REVOKE; |
2321 | 0 | int nr2 = f2 & ~DNS_KEYFLAG_REVOKE; |
2322 | 0 | if (nr1 == nr2 && |
2323 | 0 | dst_key_alg(key1->key) == dst_key_alg(key2->key) && |
2324 | 0 | dst_key_pubcompare(key1->key, key2->key, true)) |
2325 | 0 | { |
2326 | 0 | int r1, r2; |
2327 | 0 | r1 = dst_key_flags(key1->key) & |
2328 | 0 | DNS_KEYFLAG_REVOKE; |
2329 | 0 | r2 = dst_key_flags(key2->key) & |
2330 | 0 | DNS_KEYFLAG_REVOKE; |
2331 | 0 | key_revoked = (r1 != r2); |
2332 | 0 | break; |
2333 | 0 | } |
2334 | 0 | } |
2335 | | |
2336 | | /* Printable version of key1 (the newly acquired key) */ |
2337 | 0 | dst_key_format(key1->key, keystr1, sizeof(keystr1)); |
2338 | | |
2339 | | /* No match found in keys; add the new key. */ |
2340 | 0 | if (key2 == NULL) { |
2341 | 0 | ISC_LIST_UNLINK(*newkeys, key1, link); |
2342 | 0 | ISC_LIST_APPEND(*keys, key1, link); |
2343 | |
|
2344 | 0 | if (key1->source != dns_keysource_zoneapex && |
2345 | 0 | (key1->hint_publish || key1->force_publish)) |
2346 | 0 | { |
2347 | 0 | RETERR(publish_key(diff, key1, origin, ttl, |
2348 | 0 | mctx, report)); |
2349 | 0 | isc_log_write( |
2350 | 0 | dns_lctx, DNS_LOGCATEGORY_DNSSEC, |
2351 | 0 | DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO, |
2352 | 0 | "DNSKEY %s (%s) is now published", |
2353 | 0 | keystr1, |
2354 | 0 | key1->ksk ? (key1->zsk ? "CSK" : "KSK") |
2355 | 0 | : "ZSK"); |
2356 | 0 | if (key1->hint_sign || key1->force_sign) { |
2357 | 0 | key1->first_sign = true; |
2358 | 0 | isc_log_write( |
2359 | 0 | dns_lctx, |
2360 | 0 | DNS_LOGCATEGORY_DNSSEC, |
2361 | 0 | DNS_LOGMODULE_DNSSEC, |
2362 | 0 | ISC_LOG_INFO, |
2363 | 0 | "DNSKEY %s (%s) is now " |
2364 | 0 | "active", |
2365 | 0 | keystr1, |
2366 | 0 | key1->ksk ? (key1->zsk ? "CSK" |
2367 | 0 | : "KSK") |
2368 | 0 | : "ZSK"); |
2369 | 0 | } |
2370 | 0 | } |
2371 | | |
2372 | 0 | continue; |
2373 | 0 | } |
2374 | | |
2375 | | /* Printable version of key2 (the old key, if any) */ |
2376 | 0 | dst_key_format(key2->key, keystr2, sizeof(keystr2)); |
2377 | | |
2378 | | /* Copy key metadata. */ |
2379 | 0 | dst_key_copy_metadata(key2->key, key1->key); |
2380 | | |
2381 | | /* Match found: remove or update it as needed */ |
2382 | 0 | if (key1->hint_remove) { |
2383 | 0 | RETERR(remove_key(diff, key2, origin, ttl, mctx, |
2384 | 0 | "expired", report)); |
2385 | 0 | ISC_LIST_UNLINK(*keys, key2, link); |
2386 | |
|
2387 | 0 | if (removed != NULL) { |
2388 | 0 | ISC_LIST_APPEND(*removed, key2, link); |
2389 | 0 | isc_log_write( |
2390 | 0 | dns_lctx, DNS_LOGCATEGORY_DNSSEC, |
2391 | 0 | DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO, |
2392 | 0 | "DNSKEY %s (%s) is now deleted", |
2393 | 0 | keystr2, |
2394 | 0 | key2->ksk ? (key2->zsk ? "CSK" : "KSK") |
2395 | 0 | : "ZSK"); |
2396 | 0 | } else { |
2397 | 0 | dns_dnsseckey_destroy(mctx, &key2); |
2398 | 0 | } |
2399 | 0 | } else if (key_revoked && |
2400 | 0 | (dst_key_flags(key1->key) & DNS_KEYFLAG_REVOKE) != 0) |
2401 | 0 | { |
2402 | | /* |
2403 | | * A previously valid key has been revoked. |
2404 | | * We need to remove the old version and pull |
2405 | | * in the new one. |
2406 | | */ |
2407 | 0 | RETERR(remove_key(diff, key2, origin, ttl, mctx, |
2408 | 0 | "revoked", report)); |
2409 | 0 | ISC_LIST_UNLINK(*keys, key2, link); |
2410 | 0 | if (removed != NULL) { |
2411 | 0 | ISC_LIST_APPEND(*removed, key2, link); |
2412 | 0 | isc_log_write( |
2413 | 0 | dns_lctx, DNS_LOGCATEGORY_DNSSEC, |
2414 | 0 | DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO, |
2415 | 0 | "DNSKEY %s (%s) is now revoked; " |
2416 | 0 | "new ID is %05d", |
2417 | 0 | keystr2, |
2418 | 0 | key2->ksk ? (key2->zsk ? "CSK" : "KSK") |
2419 | 0 | : "ZSK", |
2420 | 0 | dst_key_id(key1->key)); |
2421 | 0 | } else { |
2422 | 0 | dns_dnsseckey_destroy(mctx, &key2); |
2423 | 0 | } |
2424 | |
|
2425 | 0 | RETERR(publish_key(diff, key1, origin, ttl, mctx, |
2426 | 0 | report)); |
2427 | 0 | ISC_LIST_UNLINK(*newkeys, key1, link); |
2428 | 0 | ISC_LIST_APPEND(*keys, key1, link); |
2429 | | |
2430 | | /* |
2431 | | * XXX: The revoke flag is only defined for trust |
2432 | | * anchors. Setting the flag on a non-KSK is legal, |
2433 | | * but not defined in any RFC. It seems reasonable |
2434 | | * to treat it the same as a KSK: keep it in the |
2435 | | * zone, sign the DNSKEY set with it, but not |
2436 | | * sign other records with it. |
2437 | | */ |
2438 | 0 | key1->ksk = true; |
2439 | 0 | continue; |
2440 | 0 | } else { |
2441 | 0 | if (!key2->is_active && |
2442 | 0 | (key1->hint_sign || key1->force_sign)) |
2443 | 0 | { |
2444 | 0 | key2->first_sign = true; |
2445 | 0 | isc_log_write( |
2446 | 0 | dns_lctx, DNS_LOGCATEGORY_DNSSEC, |
2447 | 0 | DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO, |
2448 | 0 | "DNSKEY %s (%s) is now active", keystr1, |
2449 | 0 | key1->ksk ? (key1->zsk ? "CSK" : "KSK") |
2450 | 0 | : "ZSK"); |
2451 | 0 | } else if (key2->is_active && !key1->hint_sign && |
2452 | 0 | !key1->force_sign) |
2453 | 0 | { |
2454 | 0 | isc_log_write( |
2455 | 0 | dns_lctx, DNS_LOGCATEGORY_DNSSEC, |
2456 | 0 | DNS_LOGMODULE_DNSSEC, ISC_LOG_INFO, |
2457 | 0 | "DNSKEY %s (%s) is now inactive", |
2458 | 0 | keystr1, |
2459 | 0 | key1->ksk ? (key1->zsk ? "CSK" : "KSK") |
2460 | 0 | : "ZSK"); |
2461 | 0 | } |
2462 | |
|
2463 | 0 | key2->hint_sign = key1->hint_sign; |
2464 | 0 | key2->hint_publish = key1->hint_publish; |
2465 | 0 | } |
2466 | 0 | } |
2467 | | |
2468 | | /* Free any leftover keys in newkeys */ |
2469 | 0 | while (!ISC_LIST_EMPTY(*newkeys)) { |
2470 | 0 | key1 = ISC_LIST_HEAD(*newkeys); |
2471 | 0 | ISC_LIST_UNLINK(*newkeys, key1, link); |
2472 | 0 | dns_dnsseckey_destroy(mctx, &key1); |
2473 | 0 | } |
2474 | |
|
2475 | 0 | result = ISC_R_SUCCESS; |
2476 | |
|
2477 | 0 | failure: |
2478 | 0 | return (result); |
2479 | 0 | } |
2480 | | |
2481 | | isc_result_t |
2482 | | dns_dnssec_matchdskey(dns_name_t *name, dns_rdata_t *dsrdata, |
2483 | 0 | dns_rdataset_t *keyset, dns_rdata_t *keyrdata) { |
2484 | 0 | isc_result_t result; |
2485 | 0 | unsigned char buf[DNS_DS_BUFFERSIZE]; |
2486 | 0 | dns_keytag_t keytag; |
2487 | 0 | dns_rdata_dnskey_t key; |
2488 | 0 | dns_rdata_ds_t ds; |
2489 | 0 | isc_region_t r; |
2490 | |
|
2491 | 0 | result = dns_rdata_tostruct(dsrdata, &ds, NULL); |
2492 | 0 | RUNTIME_CHECK(result == ISC_R_SUCCESS); |
2493 | |
|
2494 | 0 | for (result = dns_rdataset_first(keyset); result == ISC_R_SUCCESS; |
2495 | 0 | result = dns_rdataset_next(keyset)) |
2496 | 0 | { |
2497 | 0 | dns_rdata_t newdsrdata = DNS_RDATA_INIT; |
2498 | |
|
2499 | 0 | dns_rdata_reset(keyrdata); |
2500 | 0 | dns_rdataset_current(keyset, keyrdata); |
2501 | |
|
2502 | 0 | result = dns_rdata_tostruct(keyrdata, &key, NULL); |
2503 | 0 | RUNTIME_CHECK(result == ISC_R_SUCCESS); |
2504 | |
|
2505 | 0 | dns_rdata_toregion(keyrdata, &r); |
2506 | 0 | keytag = dst_region_computeid(&r); |
2507 | |
|
2508 | 0 | if (ds.key_tag != keytag || ds.algorithm != key.algorithm) { |
2509 | 0 | continue; |
2510 | 0 | } |
2511 | | |
2512 | 0 | result = dns_ds_buildrdata(name, keyrdata, ds.digest_type, buf, |
2513 | 0 | &newdsrdata); |
2514 | 0 | if (result != ISC_R_SUCCESS) { |
2515 | 0 | continue; |
2516 | 0 | } |
2517 | | |
2518 | 0 | if (dns_rdata_compare(dsrdata, &newdsrdata) == 0) { |
2519 | 0 | break; |
2520 | 0 | } |
2521 | 0 | } |
2522 | 0 | if (result == ISC_R_NOMORE) { |
2523 | 0 | result = ISC_R_NOTFOUND; |
2524 | 0 | } |
2525 | |
|
2526 | 0 | return (result); |
2527 | 0 | } |