/src/bind9/lib/dns/private.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) Internet Systems Consortium, Inc. ("ISC") |
3 | | * |
4 | | * SPDX-License-Identifier: MPL-2.0 |
5 | | * |
6 | | * This Source Code Form is subject to the terms of the Mozilla Public |
7 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
8 | | * file, you can obtain one at https://mozilla.org/MPL/2.0/. |
9 | | * |
10 | | * See the COPYRIGHT file distributed with this work for additional |
11 | | * information regarding copyright ownership. |
12 | | */ |
13 | | |
14 | | #include <stdbool.h> |
15 | | |
16 | | #include <isc/base64.h> |
17 | | #include <isc/result.h> |
18 | | #include <isc/string.h> |
19 | | #include <isc/types.h> |
20 | | #include <isc/util.h> |
21 | | |
22 | | #include <dns/nsec3.h> |
23 | | #include <dns/private.h> |
24 | | |
25 | | /* |
26 | | * We need to build the relevant chain if there exists a NSEC/NSEC3PARAM |
27 | | * at the apex; normally only one or the other of NSEC/NSEC3PARAM will exist. |
28 | | * |
29 | | * If a NSEC3PARAM RRset exists then we will need to build a NSEC chain |
30 | | * if all the NSEC3PARAM records (and associated chains) are slated for |
31 | | * destruction and we have not been told to NOT build the NSEC chain. |
32 | | * |
33 | | * If the NSEC set exist then check to see if there is a request to create |
34 | | * a NSEC3 chain. |
35 | | * |
36 | | * If neither NSEC/NSEC3PARAM RRsets exist at the origin and the private |
37 | | * type exists then we need to examine it to determine if NSEC3 chain has |
38 | | * been requested to be built otherwise a NSEC chain needs to be built. |
39 | | */ |
40 | | |
41 | 0 | #define REMOVE(x) (((x) & DNS_NSEC3FLAG_REMOVE) != 0) |
42 | 0 | #define CREATE(x) (((x) & DNS_NSEC3FLAG_CREATE) != 0) |
43 | | #define INITIAL(x) (((x) & DNS_NSEC3FLAG_INITIAL) != 0) |
44 | 0 | #define NONSEC(x) (((x) & DNS_NSEC3FLAG_NONSEC) != 0) |
45 | | |
46 | | #define CHECK(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 | | /* |
54 | | * Work out if 'param' should be ignored or not (i.e. it is in the process |
55 | | * of being removed). |
56 | | * |
57 | | * Note: we 'belt-and-braces' here by also checking for a CREATE private |
58 | | * record and keep the param record in this case. |
59 | | */ |
60 | | |
61 | | static bool |
62 | 0 | ignore(dns_rdata_t *param, dns_rdataset_t *privateset) { |
63 | 0 | DNS_RDATASET_FOREACH (privateset) { |
64 | 0 | unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE]; |
65 | 0 | dns_rdata_t private = DNS_RDATA_INIT; |
66 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
67 | |
|
68 | 0 | dns_rdataset_current(privateset, &private); |
69 | 0 | if (!dns_nsec3param_fromprivate(&private, &rdata, buf, |
70 | 0 | sizeof(buf))) |
71 | 0 | { |
72 | 0 | continue; |
73 | 0 | } |
74 | | /* |
75 | | * We are going to create a new NSEC3 chain so it |
76 | | * doesn't matter if we are removing this one. |
77 | | */ |
78 | 0 | if (CREATE(rdata.data[1])) { |
79 | 0 | return false; |
80 | 0 | } |
81 | 0 | if (rdata.data[0] != param->data[0] || |
82 | 0 | rdata.data[2] != param->data[2] || |
83 | 0 | rdata.data[3] != param->data[3] || |
84 | 0 | rdata.data[4] != param->data[4] || |
85 | 0 | memcmp(&rdata.data[5], ¶m->data[5], param->data[4])) |
86 | 0 | { |
87 | 0 | continue; |
88 | 0 | } |
89 | | /* |
90 | | * The removal of this NSEC3 chain does NOT cause a |
91 | | * NSEC chain to be created so we don't need to tell |
92 | | * the caller that it will be removed. |
93 | | */ |
94 | 0 | if (NONSEC(rdata.data[1])) { |
95 | 0 | return false; |
96 | 0 | } |
97 | 0 | return true; |
98 | 0 | } |
99 | 0 | return false; |
100 | 0 | } |
101 | | |
102 | | isc_result_t |
103 | | dns_private_chains(dns_db_t *db, dns_dbversion_t *ver, |
104 | | dns_rdatatype_t privatetype, bool *build_nsec, |
105 | 0 | bool *build_nsec3) { |
106 | 0 | dns_dbnode_t *node; |
107 | 0 | dns_rdataset_t nsecset, nsec3paramset, privateset; |
108 | 0 | bool nsec3chain; |
109 | 0 | bool signing; |
110 | 0 | isc_result_t result; |
111 | 0 | unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE]; |
112 | 0 | unsigned int count; |
113 | |
|
114 | 0 | node = NULL; |
115 | 0 | dns_rdataset_init(&nsecset); |
116 | 0 | dns_rdataset_init(&nsec3paramset); |
117 | 0 | dns_rdataset_init(&privateset); |
118 | |
|
119 | 0 | CHECK(dns_db_getoriginnode(db, &node)); |
120 | | |
121 | 0 | result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec, 0, |
122 | 0 | (isc_stdtime_t)0, &nsecset, NULL); |
123 | |
|
124 | 0 | if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { |
125 | 0 | goto failure; |
126 | 0 | } |
127 | | |
128 | 0 | result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param, 0, |
129 | 0 | (isc_stdtime_t)0, &nsec3paramset, NULL); |
130 | 0 | if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { |
131 | 0 | goto failure; |
132 | 0 | } |
133 | | |
134 | 0 | if (dns_rdataset_isassociated(&nsecset) && |
135 | 0 | dns_rdataset_isassociated(&nsec3paramset)) |
136 | 0 | { |
137 | 0 | SET_IF_NOT_NULL(build_nsec, true); |
138 | 0 | SET_IF_NOT_NULL(build_nsec3, true); |
139 | 0 | goto success; |
140 | 0 | } |
141 | | |
142 | 0 | if (privatetype != (dns_rdatatype_t)0) { |
143 | 0 | result = dns_db_findrdataset(db, node, ver, privatetype, 0, |
144 | 0 | (isc_stdtime_t)0, &privateset, |
145 | 0 | NULL); |
146 | 0 | if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND) { |
147 | 0 | goto failure; |
148 | 0 | } |
149 | 0 | } |
150 | | |
151 | | /* |
152 | | * Look to see if we also need to be creating a NSEC3 chain. |
153 | | */ |
154 | 0 | if (dns_rdataset_isassociated(&nsecset)) { |
155 | 0 | SET_IF_NOT_NULL(build_nsec, true); |
156 | 0 | SET_IF_NOT_NULL(build_nsec3, false); |
157 | 0 | if (!dns_rdataset_isassociated(&privateset)) { |
158 | 0 | goto success; |
159 | 0 | } |
160 | 0 | DNS_RDATASET_FOREACH (&privateset) { |
161 | 0 | dns_rdata_t private = DNS_RDATA_INIT; |
162 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
163 | |
|
164 | 0 | dns_rdataset_current(&privateset, &private); |
165 | 0 | if (!dns_nsec3param_fromprivate(&private, &rdata, buf, |
166 | 0 | sizeof(buf))) |
167 | 0 | { |
168 | 0 | continue; |
169 | 0 | } |
170 | 0 | if (REMOVE(rdata.data[1])) { |
171 | 0 | continue; |
172 | 0 | } |
173 | 0 | if (build_nsec3 != NULL) { |
174 | 0 | *build_nsec3 = true; |
175 | 0 | } |
176 | 0 | break; |
177 | 0 | } |
178 | 0 | goto success; |
179 | 0 | } |
180 | | |
181 | 0 | if (dns_rdataset_isassociated(&nsec3paramset)) { |
182 | 0 | SET_IF_NOT_NULL(build_nsec3, true); |
183 | 0 | SET_IF_NOT_NULL(build_nsec, false); |
184 | 0 | if (!dns_rdataset_isassociated(&privateset)) { |
185 | 0 | goto success; |
186 | 0 | } |
187 | | /* |
188 | | * If we are in the process of building a new NSEC3 chain |
189 | | * then we don't need to build a NSEC chain. |
190 | | */ |
191 | 0 | DNS_RDATASET_FOREACH (&privateset) { |
192 | 0 | dns_rdata_t private = DNS_RDATA_INIT; |
193 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
194 | |
|
195 | 0 | dns_rdataset_current(&privateset, &private); |
196 | 0 | if (!dns_nsec3param_fromprivate(&private, &rdata, buf, |
197 | 0 | sizeof(buf))) |
198 | 0 | { |
199 | 0 | continue; |
200 | 0 | } |
201 | 0 | if (CREATE(rdata.data[1])) { |
202 | 0 | goto success; |
203 | 0 | } |
204 | 0 | } |
205 | | |
206 | | /* |
207 | | * Check to see if there will be a active NSEC3CHAIN once |
208 | | * the changes queued complete. |
209 | | */ |
210 | 0 | count = 0; |
211 | 0 | DNS_RDATASET_FOREACH (&nsec3paramset) { |
212 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
213 | | |
214 | | /* |
215 | | * If there is more that one NSEC3 chain present then |
216 | | * we don't need to construct a NSEC chain. |
217 | | */ |
218 | 0 | if (++count > 1) { |
219 | 0 | goto success; |
220 | 0 | } |
221 | 0 | dns_rdataset_current(&nsec3paramset, &rdata); |
222 | 0 | if (ignore(&rdata, &privateset)) { |
223 | 0 | continue; |
224 | 0 | } |
225 | | /* |
226 | | * We still have a good NSEC3 chain or we are |
227 | | * not creating a NSEC chain as NONSEC is set. |
228 | | */ |
229 | 0 | goto success; |
230 | 0 | } |
231 | | |
232 | | /* |
233 | | * The last NSEC3 chain is being removed and does not have |
234 | | * have NONSEC set. |
235 | | */ |
236 | 0 | if (build_nsec != NULL) { |
237 | 0 | *build_nsec = true; |
238 | 0 | } |
239 | 0 | goto success; |
240 | 0 | } |
241 | | |
242 | 0 | SET_IF_NOT_NULL(build_nsec, false); |
243 | 0 | SET_IF_NOT_NULL(build_nsec3, false); |
244 | 0 | if (!dns_rdataset_isassociated(&privateset)) { |
245 | 0 | goto success; |
246 | 0 | } |
247 | | |
248 | 0 | signing = false; |
249 | 0 | nsec3chain = false; |
250 | |
|
251 | 0 | DNS_RDATASET_FOREACH (&privateset) { |
252 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
253 | 0 | dns_rdata_t private = DNS_RDATA_INIT; |
254 | |
|
255 | 0 | dns_rdataset_current(&privateset, &private); |
256 | 0 | if (!dns_nsec3param_fromprivate(&private, &rdata, buf, |
257 | 0 | sizeof(buf))) |
258 | 0 | { |
259 | | /* |
260 | | * Look for record that says we are signing the |
261 | | * zone with a key. |
262 | | */ |
263 | 0 | if (private.length == 5 && private.data[0] != 0 && |
264 | 0 | private.data[3] == 0 && private.data[4] == 0) |
265 | 0 | { |
266 | 0 | signing = true; |
267 | 0 | } |
268 | 0 | } else { |
269 | 0 | if (CREATE(rdata.data[1])) { |
270 | 0 | nsec3chain = true; |
271 | 0 | } |
272 | 0 | } |
273 | 0 | } |
274 | |
|
275 | 0 | if (signing) { |
276 | 0 | if (nsec3chain) { |
277 | 0 | if (build_nsec3 != NULL) { |
278 | 0 | *build_nsec3 = true; |
279 | 0 | } |
280 | 0 | } else { |
281 | 0 | if (build_nsec != NULL) { |
282 | 0 | *build_nsec = true; |
283 | 0 | } |
284 | 0 | } |
285 | 0 | } |
286 | |
|
287 | 0 | success: |
288 | 0 | result = ISC_R_SUCCESS; |
289 | 0 | failure: |
290 | 0 | if (dns_rdataset_isassociated(&nsecset)) { |
291 | 0 | dns_rdataset_disassociate(&nsecset); |
292 | 0 | } |
293 | 0 | if (dns_rdataset_isassociated(&nsec3paramset)) { |
294 | 0 | dns_rdataset_disassociate(&nsec3paramset); |
295 | 0 | } |
296 | 0 | if (dns_rdataset_isassociated(&privateset)) { |
297 | 0 | dns_rdataset_disassociate(&privateset); |
298 | 0 | } |
299 | 0 | if (node != NULL) { |
300 | 0 | dns_db_detachnode(db, &node); |
301 | 0 | } |
302 | 0 | return result; |
303 | 0 | } |
304 | | |
305 | | isc_result_t |
306 | 0 | dns_private_totext(dns_rdata_t *private, isc_buffer_t *buf) { |
307 | 0 | isc_result_t result; |
308 | |
|
309 | 0 | if (private->length < 5) { |
310 | 0 | return ISC_R_NOTFOUND; |
311 | 0 | } |
312 | | |
313 | 0 | if (private->data[0] == 0) { |
314 | 0 | unsigned char nsec3buf[DNS_NSEC3PARAM_BUFFERSIZE]; |
315 | 0 | unsigned char newbuf[DNS_NSEC3PARAM_BUFFERSIZE]; |
316 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
317 | 0 | dns_rdata_nsec3param_t nsec3param; |
318 | 0 | bool del, init, nonsec; |
319 | 0 | isc_buffer_t b; |
320 | |
|
321 | 0 | if (!dns_nsec3param_fromprivate(private, &rdata, nsec3buf, |
322 | 0 | sizeof(nsec3buf))) |
323 | 0 | { |
324 | 0 | CHECK(ISC_R_FAILURE); |
325 | 0 | } |
326 | | |
327 | 0 | CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL)); |
328 | | |
329 | 0 | del = ((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0); |
330 | 0 | init = ((nsec3param.flags & DNS_NSEC3FLAG_INITIAL) != 0); |
331 | 0 | nonsec = ((nsec3param.flags & DNS_NSEC3FLAG_NONSEC) != 0); |
332 | |
|
333 | 0 | nsec3param.flags &= |
334 | 0 | ~(DNS_NSEC3FLAG_CREATE | DNS_NSEC3FLAG_REMOVE | |
335 | 0 | DNS_NSEC3FLAG_INITIAL | DNS_NSEC3FLAG_NONSEC); |
336 | |
|
337 | 0 | if (init) { |
338 | 0 | isc_buffer_putstr(buf, "Pending NSEC3 chain "); |
339 | 0 | } else if (del) { |
340 | 0 | isc_buffer_putstr(buf, "Removing NSEC3 chain "); |
341 | 0 | } else { |
342 | 0 | isc_buffer_putstr(buf, "Creating NSEC3 chain "); |
343 | 0 | } |
344 | |
|
345 | 0 | dns_rdata_reset(&rdata); |
346 | 0 | isc_buffer_init(&b, newbuf, sizeof(newbuf)); |
347 | 0 | CHECK(dns_rdata_fromstruct(&rdata, dns_rdataclass_in, |
348 | 0 | dns_rdatatype_nsec3param, |
349 | 0 | &nsec3param, &b)); |
350 | | |
351 | 0 | CHECK(dns_rdata_totext(&rdata, NULL, buf)); |
352 | | |
353 | 0 | if (del && !nonsec) { |
354 | 0 | isc_buffer_putstr(buf, " / creating NSEC chain"); |
355 | 0 | } |
356 | 0 | } else if (private->length == 5) { |
357 | | /* Old Form */ |
358 | 0 | unsigned char alg = private->data[0]; |
359 | 0 | dns_keytag_t keyid = (private->data[2] | private->data[1] << 8); |
360 | 0 | char keybuf[DNS_SECALG_FORMATSIZE + BUFSIZ], |
361 | 0 | algbuf[DNS_SECALG_FORMATSIZE]; |
362 | 0 | bool del = private->data[3]; |
363 | 0 | bool complete = private->data[4]; |
364 | |
|
365 | 0 | if (del && complete) { |
366 | 0 | isc_buffer_putstr(buf, "Done removing signatures for "); |
367 | 0 | } else if (del) { |
368 | 0 | isc_buffer_putstr(buf, "Removing signatures for "); |
369 | 0 | } else if (complete) { |
370 | 0 | isc_buffer_putstr(buf, "Done signing with "); |
371 | 0 | } else { |
372 | 0 | isc_buffer_putstr(buf, "Signing with "); |
373 | 0 | } |
374 | |
|
375 | 0 | dns_secalg_format(alg, algbuf, sizeof(algbuf)); |
376 | 0 | snprintf(keybuf, sizeof(keybuf), "key %d/%s", keyid, algbuf); |
377 | 0 | isc_buffer_putstr(buf, keybuf); |
378 | 0 | } else if (private->length == 7) { |
379 | | /* New Form - supports private types */ |
380 | 0 | dns_keytag_t keyid = private->data[2] | (private->data[1] << 8); |
381 | 0 | char keybuf[DNS_SECALG_FORMATSIZE + BUFSIZ], |
382 | 0 | algbuf[DNS_SECALG_FORMATSIZE]; |
383 | 0 | bool del = private->data[3]; |
384 | 0 | bool complete = private->data[4]; |
385 | 0 | dst_algorithm_t alg = private->data[6] | |
386 | 0 | (private->data[5] << 8); |
387 | |
|
388 | 0 | if (dst_algorithm_tosecalg(alg) != private->data[0]) { |
389 | 0 | return ISC_R_NOTFOUND; |
390 | 0 | } |
391 | | |
392 | 0 | if (del && complete) { |
393 | 0 | isc_buffer_putstr(buf, "Done removing signatures for "); |
394 | 0 | } else if (del) { |
395 | 0 | isc_buffer_putstr(buf, "Removing signatures for "); |
396 | 0 | } else if (complete) { |
397 | 0 | isc_buffer_putstr(buf, "Done signing with "); |
398 | 0 | } else { |
399 | 0 | isc_buffer_putstr(buf, "Signing with "); |
400 | 0 | } |
401 | |
|
402 | 0 | dns_secalg_format(alg, algbuf, sizeof(algbuf)); |
403 | 0 | snprintf(keybuf, sizeof(keybuf), "key %d/%s", keyid, algbuf); |
404 | 0 | isc_buffer_putstr(buf, keybuf); |
405 | 0 | } else { |
406 | 0 | return ISC_R_NOTFOUND; |
407 | 0 | } |
408 | | |
409 | 0 | isc_buffer_putuint8(buf, 0); |
410 | 0 | result = ISC_R_SUCCESS; |
411 | 0 | failure: |
412 | 0 | return result; |
413 | 0 | } |