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