ddns-confgen.c 7.16 KB
Newer Older
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4 5 6
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 8 9
 *
 * See the COPYRIGHT file distributed with this work for additional
 * information regarding copyright ownership.
10 11 12 13 14 15 16 17 18 19 20 21 22
 */

/*! \file */

/**
 * ddns-confgen generates configuration files for dynamic DNS. It can
 * be used as a convenient alternative to writing the ddns.key file
 * and the corresponding key and update-policy statements in named.conf.
 */

#include <config.h>

#include <stdarg.h>
23 24
#include <stdbool.h>
#include <stdlib.h>
25 26 27 28 29 30 31 32 33 34 35 36 37 38

#include <isc/assertions.h>
#include <isc/base64.h>
#include <isc/buffer.h>
#include <isc/commandline.h>
#include <isc/file.h>
#include <isc/mem.h>
#include <isc/net.h>
#include <isc/print.h>
#include <isc/result.h>
#include <isc/string.h>
#include <isc/time.h>
#include <isc/util.h>

Ondřej Surý's avatar
Ondřej Surý committed
39
#if USE_PKCS11
Mark Andrews's avatar
Mark Andrews committed
40 41 42
#include <pk11/result.h>
#endif

43 44
#include <dns/keyvalues.h>
#include <dns/name.h>
Mark Andrews's avatar
Mark Andrews committed
45
#include <dns/result.h>
46 47 48 49 50 51 52

#include <dst/dst.h>
#include <confgen/os.h>

#include "util.h"
#include "keygen.h"

Evan Hunt's avatar
Evan Hunt committed
53 54
#define KEYGEN_DEFAULT		"tsig-key"
#define CONFGEN_DEFAULT		"ddns-key"
55 56 57

static char program[256];
const char *progname;
Evan Hunt's avatar
Evan Hunt committed
58
static enum { progmode_keygen, progmode_confgen} progmode;
59
bool verbose = false; /* needed by util.c but not used here */
60

Francis Dupont's avatar
Francis Dupont committed
61 62 63
ISC_PLATFORM_NORETURN_PRE static void
usage(int status) ISC_PLATFORM_NORETURN_POST;

64 65
static void
usage(int status) {
Evan Hunt's avatar
Evan Hunt committed
66 67
	if (progmode == progmode_confgen) {
		fprintf(stderr, "\
68
Usage:\n\
69
 %s [-a alg] [-k keyname] [-q] [-s name | -z zone]\n\
70
  -a alg:        algorithm (default hmac-sha256)\n\
71
  -k keyname:    name of the key as it will be used in named.conf\n\
72
  -s name:       domain name to be updated using the created key\n\
73 74
  -z zone:       name of the zone as it will be used in named.conf\n\
  -q:            quiet mode: print the key, with no explanatory text\n",
Evan Hunt's avatar
Evan Hunt committed
75 76 77 78
			 progname);
	} else {
		fprintf(stderr, "\
Usage:\n\
79 80
 %s [-a alg] [keyname]\n\
  -a alg:        algorithm (default hmac-sha256)\n\n",
Evan Hunt's avatar
Evan Hunt committed
81 82
			 progname);
	}
83 84 85 86 87 88

	exit (status);
}

