1 1.10 christos /* $NetBSD: nsec3_50.c,v 1.11 2026/01/29 18:37:52 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.8 christos * SPDX-License-Identifier: MPL-2.0 7 1.8 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.6 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.1 christos /* 17 1.1 christos * Copyright (C) 2004 Nominet, Ltd. 18 1.1 christos * 19 1.1 christos * Permission to use, copy, modify, and distribute this software for any 20 1.1 christos * purpose with or without fee is hereby granted, provided that the above 21 1.1 christos * copyright notice and this permission notice appear in all copies. 22 1.1 christos * 23 1.1 christos * THE SOFTWARE IS PROVIDED "AS IS" AND NOMINET DISCLAIMS ALL WARRANTIES WITH 24 1.1 christos * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 25 1.1 christos * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 26 1.1 christos * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 27 1.1 christos * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 28 1.1 christos * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 29 1.1 christos * PERFORMANCE OF THIS SOFTWARE. 30 1.1 christos */ 31 1.1 christos 32 1.1 christos /* RFC 5155 */ 33 1.1 christos 34 1.1 christos #ifndef RDATA_GENERIC_NSEC3_50_C 35 1.1 christos #define RDATA_GENERIC_NSEC3_50_C 36 1.1 christos 37 1.5 christos #include <isc/base32.h> 38 1.1 christos #include <isc/iterated_hash.h> 39 1.1 christos 40 1.1 christos #define RRTYPE_NSEC3_ATTRIBUTES DNS_RDATATYPEATTR_DNSSEC 41 1.1 christos 42 1.8 christos static isc_result_t 43 1.1 christos fromtext_nsec3(ARGS_FROMTEXT) { 44 1.1 christos isc_token_t token; 45 1.1 christos unsigned int flags; 46 1.1 christos unsigned char hashalg; 47 1.1 christos isc_buffer_t b; 48 1.1 christos unsigned char buf[256]; 49 1.1 christos 50 1.1 christos REQUIRE(type == dns_rdatatype_nsec3); 51 1.1 christos 52 1.1 christos UNUSED(type); 53 1.1 christos UNUSED(rdclass); 54 1.1 christos UNUSED(callbacks); 55 1.1 christos UNUSED(origin); 56 1.1 christos UNUSED(options); 57 1.1 christos 58 1.1 christos /* Hash. */ 59 1.1 christos RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string, 60 1.3 christos false)); 61 1.1 christos RETTOK(dns_hashalg_fromtext(&hashalg, &token.value.as_textregion)); 62 1.1 christos RETERR(uint8_tobuffer(hashalg, target)); 63 1.1 christos 64 1.1 christos /* Flags. */ 65 1.1 christos RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number, 66 1.3 christos false)); 67 1.1 christos flags = token.value.as_ulong; 68 1.5 christos if (flags > 255U) { 69 1.1 christos RETTOK(ISC_R_RANGE); 70 1.5 christos } 71 1.1 christos RETERR(uint8_tobuffer(flags, target)); 72 1.1 christos 73 1.1 christos /* Iterations. */ 74 1.1 christos RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_number, 75 1.3 christos false)); 76 1.5 christos if (token.value.as_ulong > 0xffffU) { 77 1.1 christos RETTOK(ISC_R_RANGE); 78 1.5 christos } 79 1.1 christos RETERR(uint16_tobuffer(token.value.as_ulong, target)); 80 1.1 christos 81 1.1 christos /* salt */ 82 1.1 christos RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string, 83 1.3 christos false)); 84 1.5 christos if (token.value.as_textregion.length > (255 * 2)) { 85 1.1 christos RETTOK(DNS_R_TEXTTOOLONG); 86 1.5 christos } 87 1.1 christos if (strcmp(DNS_AS_STR(token), "-") == 0) { 88 1.1 christos RETERR(uint8_tobuffer(0, target)); 89 1.1 christos } else { 90 1.1 christos RETERR(uint8_tobuffer(strlen(DNS_AS_STR(token)) / 2, target)); 91 1.1 christos RETERR(isc_hex_decodestring(DNS_AS_STR(token), target)); 92 1.1 christos } 93 1.1 christos 94 1.1 christos /* 95 1.1 christos * Next hash a single base32hex word. 96 1.1 christos */ 97 1.1 christos RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string, 98 1.3 christos false)); 99 1.1 christos isc_buffer_init(&b, buf, sizeof(buf)); 100 1.1 christos RETTOK(isc_base32hexnp_decodestring(DNS_AS_STR(token), &b)); 101 1.5 christos if (isc_buffer_usedlength(&b) > 0xffU) { 102 1.1 christos RETTOK(ISC_R_RANGE); 103 1.5 christos } 104 1.1 christos RETERR(uint8_tobuffer(isc_buffer_usedlength(&b), target)); 105 1.1 christos RETERR(mem_tobuffer(target, &buf, isc_buffer_usedlength(&b))); 106 1.1 christos 107 1.10 christos return typemap_fromtext(lexer, target, true); 108 1.1 christos } 109 1.1 christos 110 1.8 christos static isc_result_t 111 1.1 christos totext_nsec3(ARGS_TOTEXT) { 112 1.1 christos isc_region_t sr; 113 1.1 christos unsigned int i, j; 114 1.1 christos unsigned char hash; 115 1.1 christos unsigned char flags; 116 1.1 christos char buf[sizeof("TYPE65535")]; 117 1.3 christos uint32_t iterations; 118 1.1 christos 119 1.1 christos REQUIRE(rdata->type == dns_rdatatype_nsec3); 120 1.1 christos REQUIRE(rdata->length != 0); 121 1.1 christos 122 1.1 christos dns_rdata_toregion(rdata, &sr); 123 1.1 christos 124 1.1 christos /* Hash */ 125 1.1 christos hash = uint8_fromregion(&sr); 126 1.1 christos isc_region_consume(&sr, 1); 127 1.1 christos snprintf(buf, sizeof(buf), "%u ", hash); 128 1.1 christos RETERR(str_totext(buf, target)); 129 1.1 christos 130 1.1 christos /* Flags */ 131 1.1 christos flags = uint8_fromregion(&sr); 132 1.1 christos isc_region_consume(&sr, 1); 133 1.1 christos snprintf(buf, sizeof(buf), "%u ", flags); 134 1.1 christos RETERR(str_totext(buf, target)); 135 1.1 christos 136 1.1 christos /* Iterations */ 137 1.1 christos iterations = uint16_fromregion(&sr); 138 1.1 christos isc_region_consume(&sr, 2); 139 1.1 christos snprintf(buf, sizeof(buf), "%u ", iterations); 140 1.1 christos RETERR(str_totext(buf, target)); 141 1.1 christos 142 1.1 christos /* Salt */ 143 1.1 christos j = uint8_fromregion(&sr); 144 1.1 christos isc_region_consume(&sr, 1); 145 1.1 christos INSIST(j <= sr.length); 146 1.1 christos 147 1.1 christos if (j != 0) { 148 1.1 christos i = sr.length; 149 1.1 christos sr.length = j; 150 1.1 christos RETERR(isc_hex_totext(&sr, 1, "", target)); 151 1.1 christos sr.length = i - j; 152 1.5 christos } else { 153 1.1 christos RETERR(str_totext("-", target)); 154 1.5 christos } 155 1.1 christos 156 1.5 christos if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) { 157 1.1 christos RETERR(str_totext(" (", target)); 158 1.5 christos } 159 1.1 christos RETERR(str_totext(tctx->linebreak, target)); 160 1.1 christos 161 1.1 christos /* Next hash */ 162 1.1 christos j = uint8_fromregion(&sr); 163 1.1 christos isc_region_consume(&sr, 1); 164 1.1 christos INSIST(j <= sr.length); 165 1.1 christos 166 1.1 christos i = sr.length; 167 1.1 christos sr.length = j; 168 1.1 christos RETERR(isc_base32hexnp_totext(&sr, 1, "", target)); 169 1.1 christos sr.length = i - j; 170 1.1 christos 171 1.1 christos /* 172 1.1 christos * Don't leave a trailing space when there's no typemap present. 173 1.1 christos */ 174 1.1 christos if (((tctx->flags & DNS_STYLEFLAG_MULTILINE) == 0) && (sr.length > 0)) { 175 1.1 christos RETERR(str_totext(" ", target)); 176 1.1 christos } 177 1.1 christos RETERR(typemap_totext(&sr, tctx, target)); 178 1.1 christos 179 1.5 christos if ((tctx->flags & DNS_STYLEFLAG_MULTILINE) != 0) { 180 1.1 christos RETERR(str_totext(" )", target)); 181 1.5 christos } 182 1.1 christos 183 1.10 christos return ISC_R_SUCCESS; 184 1.1 christos } 185 1.1 christos 186 1.8 christos static isc_result_t 187 1.1 christos fromwire_nsec3(ARGS_FROMWIRE) { 188 1.1 christos isc_region_t sr, rr; 189 1.1 christos unsigned int saltlen, hashlen; 190 1.1 christos 191 1.1 christos REQUIRE(type == dns_rdatatype_nsec3); 192 1.1 christos 193 1.1 christos UNUSED(type); 194 1.1 christos UNUSED(rdclass); 195 1.1 christos UNUSED(dctx); 196 1.1 christos 197 1.1 christos isc_buffer_activeregion(source, &sr); 198 1.1 christos rr = sr; 199 1.1 christos 200 1.1 christos /* hash(1), flags(1), iteration(2), saltlen(1) */ 201 1.5 christos if (sr.length < 5U) { 202 1.1 christos RETERR(DNS_R_FORMERR); 203 1.5 christos } 204 1.1 christos saltlen = sr.base[4]; 205 1.1 christos isc_region_consume(&sr, 5); 206 1.1 christos 207 1.5 christos if (sr.length < saltlen) { 208 1.1 christos RETERR(DNS_R_FORMERR); 209 1.5 christos } 210 1.1 christos isc_region_consume(&sr, saltlen); 211 1.1 christos 212 1.5 christos if (sr.length < 1U) { 213 1.1 christos RETERR(DNS_R_FORMERR); 214 1.5 christos } 215 1.1 christos hashlen = sr.base[0]; 216 1.1 christos isc_region_consume(&sr, 1); 217 1.1 christos 218 1.6 christos if (hashlen < 1 || sr.length < hashlen) { 219 1.1 christos RETERR(DNS_R_FORMERR); 220 1.5 christos } 221 1.1 christos isc_region_consume(&sr, hashlen); 222 1.1 christos 223 1.3 christos RETERR(typemap_test(&sr, true)); 224 1.1 christos 225 1.1 christos RETERR(mem_tobuffer(target, rr.base, rr.length)); 226 1.1 christos isc_buffer_forward(source, rr.length); 227 1.10 christos return ISC_R_SUCCESS; 228 1.1 christos } 229 1.1 christos 230 1.8 christos static isc_result_t 231 1.1 christos towire_nsec3(ARGS_TOWIRE) { 232 1.1 christos isc_region_t sr; 233 1.1 christos 234 1.1 christos REQUIRE(rdata->type == dns_rdatatype_nsec3); 235 1.1 christos REQUIRE(rdata->length != 0); 236 1.1 christos 237 1.1 christos UNUSED(cctx); 238 1.1 christos 239 1.1 christos dns_rdata_toregion(rdata, &sr); 240 1.10 christos return mem_tobuffer(target, sr.base, sr.length); 241 1.1 christos } 242 1.1 christos 243 1.8 christos static int 244 1.1 christos compare_nsec3(ARGS_COMPARE) { 245 1.1 christos isc_region_t r1; 246 1.1 christos isc_region_t r2; 247 1.1 christos 248 1.1 christos REQUIRE(rdata1->type == rdata2->type); 249 1.1 christos REQUIRE(rdata1->rdclass == rdata2->rdclass); 250 1.1 christos REQUIRE(rdata1->type == dns_rdatatype_nsec3); 251 1.1 christos REQUIRE(rdata1->length != 0); 252 1.1 christos REQUIRE(rdata2->length != 0); 253 1.1 christos 254 1.1 christos dns_rdata_toregion(rdata1, &r1); 255 1.1 christos dns_rdata_toregion(rdata2, &r2); 256 1.10 christos return isc_region_compare(&r1, &r2); 257 1.1 christos } 258 1.1 christos 259 1.8 christos static isc_result_t 260 1.1 christos fromstruct_nsec3(ARGS_FROMSTRUCT) { 261 1.1 christos dns_rdata_nsec3_t *nsec3 = source; 262 1.1 christos isc_region_t region; 263 1.1 christos 264 1.1 christos REQUIRE(type == dns_rdatatype_nsec3); 265 1.4 christos REQUIRE(nsec3 != NULL); 266 1.1 christos REQUIRE(nsec3->common.rdtype == type); 267 1.1 christos REQUIRE(nsec3->common.rdclass == rdclass); 268 1.1 christos REQUIRE(nsec3->typebits != NULL || nsec3->len == 0); 269 1.1 christos REQUIRE(nsec3->hash == dns_hash_sha1); 270 1.1 christos 271 1.1 christos UNUSED(type); 272 1.1 christos UNUSED(rdclass); 273 1.1 christos 274 1.1 christos RETERR(uint8_tobuffer(nsec3->hash, target)); 275 1.1 christos RETERR(uint8_tobuffer(nsec3->flags, target)); 276 1.1 christos RETERR(uint16_tobuffer(nsec3->iterations, target)); 277 1.1 christos RETERR(uint8_tobuffer(nsec3->salt_length, target)); 278 1.1 christos RETERR(mem_tobuffer(target, nsec3->salt, nsec3->salt_length)); 279 1.1 christos RETERR(uint8_tobuffer(nsec3->next_length, target)); 280 1.1 christos RETERR(mem_tobuffer(target, nsec3->next, nsec3->next_length)); 281 1.1 christos 282 1.1 christos region.base = nsec3->typebits; 283 1.1 christos region.length = nsec3->len; 284 1.3 christos RETERR(typemap_test(®ion, true)); 285 1.10 christos return mem_tobuffer(target, nsec3->typebits, nsec3->len); 286 1.1 christos } 287 1.1 christos 288 1.8 christos static isc_result_t 289 1.1 christos tostruct_nsec3(ARGS_TOSTRUCT) { 290 1.1 christos isc_region_t region; 291 1.1 christos dns_rdata_nsec3_t *nsec3 = target; 292 1.1 christos 293 1.1 christos REQUIRE(rdata->type == dns_rdatatype_nsec3); 294 1.4 christos REQUIRE(nsec3 != NULL); 295 1.1 christos REQUIRE(rdata->length != 0); 296 1.1 christos 297 1.11 christos DNS_RDATACOMMON_INIT(nsec3, rdata->type, rdata->rdclass); 298 1.1 christos 299 1.1 christos region.base = rdata->data; 300 1.1 christos region.length = rdata->length; 301 1.1 christos nsec3->hash = uint8_consume_fromregion(®ion); 302 1.1 christos nsec3->flags = uint8_consume_fromregion(®ion); 303 1.1 christos nsec3->iterations = uint16_consume_fromregion(®ion); 304 1.1 christos 305 1.1 christos nsec3->salt_length = uint8_consume_fromregion(®ion); 306 1.7 christos INSIST(nsec3->salt_length <= region.length); 307 1.1 christos nsec3->salt = mem_maybedup(mctx, region.base, nsec3->salt_length); 308 1.1 christos isc_region_consume(®ion, nsec3->salt_length); 309 1.1 christos 310 1.1 christos nsec3->next_length = uint8_consume_fromregion(®ion); 311 1.7 christos INSIST(nsec3->next_length <= region.length); 312 1.1 christos nsec3->next = mem_maybedup(mctx, region.base, nsec3->next_length); 313 1.1 christos isc_region_consume(®ion, nsec3->next_length); 314 1.1 christos 315 1.1 christos nsec3->len = region.length; 316 1.1 christos nsec3->typebits = mem_maybedup(mctx, region.base, region.length); 317 1.1 christos nsec3->mctx = mctx; 318 1.10 christos return ISC_R_SUCCESS; 319 1.1 christos } 320 1.1 christos 321 1.8 christos static void 322 1.1 christos freestruct_nsec3(ARGS_FREESTRUCT) { 323 1.1 christos dns_rdata_nsec3_t *nsec3 = source; 324 1.1 christos 325 1.4 christos REQUIRE(nsec3 != NULL); 326 1.1 christos REQUIRE(nsec3->common.rdtype == dns_rdatatype_nsec3); 327 1.1 christos 328 1.5 christos if (nsec3->mctx == NULL) { 329 1.1 christos return; 330 1.5 christos } 331 1.1 christos 332 1.5 christos if (nsec3->salt != NULL) { 333 1.1 christos isc_mem_free(nsec3->mctx, nsec3->salt); 334 1.5 christos } 335 1.5 christos if (nsec3->next != NULL) { 336 1.1 christos isc_mem_free(nsec3->mctx, nsec3->next); 337 1.5 christos } 338 1.5 christos if (nsec3->typebits != NULL) { 339 1.1 christos isc_mem_free(nsec3->mctx, nsec3->typebits); 340 1.5 christos } 341 1.1 christos nsec3->mctx = NULL; 342 1.1 christos } 343 1.1 christos 344 1.8 christos static isc_result_t 345 1.1 christos additionaldata_nsec3(ARGS_ADDLDATA) { 346 1.1 christos REQUIRE(rdata->type == dns_rdatatype_nsec3); 347 1.1 christos 348 1.1 christos UNUSED(rdata); 349 1.9 christos UNUSED(owner); 350 1.1 christos UNUSED(add); 351 1.1 christos UNUSED(arg); 352 1.1 christos 353 1.10 christos return ISC_R_SUCCESS; 354 1.1 christos } 355 1.1 christos 356 1.8 christos static isc_result_t 357 1.1 christos digest_nsec3(ARGS_DIGEST) { 358 1.1 christos isc_region_t r; 359 1.1 christos 360 1.1 christos REQUIRE(rdata->type == dns_rdatatype_nsec3); 361 1.1 christos 362 1.1 christos dns_rdata_toregion(rdata, &r); 363 1.10 christos return (digest)(arg, &r); 364 1.1 christos } 365 1.1 christos 366 1.8 christos static bool 367 1.1 christos checkowner_nsec3(ARGS_CHECKOWNER) { 368 1.1 christos unsigned char owner[NSEC3_MAX_HASH_LENGTH]; 369 1.1 christos isc_buffer_t buffer; 370 1.1 christos dns_label_t label; 371 1.1 christos 372 1.1 christos REQUIRE(type == dns_rdatatype_nsec3); 373 1.1 christos 374 1.1 christos UNUSED(type); 375 1.1 christos UNUSED(rdclass); 376 1.1 christos UNUSED(wildcard); 377 1.1 christos 378 1.1 christos /* 379 1.1 christos * First label is a base32hex string without padding. 380 1.1 christos */ 381 1.1 christos dns_name_getlabel(name, 0, &label); 382 1.1 christos isc_region_consume(&label, 1); 383 1.1 christos isc_buffer_init(&buffer, owner, sizeof(owner)); 384 1.5 christos if (isc_base32hexnp_decoderegion(&label, &buffer) == ISC_R_SUCCESS) { 385 1.10 christos return true; 386 1.5 christos } 387 1.1 christos 388 1.10 christos return false; 389 1.1 christos } 390 1.1 christos 391 1.8 christos static bool 392 1.1 christos checknames_nsec3(ARGS_CHECKNAMES) { 393 1.1 christos REQUIRE(rdata->type == dns_rdatatype_nsec3); 394 1.1 christos 395 1.1 christos UNUSED(rdata); 396 1.1 christos UNUSED(owner); 397 1.1 christos UNUSED(bad); 398 1.1 christos 399 1.10 christos return true; 400 1.1 christos } 401 1.1 christos 402 1.8 christos static int 403 1.1 christos casecompare_nsec3(ARGS_COMPARE) { 404 1.10 christos return compare_nsec3(rdata1, rdata2); 405 1.1 christos } 406 1.1 christos 407 1.5 christos #endif /* RDATA_GENERIC_NSEC3_50_C */ 408