/src/bind9/lib/dns/private.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 | | #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 | SET_IF_NOT_NULL(build_nsec3, true); |
174 | 0 | break; |
175 | 0 | } |
176 | 0 | goto success; |
177 | 0 | } |
178 | | |
179 | 0 | if (dns_rdataset_isassociated(&nsec3paramset)) { |
180 | 0 | SET_IF_NOT_NULL(build_nsec3, true); |
181 | 0 | SET_IF_NOT_NULL(build_nsec, false); |
182 | 0 | if (!dns_rdataset_isassociated(&privateset)) { |
183 | 0 | goto success; |
184 | 0 | } |
185 | | /* |
186 | | * If we are in the process of building a new NSEC3 chain |
187 | | * then we don't need to build a NSEC chain. |
188 | | */ |
189 | 0 | DNS_RDATASET_FOREACH(&privateset) { |
190 | 0 | dns_rdata_t private = DNS_RDATA_INIT; |
191 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
192 | |
|
193 | 0 | dns_rdataset_current(&privateset, &private); |
194 | 0 | if (!dns_nsec3param_fromprivate(&private, &rdata, buf, |
195 | 0 | sizeof(buf))) |
196 | 0 | { |
197 | 0 | continue; |
198 | 0 | } |
199 | 0 | if (CREATE(rdata.data[1])) { |
200 | 0 | goto success; |
201 | 0 | } |
202 | 0 | } |
203 | | |
204 | | /* |
205 | | * Check to see if there will be a active NSEC3CHAIN once |
206 | | * the changes queued complete. |
207 | | */ |
208 | 0 | count = 0; |
209 | 0 | DNS_RDATASET_FOREACH(&nsec3paramset) { |
210 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
211 | | |
212 | | /* |
213 | | * If there is more that one NSEC3 chain present then |
214 | | * we don't need to construct a NSEC chain. |
215 | | */ |
216 | 0 | if (++count > 1) { |
217 | 0 | goto success; |
218 | 0 | } |
219 | 0 | dns_rdataset_current(&nsec3paramset, &rdata); |
220 | 0 | if (ignore(&rdata, &privateset)) { |
221 | 0 | continue; |
222 | 0 | } |
223 | | /* |
224 | | * We still have a good NSEC3 chain or we are |
225 | | * not creating a NSEC chain as NONSEC is set. |
226 | | */ |
227 | 0 | goto success; |
228 | 0 | } |
229 | | |
230 | | /* |
231 | | * The last NSEC3 chain is being removed and does not have |
232 | | * have NONSEC set. |
233 | | */ |
234 | 0 | SET_IF_NOT_NULL(build_nsec, true); |
235 | 0 | goto success; |
236 | 0 | } |
237 | | |
238 | 0 | SET_IF_NOT_NULL(build_nsec, false); |
239 | 0 | SET_IF_NOT_NULL(build_nsec3, false); |
240 | 0 | if (!dns_rdataset_isassociated(&privateset)) { |
241 | 0 | goto success; |
242 | 0 | } |
243 | | |
244 | 0 | signing = false; |
245 | 0 | nsec3chain = false; |
246 | |
|
247 | 0 | DNS_RDATASET_FOREACH(&privateset) { |
248 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
249 | 0 | dns_rdata_t private = DNS_RDATA_INIT; |
250 | |
|
251 | 0 | dns_rdataset_current(&privateset, &private); |
252 | 0 | if (!dns_nsec3param_fromprivate(&private, &rdata, buf, |
253 | 0 | sizeof(buf))) |
254 | 0 | { |
255 | | /* |
256 | | * Look for record that says we are signing the |
257 | | * zone with a key. |
258 | | */ |
259 | 0 | if (private.length == 5 && private.data[0] != 0 && |
260 | 0 | private.data[3] == 0 && private.data[4] == 0) |
261 | 0 | { |
262 | 0 | signing = true; |
263 | 0 | } |
264 | 0 | } else { |
265 | 0 | if (CREATE(rdata.data[1])) { |
266 | 0 | nsec3chain = true; |
267 | 0 | } |
268 | 0 | } |
269 | 0 | } |
270 | |
|
271 | 0 | if (signing) { |
272 | 0 | if (nsec3chain) { |
273 | 0 | SET_IF_NOT_NULL(build_nsec3, true); |
274 | 0 | } else { |
275 | 0 | SET_IF_NOT_NULL(build_nsec, true); |
276 | 0 | } |
277 | 0 | } |
278 | |
|
279 | 0 | success: |
280 | 0 | result = ISC_R_SUCCESS; |
281 | 0 | failure: |
282 | 0 | if (dns_rdataset_isassociated(&nsecset)) { |
283 | 0 | dns_rdataset_disassociate(&nsecset); |
284 | 0 | } |
285 | 0 | if (dns_rdataset_isassociated(&nsec3paramset)) { |
286 | 0 | dns_rdataset_disassociate(&nsec3paramset); |
287 | 0 | } |
288 | 0 | if (dns_rdataset_isassociated(&privateset)) { |
289 | 0 | dns_rdataset_disassociate(&privateset); |
290 | 0 | } |
291 | 0 | if (node != NULL) { |
292 | 0 | dns_db_detachnode(&node); |
293 | 0 | } |
294 | 0 | return result; |
295 | 0 | } |
296 | | |
297 | | isc_result_t |
298 | 0 | dns_private_totext(dns_rdata_t *private, isc_buffer_t *buf) { |
299 | 0 | isc_result_t result; |
300 | |
|
301 | 0 | if (private->length < 5) { |
302 | 0 | return ISC_R_NOTFOUND; |
303 | 0 | } |
304 | | |
305 | 0 | if (private->data[0] == 0) { |
306 | 0 | unsigned char nsec3buf[DNS_NSEC3PARAM_BUFFERSIZE]; |
307 | 0 | unsigned char newbuf[DNS_NSEC3PARAM_BUFFERSIZE]; |
308 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
309 | 0 | dns_rdata_nsec3param_t nsec3param; |
310 | 0 | bool del, init, nonsec; |
311 | 0 | isc_buffer_t b; |
312 | |
|
313 | 0 | if (!dns_nsec3param_fromprivate(private, &rdata, nsec3buf, |
314 | 0 | sizeof(nsec3buf))) |
315 | 0 | { |
316 | 0 | CHECK(ISC_R_FAILURE); |
317 | 0 | } |
318 | | |
319 | 0 | CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL)); |
320 | | |
321 | 0 | del = ((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0); |
322 | 0 | init = ((nsec3param.flags & DNS_NSEC3FLAG_INITIAL) != 0); |
323 | 0 | nonsec = ((nsec3param.flags & DNS_NSEC3FLAG_NONSEC) != 0); |
324 | |
|
325 | 0 | nsec3param.flags &= |
326 | 0 | ~(DNS_NSEC3FLAG_CREATE | DNS_NSEC3FLAG_REMOVE | |
327 | 0 | DNS_NSEC3FLAG_INITIAL | DNS_NSEC3FLAG_NONSEC); |
328 | |
|
329 | 0 | if (init) { |
330 | 0 | isc_buffer_putstr(buf, "Pending NSEC3 chain "); |
331 | 0 | } else if (del) { |
332 | 0 | isc_buffer_putstr(buf, "Removing NSEC3 chain "); |
333 | 0 | } else { |
334 | 0 | isc_buffer_putstr(buf, "Creating NSEC3 chain "); |
335 | 0 | } |
336 | |
|
337 | 0 | dns_rdata_reset(&rdata); |
338 | 0 | isc_buffer_init(&b, newbuf, sizeof(newbuf)); |
339 | 0 | CHECK(dns_rdata_fromstruct(&rdata, dns_rdataclass_in, |
340 | 0 | dns_rdatatype_nsec3param, |
341 | 0 | &nsec3param, &b)); |
342 | | |
343 | 0 | CHECK(dns_rdata_totext(&rdata, NULL, buf)); |
344 | | |
345 | 0 | if (del && !nonsec) { |
346 | 0 | isc_buffer_putstr(buf, " / creating NSEC chain"); |
347 | 0 | } |
348 | 0 | } else if (private->length == 5) { |
349 | | /* Old Form */ |
350 | 0 | unsigned char alg = private->data[0]; |
351 | 0 | dns_keytag_t keyid = (private->data[2] | private->data[1] << 8); |
352 | 0 | char keybuf[DNS_SECALG_FORMATSIZE + BUFSIZ], |
353 | 0 | algbuf[DNS_SECALG_FORMATSIZE]; |
354 | 0 | bool del = private->data[3]; |
355 | 0 | bool complete = private->data[4]; |
356 | |
|
357 | 0 | if (del && complete) { |
358 | 0 | isc_buffer_putstr(buf, "Done removing signatures for "); |
359 | 0 | } else if (del) { |
360 | 0 | isc_buffer_putstr(buf, "Removing signatures for "); |
361 | 0 | } else if (complete) { |
362 | 0 | isc_buffer_putstr(buf, "Done signing with "); |
363 | 0 | } else { |
364 | 0 | isc_buffer_putstr(buf, "Signing with "); |
365 | 0 | } |
366 | |
|
367 | 0 | dns_secalg_format(alg, algbuf, sizeof(algbuf)); |
368 | 0 | snprintf(keybuf, sizeof(keybuf), "key %d/%s", keyid, algbuf); |
369 | 0 | isc_buffer_putstr(buf, keybuf); |
370 | 0 | } else if (private->length == 7) { |
371 | | /* New Form - supports private types */ |
372 | 0 | dns_keytag_t keyid = private->data[2] | (private->data[1] << 8); |
373 | 0 | char keybuf[DNS_SECALG_FORMATSIZE + BUFSIZ], |
374 | 0 | algbuf[DNS_SECALG_FORMATSIZE]; |
375 | 0 | bool del = private->data[3]; |
376 | 0 | bool complete = private->data[4]; |
377 | 0 | dst_algorithm_t alg = private->data[6] | |
378 | 0 | (private->data[5] << 8); |
379 | |
|
380 | 0 | if (dst_algorithm_tosecalg(alg) != private->data[0]) { |
381 | 0 | return ISC_R_NOTFOUND; |
382 | 0 | } |
383 | | |
384 | 0 | if (del && complete) { |
385 | 0 | isc_buffer_putstr(buf, "Done removing signatures for "); |
386 | 0 | } else if (del) { |
387 | 0 | isc_buffer_putstr(buf, "Removing signatures for "); |
388 | 0 | } else if (complete) { |
389 | 0 | isc_buffer_putstr(buf, "Done signing with "); |
390 | 0 | } else { |
391 | 0 | isc_buffer_putstr(buf, "Signing with "); |
392 | 0 | } |
393 | |
|
394 | 0 | dns_secalg_format(alg, algbuf, sizeof(algbuf)); |
395 | 0 | snprintf(keybuf, sizeof(keybuf), "key %d/%s", keyid, algbuf); |
396 | 0 | isc_buffer_putstr(buf, keybuf); |
397 | 0 | } else { |
398 | 0 | return ISC_R_NOTFOUND; |
399 | 0 | } |
400 | | |
401 | 0 | isc_buffer_putuint8(buf, 0); |
402 | 0 | result = ISC_R_SUCCESS; |
403 | 0 | failure: |
404 | 0 | return result; |
405 | 0 | } |