Home | History | Annotate | Line # | Download | only in confgen
      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