1 /*	$NetBSD: ddns-confgen.c,v 1.8 2014/12/10 04:37:51 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2009, 2011, 2014  Internet Systems Consortium, Inc. ("ISC")
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
11  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
12  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
13  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
15  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16  * PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*! \file */
20 
21 /**
22  * ddns-confgen generates configuration files for dynamic DNS. It can
23  * be used as a convenient alternative to writing the ddns.key file
24  * and the corresponding key and update-policy statements in named.conf.
25  */
26 
27 #include <config.h>
28 
29 #include <stdlib.h>
30 #include <stdarg.h>
31 
32 #include <isc/assertions.h>
33 #include <isc/base64.h>
34 #include <isc/buffer.h>
35 #include <isc/commandline.h>
36 #include <isc/entropy.h>
37 #include <isc/file.h>
38 #include <isc/keyboard.h>
39 #include <isc/mem.h>
40 #include <isc/net.h>
41 #include <isc/print.h>
42 #include <isc/result.h>
43 #include <isc/string.h>
44 #include <isc/time.h>
45 #include <isc/util.h>
46 
47 #ifdef PKCS11CRYPTO
48 #include <pk11/result.h>
49 #endif
50 
51 #include <dns/keyvalues.h>
52 #include <dns/name.h>
53 #include <dns/result.h>
54 
55 #include <dst/dst.h>
56 #include <confgen/os.h>
57 
58 #include "util.h"
59 #include "keygen.h"
60 
61 #define KEYGEN_DEFAULT		"tsig-key"
62 #define CONFGEN_DEFAULT		"ddns-key"
63 
64 static char program[256];
65 const char *progname;
66 static enum { progmode_keygen, progmode_confgen} progmode;
67 isc_boolean_t verbose = ISC_FALSE; /* needed by util.c but not used here */
68 
69 ISC_PLATFORM_NORETURN_PRE static void
70 usage(int status) ISC_PLATFORM_NORETURN_POST;
71 
72 static void
73 usage(int status) {
74 	if (progmode == progmode_confgen) {
75 		fprintf(stderr, "\
76 Usage:\n\
77  %s [-a alg] [-k keyname] [-r randomfile] [-q] [-s name | -z zone]\n\
78   -a alg:        algorithm (default hmac-sha256)\n\
79   -k keyname:    name of the key as it will be used in named.conf\n\
80   -r randomfile: source of random data (use \"keyboard\" for key timing)\n\
81   -s name:       domain name to be updated using the created key\n\
82   -z zone:       name of the zone as it will be used in named.conf\n\
83   -q:            quiet mode: print the key, with no explanatory text\n",
84 			 progname);
85 	} else {
86 		fprintf(stderr, "\
87 Usage:\n\
88  %s [-a alg] [-r randomfile] [keyname]\n\
89   -a alg:        algorithm (default hmac-sha256)\n\
90   -r randomfile: source of random data (use \"keyboard\" for key timing)\n",
91 			 progname);
92 	}
93 
94 	exit (status);
95 }
96 
97 int
98 main(int argc, char **argv) {
99 	isc_result_t result = ISC_R_SUCCESS;
100 	isc_boolean_t show_final_mem = ISC_FALSE;
101 	isc_boolean_t quiet = ISC_FALSE;
102 	isc_buffer_t key_txtbuffer;
103 	char key_txtsecret[256];
104 	isc_mem_t *mctx = NULL;
105 	const char *randomfile = NULL;
106 	const char *keyname = NULL;
107 	const char *zone = NULL;
108 	const char *self_domain = NULL;
109 	char *keybuf = NULL;
110 	dns_secalg_t alg = DST_ALG_HMACSHA256;
111 	const char *algname;
112 	int keysize = 256;
113 	int len = 0;
114 	int ch;
115 
116 #ifdef PKCS11CRYPTO
117 	pk11_result_register();
118 #endif
119 	dns_result_register();
120 
121 	result = isc_file_progname(*argv, program, sizeof(program));
122 	if (result != ISC_R_SUCCESS)
123 		memmove(program, "tsig-keygen", 11);
124 	progname = program;
125 
126 	/*
127 	 * Libtool doesn't preserve the program name prior to final
128 	 * installation.  Remove the libtool prefix ("lt-").
129 	 */
130 	if (strncmp(progname, "lt-", 3) == 0)
131 		progname += 3;
132 
133 #define PROGCMP(X) \
134 	(strcasecmp(progname, X) == 0 || strcasecmp(progname, X ".exe") == 0)
135 
136 	if (PROGCMP("tsig-keygen")) {
137 		progmode = progmode_keygen;
138 		quiet = ISC_TRUE;
139 	} else if (PROGCMP("ddns-confgen"))
140 		progmode = progmode_confgen;
141 	else
142 		INSIST(0);
143 
144 	isc_commandline_errprint = ISC_FALSE;
145 
146 	while ((ch = isc_commandline_parse(argc, argv,
147 					   "a:hk:Mmr:qs:y:z:")) != -1) {
148 		switch (ch) {
149 		case 'a':
150 			algname = isc_commandline_argument;
151 			alg = alg_fromtext(algname);
152 			if (alg == DST_ALG_UNKNOWN)
153 				fatal("Unsupported algorithm '%s'", algname);
154 			keysize = alg_bits(alg);
155 			break;
156 		case 'h':
157 			usage(0);
158 		case 'k':
159 		case 'y':
160 			if (progmode == progmode_confgen)
161 				keyname = isc_commandline_argument;
162 			else
163 				usage(1);
164 			break;
165 		case 'M':
166 			isc_mem_debugging = ISC_MEM_DEBUGTRACE;
167 			break;
168 		case 'm':
169 			show_final_mem = ISC_TRUE;
170 			break;
171 		case 'q':
172 			if (progmode == progmode_confgen)
173 				quiet = ISC_TRUE;
174 			else
175 				usage(1);
176 			break;
177 		case 'r':
178 			randomfile = isc_commandline_argument;
179 			break;
180 		case 's':
181 			if (progmode == progmode_confgen)
182 				self_domain = isc_commandline_argument;
183 			else
184 				usage(1);
185 			break;
186 		case 'z':
187 			if (progmode == progmode_confgen)
188 				zone = isc_commandline_argument;
189 			else
190 				usage(1);
191 			break;
192 		case '?':
193 			if (isc_commandline_option != '?') {
194 				fprintf(stderr, "%s: invalid argument -%c\n",
195 					program, isc_commandline_option);
196 				usage(1);
197 			} else
198 				usage(0);
199 			break;
200 		default:
201 			fprintf(stderr, "%s: unhandled option -%c\n",
202 				program, isc_commandline_option);
203 			exit(1);
204 		}
205 	}
206 
207 	if (progmode == progmode_keygen)
208 		keyname = argv[isc_commandline_index++];
209 
210 	POST(argv);
211 
212 	if (self_domain != NULL && zone != NULL)
213 		usage(1);	/* -s and -z cannot coexist */
214 
215 	if (argc > isc_commandline_index)
216 		usage(1);
217 
218 	/* Use canonical algorithm name */
219 	algname = alg_totext(alg);
220 
221 	DO("create memory context", isc_mem_create(0, 0, &mctx));
222 
223 	if (keyname == NULL) {
224 		const char *suffix = NULL;
225 
226 		keyname = ((progmode == progmode_keygen)
227 			?  KEYGEN_DEFAULT
228 			: CONFGEN_DEFAULT);
229 		if (self_domain != NULL)
230 			suffix = self_domain;
231 		else if (zone != NULL)
232 			suffix = zone;
233 		if (suffix != NULL) {
234 			len = strlen(keyname) + strlen(suffix) + 2;
235 			keybuf = isc_mem_get(mctx, len);
236 			if (keybuf == NULL)
237 				fatal("failed to allocate memory for keyname");
238 			snprintf(keybuf, len, "%s.%s", keyname, suffix);
239 			keyname = (const char *) keybuf;
240 		}
241 	}
242 
243 	isc_buffer_init(&key_txtbuffer, &key_txtsecret, sizeof(key_txtsecret));
244 
245 	generate_key(mctx, randomfile, alg, keysize, &key_txtbuffer);
246 
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 	printf("\
255 key \"%s\" {\n\
256 	algorithm %s;\n\
257 	secret \"%.*s\";\n\
258 };\n",
259 	       keyname, algname,
260 	       (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 
301 	if (keybuf != NULL)
302 		isc_mem_put(mctx, keybuf, len);
303 
304 	if (show_final_mem)
305 		isc_mem_stats(mctx, stderr);
306 
307 	isc_mem_destroy(&mctx);
308 
309 	return (0);
310 }
311