xref: /netbsd-src/external/mpl/bind/dist/bin/confgen/tsig-keygen.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
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