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