dnssec-keygen.c 11.7 KB
Newer Older
Brian Wellington's avatar
Brian Wellington committed
1
/*
Mark Andrews's avatar
Mark Andrews committed
2
 * Portions Copyright (C) 2004, 2005  Internet Systems Consortium, Inc. ("ISC")
Mark Andrews's avatar
Mark Andrews committed
3
 * Portions Copyright (C) 2000-2003  Internet Software Consortium.
4
 * Portions Copyright (C) 1995-2000 by Network Associates, Inc.
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
Brian Wellington's avatar
Brian Wellington committed
7
8
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
9
 *
Mark Andrews's avatar
Mark Andrews committed
10
11
12
13
14
15
16
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
 * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Brian Wellington's avatar
Brian Wellington committed
17
18
 */

Mark Andrews's avatar
Mark Andrews committed
19
/* $Id: dnssec-keygen.c,v 1.69 2005/04/29 00:22:24 marka Exp $ */
20
21

/*! \file */
Brian Wellington's avatar
Brian Wellington committed
22
23
24
25
26

#include <config.h>

#include <stdlib.h>

27
#include <isc/buffer.h>
28
#include <isc/commandline.h>
Brian Wellington's avatar
Brian Wellington committed
29
#include <isc/entropy.h>
Brian Wellington's avatar
Brian Wellington committed
30
#include <isc/mem.h>
31
32
#include <isc/region.h>
#include <isc/string.h>
Bob Halley's avatar
Bob Halley committed
33
34
#include <isc/util.h>

35
#include <dns/fixedname.h>
Brian Wellington's avatar
Brian Wellington committed
36
#include <dns/keyvalues.h>
37
#include <dns/log.h>
38
#include <dns/name.h>
39
#include <dns/rdataclass.h>
40
#include <dns/result.h>
Brian Wellington's avatar
Brian Wellington committed
41
#include <dns/secalg.h>
42

Brian Wellington's avatar
Brian Wellington committed
43
44
#include <dst/dst.h>

45
#include "dnssectool.h"
Brian Wellington's avatar
Brian Wellington committed
46

47
#define MAX_RSA 4096 /* should be long enough... */
48

David Lawrence's avatar
David Lawrence committed
49
const char *program = "dnssec-keygen";
50
int verbose;
51

52
53
static const char *algs = "RSA | RSAMD5 | DH | DSA | RSASHA1 | HMAC-MD5";

54
55
56
57
58
59
static isc_boolean_t
dsa_size_ok(int size) {
	return (ISC_TF(size >= 512 && size <= 1024 && size % 64 == 0));
}

static void
60
usage(void) {
61
62
63
	fprintf(stderr, "Usage:\n");
	fprintf(stderr, "    %s -a alg -b bits -n type [options] name\n\n",
		program);
64
	fprintf(stderr, "Version: %s\n", VERSION);
65
	fprintf(stderr, "Required options:\n");
66
	fprintf(stderr, "    -a algorithm: %s\n", algs);
67
	fprintf(stderr, "    -b key size, in bits:\n");
Brian Wellington's avatar
Brian Wellington committed
68
69
	fprintf(stderr, "        RSAMD5:\t\t[512..%d]\n", MAX_RSA);
	fprintf(stderr, "        RSASHA1:\t\t[512..%d]\n", MAX_RSA);
70
71
72
	fprintf(stderr, "        DH:\t\t[128..4096]\n");
	fprintf(stderr, "        DSA:\t\t[512..1024] and divisible by 64\n");
	fprintf(stderr, "        HMAC-MD5:\t[1..512]\n");
73
	fprintf(stderr, "    -n nametype: ZONE | HOST | ENTITY | USER | OTHER\n");
74
75
	fprintf(stderr, "    name: owner of the key\n");
	fprintf(stderr, "Other options:\n");
Brian Wellington's avatar
Brian Wellington committed
76
	fprintf(stderr, "    -c <class> (default: IN)\n");
Brian Wellington's avatar
Brian Wellington committed
77
	fprintf(stderr, "    -e use large exponent (RSAMD5/RSASHA1 only)\n");
78
	fprintf(stderr, "    -f keyflag: KSK\n");
Brian Wellington's avatar
Brian Wellington committed
79
80
81
82
83
84
	fprintf(stderr, "    -g <generator> use specified generator "
		"(DH only)\n");
	fprintf(stderr, "    -t <type>: "
		"AUTHCONF | NOAUTHCONF | NOAUTH | NOCONF "
		"(default: AUTHCONF)\n");
	fprintf(stderr, "    -p <protocol>: "
85
	       "default: 3 [dnssec]\n");
Brian Wellington's avatar
Brian Wellington committed
86
87
88
89
	fprintf(stderr, "    -s <strength> strength value this key signs DNS "
		"records with (default: 0)\n");
	fprintf(stderr, "    -r <randomdev>: a file containing random data\n");
	fprintf(stderr, "    -v <verbose level>\n");
90
	fprintf(stderr, "    -k : generate a TYPE=KEY key\n");
91
92
93
	fprintf(stderr, "Output:\n");
	fprintf(stderr, "     K<name>+<alg>+<id>.key, "
		"K<name>+<alg>+<id>.private\n");
94
95
96
97

	exit (-1);
}

