1 /* $NetBSD: tsig-keygen.c,v 1.4 2025/01/26 16:24:32 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 /*! \file */ 17 18 /** 19 * tsig-keygen generates TSIG keys that can be used in named configuration 20 * files for dynamic DNS. 21 */ 22 23 #include <stdarg.h> 24 #include <stdbool.h> 25 #include <stdlib.h> 26 27 #include <isc/assertions.h> 28 #include <isc/attributes.h> 29 #include <isc/base64.h> 30 #include <isc/buffer.h> 31 #include <isc/commandline.h> 32 #include <isc/file.h> 33 #include <isc/mem.h> 34 #include <isc/net.h> 35 #include <isc/result.h> 36 #include <isc/string.h> 37 #include <isc/time.h> 38 #include <isc/util.h> 39 40 #include <dns/keyvalues.h> 41 #include <dns/name.h> 42 43 #include <dst/dst.h> 44 45 #include <confgen/os.h> 46 47 #include "keygen.h" 48 #include "util.h" 49 50 #define KEYGEN_DEFAULT "tsig-key" 51 #define CONFGEN_DEFAULT "ddns-key" 52 53 static char program[256]; 54 const char *progname; 55 static enum { progmode_keygen, progmode_confgen } progmode; 56 bool verbose = false; /* needed by util.c but not used here */ 57 58 noreturn static void 59 usage(int status); 60 61 static void 62 usage(int status) { 63 if (progmode == progmode_confgen) { 64 fprintf(stderr, "\ 65 Usage:\n\ 66 %s [-a alg] [-k keyname] [-q] [-s name | -z zone]\n\ 67 -a alg: algorithm (default hmac-sha256)\n\ 68 -k keyname: name of the key as it will be used in named.conf\n\ 69 -s name: domain name to be updated using the created key\n\ 70 -z zone: name of the zone as it will be used in named.conf\n\ 71 -q: quiet mode: print the key, with no explanatory text\n", 72 progname); 73 } else { 74 fprintf(stderr, "\ 75 Usage:\n\ 76 %s [-a alg] [keyname]\n\ 77 -a alg: algorithm (default hmac-sha256)\n\n", 78 progname); 79 } 80 81 exit(status); 82 } 83 84 int 85 main(int argc, char **argv) { 86 isc_result_t result = ISC_R_SUCCESS; 87 bool show_final_mem = false; 88 bool quiet = false; 89 isc_buffer_t key_txtbuffer; 90 char key_txtsecret[256]; 91 isc_mem_t *mctx = NULL; 92 const char *keyname = NULL; 93 const char *zone = NULL; 94 const char *self_domain = NULL; 95 char *keybuf = NULL; 96 dns_secalg_t alg = DST_ALG_HMACSHA256; 97 const char *algname; 98 int keysize = 256; 99 int len = 0; 100 int ch; 101 102 result = isc_file_progname(*argv, program, sizeof(program)); 103 if (result != ISC_R_SUCCESS) { 104 memmove(program, "tsig-keygen", 11); 105 } 106 progname = program; 107 108 /* 109 * Libtool doesn't preserve the program name prior to final 110 * installation. Remove the libtool prefix ("lt-"). 111 */ 112 if (strncmp(progname, "lt-", 3) == 0) { 113 progname += 3; 114 } 115 116 #define PROGCMP(X) \ 117 (strcasecmp(progname, X) == 0 || strcasecmp(progname, X ".exe") == 0) 118 119 if (PROGCMP("tsig-keygen")) { 120 progmode = progmode_keygen; 121 quiet = true; 122 } else if (PROGCMP("ddns-confgen")) { 123 progmode = progmode_confgen; 124 } else { 125 UNREACHABLE(); 126 } 127 128 isc_commandline_errprint = false; 129 130 while ((ch = isc_commandline_parse(argc, argv, "a:hk:Mmr:qs:y:z:")) != 131 -1) 132 { 133 switch (ch) { 134 case 'a': 135 algname = isc_commandline_argument; 136 alg = alg_fromtext(algname); 137 if (alg == DST_ALG_UNKNOWN) { 138 fatal("Unsupported algorithm '%s'", algname); 139 } 140 keysize = alg_bits(alg); 141 break; 142 case 'h': 143 usage(EXIT_SUCCESS); 144 case 'k': 145 case 'y': 146 if (progmode == progmode_confgen) { 147 keyname = isc_commandline_argument; 148 } else { 149 usage(EXIT_FAILURE); 150 } 151 break; 152 case 'M': 153 isc_mem_debugging = ISC_MEM_DEBUGTRACE; 154 break; 155 case 'm': 156 show_final_mem = true; 157 break; 158 case 'q': 159 if (progmode == progmode_confgen) { 160 quiet = true; 161 } else { 162 usage(EXIT_FAILURE); 163 } 164 break; 165 case 'r': 166 fatal("The -r option has been deprecated."); 167 break; 168 case 's': 169 if (progmode == progmode_confgen) { 170 self_domain = isc_commandline_argument; 171 } else { 172 usage(EXIT_FAILURE); 173 } 174 break; 175 case 'z': 176 if (progmode == progmode_confgen) { 177 zone = isc_commandline_argument; 178 } else { 179 usage(EXIT_FAILURE); 180 } 181 break; 182 case '?': 183 if (isc_commandline_option != '?') { 184 fprintf(stderr, "%s: invalid argument -%c\n", 185 program, isc_commandline_option); 186 usage(EXIT_FAILURE); 187 } else { 188 usage(EXIT_SUCCESS); 189 } 190 break; 191 default: 192 fprintf(stderr, "%s: unhandled option -%c\n", program, 193 isc_commandline_option); 194 exit(EXIT_FAILURE); 195 } 196 } 197 198 if (progmode == progmode_keygen) { 199 keyname = argv[isc_commandline_index++]; 200 } 201 202 POST(argv); 203 204 if (self_domain != NULL && zone != NULL) { 205 usage(EXIT_FAILURE); /* -s and -z cannot coexist */ 206 } 207 208 if (argc > isc_commandline_index) { 209 usage(EXIT_FAILURE); 210 } 211 212 /* Use canonical algorithm name */ 213 algname = dst_hmac_algorithm_totext(alg); 214 215 isc_mem_create(&mctx); 216 217 if (keyname == NULL) { 218 const char *suffix = NULL; 219 220 keyname = ((progmode == progmode_keygen) ? KEYGEN_DEFAULT 221 : CONFGEN_DEFAULT); 222 if (self_domain != NULL) { 223 suffix = self_domain; 224 } else if (zone != NULL) { 225 suffix = zone; 226 } 227 if (suffix != NULL) { 228 len = strlen(keyname) + strlen(suffix) + 2; 229 keybuf = isc_mem_get(mctx, len); 230 snprintf(keybuf, len, "%s.%s", keyname, suffix); 231 keyname = (const char *)keybuf; 232 } 233 } 234 235 isc_buffer_init(&key_txtbuffer, &key_txtsecret, sizeof(key_txtsecret)); 236 237 generate_key(mctx, alg, keysize, &key_txtbuffer); 238 239 if (!quiet) { 240 printf("\ 241 # To activate this key, place the following in named.conf, and\n\ 242 # in a separate keyfile on the system or systems from which nsupdate\n\ 243 # will be run:\n"); 244 } 245 246 printf("\ 247 key \"%s\" {\n\ 248 algorithm %s;\n\ 249 secret \"%.*s\";\n\ 250 };\n", 251 keyname, algname, (int)isc_buffer_usedlength(&key_txtbuffer), 252 (char *)isc_buffer_base(&key_txtbuffer)); 253 254 if (!quiet) { 255 if (self_domain != NULL) { 256 printf("\n\ 257 # Then, in the \"zone\" statement for the zone containing the\n\ 258 # name \"%s\", place an \"update-policy\" statement\n\ 259 # like this one, adjusted as needed for your preferred permissions:\n\ 260 update-policy {\n\ 261 grant %s name %s ANY;\n\ 262 };\n", 263 self_domain, keyname, self_domain); 264 } else if (zone != NULL) { 265 printf("\n\ 266 # Then, in the \"zone\" definition statement for \"%s\",\n\ 267 # place an \"update-policy\" statement like this one, adjusted as \n\ 268 # needed for your preferred permissions:\n\ 269 update-policy {\n\ 270 grant %s zonesub ANY;\n\ 271 };\n", 272 zone, keyname); 273 } else { 274 printf("\n\ 275 # Then, in the \"zone\" statement for each zone you wish to dynamically\n\ 276 # update, place an \"update-policy\" statement granting update permission\n\ 277 # to this key. For example, the following statement grants this key\n\ 278 # permission to update any name within the zone:\n\ 279 update-policy {\n\ 280 grant %s zonesub ANY;\n\ 281 };\n", 282 keyname); 283 } 284 285 printf("\n\ 286 # After the keyfile has been placed, the following command will\n\ 287 # execute nsupdate using this key:\n\ 288 nsupdate -k <keyfile>\n"); 289 } 290 291 if (keybuf != NULL) { 292 isc_mem_put(mctx, keybuf, len); 293 } 294 295 if (show_final_mem) { 296 isc_mem_stats(mctx, stderr); 297 } 298 299 isc_mem_destroy(&mctx); 300 301 return 0; 302 } 303