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