1224090Sdougb/*
2254897Serwin * Copyright (C) 2009, 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
3224090Sdougb *
4224090Sdougb * Permission to use, copy, modify, and/or distribute this software for any
5224090Sdougb * purpose with or without fee is hereby granted, provided that the above
6224090Sdougb * copyright notice and this permission notice appear in all copies.
7224090Sdougb *
8224090Sdougb * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9224090Sdougb * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10224090Sdougb * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11224090Sdougb * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12224090Sdougb * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13224090Sdougb * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14224090Sdougb * PERFORMANCE OF THIS SOFTWARE.
15224090Sdougb */
16224090Sdougb
17234010Sdougb/* $Id$ */
18224090Sdougb
19224090Sdougb#include "config.h"
20224090Sdougb
21224090Sdougb#include <isc/result.h>
22224090Sdougb#include <isc/string.h>
23224090Sdougb#include <isc/types.h>
24224090Sdougb#include <isc/base64.h>
25224090Sdougb
26224090Sdougb#include <dns/nsec3.h>
27224090Sdougb#include <dns/private.h>
28224090Sdougb
29224090Sdougb/*
30224090Sdougb * We need to build the relevant chain if there exists a NSEC/NSEC3PARAM
31224090Sdougb * at the apex; normally only one or the other of NSEC/NSEC3PARAM will exist.
32224090Sdougb *
33224090Sdougb * If a NSEC3PARAM RRset exists then we will need to build a NSEC chain
34224090Sdougb * if all the NSEC3PARAM records (and associated chains) are slated for
35224090Sdougb * destruction and we have not been told to NOT build the NSEC chain.
36224090Sdougb *
37224090Sdougb * If the NSEC set exist then check to see if there is a request to create
38224090Sdougb * a NSEC3 chain.
39224090Sdougb *
40224090Sdougb * If neither NSEC/NSEC3PARAM RRsets exist at the origin and the private
41224090Sdougb * type exists then we need to examine it to determine if NSEC3 chain has
42224090Sdougb * been requested to be built otherwise a NSEC chain needs to be built.
43224090Sdougb */
44224090Sdougb
45224090Sdougb#define REMOVE(x) (((x) & DNS_NSEC3FLAG_REMOVE) != 0)
46224090Sdougb#define CREATE(x) (((x) & DNS_NSEC3FLAG_CREATE) != 0)
47254897Serwin#define INITIAL(x) (((x) & DNS_NSEC3FLAG_INITIAL) != 0)
48224090Sdougb#define NONSEC(x) (((x) & DNS_NSEC3FLAG_NONSEC) != 0)
49224090Sdougb
50224090Sdougb#define CHECK(x) do {					\
51224090Sdougb			 result = (x);			\
52224090Sdougb			 if (result != ISC_R_SUCCESS)	\
53224090Sdougb				goto failure;		\
54224090Sdougb		 } while (0)
55224090Sdougb
56224090Sdougb/*
57224090Sdougb * Work out if 'param' should be ignored or not (i.e. it is in the process
58224090Sdougb * of being removed).
59224090Sdougb *
60224090Sdougb * Note: we 'belt-and-braces' here by also checking for a CREATE private
61224090Sdougb * record and keep the param record in this case.
62224090Sdougb */
63224090Sdougb
64224090Sdougbstatic isc_boolean_t
65224090Sdougbignore(dns_rdata_t *param, dns_rdataset_t *privateset) {
66224090Sdougb	isc_result_t result;
67224090Sdougb
68224090Sdougb	for (result = dns_rdataset_first(privateset);
69224090Sdougb	     result == ISC_R_SUCCESS;
70224090Sdougb	     result = dns_rdataset_next(privateset)) {
71224090Sdougb		unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
72224090Sdougb		dns_rdata_t private = DNS_RDATA_INIT;
73224090Sdougb		dns_rdata_t rdata = DNS_RDATA_INIT;
74224090Sdougb
75224090Sdougb		dns_rdataset_current(privateset, &private);
76224090Sdougb		if (!dns_nsec3param_fromprivate(&private, &rdata,
77224090Sdougb						buf, sizeof(buf)))
78224090Sdougb			continue;
79224090Sdougb		/*
80224090Sdougb		 * We are going to create a new NSEC3 chain so it
81224090Sdougb		 * doesn't matter if we are removing this one.
82224090Sdougb		 */
83224090Sdougb		if (CREATE(rdata.data[1]))
84224090Sdougb			return (ISC_FALSE);
85224090Sdougb		if (rdata.data[0] != param->data[0] ||
86224090Sdougb		    rdata.data[2] != param->data[2] ||
87224090Sdougb		    rdata.data[3] != param->data[3] ||
88224090Sdougb		    rdata.data[4] != param->data[4] ||
89224090Sdougb		    memcmp(&rdata.data[5], &param->data[5], param->data[4]))
90224090Sdougb			continue;
91224090Sdougb		/*
92224090Sdougb		 * The removal of this NSEC3 chain does NOT cause a
93224090Sdougb		 * NSEC chain to be created so we don't need to tell
94224090Sdougb		 * the caller that it will be removed.
95224090Sdougb		 */
96224090Sdougb		if (NONSEC(rdata.data[1]))
97224090Sdougb			return (ISC_FALSE);
98224090Sdougb		return (ISC_TRUE);
99224090Sdougb	}
100224090Sdougb	return (ISC_FALSE);
101224090Sdougb}
102224090Sdougb
103224090Sdougbisc_result_t
104224090Sdougbdns_private_chains(dns_db_t *db, dns_dbversion_t *ver,
105224090Sdougb		   dns_rdatatype_t privatetype,
106224090Sdougb		   isc_boolean_t *build_nsec, isc_boolean_t *build_nsec3)
107224090Sdougb{
108224090Sdougb	dns_dbnode_t *node;
109224090Sdougb	dns_rdataset_t nsecset, nsec3paramset, privateset;
110224090Sdougb	isc_boolean_t nsec3chain;
111224090Sdougb	isc_boolean_t signing;
112224090Sdougb	isc_result_t result;
113224090Sdougb	unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
114224090Sdougb	unsigned int count;
115224090Sdougb
116224090Sdougb	node = NULL;
117224090Sdougb	dns_rdataset_init(&nsecset);
118224090Sdougb	dns_rdataset_init(&nsec3paramset);
119224090Sdougb	dns_rdataset_init(&privateset);
120224090Sdougb
121224090Sdougb	CHECK(dns_db_getoriginnode(db, &node));
122224090Sdougb
123224090Sdougb	result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec,
124224090Sdougb				     0, (isc_stdtime_t) 0, &nsecset, NULL);
125224090Sdougb
126224090Sdougb	if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
127224090Sdougb		goto failure;
128224090Sdougb
129224090Sdougb	result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param,
130224090Sdougb				     0, (isc_stdtime_t) 0, &nsec3paramset,
131224090Sdougb				     NULL);
132224090Sdougb	if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
133224090Sdougb		goto failure;
134224090Sdougb
135224090Sdougb	if (dns_rdataset_isassociated(&nsecset) &&
136224090Sdougb	    dns_rdataset_isassociated(&nsec3paramset)) {
137224090Sdougb		if (build_nsec != NULL)
138224090Sdougb			*build_nsec = ISC_TRUE;
139224090Sdougb		if (build_nsec3 != NULL)
140224090Sdougb			*build_nsec3 = ISC_TRUE;
141224090Sdougb		goto success;
142224090Sdougb	}
143224090Sdougb
144224090Sdougb	if (privatetype != (dns_rdatatype_t)0) {
145224090Sdougb		result = dns_db_findrdataset(db, node, ver, privatetype,
146224090Sdougb					     0, (isc_stdtime_t) 0,
147224090Sdougb					     &privateset, NULL);
148224090Sdougb		if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
149224090Sdougb			goto failure;
150224090Sdougb	}
151224090Sdougb
152224090Sdougb	/*
153254897Serwin	 * Look to see if we also need to be creating a NSEC3 chain.
154224090Sdougb	 */
155224090Sdougb	if (dns_rdataset_isassociated(&nsecset)) {
156224090Sdougb		if (build_nsec != NULL)
157224090Sdougb			*build_nsec = ISC_TRUE;
158224090Sdougb		if (build_nsec3 != NULL)
159224090Sdougb			*build_nsec3 = ISC_FALSE;
160224090Sdougb		if (!dns_rdataset_isassociated(&privateset))
161224090Sdougb			goto success;
162224090Sdougb		for (result = dns_rdataset_first(&privateset);
163224090Sdougb		     result == ISC_R_SUCCESS;
164224090Sdougb		     result = dns_rdataset_next(&privateset)) {
165224090Sdougb			unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
166224090Sdougb			dns_rdata_t private = DNS_RDATA_INIT;
167224090Sdougb			dns_rdata_t rdata = DNS_RDATA_INIT;
168224090Sdougb
169224090Sdougb			dns_rdataset_current(&privateset, &private);
170224090Sdougb			if (!dns_nsec3param_fromprivate(&private, &rdata,
171224090Sdougb							buf, sizeof(buf)))
172224090Sdougb				continue;
173224090Sdougb			if (REMOVE(rdata.data[1]))
174224090Sdougb				continue;
175224090Sdougb			if (build_nsec3 != NULL)
176224090Sdougb				*build_nsec3 = ISC_TRUE;
177224090Sdougb			break;
178224090Sdougb		}
179224090Sdougb		goto success;
180224090Sdougb	}
181224090Sdougb
182224090Sdougb	if (dns_rdataset_isassociated(&nsec3paramset)) {
183224090Sdougb		if (build_nsec3 != NULL)
184224090Sdougb			*build_nsec3 = ISC_TRUE;
185224090Sdougb		if (build_nsec != NULL)
186224090Sdougb			*build_nsec = ISC_FALSE;
187224090Sdougb		if (!dns_rdataset_isassociated(&privateset))
188224090Sdougb			goto success;
189224090Sdougb		/*
190224090Sdougb		 * If we are in the process of building a new NSEC3 chain
191224090Sdougb		 * then we don't need to build a NSEC chain.
192224090Sdougb		 */
193224090Sdougb		for (result = dns_rdataset_first(&privateset);
194224090Sdougb		     result == ISC_R_SUCCESS;
195224090Sdougb		     result = dns_rdataset_next(&privateset)) {
196224090Sdougb			dns_rdata_t private = DNS_RDATA_INIT;
197224090Sdougb			dns_rdata_t rdata = DNS_RDATA_INIT;
198224090Sdougb
199224090Sdougb			dns_rdataset_current(&privateset, &private);
200224090Sdougb			if (!dns_nsec3param_fromprivate(&private, &rdata,
201224090Sdougb							buf, sizeof(buf)))
202224090Sdougb				continue;
203224090Sdougb			if (CREATE(rdata.data[1]))
204224090Sdougb				goto success;
205224090Sdougb		}
206224090Sdougb
207224090Sdougb		/*
208224090Sdougb		 * Check to see if there will be a active NSEC3CHAIN once
209224090Sdougb		 * the changes queued complete.
210224090Sdougb		 */
211224090Sdougb		count = 0;
212224090Sdougb		for (result = dns_rdataset_first(&nsec3paramset);
213224090Sdougb		     result == ISC_R_SUCCESS;
214224090Sdougb		     result = dns_rdataset_next(&nsec3paramset)) {
215224090Sdougb			dns_rdata_t rdata = DNS_RDATA_INIT;
216224090Sdougb
217224090Sdougb			/*
218224090Sdougb			 * If there is more that one NSEC3 chain present then
219224090Sdougb			 * we don't need to construct a NSEC chain.
220224090Sdougb			 */
221224090Sdougb			if (++count > 1)
222224090Sdougb				goto success;
223224090Sdougb			dns_rdataset_current(&nsec3paramset, &rdata);
224224090Sdougb			if (ignore(&rdata, &privateset))
225224090Sdougb				continue;
226224090Sdougb			/*
227224090Sdougb			 * We still have a good NSEC3 chain or we are
228224090Sdougb			 * not creating a NSEC chain as NONSEC is set.
229224090Sdougb			 */
230224090Sdougb			goto success;
231224090Sdougb		}
232224090Sdougb
233224090Sdougb		/*
234224090Sdougb		 * The last NSEC3 chain is being removed and does not have
235224090Sdougb		 * have NONSEC set.
236224090Sdougb		 */
237224090Sdougb		if (build_nsec != NULL)
238224090Sdougb			*build_nsec = ISC_TRUE;
239224090Sdougb		goto success;
240224090Sdougb	}
241224090Sdougb
242224090Sdougb	if (build_nsec != NULL)
243224090Sdougb		*build_nsec = ISC_FALSE;
244224090Sdougb	if (build_nsec3 != NULL)
245224090Sdougb		*build_nsec3 = ISC_FALSE;
246224090Sdougb	if (!dns_rdataset_isassociated(&privateset))
247224090Sdougb		goto success;
248224090Sdougb
249224090Sdougb	signing = ISC_FALSE;
250224090Sdougb	nsec3chain = ISC_FALSE;
251224090Sdougb
252224090Sdougb	for (result = dns_rdataset_first(&privateset);
253224090Sdougb	     result == ISC_R_SUCCESS;
254224090Sdougb	     result = dns_rdataset_next(&privateset)) {
255224090Sdougb		dns_rdata_t rdata = DNS_RDATA_INIT;
256224090Sdougb		dns_rdata_t private = DNS_RDATA_INIT;
257224090Sdougb
258224090Sdougb		dns_rdataset_current(&privateset, &private);
259224090Sdougb		if (!dns_nsec3param_fromprivate(&private, &rdata,
260224090Sdougb						buf, sizeof(buf))) {
261224090Sdougb			/*
262224090Sdougb			 * Look for record that says we are signing the
263224090Sdougb			 * zone with a key.
264224090Sdougb			 */
265224090Sdougb			if (private.length == 5 && private.data[0] != 0 &&
266224090Sdougb			    private.data[3] == 0 && private.data[4] == 0)
267224090Sdougb				signing = ISC_TRUE;
268224090Sdougb		} else {
269224090Sdougb			if (CREATE(rdata.data[1]))
270224090Sdougb				nsec3chain = ISC_TRUE;
271224090Sdougb		}
272224090Sdougb	}
273224090Sdougb
274224090Sdougb	if (signing) {
275224090Sdougb		if (nsec3chain) {
276224090Sdougb			if (build_nsec3 != NULL)
277224090Sdougb				*build_nsec3 = ISC_TRUE;
278224090Sdougb		} else {
279224090Sdougb			if (build_nsec != NULL)
280224090Sdougb				*build_nsec = ISC_TRUE;
281224090Sdougb		}
282224090Sdougb	}
283224090Sdougb
284224090Sdougb success:
285224090Sdougb	result = ISC_R_SUCCESS;
286224090Sdougb failure:
287224090Sdougb	if (dns_rdataset_isassociated(&nsecset))
288224090Sdougb		dns_rdataset_disassociate(&nsecset);
289224090Sdougb	if (dns_rdataset_isassociated(&nsec3paramset))
290224090Sdougb		dns_rdataset_disassociate(&nsec3paramset);
291224090Sdougb	if (dns_rdataset_isassociated(&privateset))
292224090Sdougb		dns_rdataset_disassociate(&privateset);
293224090Sdougb	if (node != NULL)
294224090Sdougb		dns_db_detachnode(db, &node);
295224090Sdougb	return (result);
296224090Sdougb}
297254897Serwin
298254897Serwinisc_result_t
299254897Serwindns_private_totext(dns_rdata_t *private, isc_buffer_t *buf) {
300254897Serwin	isc_result_t result;
301254897Serwin
302254897Serwin	if (private->length < 5)
303254897Serwin		return (ISC_R_NOTFOUND);
304254897Serwin
305254897Serwin	if (private->data[0] == 0) {
306254897Serwin		unsigned char nsec3buf[DNS_NSEC3PARAM_BUFFERSIZE];
307254897Serwin		unsigned char newbuf[DNS_NSEC3PARAM_BUFFERSIZE];
308254897Serwin		dns_rdata_t rdata = DNS_RDATA_INIT;
309254897Serwin		dns_rdata_nsec3param_t nsec3param;
310254897Serwin		isc_boolean_t remove, init, nonsec;
311254897Serwin		isc_buffer_t b;
312254897Serwin
313254897Serwin		if (!dns_nsec3param_fromprivate(private, &rdata, nsec3buf,
314254897Serwin						sizeof(nsec3buf)))
315254897Serwin			CHECK(ISC_R_FAILURE);
316254897Serwin
317254897Serwin		CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL));
318254897Serwin
319254897Serwin		remove = ISC_TF((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0);
320254897Serwin		init = ISC_TF((nsec3param.flags & DNS_NSEC3FLAG_INITIAL) != 0);
321254897Serwin		nonsec = ISC_TF((nsec3param.flags & DNS_NSEC3FLAG_NONSEC) != 0);
322254897Serwin
323254897Serwin		nsec3param.flags &= ~(DNS_NSEC3FLAG_CREATE|
324254897Serwin				      DNS_NSEC3FLAG_REMOVE|
325254897Serwin				      DNS_NSEC3FLAG_INITIAL|
326254897Serwin				      DNS_NSEC3FLAG_NONSEC);
327254897Serwin
328254897Serwin		if (init)
329254897Serwin			isc_buffer_putstr(buf, "Pending NSEC3 chain ");
330254897Serwin		else if (remove)
331254897Serwin			isc_buffer_putstr(buf, "Removing NSEC3 chain ");
332254897Serwin		else
333254897Serwin			isc_buffer_putstr(buf, "Creating NSEC3 chain ");
334254897Serwin
335254897Serwin		dns_rdata_reset(&rdata);
336254897Serwin		isc_buffer_init(&b, newbuf, sizeof(newbuf));
337254897Serwin		CHECK(dns_rdata_fromstruct(&rdata, dns_rdataclass_in,
338254897Serwin					   dns_rdatatype_nsec3param,
339254897Serwin					   &nsec3param, &b));
340254897Serwin
341254897Serwin		CHECK(dns_rdata_totext(&rdata, NULL, buf));
342254897Serwin
343254897Serwin		if (remove && !nonsec)
344254897Serwin			isc_buffer_putstr(buf, " / creating NSEC chain");
345254897Serwin	} else if (private->length == 5) {
346254897Serwin		unsigned char alg = private->data[0];
347254897Serwin		dns_keytag_t keyid = (private->data[2] | private->data[1] << 8);
348254897Serwin		char keybuf[BUFSIZ], algbuf[DNS_SECALG_FORMATSIZE];
349254897Serwin		isc_boolean_t remove = ISC_TF(private->data[3] != 0);
350254897Serwin		isc_boolean_t complete = ISC_TF(private->data[4] != 0);
351254897Serwin
352254897Serwin		if (remove && complete)
353254897Serwin			isc_buffer_putstr(buf, "Done removing signatures for ");
354254897Serwin		else if (remove)
355254897Serwin			isc_buffer_putstr(buf, "Removing signatures for ");
356254897Serwin		else if (complete)
357254897Serwin			isc_buffer_putstr(buf, "Done signing with ");
358254897Serwin		else
359254897Serwin			isc_buffer_putstr(buf, "Signing with ");
360254897Serwin
361254897Serwin		dns_secalg_format(alg, algbuf, sizeof(algbuf));
362254897Serwin		sprintf(keybuf, "key %d/%s", keyid, algbuf);
363254897Serwin		isc_buffer_putstr(buf, keybuf);
364254897Serwin	} else
365254897Serwin		return (ISC_R_NOTFOUND);
366254897Serwin
367254897Serwin	isc_buffer_putuint8(buf, 0);
368254897Serwin	result = ISC_R_SUCCESS;
369254897Serwin failure:
370254897Serwin	return (result);
371254897Serwin}
372