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