/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 | | /* |
47 | | * Work out if 'param' should be ignored or not (i.e. it is in the process |
48 | | * of being removed). |
49 | | * |
50 | | * Note: we 'belt-and-braces' here by also checking for a CREATE private |
51 | | * record and keep the param record in this case. |
52 | | */ |
53 | | |
54 | | static bool |
55 | 0 | ignore(dns_rdata_t *param, dns_rdataset_t *privateset) { |
56 | 0 | DNS_RDATASET_FOREACH(privateset) { |
57 | 0 | unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE]; |
58 | 0 | dns_rdata_t private = DNS_RDATA_INIT; |
59 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
60 | |
|
61 | 0 | dns_rdataset_current(privateset, &private); |
62 | 0 | if (!dns_nsec3param_fromprivate(&private, &rdata, buf, |
63 | 0 | sizeof(buf))) |
64 | 0 | { |
65 | 0 | continue; |
66 | 0 | } |
67 | | /* |
68 | | * We are going to create a new NSEC3 chain so it |
69 | | * doesn't matter if we are removing this one. |
70 | | */ |
71 | 0 | if (CREATE(rdata.data[1])) { |
72 | 0 | return false; |
73 | 0 | } |
74 | 0 | if (rdata.data[0] != param->data[0] || |
75 | 0 | rdata.data[2] != param->data[2] || |
76 | 0 | rdata.data[3] != param->data[3] || |
77 | 0 | rdata.data[4] != param->data[4] || |
78 | 0 | memcmp(&rdata.data[5], ¶m->data[5], param->data[4])) |
79 | 0 | { |
80 | 0 | continue; |
81 | 0 | } |
82 | | /* |
83 | | * The removal of this NSEC3 chain does NOT cause a |
84 | | * NSEC chain to be created so we don't need to tell |
85 | | * the caller that it will be removed. |
86 | | */ |
87 | 0 | if (NONSEC(rdata.data[1])) { |
88 | 0 | return false; |
89 | 0 | } |
90 | 0 | return true; |
91 | 0 | } |
92 | 0 | return false; |
93 | 0 | } |
94 | | |
95 | | isc_result_t |
96 | | dns_private_chains(dns_db_t *db, dns_dbversion_t *ver, |
97 | | dns_rdatatype_t privatetype, bool *build_nsec, |
98 | 0 | bool *build_nsec3) { |
99 | 0 | dns_dbnode_t *node; |
100 | 0 | dns_rdataset_t nsecset, nsec3paramset, privateset; |
101 | 0 | bool nsec3chain; |
102 | 0 | bool signing; |
103 | 0 | isc_result_t result; |
104 | 0 | unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE]; |
105 | 0 | unsigned int count; |
106 | |
|
107 | 0 | node = NULL; |
108 | 0 | dns_rdataset_init(&nsecset); |
109 | 0 | dns_rdataset_init(&nsec3paramset); |
110 | 0 | dns_rdataset_init(&privateset); |
111 | |
|
112 | 0 | CHECK(dns_db_getoriginnode(db, &node)); |
113 | |
|
114 | 0 | result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec, 0, |
115 | 0 | (isc_stdtime_t)0, &nsecset, NULL); |
116 | |
|
117 | 0 | if (result != ISC_R_NOTFOUND) { |
118 | 0 | CHECK(result); |
119 | 0 | } |
120 | | |
121 | 0 | result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param, 0, |
122 | 0 | (isc_stdtime_t)0, &nsec3paramset, NULL); |
123 | 0 | if (result != ISC_R_NOTFOUND) { |
124 | 0 | CHECK(result); |
125 | 0 | } |
126 | | |
127 | 0 | if (dns_rdataset_isassociated(&nsecset) && |
128 | 0 | dns_rdataset_isassociated(&nsec3paramset)) |
129 | 0 | { |
130 | 0 | SET_IF_NOT_NULL(build_nsec, true); |
131 | 0 | SET_IF_NOT_NULL(build_nsec3, true); |
132 | 0 | goto success; |
133 | 0 | } |
134 | | |
135 | 0 | if (privatetype != (dns_rdatatype_t)0) { |
136 | 0 | result = dns_db_findrdataset(db, node, ver, privatetype, 0, |
137 | 0 | (isc_stdtime_t)0, &privateset, |
138 | 0 | NULL); |
139 | 0 | if (result != ISC_R_NOTFOUND) { |
140 | 0 | CHECK(result); |
141 | 0 | } |
142 | 0 | } |
143 | | |
144 | | /* |
145 | | * Look to see if we also need to be creating a NSEC3 chain. |
146 | | */ |
147 | 0 | if (dns_rdataset_isassociated(&nsecset)) { |
148 | 0 | SET_IF_NOT_NULL(build_nsec, true); |
149 | 0 | SET_IF_NOT_NULL(build_nsec3, false); |
150 | 0 | if (!dns_rdataset_isassociated(&privateset)) { |
151 | 0 | goto success; |
152 | 0 | } |
153 | 0 | DNS_RDATASET_FOREACH(&privateset) { |
154 | 0 | dns_rdata_t private = DNS_RDATA_INIT; |
155 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
156 | |
|
157 | 0 | dns_rdataset_current(&privateset, &private); |
158 | 0 | if (!dns_nsec3param_fromprivate(&private, &rdata, buf, |
159 | 0 | sizeof(buf))) |
160 | 0 | { |
161 | 0 | continue; |
162 | 0 | } |
163 | 0 | if (REMOVE(rdata.data[1])) { |
164 | 0 | continue; |
165 | 0 | } |
166 | 0 | SET_IF_NOT_NULL(build_nsec3, true); |
167 | 0 | break; |
168 | 0 | } |
169 | 0 | goto success; |
170 | 0 | } |
171 | | |
172 | 0 | if (dns_rdataset_isassociated(&nsec3paramset)) { |
173 | 0 | SET_IF_NOT_NULL(build_nsec3, true); |
174 | 0 | SET_IF_NOT_NULL(build_nsec, false); |
175 | 0 | if (!dns_rdataset_isassociated(&privateset)) { |
176 | 0 | goto success; |
177 | 0 | } |
178 | | /* |
179 | | * If we are in the process of building a new NSEC3 chain |
180 | | * then we don't need to build a NSEC chain. |
181 | | */ |
182 | 0 | DNS_RDATASET_FOREACH(&privateset) { |
183 | 0 | dns_rdata_t private = DNS_RDATA_INIT; |
184 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
185 | |
|
186 | 0 | dns_rdataset_current(&privateset, &private); |
187 | 0 | if (!dns_nsec3param_fromprivate(&private, &rdata, buf, |
188 | 0 | sizeof(buf))) |
189 | 0 | { |
190 | 0 | continue; |
191 | 0 | } |
192 | 0 | if (CREATE(rdata.data[1])) { |
193 | 0 | goto success; |
194 | 0 | } |
195 | 0 | } |
196 | | |
197 | | /* |
198 | | * Check to see if there will be a active NSEC3CHAIN once |
199 | | * the changes queued complete. |
200 | | */ |
201 | 0 | count = 0; |
202 | 0 | DNS_RDATASET_FOREACH(&nsec3paramset) { |
203 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
204 | | |
205 | | /* |
206 | | * If there is more that one NSEC3 chain present then |
207 | | * we don't need to construct a NSEC chain. |
208 | | */ |
209 | 0 | if (++count > 1) { |
210 | 0 | goto success; |
211 | 0 | } |
212 | 0 | dns_rdataset_current(&nsec3paramset, &rdata); |
213 | 0 | if (ignore(&rdata, &privateset)) { |
214 | 0 | continue; |
215 | 0 | } |
216 | | /* |
217 | | * We still have a good NSEC3 chain or we are |
218 | | * not creating a NSEC chain as NONSEC is set. |
219 | | */ |
220 | 0 | goto success; |
221 | 0 | } |
222 | | |
223 | | /* |
224 | | * The last NSEC3 chain is being removed and does not have |
225 | | * have NONSEC set. |
226 | | */ |
227 | 0 | SET_IF_NOT_NULL(build_nsec, true); |
228 | 0 | goto success; |
229 | 0 | } |
230 | | |
231 | 0 | SET_IF_NOT_NULL(build_nsec, false); |
232 | 0 | SET_IF_NOT_NULL(build_nsec3, false); |
233 | 0 | if (!dns_rdataset_isassociated(&privateset)) { |
234 | 0 | goto success; |
235 | 0 | } |
236 | | |
237 | 0 | signing = false; |
238 | 0 | nsec3chain = false; |
239 | |
|
240 | 0 | DNS_RDATASET_FOREACH(&privateset) { |
241 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
242 | 0 | dns_rdata_t private = DNS_RDATA_INIT; |
243 | |
|
244 | 0 | dns_rdataset_current(&privateset, &private); |
245 | 0 | if (!dns_nsec3param_fromprivate(&private, &rdata, buf, |
246 | 0 | sizeof(buf))) |
247 | 0 | { |
248 | | /* |
249 | | * Look for record that says we are signing the |
250 | | * zone with a key. |
251 | | */ |
252 | 0 | if (private.length == 5 && private.data[0] != 0 && |
253 | 0 | private.data[3] == 0 && private.data[4] == 0) |
254 | 0 | { |
255 | 0 | signing = true; |
256 | 0 | } |
257 | 0 | } else { |
258 | 0 | if (CREATE(rdata.data[1])) { |
259 | 0 | nsec3chain = true; |
260 | 0 | } |
261 | 0 | } |
262 | 0 | } |
263 | |
|
264 | 0 | if (signing) { |
265 | 0 | if (nsec3chain) { |
266 | 0 | SET_IF_NOT_NULL(build_nsec3, true); |
267 | 0 | } else { |
268 | 0 | SET_IF_NOT_NULL(build_nsec, true); |
269 | 0 | } |
270 | 0 | } |
271 | |
|
272 | 0 | success: |
273 | 0 | result = ISC_R_SUCCESS; |
274 | 0 | cleanup: |
275 | 0 | dns_rdataset_cleanup(&nsecset); |
276 | 0 | dns_rdataset_cleanup(&nsec3paramset); |
277 | 0 | dns_rdataset_cleanup(&privateset); |
278 | 0 | if (node != NULL) { |
279 | 0 | dns_db_detachnode(&node); |
280 | 0 | } |
281 | 0 | return result; |
282 | 0 | } |
283 | | |
284 | | isc_result_t |
285 | 0 | dns_private_totext(dns_rdata_t *private, isc_buffer_t *buf) { |
286 | 0 | isc_result_t result; |
287 | |
|
288 | 0 | if (private->length < 5) { |
289 | 0 | return ISC_R_NOTFOUND; |
290 | 0 | } |
291 | | |
292 | 0 | if (private->data[0] == 0) { |
293 | 0 | unsigned char nsec3buf[DNS_NSEC3PARAM_BUFFERSIZE]; |
294 | 0 | unsigned char newbuf[DNS_NSEC3PARAM_BUFFERSIZE]; |
295 | 0 | dns_rdata_t rdata = DNS_RDATA_INIT; |
296 | 0 | dns_rdata_nsec3param_t nsec3param; |
297 | 0 | bool del, init, nonsec; |
298 | 0 | isc_buffer_t b; |
299 | |
|
300 | 0 | if (!dns_nsec3param_fromprivate(private, &rdata, nsec3buf, |
301 | 0 | sizeof(nsec3buf))) |
302 | 0 | { |
303 | 0 | CLEANUP(ISC_R_FAILURE); |
304 | 0 | } |
305 | | |
306 | 0 | CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL)); |
307 | |
|
308 | 0 | del = ((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0); |
309 | 0 | init = ((nsec3param.flags & DNS_NSEC3FLAG_INITIAL) != 0); |
310 | 0 | nonsec = ((nsec3param.flags & DNS_NSEC3FLAG_NONSEC) != 0); |
311 | |
|
312 | 0 | nsec3param.flags &= |
313 | 0 | ~(DNS_NSEC3FLAG_CREATE | DNS_NSEC3FLAG_REMOVE | |
314 | 0 | DNS_NSEC3FLAG_INITIAL | DNS_NSEC3FLAG_NONSEC); |
315 | |
|
316 | 0 | if (init) { |
317 | 0 | isc_buffer_putstr(buf, "Pending NSEC3 chain "); |
318 | 0 | } else if (del) { |
319 | 0 | isc_buffer_putstr(buf, "Removing NSEC3 chain "); |
320 | 0 | } else { |
321 | 0 | isc_buffer_putstr(buf, "Creating NSEC3 chain "); |
322 | 0 | } |
323 | |
|
324 | 0 | dns_rdata_reset(&rdata); |
325 | 0 | isc_buffer_init(&b, newbuf, sizeof(newbuf)); |
326 | 0 | CHECK(dns_rdata_fromstruct(&rdata, dns_rdataclass_in, |
327 | 0 | dns_rdatatype_nsec3param, |
328 | 0 | &nsec3param, &b)); |
329 | |
|
330 | 0 | CHECK(dns_rdata_totext(&rdata, NULL, buf)); |
331 | |
|
332 | 0 | if (del && !nonsec) { |
333 | 0 | isc_buffer_putstr(buf, " / creating NSEC chain"); |
334 | 0 | } |
335 | 0 | } else if (private->length == 5) { |
336 | | /* Old Form */ |
337 | 0 | unsigned char alg = private->data[0]; |
338 | 0 | dns_keytag_t keyid = (private->data[2] | private->data[1] << 8); |
339 | 0 | char keybuf[DNS_SECALG_FORMATSIZE + BUFSIZ], |
340 | 0 | algbuf[DNS_SECALG_FORMATSIZE]; |
341 | 0 | bool del = private->data[3]; |
342 | 0 | bool complete = private->data[4]; |
343 | |
|
344 | 0 | if (del && complete) { |
345 | 0 | isc_buffer_putstr(buf, "Done removing signatures for "); |
346 | 0 | } else if (del) { |
347 | 0 | isc_buffer_putstr(buf, "Removing signatures for "); |
348 | 0 | } else if (complete) { |
349 | 0 | isc_buffer_putstr(buf, "Done signing with "); |
350 | 0 | } else { |
351 | 0 | isc_buffer_putstr(buf, "Signing with "); |
352 | 0 | } |
353 | |
|
354 | 0 | dns_secalg_format(alg, algbuf, sizeof(algbuf)); |
355 | 0 | snprintf(keybuf, sizeof(keybuf), "key %d/%s", keyid, algbuf); |
356 | 0 | isc_buffer_putstr(buf, keybuf); |
357 | 0 | } else if (private->length == 7) { |
358 | | /* New Form - supports private types */ |
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 | 0 | dst_algorithm_t alg = private->data[6] | |
365 | 0 | (private->data[5] << 8); |
366 | |
|
367 | 0 | if (dst_algorithm_tosecalg(alg) != private->data[0]) { |
368 | 0 | return ISC_R_NOTFOUND; |
369 | 0 | } |
370 | | |
371 | 0 | if (del && complete) { |
372 | 0 | isc_buffer_putstr(buf, "Done removing signatures for "); |
373 | 0 | } else if (del) { |
374 | 0 | isc_buffer_putstr(buf, "Removing signatures for "); |
375 | 0 | } else if (complete) { |
376 | 0 | isc_buffer_putstr(buf, "Done signing with "); |
377 | 0 | } else { |
378 | 0 | isc_buffer_putstr(buf, "Signing with "); |
379 | 0 | } |
380 | |
|
381 | 0 | dns_secalg_format(alg, algbuf, sizeof(algbuf)); |
382 | 0 | snprintf(keybuf, sizeof(keybuf), "key %d/%s", keyid, algbuf); |
383 | 0 | isc_buffer_putstr(buf, keybuf); |
384 | 0 | } else { |
385 | 0 | return ISC_R_NOTFOUND; |
386 | 0 | } |
387 | | |
388 | 0 | isc_buffer_putuint8(buf, 0); |
389 | 0 | result = ISC_R_SUCCESS; |
390 | 0 | cleanup: |
391 | 0 | return result; |
392 | 0 | } |