1 /* $NetBSD: rcode.c,v 1.6 2019/11/27 05:48:41 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * This Source Code Form is subject to the terms of the Mozilla Public 7 * License, v. 2.0. If a copy of the MPL was not distributed with this 8 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * See the COPYRIGHT file distributed with this work for additional 11 * information regarding copyright ownership. 12 */ 13 14 15 #include <config.h> 16 17 #include <ctype.h> 18 #include <inttypes.h> 19 #include <stdbool.h> 20 #include <stdlib.h> 21 22 #include <isc/buffer.h> 23 #include <isc/parseint.h> 24 #include <isc/print.h> 25 #include <isc/region.h> 26 #include <isc/result.h> 27 #include <isc/stdio.h> 28 #include <isc/string.h> 29 #include <isc/types.h> 30 #include <isc/util.h> 31 32 #include <pk11/site.h> 33 34 #include <dns/cert.h> 35 #include <dns/ds.h> 36 #include <dns/dsdigest.h> 37 #include <dns/keyflags.h> 38 #include <dns/keyvalues.h> 39 #include <dns/rcode.h> 40 #include <dns/rdataclass.h> 41 #include <dns/result.h> 42 #include <dns/secalg.h> 43 #include <dns/secproto.h> 44 45 #define RETERR(x) \ 46 do { \ 47 isc_result_t _r = (x); \ 48 if (_r != ISC_R_SUCCESS) \ 49 return (_r); \ 50 } while (/*CONSTCOND*/0) 51 52 #define NUMBERSIZE sizeof("037777777777") /* 2^32-1 octal + NUL */ 53 54 #define TOTEXTONLY 0x01 55 56 #define RCODENAMES \ 57 /* standard rcodes */ \ 58 { dns_rcode_noerror, "NOERROR", 0}, \ 59 { dns_rcode_formerr, "FORMERR", 0}, \ 60 { dns_rcode_servfail, "SERVFAIL", 0}, \ 61 { dns_rcode_nxdomain, "NXDOMAIN", 0}, \ 62 { dns_rcode_notimp, "NOTIMP", 0}, \ 63 { dns_rcode_refused, "REFUSED", 0}, \ 64 { dns_rcode_yxdomain, "YXDOMAIN", 0}, \ 65 { dns_rcode_yxrrset, "YXRRSET", 0}, \ 66 { dns_rcode_nxrrset, "NXRRSET", 0}, \ 67 { dns_rcode_notauth, "NOTAUTH", 0}, \ 68 { dns_rcode_notzone, "NOTZONE", 0}, \ 69 { 11, "RESERVED11", TOTEXTONLY}, \ 70 { 12, "RESERVED12", TOTEXTONLY}, \ 71 { 13, "RESERVED13", TOTEXTONLY}, \ 72 { 14, "RESERVED14", TOTEXTONLY}, \ 73 { 15, "RESERVED15", TOTEXTONLY}, 74 75 #define ERCODENAMES \ 76 /* extended rcodes */ \ 77 { dns_rcode_badvers, "BADVERS", 0}, \ 78 { dns_rcode_badcookie, "BADCOOKIE", 0}, \ 79 { 0, NULL, 0 } 80 81 #define TSIGRCODENAMES \ 82 /* extended rcodes */ \ 83 { dns_tsigerror_badsig, "BADSIG", 0}, \ 84 { dns_tsigerror_badkey, "BADKEY", 0}, \ 85 { dns_tsigerror_badtime, "BADTIME", 0}, \ 86 { dns_tsigerror_badmode, "BADMODE", 0}, \ 87 { dns_tsigerror_badname, "BADNAME", 0}, \ 88 { dns_tsigerror_badalg, "BADALG", 0}, \ 89 { dns_tsigerror_badtrunc, "BADTRUNC", 0}, \ 90 { 0, NULL, 0 } 91 92 /* RFC4398 section 2.1 */ 93 94 #define CERTNAMES \ 95 { 1, "PKIX", 0}, \ 96 { 2, "SPKI", 0}, \ 97 { 3, "PGP", 0}, \ 98 { 4, "IPKIX", 0}, \ 99 { 5, "ISPKI", 0}, \ 100 { 6, "IPGP", 0}, \ 101 { 7, "ACPKIX", 0}, \ 102 { 8, "IACPKIX", 0}, \ 103 { 253, "URI", 0}, \ 104 { 254, "OID", 0}, \ 105 { 0, NULL, 0} 106 107 /* RFC2535 section 7, RFC3110 */ 108 109 #define SECALGNAMES \ 110 { DNS_KEYALG_RSAMD5, "RSAMD5", 0 }, \ 111 { DNS_KEYALG_DH, "DH", 0 }, \ 112 { DNS_KEYALG_DSA, "DSA", 0 }, \ 113 { DNS_KEYALG_RSASHA1, "RSASHA1", 0 }, \ 114 { DNS_KEYALG_NSEC3DSA, "NSEC3DSA", 0 }, \ 115 { DNS_KEYALG_NSEC3RSASHA1, "NSEC3RSASHA1", 0 }, \ 116 { DNS_KEYALG_RSASHA256, "RSASHA256", 0 }, \ 117 { DNS_KEYALG_RSASHA512, "RSASHA512", 0 }, \ 118 { DNS_KEYALG_ECCGOST, "ECCGOST", 0 }, \ 119 { DNS_KEYALG_ECDSA256, "ECDSAP256SHA256", 0 }, \ 120 { DNS_KEYALG_ECDSA256, "ECDSA256", 0 }, \ 121 { DNS_KEYALG_ECDSA384, "ECDSAP384SHA384", 0 }, \ 122 { DNS_KEYALG_ECDSA384, "ECDSA384", 0 }, \ 123 { DNS_KEYALG_ED25519, "ED25519", 0 }, \ 124 { DNS_KEYALG_ED448, "ED448", 0 }, \ 125 { DNS_KEYALG_INDIRECT, "INDIRECT", 0 }, \ 126 { DNS_KEYALG_PRIVATEDNS, "PRIVATEDNS", 0 }, \ 127 { DNS_KEYALG_PRIVATEOID, "PRIVATEOID", 0 }, \ 128 { 0, NULL, 0} 129 130 /* RFC2535 section 7.1 */ 131 132 #define SECPROTONAMES \ 133 { 0, "NONE", 0 }, \ 134 { 1, "TLS", 0 }, \ 135 { 2, "EMAIL", 0 }, \ 136 { 3, "DNSSEC", 0 }, \ 137 { 4, "IPSEC", 0 }, \ 138 { 255, "ALL", 0 }, \ 139 { 0, NULL, 0} 140 141 #define HASHALGNAMES \ 142 { 1, "SHA-1", 0 }, \ 143 { 0, NULL, 0 } 144 145 /* RFC3658, RFC4509, RFC5933, RFC6605 */ 146 147 #define DSDIGESTNAMES \ 148 { DNS_DSDIGEST_SHA1, "SHA-1", 0 }, \ 149 { DNS_DSDIGEST_SHA256, "SHA-256", 0 }, \ 150 { DNS_DSDIGEST_GOST, "GOST", 0 }, \ 151 { DNS_DSDIGEST_SHA384, "SHA-384", 0 }, \ 152 { 0, NULL, 0} 153 154 struct tbl { 155 unsigned int value; 156 const char *name; 157 int flags; 158 }; 159 160 static struct tbl rcodes[] = { RCODENAMES ERCODENAMES }; 161 static struct tbl tsigrcodes[] = { RCODENAMES TSIGRCODENAMES }; 162 static struct tbl certs[] = { CERTNAMES }; 163 static struct tbl secalgs[] = { SECALGNAMES }; 164 static struct tbl secprotos[] = { SECPROTONAMES }; 165 static struct tbl hashalgs[] = { HASHALGNAMES }; 166 static struct tbl dsdigests[] = { DSDIGESTNAMES }; 167 168 static struct keyflag { 169 const char *name; 170 unsigned int value; 171 unsigned int mask; 172 } keyflags[] = { 173 { "NOCONF", 0x4000, 0xC000 }, 174 { "NOAUTH", 0x8000, 0xC000 }, 175 { "NOKEY", 0xC000, 0xC000 }, 176 { "FLAG2", 0x2000, 0x2000 }, 177 { "EXTEND", 0x1000, 0x1000 }, 178 { "FLAG4", 0x0800, 0x0800 }, 179 { "FLAG5", 0x0400, 0x0400 }, 180 { "USER", 0x0000, 0x0300 }, 181 { "ZONE", 0x0100, 0x0300 }, 182 { "HOST", 0x0200, 0x0300 }, 183 { "NTYP3", 0x0300, 0x0300 }, 184 { "FLAG8", 0x0080, 0x0080 }, 185 { "FLAG9", 0x0040, 0x0040 }, 186 { "FLAG10", 0x0020, 0x0020 }, 187 { "FLAG11", 0x0010, 0x0010 }, 188 { "SIG0", 0x0000, 0x000F }, 189 { "SIG1", 0x0001, 0x000F }, 190 { "SIG2", 0x0002, 0x000F }, 191 { "SIG3", 0x0003, 0x000F }, 192 { "SIG4", 0x0004, 0x000F }, 193 { "SIG5", 0x0005, 0x000F }, 194 { "SIG6", 0x0006, 0x000F }, 195 { "SIG7", 0x0007, 0x000F }, 196 { "SIG8", 0x0008, 0x000F }, 197 { "SIG9", 0x0009, 0x000F }, 198 { "SIG10", 0x000A, 0x000F }, 199 { "SIG11", 0x000B, 0x000F }, 200 { "SIG12", 0x000C, 0x000F }, 201 { "SIG13", 0x000D, 0x000F }, 202 { "SIG14", 0x000E, 0x000F }, 203 { "SIG15", 0x000F, 0x000F }, 204 { "KSK", DNS_KEYFLAG_KSK, DNS_KEYFLAG_KSK }, 205 { NULL, 0, 0 } 206 }; 207 208 static isc_result_t 209 str_totext(const char *source, isc_buffer_t *target) { 210 unsigned int l; 211 isc_region_t region; 212 213 isc_buffer_availableregion(target, ®ion); 214 l = strlen(source); 215 216 if (l > region.length) 217 return (ISC_R_NOSPACE); 218 219 memmove(region.base, source, l); 220 isc_buffer_add(target, l); 221 return (ISC_R_SUCCESS); 222 } 223 224 static isc_result_t 225 maybe_numeric(unsigned int *valuep, isc_textregion_t *source, 226 unsigned int max, bool hex_allowed) 227 { 228 isc_result_t result; 229 uint32_t n; 230 char buffer[NUMBERSIZE]; 231 int v; 232 233 if (! isdigit(source->base[0] & 0xff) || 234 source->length > NUMBERSIZE - 1) 235 { 236 return (ISC_R_BADNUMBER); 237 } 238 239 /* 240 * We have a potential number. Try to parse it with 241 * isc_parse_uint32(). isc_parse_uint32() requires 242 * null termination, so we must make a copy. 243 */ 244 v = snprintf(buffer, sizeof(buffer), "%.*s", 245 (int)source->length, source->base); 246 if (v < 0 || (unsigned)v != source->length) { 247 return (ISC_R_BADNUMBER); 248 } 249 INSIST(buffer[source->length] == '\0'); 250 251 result = isc_parse_uint32(&n, buffer, 10); 252 if (result == ISC_R_BADNUMBER && hex_allowed) { 253 result = isc_parse_uint32(&n, buffer, 16); 254 } 255 if (result != ISC_R_SUCCESS) { 256 return (result); 257 } 258 if (n > max) { 259 return (ISC_R_RANGE); 260 } 261 *valuep = n; 262 return (ISC_R_SUCCESS); 263 } 264 265 static isc_result_t 266 dns_mnemonic_fromtext(unsigned int *valuep, isc_textregion_t *source, 267 struct tbl *table, unsigned int max) 268 { 269 isc_result_t result; 270 int i; 271 272 result = maybe_numeric(valuep, source, max, false); 273 if (result != ISC_R_BADNUMBER) 274 return (result); 275 276 for (i = 0; table[i].name != NULL; i++) { 277 unsigned int n; 278 n = strlen(table[i].name); 279 if (n == source->length && 280 (table[i].flags & TOTEXTONLY) == 0 && 281 strncasecmp(source->base, table[i].name, n) == 0) { 282 *valuep = table[i].value; 283 return (ISC_R_SUCCESS); 284 } 285 } 286 return (DNS_R_UNKNOWN); 287 } 288 289 static isc_result_t 290 dns_mnemonic_totext(unsigned int value, isc_buffer_t *target, 291 struct tbl *table) 292 { 293 int i = 0; 294 char buf[sizeof("4294967296")]; 295 while (table[i].name != NULL) { 296 if (table[i].value == value) { 297 return (str_totext(table[i].name, target)); 298 } 299 i++; 300 } 301 snprintf(buf, sizeof(buf), "%u", value); 302 return (str_totext(buf, target)); 303 } 304 305 isc_result_t 306 dns_rcode_fromtext(dns_rcode_t *rcodep, isc_textregion_t *source) { 307 unsigned int value; 308 RETERR(dns_mnemonic_fromtext(&value, source, rcodes, 0xffff)); 309 *rcodep = value; 310 return (ISC_R_SUCCESS); 311 } 312 313 isc_result_t 314 dns_rcode_totext(dns_rcode_t rcode, isc_buffer_t *target) { 315 return (dns_mnemonic_totext(rcode, target, rcodes)); 316 } 317 318 isc_result_t 319 dns_tsigrcode_fromtext(dns_rcode_t *rcodep, isc_textregion_t *source) { 320 unsigned int value; 321 RETERR(dns_mnemonic_fromtext(&value, source, tsigrcodes, 0xffff)); 322 *rcodep = value; 323 return (ISC_R_SUCCESS); 324 } 325 326 isc_result_t 327 dns_tsigrcode_totext(dns_rcode_t rcode, isc_buffer_t *target) { 328 return (dns_mnemonic_totext(rcode, target, tsigrcodes)); 329 } 330 331 isc_result_t 332 dns_cert_fromtext(dns_cert_t *certp, isc_textregion_t *source) { 333 unsigned int value; 334 RETERR(dns_mnemonic_fromtext(&value, source, certs, 0xffff)); 335 *certp = value; 336 return (ISC_R_SUCCESS); 337 } 338 339 isc_result_t 340 dns_cert_totext(dns_cert_t cert, isc_buffer_t *target) { 341 return (dns_mnemonic_totext(cert, target, certs)); 342 } 343 344 isc_result_t 345 dns_secalg_fromtext(dns_secalg_t *secalgp, isc_textregion_t *source) { 346 unsigned int value; 347 RETERR(dns_mnemonic_fromtext(&value, source, secalgs, 0xff)); 348 *secalgp = value; 349 return (ISC_R_SUCCESS); 350 } 351 352 isc_result_t 353 dns_secalg_totext(dns_secalg_t secalg, isc_buffer_t *target) { 354 return (dns_mnemonic_totext(secalg, target, secalgs)); 355 } 356 357 void 358 dns_secalg_format(dns_secalg_t alg, char *cp, unsigned int size) { 359 isc_buffer_t b; 360 isc_region_t r; 361 isc_result_t result; 362 363 REQUIRE(cp != NULL && size > 0); 364 isc_buffer_init(&b, cp, size - 1); 365 result = dns_secalg_totext(alg, &b); 366 isc_buffer_usedregion(&b, &r); 367 r.base[r.length] = 0; 368 if (result != ISC_R_SUCCESS) { 369 r.base[0] = 0; 370 } 371 } 372 373 isc_result_t 374 dns_secproto_fromtext(dns_secproto_t *secprotop, isc_textregion_t *source) { 375 unsigned int value; 376 RETERR(dns_mnemonic_fromtext(&value, source, secprotos, 0xff)); 377 *secprotop = value; 378 return (ISC_R_SUCCESS); 379 } 380 381 isc_result_t 382 dns_secproto_totext(dns_secproto_t secproto, isc_buffer_t *target) { 383 return (dns_mnemonic_totext(secproto, target, secprotos)); 384 } 385 386 isc_result_t 387 dns_hashalg_fromtext(unsigned char *hashalg, isc_textregion_t *source) { 388 unsigned int value; 389 RETERR(dns_mnemonic_fromtext(&value, source, hashalgs, 0xff)); 390 *hashalg = value; 391 return (ISC_R_SUCCESS); 392 } 393 394 isc_result_t 395 dns_keyflags_fromtext(dns_keyflags_t *flagsp, isc_textregion_t *source) 396 { 397 isc_result_t result; 398 char *text, *end; 399 unsigned int value = 0; 400 #ifdef notyet 401 unsigned int mask = 0; 402 #endif 403 404 result = maybe_numeric(&value, source, 0xffff, true); 405 if (result == ISC_R_SUCCESS) { 406 *flagsp = value; 407 return (ISC_R_SUCCESS); 408 } 409 if (result != ISC_R_BADNUMBER) 410 return (result); 411 412 text = source->base; 413 end = source->base + source->length; 414 415 while (text < end) { 416 struct keyflag *p; 417 unsigned int len; 418 char *delim = memchr(text, '|', end - text); 419 if (delim != NULL) 420 len = (unsigned int)(delim - text); 421 else 422 len = (unsigned int)(end - text); 423 for (p = keyflags; p->name != NULL; p++) { 424 if (strncasecmp(p->name, text, len) == 0) 425 break; 426 } 427 if (p->name == NULL) 428 return (DNS_R_UNKNOWNFLAG); 429 value |= p->value; 430 #ifdef notyet 431 if ((mask & p->mask) != 0) 432 warn("overlapping key flags"); 433 mask |= p->mask; 434 #endif 435 text += len; 436 if (delim != NULL) 437 text++; /* Skip "|" */ 438 } 439 *flagsp = value; 440 return (ISC_R_SUCCESS); 441 } 442 443 isc_result_t 444 dns_dsdigest_fromtext(dns_dsdigest_t *dsdigestp, isc_textregion_t *source) { 445 unsigned int value; 446 RETERR(dns_mnemonic_fromtext(&value, source, dsdigests, 0xff)); 447 *dsdigestp = value; 448 return (ISC_R_SUCCESS); 449 } 450 451 isc_result_t 452 dns_dsdigest_totext(dns_dsdigest_t dsdigest, isc_buffer_t *target) { 453 return (dns_mnemonic_totext(dsdigest, target, dsdigests)); 454 } 455 456 void 457 dns_dsdigest_format(dns_dsdigest_t typ, char *cp, unsigned int size) { 458 isc_buffer_t b; 459 isc_region_t r; 460 isc_result_t result; 461 462 REQUIRE(cp != NULL && size > 0); 463 isc_buffer_init(&b, cp, size - 1); 464 result = dns_dsdigest_totext(typ, &b); 465 isc_buffer_usedregion(&b, &r); 466 r.base[r.length] = 0; 467 if (result != ISC_R_SUCCESS) { 468 r.base[0] = 0; 469 } 470 } 471 472 /* 473 * This uses lots of hard coded values, but how often do we actually 474 * add classes? 475 */ 476 isc_result_t 477 dns_rdataclass_fromtext(dns_rdataclass_t *classp, isc_textregion_t *source) { 478 #define COMPARE(string, rdclass) \ 479 if (((sizeof(string) - 1) == source->length) \ 480 && (strncasecmp(source->base, string, source->length) == 0)) { \ 481 *classp = rdclass; \ 482 return (ISC_R_SUCCESS); \ 483 } 484 485 switch (tolower((unsigned char)source->base[0])) { 486 case 'a': 487 COMPARE("any", dns_rdataclass_any); 488 break; 489 case 'c': 490 /* 491 * RFC1035 says the mnemonic for the CHAOS class is CH, 492 * but historical BIND practice is to call it CHAOS. 493 * We will accept both forms, but only generate CH. 494 */ 495 COMPARE("ch", dns_rdataclass_chaos); 496 COMPARE("chaos", dns_rdataclass_chaos); 497 498 if (source->length > 5 && 499 source->length < (5 + sizeof("65000")) && 500 strncasecmp("class", source->base, 5) == 0) { 501 char buf[sizeof("65000")]; 502 char *endp; 503 unsigned int val; 504 505 /* 506 * source->base is not required to be NUL terminated. 507 * Copy up to remaining bytes and NUL terminate. 508 */ 509 snprintf(buf, sizeof(buf), "%.*s", 510 (int)(source->length - 5), source->base + 5); 511 val = strtoul(buf, &endp, 10); 512 if (*endp == '\0' && val <= 0xffff) { 513 *classp = (dns_rdataclass_t)val; 514 return (ISC_R_SUCCESS); 515 } 516 } 517 break; 518 case 'h': 519 COMPARE("hs", dns_rdataclass_hs); 520 COMPARE("hesiod", dns_rdataclass_hs); 521 break; 522 case 'i': 523 COMPARE("in", dns_rdataclass_in); 524 break; 525 case 'n': 526 COMPARE("none", dns_rdataclass_none); 527 break; 528 case 'r': 529 COMPARE("reserved0", dns_rdataclass_reserved0); 530 break; 531 } 532 533 #undef COMPARE 534 535 return (DNS_R_UNKNOWN); 536 } 537 538 isc_result_t 539 dns_rdataclass_totext(dns_rdataclass_t rdclass, isc_buffer_t *target) { 540 switch (rdclass) { 541 case dns_rdataclass_any: 542 return (str_totext("ANY", target)); 543 case dns_rdataclass_chaos: 544 return (str_totext("CH", target)); 545 case dns_rdataclass_hs: 546 return (str_totext("HS", target)); 547 case dns_rdataclass_in: 548 return (str_totext("IN", target)); 549 case dns_rdataclass_none: 550 return (str_totext("NONE", target)); 551 case dns_rdataclass_reserved0: 552 return (str_totext("RESERVED0", target)); 553 default: 554 return (dns_rdataclass_tounknowntext(rdclass, target)); 555 } 556 } 557 558 isc_result_t 559 dns_rdataclass_tounknowntext(dns_rdataclass_t rdclass, isc_buffer_t *target) { 560 char buf[sizeof("CLASS65535")]; 561 562 snprintf(buf, sizeof(buf), "CLASS%u", rdclass); 563 return (str_totext(buf, target)); 564 } 565 566 void 567 dns_rdataclass_format(dns_rdataclass_t rdclass, 568 char *array, unsigned int size) 569 { 570 isc_result_t result; 571 isc_buffer_t buf; 572 573 if (size == 0U) 574 return; 575 576 isc_buffer_init(&buf, array, size); 577 result = dns_rdataclass_totext(rdclass, &buf); 578 /* 579 * Null terminate. 580 */ 581 if (result == ISC_R_SUCCESS) { 582 if (isc_buffer_availablelength(&buf) >= 1) 583 isc_buffer_putuint8(&buf, 0); 584 else 585 result = ISC_R_NOSPACE; 586 } 587 if (result != ISC_R_SUCCESS) 588 strlcpy(array, "<unknown>", size); 589 } 590