Brian Wellington's avatar
Brian Wellington committed
98
99
int
main(int argc, char **argv) {
100
	char		*algname = NULL, *nametype = NULL, *type = NULL;
101
	char		*classname = NULL;
Brian Wellington's avatar
Brian Wellington committed
102
	char		*endp;
103
	dst_key_t	*key = NULL, *oldkey;
104
105
	dns_fixedname_t	fname;
	dns_name_t	*name;
106
	isc_uint16_t	flags = 0, ksk = 0;
107
	dns_secalg_t	alg;
Brian Wellington's avatar
Brian Wellington committed
108
	isc_boolean_t	conflict = ISC_FALSE, null_key = ISC_FALSE;
109
110
111
112
113
	isc_mem_t	*mctx = NULL;
	int		ch, rsa_exp = 0, generator = 0, param = 0;
	int		protocol = -1, size = -1, signatory = 0;
	isc_result_t	ret;
	isc_textregion_t r;
114
115
	char		filename[255];
	isc_buffer_t	buf;
116
	isc_log_t	*log = NULL;
Brian Wellington's avatar
Brian Wellington committed
117
	isc_entropy_t	*ectx = NULL;
118
	dns_rdataclass_t rdclass;
119
	int		options = DST_TYPE_PRIVATE | DST_TYPE_PUBLIC;
Brian Wellington's avatar
Brian Wellington committed
120

Brian Wellington's avatar
Brian Wellington committed
121
	if (argc == 1)
122
		usage();
Brian Wellington's avatar
Brian Wellington committed
123

Brian Wellington's avatar
Brian Wellington committed
124
125
	RUNTIME_CHECK(isc_mem_create(0, 0, &mctx) == ISC_R_SUCCESS);

126
127
	dns_result_register();

128
	while ((ch = isc_commandline_parse(argc, argv,
129
					   "a:b:c:ef:g:kn:t:p:s:r:v:h")) != -1)
Brian Wellington's avatar
Brian Wellington committed
130
	{
Brian Wellington's avatar
Brian Wellington committed
131
132
	    switch (ch) {
		case 'a':
133
			algname = isc_commandline_argument;
Brian Wellington's avatar
Brian Wellington committed
134
			break;
Brian Wellington's avatar
Brian Wellington committed
135
136
137
		case 'b':
			size = strtol(isc_commandline_argument, &endp, 10);
			if (*endp != '\0' || size < 0)
138
				fatal("-b requires a non-negative number");
Brian Wellington's avatar
Brian Wellington committed
139
			break;
140
141
142
		case 'c':
			classname = isc_commandline_argument;
			break;
Brian Wellington's avatar
Brian Wellington committed
143
144
		case 'e':
			rsa_exp = 1;
Brian Wellington's avatar
Brian Wellington committed
145
			break;
146
147
		case 'f':
			if (strcasecmp(isc_commandline_argument, "KSK") == 0)
148
				ksk = DNS_KEYFLAG_KSK;
149
150
151
152
			else
				fatal("unknown flag '%s'",
				      isc_commandline_argument);
			break;
153
		case 'g':
154
155
			generator = strtol(isc_commandline_argument,
					   &endp, 10);
Brian Wellington's avatar
Brian Wellington committed
156
			if (*endp != '\0' || generator <= 0)
157
				fatal("-g requires a positive number");
Brian Wellington's avatar
Brian Wellington committed
158
			break;
159
160
161
		case 'k':
			options |= DST_TYPE_KEY;
			break;
Brian Wellington's avatar
Brian Wellington committed
162
		case 'n':
163
			nametype = isc_commandline_argument;
Brian Wellington's avatar
Brian Wellington committed
164
165
			break;
		case 't':
166
			type = isc_commandline_argument;
167
			break;
Brian Wellington's avatar
Brian Wellington committed
168
		case 'p':
Brian Wellington's avatar
Brian Wellington committed
169
170
			protocol = strtol(isc_commandline_argument, &endp, 10);
			if (*endp != '\0' || protocol < 0 || protocol > 255)
171
172
				fatal("-p must be followed by a number "
				      "[0..255]");
Brian Wellington's avatar
Brian Wellington committed
173
174
			break;
		case 's':
175
176
			signatory = strtol(isc_commandline_argument,
					   &endp, 10);
Brian Wellington's avatar
Brian Wellington committed
177
			if (*endp != '\0' || signatory < 0 || signatory > 15)
178
179
				fatal("-s must be followed by a number "
				      "[0..15]");
Brian Wellington's avatar
Brian Wellington committed
180
			break;
181
		case 'r':
182
			setup_entropy(mctx, isc_commandline_argument, &ectx);
183
			break;
184
185
186
187
		case 'v':
			endp = NULL;
			verbose = strtol(isc_commandline_argument, &endp, 0);
			if (*endp != '\0')
188
				fatal("-v must be followed by a number");
189
190
			break;

Brian Wellington's avatar
Brian Wellington committed
191
		case 'h':
192
			usage();
Brian Wellington's avatar
Brian Wellington committed
193
		default:
194
			fprintf(stderr, "%s: invalid argument -%c\n",
195
				program, ch);
196
			usage();
197
		}
Brian Wellington's avatar
Brian Wellington committed
198
199
	}

200
201
	if (ectx == NULL)
		setup_entropy(mctx, NULL, &ectx);
Brian Wellington's avatar
Brian Wellington committed
202
203
204
205
206
	ret = dst_lib_init(mctx, ectx,
			   ISC_ENTROPY_BLOCKING | ISC_ENTROPY_GOODONLY);
	if (ret != ISC_R_SUCCESS)
		fatal("could not initialize dst");

207
208
	setup_logging(verbose, mctx, &log);

209
	if (argc < isc_commandline_index + 1)
210
		fatal("the key name was not specified");
211
	if (argc > isc_commandline_index + 1)
212
		fatal("extraneous arguments");
Brian Wellington's avatar
Brian Wellington committed
213

Brian Wellington's avatar
Brian Wellington committed
214
	if (algname == NULL)
215
		fatal("no algorithm was specified");
216
217
	if (strcasecmp(algname, "HMAC-MD5") == 0) {
		options |= DST_TYPE_KEY;
Brian Wellington's avatar
Brian Wellington committed
218
		alg = DST_ALG_HMACMD5;
219
	} else {
Brian Wellington's avatar
Brian Wellington committed
220
221
222
223
		r.base = algname;
		r.length = strlen(algname);
		ret = dns_secalg_fromtext(&alg, &r);
		if (ret != ISC_R_SUCCESS)
224
			fatal("unknown algorithm %s", algname);
225
226
		if (alg == DST_ALG_DH)
			options |= DST_TYPE_KEY;
Brian Wellington's avatar
Brian Wellington committed
227
	}
Brian Wellington's avatar
Brian Wellington committed
228

229
	if (type != NULL && (options & DST_TYPE_KEY) != 0) {
Brian Wellington's avatar
Brian Wellington committed
230
231
232
233
		if (strcasecmp(type, "NOAUTH") == 0)
			flags |= DNS_KEYTYPE_NOAUTH;
		else if (strcasecmp(type, "NOCONF") == 0)
			flags |= DNS_KEYTYPE_NOCONF;
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
234
		else if (strcasecmp(type, "NOAUTHCONF") == 0) {
Brian Wellington's avatar
Brian Wellington committed
235
			flags |= (DNS_KEYTYPE_NOAUTH | DNS_KEYTYPE_NOCONF);
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
236
237
238
			if (size < 0)
				size = 0;
		}
Brian Wellington's avatar
Brian Wellington committed
239
240
241
		else if (strcasecmp(type, "AUTHCONF") == 0)
			/* nothing */;
		else
242
			fatal("invalid type %s", type);
Brian Wellington's avatar
Brian Wellington committed
243
244
	}

Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
245
	if (size < 0)
246
		fatal("key size not specified (-b option)");
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
247

Brian Wellington's avatar
Brian Wellington committed
248
	switch (alg) {
Brian Wellington's avatar
Brian Wellington committed
249
250
	case DNS_KEYALG_RSAMD5:
	case DNS_KEYALG_RSASHA1:
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
251
		if (size != 0 && (size < 512 || size > MAX_RSA))
252
			fatal("RSA key size %d out of range", size);
Brian Wellington's avatar
Brian Wellington committed
253
254
255
		break;
	case DNS_KEYALG_DH:
		if (size != 0 && (size < 128 || size > 4096))
256
			fatal("DH key size %d out of range", size);
Brian Wellington's avatar
Brian Wellington committed
257
258
		break;
	case DNS_KEYALG_DSA:
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
259
		if (size != 0 && !dsa_size_ok(size))
260
			fatal("invalid DSS key size: %d", size);
Brian Wellington's avatar
Brian Wellington committed
261
262
263
		break;
	case DST_ALG_HMACMD5:
		if (size < 1 || size > 512)
264
			fatal("HMAC-MD5 key size %d out of range", size);
Brian Wellington's avatar
Brian Wellington committed
265
266
267
		break;
	}

Brian Wellington's avatar
Brian Wellington committed
268
269
270
	if (!(alg == DNS_KEYALG_RSAMD5 || alg == DNS_KEYALG_RSASHA1) &&
	    rsa_exp != 0)
		fatal("specified RSA exponent for a non-RSA key");
Brian Wellington's avatar
Brian Wellington committed
271
272

	if (alg != DNS_KEYALG_DH && generator != 0)
Brian Wellington's avatar
Brian Wellington committed
273
		fatal("specified DH generator for a non-DH key");
Brian Wellington's avatar
Brian Wellington committed
274
275

	if (nametype == NULL)
276
		fatal("no nametype specified");
Brian Wellington's avatar
Brian Wellington committed
277
278
	if (strcasecmp(nametype, "zone") == 0)
		flags |= DNS_KEYOWNER_ZONE;
279
280
281
282
283
284
285
286
287
288
	else if ((options & DST_TYPE_KEY) != 0)	{ /* KEY */
		if (strcasecmp(nametype, "host") == 0 ||
			 strcasecmp(nametype, "entity") == 0)
			flags |= DNS_KEYOWNER_ENTITY;
		else if (strcasecmp(nametype, "user") == 0)
			flags |= DNS_KEYOWNER_USER;
		else
			fatal("invalid KEY nametype %s", nametype);
	} else if (strcasecmp(nametype, "other") != 0) /* DNSKEY */
		fatal("invalid DNSKEY nametype %s", nametype);
Brian Wellington's avatar
Brian Wellington committed
289

290
	rdclass = strtoclass(classname);
291

292
293
294
295
	if ((options & DST_TYPE_KEY) != 0)  /* KEY */
		flags |= signatory;
	else if ((flags & DNS_KEYOWNER_ZONE) != 0) /* DNSKEY */
		flags |= ksk;
Brian Wellington's avatar
Brian Wellington committed
296

297
298
	if (protocol == -1)
		protocol = DNS_KEYPROTO_DNSSEC;
299
300
301
	else if ((options & DST_TYPE_KEY) == 0 &&
		 protocol != DNS_KEYPROTO_DNSSEC)
		fatal("invalid DNSKEY protocol: %d", protocol);
Brian Wellington's avatar
Brian Wellington committed
302
303
304

	if ((flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY) {
		if (size > 0)
305
			fatal("specified null key with non-zero size");
Brian Wellington's avatar
Brian Wellington committed
306
		if ((flags & DNS_KEYFLAG_SIGNATORYMASK) != 0)
307
			fatal("specified null key with signing authority");
Brian Wellington's avatar
Brian Wellington committed
308
	}
Brian Wellington's avatar
Brian Wellington committed
309

310
311
	if ((flags & DNS_KEYFLAG_OWNERMASK) == DNS_KEYOWNER_ZONE &&
	    (alg == DNS_KEYALG_DH || alg == DST_ALG_HMACMD5))
312
		fatal("a key with algorithm '%s' cannot be a zone key",
313
314
		      algname);

315
316
317
318
319
320
321
	dns_fixedname_init(&fname);
	name = dns_fixedname_name(&fname);
	isc_buffer_init(&buf, argv[isc_commandline_index],
			strlen(argv[isc_commandline_index]));
	isc_buffer_add(&buf, strlen(argv[isc_commandline_index]));
	ret = dns_name_fromtext(name, &buf, dns_rootname, ISC_FALSE, NULL);
	if (ret != ISC_R_SUCCESS)
322
		fatal("invalid key name %s: %s", argv[isc_commandline_index],
323
		      isc_result_totext(ret));
Brian Wellington's avatar
Brian Wellington committed
324
325

	switch(alg) {
Brian Wellington's avatar
Brian Wellington committed
326
327
	case DNS_KEYALG_RSAMD5:
	case DNS_KEYALG_RSASHA1:
Brian Wellington's avatar
Brian Wellington committed
328
329
330
331
332
333
334
335
336
		param = rsa_exp;
		break;
	case DNS_KEYALG_DH:
		param = generator;
		break;
	case DNS_KEYALG_DSA:
	case DST_ALG_HMACMD5:
		param = 0;
		break;
Brian Wellington's avatar
Brian Wellington committed
337
338
	}

339
340
341
342
343
	if ((flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY)
		null_key = ISC_TRUE;

	isc_buffer_init(&buf, filename, sizeof(filename) - 1);

344
345
	do {
		conflict = ISC_FALSE;
346
		oldkey = NULL;
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
347
348
349

		/* generate the key */
		ret = dst_key_generate(name, alg, size, param, flags, protocol,
350
				       rdclass, mctx, &key);
351
		isc_entropy_stopcallbacksources(ectx);
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
352
353

		if (ret != ISC_R_SUCCESS) {
354
355
			char namestr[DNS_NAME_FORMATSIZE];
			char algstr[ALG_FORMATSIZE];
Andreas Gustafsson's avatar
Andreas Gustafsson committed
356
357
			dns_name_format(name, namestr, sizeof(namestr));
			alg_format(alg, algstr, sizeof(algstr));
358
			fatal("failed to generate key %s/%s: %s\n",
359
			      namestr, algstr, isc_result_totext(ret));
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
360
361
			exit(-1);
		}
362

363
364
		/*
		 * Try to read a key with the same name, alg and id from disk.
365
		 * If there is one we must continue generating a new one
366
367
		 * unless we were asked to generate a null key, in which
		 * case we return failure.
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
368
		 */
369
		ret = dst_key_fromfile(name, dst_key_id(key), alg,
Brian Wellington's avatar
Brian Wellington committed
370
				       DST_TYPE_PRIVATE, NULL, mctx, &oldkey);
371
372
		/* do not overwrite an existing key  */
		if (ret == ISC_R_SUCCESS) {
373
			dst_key_free(&oldkey);
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
374
			conflict = ISC_TRUE;
375
376
			if (null_key)
				break;
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
377
		}
378
379
380
		if (conflict == ISC_TRUE) {
			if (verbose > 0) {
				isc_buffer_clear(&buf);
Brian Wellington's avatar
Brian Wellington committed
381
				ret = dst_key_buildfilename(key, 0, NULL, &buf);
382
383
384
				fprintf(stderr,
					"%s: %s already exists, "
					"generating a new key\n",
385
					program, filename);
386
			}
387
			dst_key_free(&key);
388
		}
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
389

390
	} while (conflict == ISC_TRUE);
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
391

392
	if (conflict)
393
394
		fatal("cannot generate a null key when a key with id 0 "
		      "already exists");
Olafur Gudmundsson's avatar
Olafur Gudmundsson committed
395

396
	ret = dst_key_tofile(key, options, NULL);
397
398
	if (ret != ISC_R_SUCCESS) {
		char keystr[KEY_FORMATSIZE];
Andreas Gustafsson's avatar
Andreas Gustafsson committed
399
		key_format(key, keystr, sizeof(keystr));
400
401
402
		fatal("failed to write key %s: %s\n", keystr,
		      isc_result_totext(ret));
	}
403
404

	isc_buffer_clear(&buf);
Brian Wellington's avatar
Brian Wellington committed
405
	ret = dst_key_buildfilename(key, 0, NULL, &buf);
406
	printf("%s\n", filename);
407
	dst_key_free(&key);
408

Brian Wellington's avatar
Brian Wellington committed
409
	cleanup_logging(&log);
Brian Wellington's avatar
Brian Wellington committed
410
	cleanup_entropy(&ectx);
Brian Wellington's avatar
Brian Wellington committed
411
	dst_lib_destroy();
412
413
	if (verbose > 10)
		isc_mem_stats(mctx, stdout);
Brian Wellington's avatar
Brian Wellington committed
414
	isc_mem_destroy(&mctx);
415
416

	return (0);
Brian Wellington's avatar
Brian Wellington committed
417
}