1 1.9 christos /* $NetBSD: rndc-confgen.c,v 1.9 2025/01/26 16:24:32 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.6 christos * SPDX-License-Identifier: MPL-2.0 7 1.6 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.5 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 /*! \file */ 17 1.1 christos 18 1.1 christos /** 19 1.1 christos * rndc-confgen generates configuration files for rndc. It can be used 20 1.1 christos * as a convenient alternative to writing the rndc.conf file and the 21 1.1 christos * corresponding controls and key statements in named.conf by hand. 22 1.1 christos * Alternatively, it can be run with the -a option to set up a 23 1.1 christos * rndc.key file and avoid the need for a rndc.conf file and a 24 1.1 christos * controls statement altogether. 25 1.1 christos */ 26 1.1 christos 27 1.3 christos #include <stdarg.h> 28 1.3 christos #include <stdbool.h> 29 1.1 christos #include <stdlib.h> 30 1.1 christos 31 1.1 christos #include <isc/assertions.h> 32 1.7 christos #include <isc/attributes.h> 33 1.1 christos #include <isc/base64.h> 34 1.1 christos #include <isc/buffer.h> 35 1.1 christos #include <isc/commandline.h> 36 1.1 christos #include <isc/file.h> 37 1.1 christos #include <isc/mem.h> 38 1.1 christos #include <isc/net.h> 39 1.1 christos #include <isc/result.h> 40 1.1 christos #include <isc/string.h> 41 1.1 christos #include <isc/time.h> 42 1.1 christos #include <isc/util.h> 43 1.1 christos 44 1.1 christos #include <dns/keyvalues.h> 45 1.1 christos #include <dns/name.h> 46 1.1 christos 47 1.1 christos #include <dst/dst.h> 48 1.4 christos 49 1.1 christos #include <confgen/os.h> 50 1.1 christos 51 1.4 christos #include "keygen.h" 52 1.1 christos #include "util.h" 53 1.1 christos 54 1.4 christos #define DEFAULT_KEYNAME "rndc-key" 55 1.4 christos #define DEFAULT_SERVER "127.0.0.1" 56 1.4 christos #define DEFAULT_PORT 953 57 1.1 christos 58 1.1 christos static char program[256]; 59 1.1 christos const char *progname; 60 1.1 christos 61 1.3 christos bool verbose = false; 62 1.1 christos 63 1.1 christos const char *keyfile, *keydef; 64 1.1 christos 65 1.7 christos noreturn static void 66 1.7 christos usage(int status); 67 1.1 christos 68 1.1 christos static void 69 1.1 christos usage(int status) { 70 1.1 christos fprintf(stderr, "\ 71 1.1 christos Usage:\n\ 72 1.3 christos %s [-a] [-b bits] [-c keyfile] [-k keyname] [-p port] \ 73 1.1 christos [-s addr] [-t chrootdir] [-u user]\n\ 74 1.1 christos -a: generate just the key clause and write it to keyfile (%s)\n\ 75 1.1 christos -A alg: algorithm (default hmac-sha256)\n\ 76 1.1 christos -b bits: from 1 through 512, default 256; total length of the secret\n\ 77 1.1 christos -c keyfile: specify an alternate key file (requires -a)\n\ 78 1.1 christos -k keyname: the name as it will be used in named.conf and rndc.conf\n\ 79 1.1 christos -p port: the port named will listen on and rndc will connect to\n\ 80 1.7 christos -q: suppress printing written key path\n\ 81 1.1 christos -s addr: the address to which rndc should connect\n\ 82 1.1 christos -t chrootdir: write a keyfile in chrootdir as well (requires -a)\n\ 83 1.1 christos -u user: set the keyfile owner to \"user\" (requires -a)\n", 84 1.4 christos progname, keydef); 85 1.1 christos 86 1.4 christos exit(status); 87 1.1 christos } 88 1.1 christos 89 1.1 christos int 90 1.1 christos main(int argc, char **argv) { 91 1.3 christos bool show_final_mem = false; 92 1.1 christos isc_buffer_t key_txtbuffer; 93 1.1 christos char key_txtsecret[256]; 94 1.1 christos isc_mem_t *mctx = NULL; 95 1.1 christos isc_result_t result = ISC_R_SUCCESS; 96 1.1 christos const char *keyname = NULL; 97 1.1 christos const char *serveraddr = NULL; 98 1.1 christos dns_secalg_t alg; 99 1.1 christos const char *algname; 100 1.1 christos char *p; 101 1.1 christos int ch; 102 1.1 christos int port; 103 1.1 christos int keysize = -1; 104 1.1 christos struct in_addr addr4_dummy; 105 1.1 christos struct in6_addr addr6_dummy; 106 1.1 christos char *chrootdir = NULL; 107 1.1 christos char *user = NULL; 108 1.3 christos bool keyonly = false; 109 1.7 christos bool quiet = false; 110 1.1 christos int len; 111 1.1 christos 112 1.1 christos keydef = keyfile = RNDC_KEYFILE; 113 1.1 christos 114 1.1 christos result = isc_file_progname(*argv, program, sizeof(program)); 115 1.4 christos if (result != ISC_R_SUCCESS) { 116 1.1 christos memmove(program, "rndc-confgen", 13); 117 1.4 christos } 118 1.1 christos progname = program; 119 1.1 christos 120 1.1 christos keyname = DEFAULT_KEYNAME; 121 1.1 christos alg = DST_ALG_HMACSHA256; 122 1.1 christos serveraddr = DEFAULT_SERVER; 123 1.1 christos port = DEFAULT_PORT; 124 1.1 christos 125 1.3 christos isc_commandline_errprint = false; 126 1.1 christos 127 1.1 christos while ((ch = isc_commandline_parse(argc, argv, 128 1.1 christos "aA:b:c:hk:Mmp:r:s:t:u:Vy")) != -1) 129 1.1 christos { 130 1.1 christos switch (ch) { 131 1.1 christos case 'a': 132 1.3 christos keyonly = true; 133 1.1 christos break; 134 1.1 christos case 'A': 135 1.1 christos algname = isc_commandline_argument; 136 1.1 christos alg = alg_fromtext(algname); 137 1.4 christos if (alg == DST_ALG_UNKNOWN) { 138 1.1 christos fatal("Unsupported algorithm '%s'", algname); 139 1.4 christos } 140 1.1 christos break; 141 1.1 christos case 'b': 142 1.1 christos keysize = strtol(isc_commandline_argument, &p, 10); 143 1.4 christos if (*p != '\0' || keysize < 0) { 144 1.1 christos fatal("-b requires a non-negative number"); 145 1.4 christos } 146 1.1 christos break; 147 1.1 christos case 'c': 148 1.1 christos keyfile = isc_commandline_argument; 149 1.1 christos break; 150 1.1 christos case 'h': 151 1.8 christos usage(EXIT_SUCCESS); 152 1.8 christos break; 153 1.1 christos case 'k': 154 1.4 christos case 'y': /* Compatible with rndc -y. */ 155 1.1 christos keyname = isc_commandline_argument; 156 1.1 christos break; 157 1.1 christos case 'M': 158 1.1 christos isc_mem_debugging = ISC_MEM_DEBUGTRACE; 159 1.1 christos break; 160 1.1 christos 161 1.1 christos case 'm': 162 1.3 christos show_final_mem = true; 163 1.1 christos break; 164 1.1 christos case 'p': 165 1.1 christos port = strtol(isc_commandline_argument, &p, 10); 166 1.4 christos if (*p != '\0' || port < 0 || port > 65535) { 167 1.1 christos fatal("port '%s' out of range", 168 1.1 christos isc_commandline_argument); 169 1.4 christos } 170 1.1 christos break; 171 1.7 christos case 'q': 172 1.7 christos quiet = true; 173 1.7 christos break; 174 1.1 christos case 'r': 175 1.3 christos fatal("The -r option has been deprecated."); 176 1.1 christos break; 177 1.1 christos case 's': 178 1.1 christos serveraddr = isc_commandline_argument; 179 1.1 christos if (inet_pton(AF_INET, serveraddr, &addr4_dummy) != 1 && 180 1.1 christos inet_pton(AF_INET6, serveraddr, &addr6_dummy) != 1) 181 1.4 christos { 182 1.1 christos fatal("-s should be an IPv4 or IPv6 address"); 183 1.4 christos } 184 1.1 christos break; 185 1.1 christos case 't': 186 1.1 christos chrootdir = isc_commandline_argument; 187 1.1 christos break; 188 1.1 christos case 'u': 189 1.1 christos user = isc_commandline_argument; 190 1.1 christos break; 191 1.1 christos case 'V': 192 1.3 christos verbose = true; 193 1.1 christos break; 194 1.1 christos case '?': 195 1.1 christos if (isc_commandline_option != '?') { 196 1.1 christos fprintf(stderr, "%s: invalid argument -%c\n", 197 1.1 christos program, isc_commandline_option); 198 1.8 christos usage(EXIT_FAILURE); 199 1.4 christos } else { 200 1.8 christos usage(EXIT_SUCCESS); 201 1.4 christos } 202 1.1 christos break; 203 1.1 christos default: 204 1.4 christos fprintf(stderr, "%s: unhandled option -%c\n", program, 205 1.4 christos isc_commandline_option); 206 1.8 christos exit(EXIT_FAILURE); 207 1.1 christos } 208 1.1 christos } 209 1.1 christos 210 1.1 christos argc -= isc_commandline_index; 211 1.1 christos argv += isc_commandline_index; 212 1.1 christos POST(argv); 213 1.1 christos 214 1.4 christos if (argc > 0) { 215 1.8 christos usage(EXIT_FAILURE); 216 1.4 christos } 217 1.1 christos 218 1.1 christos if (alg == DST_ALG_HMACMD5) { 219 1.4 christos fprintf(stderr, "warning: use of hmac-md5 for RNDC keys " 220 1.4 christos "is deprecated; hmac-sha256 is now " 221 1.4 christos "recommended.\n"); 222 1.1 christos } 223 1.1 christos 224 1.4 christos if (keysize < 0) { 225 1.1 christos keysize = alg_bits(alg); 226 1.4 christos } 227 1.7 christos algname = dst_hmac_algorithm_totext(alg); 228 1.1 christos 229 1.4 christos isc_mem_create(&mctx); 230 1.1 christos isc_buffer_init(&key_txtbuffer, &key_txtsecret, sizeof(key_txtsecret)); 231 1.1 christos 232 1.3 christos generate_key(mctx, alg, keysize, &key_txtbuffer); 233 1.1 christos 234 1.1 christos if (keyonly) { 235 1.1 christos write_key_file(keyfile, chrootdir == NULL ? user : NULL, 236 1.1 christos keyname, &key_txtbuffer, alg); 237 1.7 christos if (!quiet) { 238 1.7 christos printf("wrote key file \"%s\"\n", keyfile); 239 1.7 christos } 240 1.1 christos 241 1.1 christos if (chrootdir != NULL) { 242 1.1 christos char *buf; 243 1.1 christos len = strlen(chrootdir) + strlen(keyfile) + 2; 244 1.1 christos buf = isc_mem_get(mctx, len); 245 1.1 christos snprintf(buf, len, "%s%s%s", chrootdir, 246 1.1 christos (*keyfile != '/') ? "/" : "", keyfile); 247 1.1 christos 248 1.1 christos write_key_file(buf, user, keyname, &key_txtbuffer, alg); 249 1.7 christos if (!quiet) { 250 1.7 christos printf("wrote key file \"%s\"\n", buf); 251 1.7 christos } 252 1.1 christos isc_mem_put(mctx, buf, len); 253 1.1 christos } 254 1.1 christos } else { 255 1.1 christos printf("\ 256 1.1 christos # Start of rndc.conf\n\ 257 1.1 christos key \"%s\" {\n\ 258 1.1 christos algorithm %s;\n\ 259 1.1 christos secret \"%.*s\";\n\ 260 1.1 christos };\n\ 261 1.1 christos \n\ 262 1.1 christos options {\n\ 263 1.1 christos default-key \"%s\";\n\ 264 1.1 christos default-server %s;\n\ 265 1.1 christos default-port %d;\n\ 266 1.1 christos };\n\ 267 1.1 christos # End of rndc.conf\n\ 268 1.1 christos \n\ 269 1.1 christos # Use with the following in named.conf, adjusting the allow list as needed:\n\ 270 1.1 christos # key \"%s\" {\n\ 271 1.1 christos # algorithm %s;\n\ 272 1.1 christos # secret \"%.*s\";\n\ 273 1.1 christos # };\n\ 274 1.1 christos # \n\ 275 1.1 christos # controls {\n\ 276 1.1 christos # inet %s port %d\n\ 277 1.1 christos # allow { %s; } keys { \"%s\"; };\n\ 278 1.1 christos # };\n\ 279 1.1 christos # End of named.conf\n", 280 1.1 christos keyname, algname, 281 1.1 christos (int)isc_buffer_usedlength(&key_txtbuffer), 282 1.4 christos (char *)isc_buffer_base(&key_txtbuffer), keyname, 283 1.4 christos serveraddr, port, keyname, algname, 284 1.1 christos (int)isc_buffer_usedlength(&key_txtbuffer), 285 1.4 christos (char *)isc_buffer_base(&key_txtbuffer), serveraddr, 286 1.4 christos port, serveraddr, keyname); 287 1.1 christos } 288 1.1 christos 289 1.4 christos if (show_final_mem) { 290 1.1 christos isc_mem_stats(mctx, stderr); 291 1.4 christos } 292 1.1 christos 293 1.1 christos isc_mem_destroy(&mctx); 294 1.1 christos 295 1.9 christos return 0; 296 1.1 christos } 297