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