1 /* $NetBSD: nsec3hash.c,v 1.9 2025/01/26 16:25:10 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 7 * 8 * This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 * 12 * See the COPYRIGHT file distributed with this work for additional 13 * information regarding copyright ownership. 14 */ 15 16 #include <stdarg.h> 17 #include <stdbool.h> 18 #include <stdlib.h> 19 #include <unistd.h> 20 21 #include <isc/attributes.h> 22 #include <isc/base32.h> 23 #include <isc/buffer.h> 24 #include <isc/commandline.h> 25 #include <isc/file.h> 26 #include <isc/hex.h> 27 #include <isc/iterated_hash.h> 28 #include <isc/result.h> 29 #include <isc/string.h> 30 #include <isc/tls.h> 31 #include <isc/types.h> 32 #include <isc/util.h> 33 34 #include <dns/fixedname.h> 35 #include <dns/name.h> 36 #include <dns/nsec3.h> 37 #include <dns/types.h> 38 39 const char *program = "nsec3hash"; 40 41 noreturn static void 42 fatal(const char *format, ...); 43 44 static void 45 fatal(const char *format, ...) { 46 va_list args; 47 48 fprintf(stderr, "%s: ", program); 49 va_start(args, format); 50 vfprintf(stderr, format, args); 51 va_end(args); 52 fprintf(stderr, "\n"); 53 _exit(EXIT_FAILURE); 54 } 55 56 static void 57 check_result(isc_result_t result, const char *message) { 58 if (result != ISC_R_SUCCESS) { 59 fatal("%s: %s", message, isc_result_totext(result)); 60 } 61 } 62 63 static void 64 usage(void) { 65 fprintf(stderr, "Usage: %s salt algorithm iterations domain\n", 66 program); 67 fprintf(stderr, " %s -r algorithm flags iterations salt domain\n", 68 program); 69 exit(EXIT_FAILURE); 70 } 71 72 typedef void 73 nsec3printer(unsigned int algo, unsigned int flags, unsigned int iters, 74 const char *saltstr, const char *domain, const char *digest); 75 76 static void 77 nsec3hash(nsec3printer *nsec3print, const char *algostr, const char *flagstr, 78 const char *iterstr, const char *saltstr, const char *domain) { 79 dns_fixedname_t fixed; 80 dns_name_t *name; 81 isc_buffer_t buffer; 82 isc_region_t region; 83 isc_result_t result; 84 unsigned char hash[NSEC3_MAX_HASH_LENGTH]; 85 unsigned char salt[DNS_NSEC3_SALTSIZE]; 86 unsigned char text[1024]; 87 unsigned int hash_alg; 88 unsigned int flags; 89 unsigned int length; 90 unsigned int iterations; 91 unsigned int salt_length; 92 const char dash[] = "-"; 93 94 if (strcmp(saltstr, "-") == 0) { 95 salt_length = 0; 96 salt[0] = 0; 97 } else { 98 isc_buffer_init(&buffer, salt, sizeof(salt)); 99 result = isc_hex_decodestring(saltstr, &buffer); 100 check_result(result, "isc_hex_decodestring(salt)"); 101 salt_length = isc_buffer_usedlength(&buffer); 102 if (salt_length > DNS_NSEC3_SALTSIZE) { 103 fatal("salt too long"); 104 } 105 if (salt_length == 0) { 106 saltstr = dash; 107 } 108 } 109 hash_alg = atoi(algostr); 110 if (hash_alg > 255U) { 111 fatal("hash algorithm too large"); 112 } 113 flags = flagstr == NULL ? 0 : atoi(flagstr); 114 if (flags > 255U) { 115 fatal("flags too large"); 116 } 117 iterations = atoi(iterstr); 118 if (iterations > 0xffffU) { 119 fatal("iterations to large"); 120 } 121 122 name = dns_fixedname_initname(&fixed); 123 isc_buffer_constinit(&buffer, domain, strlen(domain)); 124 isc_buffer_add(&buffer, strlen(domain)); 125 result = dns_name_fromtext(name, &buffer, dns_rootname, 0, NULL); 126 check_result(result, "dns_name_fromtext() failed"); 127 128 dns_name_downcase(name, name, NULL); 129 length = isc_iterated_hash(hash, hash_alg, iterations, salt, 130 salt_length, name->ndata, name->length); 131 if (length == 0) { 132 fatal("isc_iterated_hash failed"); 133 } 134 region.base = hash; 135 region.length = length; 136 isc_buffer_init(&buffer, text, sizeof(text)); 137 isc_base32hexnp_totext(®ion, 1, "", &buffer); 138 isc_buffer_putuint8(&buffer, '\0'); 139 140 nsec3print(hash_alg, flags, iterations, saltstr, domain, (char *)text); 141 } 142 143 static void 144 nsec3hash_print(unsigned int algo, unsigned int flags, unsigned int iters, 145 const char *saltstr, const char *domain, const char *digest) { 146 UNUSED(flags); 147 UNUSED(domain); 148 149 fprintf(stdout, "%s (salt=%s, hash=%u, iterations=%u)\n", digest, 150 saltstr, algo, iters); 151 } 152 153 static void 154 nsec3hash_rdata_print(unsigned int algo, unsigned int flags, unsigned int iters, 155 const char *saltstr, const char *domain, 156 const char *digest) { 157 fprintf(stdout, "%s NSEC3 %u %u %u %s %s\n", domain, algo, flags, iters, 158 saltstr, digest); 159 } 160 161 int 162 main(int argc, char *argv[]) { 163 bool rdata_format = false; 164 int ch; 165 166 while ((ch = isc_commandline_parse(argc, argv, "-r")) != -1) { 167 switch (ch) { 168 case 'r': 169 rdata_format = true; 170 break; 171 case '-': 172 isc_commandline_index -= 1; 173 goto skip; 174 default: 175 break; 176 } 177 } 178 179 skip: 180 argc -= isc_commandline_index; 181 argv += isc_commandline_index; 182 183 if (rdata_format) { 184 if (argc != 5) { 185 usage(); 186 } 187 nsec3hash(nsec3hash_rdata_print, argv[0], argv[1], argv[2], 188 argv[3], argv[4]); 189 } else { 190 if (argc != 4) { 191 usage(); 192 } 193 nsec3hash(nsec3hash_print, argv[1], NULL, argv[2], argv[0], 194 argv[3]); 195 } 196 return 0; 197 } 198