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