int
main(int argc, char **argv) {
Evan Hunt's avatar
Evan Hunt committed
89
	isc_result_t result = ISC_R_SUCCESS;
90 91
	bool show_final_mem = false;
	bool quiet = false;
92 93 94 95 96
	isc_buffer_t key_txtbuffer;
	char key_txtsecret[256];
	isc_mem_t *mctx = NULL;
	const char *keyname = NULL;
	const char *zone = NULL;
97
	const char *self_domain = NULL;
98 99
	char *keybuf = NULL;
	dns_secalg_t alg = DST_ALG_HMACSHA256;
Evan Hunt's avatar
Evan Hunt committed
100
	const char *algname;
101 102 103 104
	int keysize = 256;
	int len = 0;
	int ch;

Ondřej Surý's avatar
Ondřej Surý committed
105
#if USE_PKCS11
106 107 108 109
	pk11_result_register();
#endif
	dns_result_register();

110 111
	result = isc_file_progname(*argv, program, sizeof(program));
	if (result != ISC_R_SUCCESS)
Evan Hunt's avatar
Evan Hunt committed
112
		memmove(program, "tsig-keygen", 11);
113 114
	progname = program;

Evan Hunt's avatar
Evan Hunt committed
115 116 117 118 119 120 121 122 123 124 125 126
	/*
	 * Libtool doesn't preserve the program name prior to final
	 * installation.  Remove the libtool prefix ("lt-").
	 */
	if (strncmp(progname, "lt-", 3) == 0)
		progname += 3;

#define PROGCMP(X) \
	(strcasecmp(progname, X) == 0 || strcasecmp(progname, X ".exe") == 0)

	if (PROGCMP("tsig-keygen")) {
		progmode = progmode_keygen;
127
		quiet = true;
128
	} else if (PROGCMP("ddns-confgen")) {
Evan Hunt's avatar
Evan Hunt committed
129
		progmode = progmode_confgen;
130
	} else {
Evan Hunt's avatar
Evan Hunt committed
131
		INSIST(0);
132 133
		ISC_UNREACHABLE();
	}
Evan Hunt's avatar
Evan Hunt committed
134

135
	isc_commandline_errprint = false;
136 137

	while ((ch = isc_commandline_parse(argc, argv,
Evan Hunt's avatar
Evan Hunt committed
138
					   "a:hk:Mmr:qs:y:z:")) != -1) {
139 140 141
		switch (ch) {
		case 'a':
			algname = isc_commandline_argument;
Automatic Updater's avatar
Automatic Updater committed
142 143
			alg = alg_fromtext(algname);
			if (alg == DST_ALG_UNKNOWN)
144
				fatal("Unsupported algorithm '%s'", algname);
Automatic Updater's avatar
Automatic Updater committed
145
			keysize = alg_bits(alg);
146 147 148 149 150
			break;
		case 'h':
			usage(0);
		case 'k':
		case 'y':
Evan Hunt's avatar
Evan Hunt committed
151 152 153 154
			if (progmode == progmode_confgen)
				keyname = isc_commandline_argument;
			else
				usage(1);
155 156 157 158 159
			break;
		case 'M':
			isc_mem_debugging = ISC_MEM_DEBUGTRACE;
			break;
		case 'm':
160
			show_final_mem = true;
161 162
			break;
		case 'q':
Evan Hunt's avatar
Evan Hunt committed
163
			if (progmode == progmode_confgen)
164
				quiet = true;
Evan Hunt's avatar
Evan Hunt committed
165 166
			else
				usage(1);
167 168
			break;
		case 'r':
169
			fatal("The -r option has been deprecated.");
170 171
			break;
		case 's':
Evan Hunt's avatar
Evan Hunt committed
172 173 174 175
			if (progmode == progmode_confgen)
				self_domain = isc_commandline_argument;
			else
				usage(1);
176
			break;
177
		case 'z':
Evan Hunt's avatar
Evan Hunt committed
178 179 180 181
			if (progmode == progmode_confgen)
				zone = isc_commandline_argument;
			else
				usage(1);
182
			break;
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
		case '?':
			if (isc_commandline_option != '?') {
				fprintf(stderr, "%s: invalid argument -%c\n",
					program, isc_commandline_option);
				usage(1);
			} else
				usage(0);
			break;
		default:
			fprintf(stderr, "%s: unhandled option -%c\n",
				program, isc_commandline_option);
			exit(1);
		}
	}

Evan Hunt's avatar
Evan Hunt committed
198 199 200
	if (progmode == progmode_keygen)
		keyname = argv[isc_commandline_index++];

201
	POST(argv);
202

203 204
	if (self_domain != NULL && zone != NULL)
		usage(1);	/* -s and -z cannot coexist */
Tinderbox User's avatar
Tinderbox User committed
205

Evan Hunt's avatar
Evan Hunt committed
206
	if (argc > isc_commandline_index)
207 208
		usage(1);

Evan Hunt's avatar
Evan Hunt committed
209 210 211
	/* Use canonical algorithm name */
	algname = alg_totext(alg);

212 213
	DO("create memory context", isc_mem_create(0, 0, &mctx));

214 215
	if (keyname == NULL) {
		const char *suffix = NULL;
Automatic Updater's avatar
Automatic Updater committed
216

Evan Hunt's avatar
Evan Hunt committed
217 218 219
		keyname = ((progmode == progmode_keygen)
			?  KEYGEN_DEFAULT
			: CONFGEN_DEFAULT);
220 221 222 223 224 225
		if (self_domain != NULL)
			suffix = self_domain;
		else if (zone != NULL)
			suffix = zone;
		if (suffix != NULL) {
			len = strlen(keyname) + strlen(suffix) + 2;
Automatic Updater's avatar
Automatic Updater committed
226
			keybuf = isc_mem_get(mctx, len);
227 228 229
			if (keybuf == NULL)
				fatal("failed to allocate memory for keyname");
			snprintf(keybuf, len, "%s.%s", keyname, suffix);
Automatic Updater's avatar
Automatic Updater committed
230 231 232
			keyname = (const char *) keybuf;
		}
	}
233 234 235

	isc_buffer_init(&key_txtbuffer, &key_txtsecret, sizeof(key_txtsecret));

236
	generate_key(mctx, alg, keysize, &key_txtbuffer);
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254


	if (!quiet)
		printf("\
# To activate this key, place the following in named.conf, and\n\
# in a separate keyfile on the system or systems from which nsupdate\n\
# will be run:\n");

	printf("\
key \"%s\" {\n\
	algorithm %s;\n\
	secret \"%.*s\";\n\
};\n",
	       keyname, algname,
	       (int)isc_buffer_usedlength(&key_txtbuffer),
	       (char *)isc_buffer_base(&key_txtbuffer));

	if (!quiet) {
255
		if (self_domain != NULL) {
256
			printf("\n\
257
# Then, in the \"zone\" statement for the zone containing the\n\
258 259 260
# name \"%s\", place an \"update-policy\" statement\n\
# like this one, adjusted as needed for your preferred permissions:\n\
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 268 269
# place an \"update-policy\" statement like this one, adjusted as \n\
# needed for your preferred permissions:\n\
update-policy {\n\
270
	  grant %s zonesub ANY;\n\
271
};\n",
272 273 274 275 276 277 278 279 280 281 282
			       zone, keyname);
		} else {
			printf("\n\
# Then, in the \"zone\" statement for each zone you wish to dynamically\n\
# update, place an \"update-policy\" statement granting update permission\n\
# to this key.  For example, the following statement grants this key\n\
# permission to update any name within the zone:\n\
update-policy {\n\
	grant %s zonesub ANY;\n\
};\n",
			       keyname);
283 284
		}

285
		printf("\n\
286
# After the keyfile has been placed, the following command will\n\
287 288 289
# execute nsupdate using this key:\n\
nsupdate -k <keyfile>\n");

290 291
	}

292
	if (keybuf != NULL)
293 294 295 296 297 298 299 300 301
		isc_mem_put(mctx, keybuf, len);

	if (show_final_mem)
		isc_mem_stats(mctx, stderr);

	isc_mem_destroy(&mctx);

	return (0);
}