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