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