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], ¶m->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