xref: /freebsd/crypto/openssh/ssh-keygen.c (revision 87569f75a91f298c52a71823c04d41cf53c88889)
1 /*
2  * Author: Tatu Ylonen <ylo@cs.hut.fi>
3  * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
4  *                    All rights reserved
5  * Identity and host key generation and maintenance.
6  *
7  * As far as I am concerned, the code I have written for this software
8  * can be used freely for any purpose.  Any derived versions of this
9  * software must be clearly marked as such, and if the derived work is
10  * incompatible with the protocol description in the RFC file, it must be
11  * called by a name other than "ssh" or "Secure Shell".
12  */
13 
14 #include "includes.h"
15 RCSID("$OpenBSD: ssh-keygen.c,v 1.128 2005/07/17 07:17:55 djm Exp $");
16 
17 #include <openssl/evp.h>
18 #include <openssl/pem.h>
19 
20 #include "xmalloc.h"
21 #include "key.h"
22 #include "rsa.h"
23 #include "authfile.h"
24 #include "uuencode.h"
25 #include "buffer.h"
26 #include "bufaux.h"
27 #include "pathnames.h"
28 #include "log.h"
29 #include "misc.h"
30 #include "match.h"
31 #include "hostfile.h"
32 
33 #ifdef SMARTCARD
34 #include "scard.h"
35 #endif
36 #include "dns.h"
37 
38 /* Number of bits in the RSA/DSA key.  This value can be changed on the command line. */
39 u_int32_t bits = 2048;
40 
41 /*
42  * Flag indicating that we just want to change the passphrase.  This can be
43  * set on the command line.
44  */
45 int change_passphrase = 0;
46 
47 /*
48  * Flag indicating that we just want to change the comment.  This can be set
49  * on the command line.
50  */
51 int change_comment = 0;
52 
53 int quiet = 0;
54 
55 /* Flag indicating that we want to hash a known_hosts file */
56 int hash_hosts = 0;
57 /* Flag indicating that we want lookup a host in known_hosts file */
58 int find_host = 0;
59 /* Flag indicating that we want to delete a host from a known_hosts file */
60 int delete_host = 0;
61 
62 /* Flag indicating that we just want to see the key fingerprint */
63 int print_fingerprint = 0;
64 int print_bubblebabble = 0;
65 
66 /* The identity file name, given on the command line or entered by the user. */
67 char identity_file[1024];
68 int have_identity = 0;
69 
70 /* This is set to the passphrase if given on the command line. */
71 char *identity_passphrase = NULL;
72 
73 /* This is set to the new passphrase if given on the command line. */
74 char *identity_new_passphrase = NULL;
75 
76 /* This is set to the new comment if given on the command line. */
77 char *identity_comment = NULL;
78 
79 /* Dump public key file in format used by real and the original SSH 2 */
80 int convert_to_ssh2 = 0;
81 int convert_from_ssh2 = 0;
82 int print_public = 0;
83 int print_generic = 0;
84 
85 char *key_type_name = NULL;
86 
87 /* argv0 */
88 extern char *__progname;
89 
90 char hostname[MAXHOSTNAMELEN];
91 
92 /* moduli.c */
93 int gen_candidates(FILE *, u_int32_t, u_int32_t, BIGNUM *);
94 int prime_test(FILE *, FILE *, u_int32_t, u_int32_t);
95 
96 static void
97 ask_filename(struct passwd *pw, const char *prompt)
98 {
99 	char buf[1024];
100 	char *name = NULL;
101 
102 	if (key_type_name == NULL)
103 		name = _PATH_SSH_CLIENT_ID_RSA;
104 	else
105 		switch (key_type_from_name(key_type_name)) {
106 		case KEY_RSA1:
107 			name = _PATH_SSH_CLIENT_IDENTITY;
108 			break;
109 		case KEY_DSA:
110 			name = _PATH_SSH_CLIENT_ID_DSA;
111 			break;
112 		case KEY_RSA:
113 			name = _PATH_SSH_CLIENT_ID_RSA;
114 			break;
115 		default:
116 			fprintf(stderr, "bad key type");
117 			exit(1);
118 			break;
119 		}
120 
121 	snprintf(identity_file, sizeof(identity_file), "%s/%s", pw->pw_dir, name);
122 	fprintf(stderr, "%s (%s): ", prompt, identity_file);
123 	if (fgets(buf, sizeof(buf), stdin) == NULL)
124 		exit(1);
125 	if (strchr(buf, '\n'))
126 		*strchr(buf, '\n') = 0;
127 	if (strcmp(buf, "") != 0)
128 		strlcpy(identity_file, buf, sizeof(identity_file));
129 	have_identity = 1;
130 }
131 
132 static Key *
133 load_identity(char *filename)
134 {
135 	char *pass;
136 	Key *prv;
137 
138 	prv = key_load_private(filename, "", NULL);
139 	if (prv == NULL) {
140 		if (identity_passphrase)
141 			pass = xstrdup(identity_passphrase);
142 		else
143 			pass = read_passphrase("Enter passphrase: ",
144 			    RP_ALLOW_STDIN);
145 		prv = key_load_private(filename, pass, NULL);
146 		memset(pass, 0, strlen(pass));
147 		xfree(pass);
148 	}
149 	return prv;
150 }
151 
152 #define SSH_COM_PUBLIC_BEGIN		"---- BEGIN SSH2 PUBLIC KEY ----"
153 #define SSH_COM_PUBLIC_END		"---- END SSH2 PUBLIC KEY ----"
154 #define SSH_COM_PRIVATE_BEGIN		"---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----"
155 #define	SSH_COM_PRIVATE_KEY_MAGIC	0x3f6ff9eb
156 
157 static void
158 do_convert_to_ssh2(struct passwd *pw)
159 {
160 	Key *k;
161 	u_int len;
162 	u_char *blob;
163 	struct stat st;
164 
165 	if (!have_identity)
166 		ask_filename(pw, "Enter file in which the key is");
167 	if (stat(identity_file, &st) < 0) {
168 		perror(identity_file);
169 		exit(1);
170 	}
171 	if ((k = key_load_public(identity_file, NULL)) == NULL) {
172 		if ((k = load_identity(identity_file)) == NULL) {
173 			fprintf(stderr, "load failed\n");
174 			exit(1);
175 		}
176 	}
177 	if (k->type == KEY_RSA1) {
178 		fprintf(stderr, "version 1 keys are not supported\n");
179 		exit(1);
180 	}
181 	if (key_to_blob(k, &blob, &len) <= 0) {
182 		fprintf(stderr, "key_to_blob failed\n");
183 		exit(1);
184 	}
185 	fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN);
186 	fprintf(stdout,
187 	    "Comment: \"%u-bit %s, converted from OpenSSH by %s@%s\"\n",
188 	    key_size(k), key_type(k),
189 	    pw->pw_name, hostname);
190 	dump_base64(stdout, blob, len);
191 	fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END);
192 	key_free(k);
193 	xfree(blob);
194 	exit(0);
195 }
196 
197 static void
198 buffer_get_bignum_bits(Buffer *b, BIGNUM *value)
199 {
200 	u_int bignum_bits = buffer_get_int(b);
201 	u_int bytes = (bignum_bits + 7) / 8;
202 
203 	if (buffer_len(b) < bytes)
204 		fatal("buffer_get_bignum_bits: input buffer too small: "
205 		    "need %d have %d", bytes, buffer_len(b));
206 	BN_bin2bn(buffer_ptr(b), bytes, value);
207 	buffer_consume(b, bytes);
208 }
209 
210 static Key *
211 do_convert_private_ssh2_from_blob(u_char *blob, u_int blen)
212 {
213 	Buffer b;
214 	Key *key = NULL;
215 	char *type, *cipher;
216 	u_char *sig, data[] = "abcde12345";
217 	int magic, rlen, ktype, i1, i2, i3, i4;
218 	u_int slen;
219 	u_long e;
220 
221 	buffer_init(&b);
222 	buffer_append(&b, blob, blen);
223 
224 	magic  = buffer_get_int(&b);
225 	if (magic != SSH_COM_PRIVATE_KEY_MAGIC) {
226 		error("bad magic 0x%x != 0x%x", magic, SSH_COM_PRIVATE_KEY_MAGIC);
227 		buffer_free(&b);
228 		return NULL;
229 	}
230 	i1 = buffer_get_int(&b);
231 	type   = buffer_get_string(&b, NULL);
232 	cipher = buffer_get_string(&b, NULL);
233 	i2 = buffer_get_int(&b);
234 	i3 = buffer_get_int(&b);
235 	i4 = buffer_get_int(&b);
236 	debug("ignore (%d %d %d %d)", i1,i2,i3,i4);
237 	if (strcmp(cipher, "none") != 0) {
238 		error("unsupported cipher %s", cipher);
239 		xfree(cipher);
240 		buffer_free(&b);
241 		xfree(type);
242 		return NULL;
243 	}
244 	xfree(cipher);
245 
246 	if (strstr(type, "dsa")) {
247 		ktype = KEY_DSA;
248 	} else if (strstr(type, "rsa")) {
249 		ktype = KEY_RSA;
250 	} else {
251 		buffer_free(&b);
252 		xfree(type);
253 		return NULL;
254 	}
255 	key = key_new_private(ktype);
256 	xfree(type);
257 
258 	switch (key->type) {
259 	case KEY_DSA:
260 		buffer_get_bignum_bits(&b, key->dsa->p);
261 		buffer_get_bignum_bits(&b, key->dsa->g);
262 		buffer_get_bignum_bits(&b, key->dsa->q);
263 		buffer_get_bignum_bits(&b, key->dsa->pub_key);
264 		buffer_get_bignum_bits(&b, key->dsa->priv_key);
265 		break;
266 	case KEY_RSA:
267 		e  = buffer_get_char(&b);
268 		debug("e %lx", e);
269 		if (e < 30) {
270 			e <<= 8;
271 			e += buffer_get_char(&b);
272 			debug("e %lx", e);
273 			e <<= 8;
274 			e += buffer_get_char(&b);
275 			debug("e %lx", e);
276 		}
277 		if (!BN_set_word(key->rsa->e, e)) {
278 			buffer_free(&b);
279 			key_free(key);
280 			return NULL;
281 		}
282 		buffer_get_bignum_bits(&b, key->rsa->d);
283 		buffer_get_bignum_bits(&b, key->rsa->n);
284 		buffer_get_bignum_bits(&b, key->rsa->iqmp);
285 		buffer_get_bignum_bits(&b, key->rsa->q);
286 		buffer_get_bignum_bits(&b, key->rsa->p);
287 		rsa_generate_additional_parameters(key->rsa);
288 		break;
289 	}
290 	rlen = buffer_len(&b);
291 	if (rlen != 0)
292 		error("do_convert_private_ssh2_from_blob: "
293 		    "remaining bytes in key blob %d", rlen);
294 	buffer_free(&b);
295 
296 	/* try the key */
297 	key_sign(key, &sig, &slen, data, sizeof(data));
298 	key_verify(key, sig, slen, data, sizeof(data));
299 	xfree(sig);
300 	return key;
301 }
302 
303 static void
304 do_convert_from_ssh2(struct passwd *pw)
305 {
306 	Key *k;
307 	int blen;
308 	u_int len;
309 	char line[1024], *p;
310 	u_char blob[8096];
311 	char encoded[8096];
312 	struct stat st;
313 	int escaped = 0, private = 0, ok;
314 	FILE *fp;
315 
316 	if (!have_identity)
317 		ask_filename(pw, "Enter file in which the key is");
318 	if (stat(identity_file, &st) < 0) {
319 		perror(identity_file);
320 		exit(1);
321 	}
322 	fp = fopen(identity_file, "r");
323 	if (fp == NULL) {
324 		perror(identity_file);
325 		exit(1);
326 	}
327 	encoded[0] = '\0';
328 	while (fgets(line, sizeof(line), fp)) {
329 		if (!(p = strchr(line, '\n'))) {
330 			fprintf(stderr, "input line too long.\n");
331 			exit(1);
332 		}
333 		if (p > line && p[-1] == '\\')
334 			escaped++;
335 		if (strncmp(line, "----", 4) == 0 ||
336 		    strstr(line, ": ") != NULL) {
337 			if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL)
338 				private = 1;
339 			if (strstr(line, " END ") != NULL) {
340 				break;
341 			}
342 			/* fprintf(stderr, "ignore: %s", line); */
343 			continue;
344 		}
345 		if (escaped) {
346 			escaped--;
347 			/* fprintf(stderr, "escaped: %s", line); */
348 			continue;
349 		}
350 		*p = '\0';
351 		strlcat(encoded, line, sizeof(encoded));
352 	}
353 	len = strlen(encoded);
354 	if (((len % 4) == 3) &&
355 	    (encoded[len-1] == '=') &&
356 	    (encoded[len-2] == '=') &&
357 	    (encoded[len-3] == '='))
358 		encoded[len-3] = '\0';
359 	blen = uudecode(encoded, blob, sizeof(blob));
360 	if (blen < 0) {
361 		fprintf(stderr, "uudecode failed.\n");
362 		exit(1);
363 	}
364 	k = private ?
365 	    do_convert_private_ssh2_from_blob(blob, blen) :
366 	    key_from_blob(blob, blen);
367 	if (k == NULL) {
368 		fprintf(stderr, "decode blob failed.\n");
369 		exit(1);
370 	}
371 	ok = private ?
372 	    (k->type == KEY_DSA ?
373 		 PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, NULL, 0, NULL, NULL) :
374 		 PEM_write_RSAPrivateKey(stdout, k->rsa, NULL, NULL, 0, NULL, NULL)) :
375 	    key_write(k, stdout);
376 	if (!ok) {
377 		fprintf(stderr, "key write failed");
378 		exit(1);
379 	}
380 	key_free(k);
381 	if (!private)
382 		fprintf(stdout, "\n");
383 	fclose(fp);
384 	exit(0);
385 }
386 
387 static void
388 do_print_public(struct passwd *pw)
389 {
390 	Key *prv;
391 	struct stat st;
392 
393 	if (!have_identity)
394 		ask_filename(pw, "Enter file in which the key is");
395 	if (stat(identity_file, &st) < 0) {
396 		perror(identity_file);
397 		exit(1);
398 	}
399 	prv = load_identity(identity_file);
400 	if (prv == NULL) {
401 		fprintf(stderr, "load failed\n");
402 		exit(1);
403 	}
404 	if (!key_write(prv, stdout))
405 		fprintf(stderr, "key_write failed");
406 	key_free(prv);
407 	fprintf(stdout, "\n");
408 	exit(0);
409 }
410 
411 #ifdef SMARTCARD
412 static void
413 do_upload(struct passwd *pw, const char *sc_reader_id)
414 {
415 	Key *prv = NULL;
416 	struct stat st;
417 	int ret;
418 
419 	if (!have_identity)
420 		ask_filename(pw, "Enter file in which the key is");
421 	if (stat(identity_file, &st) < 0) {
422 		perror(identity_file);
423 		exit(1);
424 	}
425 	prv = load_identity(identity_file);
426 	if (prv == NULL) {
427 		error("load failed");
428 		exit(1);
429 	}
430 	ret = sc_put_key(prv, sc_reader_id);
431 	key_free(prv);
432 	if (ret < 0)
433 		exit(1);
434 	logit("loading key done");
435 	exit(0);
436 }
437 
438 static void
439 do_download(struct passwd *pw, const char *sc_reader_id)
440 {
441 	Key **keys = NULL;
442 	int i;
443 
444 	keys = sc_get_keys(sc_reader_id, NULL);
445 	if (keys == NULL)
446 		fatal("cannot read public key from smartcard");
447 	for (i = 0; keys[i]; i++) {
448 		key_write(keys[i], stdout);
449 		key_free(keys[i]);
450 		fprintf(stdout, "\n");
451 	}
452 	xfree(keys);
453 	exit(0);
454 }
455 #endif /* SMARTCARD */
456 
457 static void
458 do_fingerprint(struct passwd *pw)
459 {
460 	FILE *f;
461 	Key *public;
462 	char *comment = NULL, *cp, *ep, line[16*1024], *fp;
463 	int i, skip = 0, num = 1, invalid = 1;
464 	enum fp_rep rep;
465 	enum fp_type fptype;
466 	struct stat st;
467 
468 	fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5;
469 	rep =    print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_HEX;
470 
471 	if (!have_identity)
472 		ask_filename(pw, "Enter file in which the key is");
473 	if (stat(identity_file, &st) < 0) {
474 		perror(identity_file);
475 		exit(1);
476 	}
477 	public = key_load_public(identity_file, &comment);
478 	if (public != NULL) {
479 		fp = key_fingerprint(public, fptype, rep);
480 		printf("%u %s %s\n", key_size(public), fp, comment);
481 		key_free(public);
482 		xfree(comment);
483 		xfree(fp);
484 		exit(0);
485 	}
486 	if (comment)
487 		xfree(comment);
488 
489 	f = fopen(identity_file, "r");
490 	if (f != NULL) {
491 		while (fgets(line, sizeof(line), f)) {
492 			i = strlen(line) - 1;
493 			if (line[i] != '\n') {
494 				error("line %d too long: %.40s...", num, line);
495 				skip = 1;
496 				continue;
497 			}
498 			num++;
499 			if (skip) {
500 				skip = 0;
501 				continue;
502 			}
503 			line[i] = '\0';
504 
505 			/* Skip leading whitespace, empty and comment lines. */
506 			for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
507 				;
508 			if (!*cp || *cp == '\n' || *cp == '#')
509 				continue ;
510 			i = strtol(cp, &ep, 10);
511 			if (i == 0 || ep == NULL || (*ep != ' ' && *ep != '\t')) {
512 				int quoted = 0;
513 				comment = cp;
514 				for (; *cp && (quoted || (*cp != ' ' &&
515 				    *cp != '\t')); cp++) {
516 					if (*cp == '\\' && cp[1] == '"')
517 						cp++;	/* Skip both */
518 					else if (*cp == '"')
519 						quoted = !quoted;
520 				}
521 				if (!*cp)
522 					continue;
523 				*cp++ = '\0';
524 			}
525 			ep = cp;
526 			public = key_new(KEY_RSA1);
527 			if (key_read(public, &cp) != 1) {
528 				cp = ep;
529 				key_free(public);
530 				public = key_new(KEY_UNSPEC);
531 				if (key_read(public, &cp) != 1) {
532 					key_free(public);
533 					continue;
534 				}
535 			}
536 			comment = *cp ? cp : comment;
537 			fp = key_fingerprint(public, fptype, rep);
538 			printf("%u %s %s\n", key_size(public), fp,
539 			    comment ? comment : "no comment");
540 			xfree(fp);
541 			key_free(public);
542 			invalid = 0;
543 		}
544 		fclose(f);
545 	}
546 	if (invalid) {
547 		printf("%s is not a public key file.\n", identity_file);
548 		exit(1);
549 	}
550 	exit(0);
551 }
552 
553 static void
554 print_host(FILE *f, char *name, Key *public, int hash)
555 {
556 	if (hash && (name = host_hash(name, NULL, 0)) == NULL)
557 		fatal("hash_host failed");
558 	fprintf(f, "%s ", name);
559 	if (!key_write(public, f))
560 		fatal("key_write failed");
561 	fprintf(f, "\n");
562 }
563 
564 static void
565 do_known_hosts(struct passwd *pw, const char *name)
566 {
567 	FILE *in, *out = stdout;
568 	Key *public;
569 	char *cp, *cp2, *kp, *kp2;
570 	char line[16*1024], tmp[MAXPATHLEN], old[MAXPATHLEN];
571 	int c, i, skip = 0, inplace = 0, num = 0, invalid = 0, has_unhashed = 0;
572 
573 	if (!have_identity) {
574 		cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid);
575 		if (strlcpy(identity_file, cp, sizeof(identity_file)) >=
576 		    sizeof(identity_file))
577 			fatal("Specified known hosts path too long");
578 		xfree(cp);
579 		have_identity = 1;
580 	}
581 	if ((in = fopen(identity_file, "r")) == NULL)
582 		fatal("fopen: %s", strerror(errno));
583 
584 	/*
585 	 * Find hosts goes to stdout, hash and deletions happen in-place
586 	 * A corner case is ssh-keygen -HF foo, which should go to stdout
587 	 */
588 	if (!find_host && (hash_hosts || delete_host)) {
589 		if (strlcpy(tmp, identity_file, sizeof(tmp)) >= sizeof(tmp) ||
590 		    strlcat(tmp, ".XXXXXXXXXX", sizeof(tmp)) >= sizeof(tmp) ||
591 		    strlcpy(old, identity_file, sizeof(old)) >= sizeof(old) ||
592 		    strlcat(old, ".old", sizeof(old)) >= sizeof(old))
593 			fatal("known_hosts path too long");
594 		umask(077);
595 		if ((c = mkstemp(tmp)) == -1)
596 			fatal("mkstemp: %s", strerror(errno));
597 		if ((out = fdopen(c, "w")) == NULL) {
598 			c = errno;
599 			unlink(tmp);
600 			fatal("fdopen: %s", strerror(c));
601 		}
602 		inplace = 1;
603 	}
604 
605 	while (fgets(line, sizeof(line), in)) {
606 		num++;
607 		i = strlen(line) - 1;
608 		if (line[i] != '\n') {
609 			error("line %d too long: %.40s...", num, line);
610 			skip = 1;
611 			invalid = 1;
612 			continue;
613 		}
614 		if (skip) {
615 			skip = 0;
616 			continue;
617 		}
618 		line[i] = '\0';
619 
620 		/* Skip leading whitespace, empty and comment lines. */
621 		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
622 			;
623 		if (!*cp || *cp == '\n' || *cp == '#') {
624 			if (inplace)
625 				fprintf(out, "%s\n", cp);
626 			continue;
627 		}
628 		/* Find the end of the host name portion. */
629 		for (kp = cp; *kp && *kp != ' ' && *kp != '\t'; kp++)
630 			;
631 		if (*kp == '\0' || *(kp + 1) == '\0') {
632 			error("line %d missing key: %.40s...",
633 			    num, line);
634 			invalid = 1;
635 			continue;
636 		}
637 		*kp++ = '\0';
638 		kp2 = kp;
639 
640 		public = key_new(KEY_RSA1);
641 		if (key_read(public, &kp) != 1) {
642 			kp = kp2;
643 			key_free(public);
644 			public = key_new(KEY_UNSPEC);
645 			if (key_read(public, &kp) != 1) {
646 				error("line %d invalid key: %.40s...",
647 				    num, line);
648 				key_free(public);
649 				invalid = 1;
650 				continue;
651 			}
652 		}
653 
654 		if (*cp == HASH_DELIM) {
655 			if (find_host || delete_host) {
656 				cp2 = host_hash(name, cp, strlen(cp));
657 				if (cp2 == NULL) {
658 					error("line %d: invalid hashed "
659 					    "name: %.64s...", num, line);
660 					invalid = 1;
661 					continue;
662 				}
663 				c = (strcmp(cp2, cp) == 0);
664 				if (find_host && c) {
665 					printf("# Host %s found: "
666 					    "line %d type %s\n", name,
667 					    num, key_type(public));
668 					print_host(out, cp, public, 0);
669 				}
670 				if (delete_host && !c)
671 					print_host(out, cp, public, 0);
672 			} else if (hash_hosts)
673 				print_host(out, cp, public, 0);
674 		} else {
675 			if (find_host || delete_host) {
676 				c = (match_hostname(name, cp,
677 				    strlen(cp)) == 1);
678 				if (find_host && c) {
679 					printf("# Host %s found: "
680 					    "line %d type %s\n", name,
681 					    num, key_type(public));
682 					print_host(out, cp, public, hash_hosts);
683 				}
684 				if (delete_host && !c)
685 					print_host(out, cp, public, 0);
686 			} else if (hash_hosts) {
687 				for (cp2 = strsep(&cp, ",");
688 				    cp2 != NULL && *cp2 != '\0';
689 				    cp2 = strsep(&cp, ",")) {
690 					if (strcspn(cp2, "*?!") != strlen(cp2))
691 						fprintf(stderr, "Warning: "
692 						    "ignoring host name with "
693 						    "metacharacters: %.64s\n",
694 						    cp2);
695 					else
696 						print_host(out, cp2, public, 1);
697 				}
698 				has_unhashed = 1;
699 			}
700 		}
701 		key_free(public);
702 	}
703 	fclose(in);
704 
705 	if (invalid) {
706 		fprintf(stderr, "%s is not a valid known_host file.\n",
707 		    identity_file);
708 		if (inplace) {
709 			fprintf(stderr, "Not replacing existing known_hosts "
710 			    "file because of errors\n");
711 			fclose(out);
712 			unlink(tmp);
713 		}
714 		exit(1);
715 	}
716 
717 	if (inplace) {
718 		fclose(out);
719 
720 		/* Backup existing file */
721 		if (unlink(old) == -1 && errno != ENOENT)
722 			fatal("unlink %.100s: %s", old, strerror(errno));
723 		if (link(identity_file, old) == -1)
724 			fatal("link %.100s to %.100s: %s", identity_file, old,
725 			    strerror(errno));
726 		/* Move new one into place */
727 		if (rename(tmp, identity_file) == -1) {
728 			error("rename\"%s\" to \"%s\": %s", tmp, identity_file,
729 			    strerror(errno));
730 			unlink(tmp);
731 			unlink(old);
732 			exit(1);
733 		}
734 
735 		fprintf(stderr, "%s updated.\n", identity_file);
736 		fprintf(stderr, "Original contents retained as %s\n", old);
737 		if (has_unhashed) {
738 			fprintf(stderr, "WARNING: %s contains unhashed "
739 			    "entries\n", old);
740 			fprintf(stderr, "Delete this file to ensure privacy "
741 			    "of hostnames\n");
742 		}
743 	}
744 
745 	exit(0);
746 }
747 
748 /*
749  * Perform changing a passphrase.  The argument is the passwd structure
750  * for the current user.
751  */
752 static void
753 do_change_passphrase(struct passwd *pw)
754 {
755 	char *comment;
756 	char *old_passphrase, *passphrase1, *passphrase2;
757 	struct stat st;
758 	Key *private;
759 
760 	if (!have_identity)
761 		ask_filename(pw, "Enter file in which the key is");
762 	if (stat(identity_file, &st) < 0) {
763 		perror(identity_file);
764 		exit(1);
765 	}
766 	/* Try to load the file with empty passphrase. */
767 	private = key_load_private(identity_file, "", &comment);
768 	if (private == NULL) {
769 		if (identity_passphrase)
770 			old_passphrase = xstrdup(identity_passphrase);
771 		else
772 			old_passphrase =
773 			    read_passphrase("Enter old passphrase: ",
774 			    RP_ALLOW_STDIN);
775 		private = key_load_private(identity_file, old_passphrase,
776 		    &comment);
777 		memset(old_passphrase, 0, strlen(old_passphrase));
778 		xfree(old_passphrase);
779 		if (private == NULL) {
780 			printf("Bad passphrase.\n");
781 			exit(1);
782 		}
783 	}
784 	printf("Key has comment '%s'\n", comment);
785 
786 	/* Ask the new passphrase (twice). */
787 	if (identity_new_passphrase) {
788 		passphrase1 = xstrdup(identity_new_passphrase);
789 		passphrase2 = NULL;
790 	} else {
791 		passphrase1 =
792 			read_passphrase("Enter new passphrase (empty for no "
793 			    "passphrase): ", RP_ALLOW_STDIN);
794 		passphrase2 = read_passphrase("Enter same passphrase again: ",
795 		    RP_ALLOW_STDIN);
796 
797 		/* Verify that they are the same. */
798 		if (strcmp(passphrase1, passphrase2) != 0) {
799 			memset(passphrase1, 0, strlen(passphrase1));
800 			memset(passphrase2, 0, strlen(passphrase2));
801 			xfree(passphrase1);
802 			xfree(passphrase2);
803 			printf("Pass phrases do not match.  Try again.\n");
804 			exit(1);
805 		}
806 		/* Destroy the other copy. */
807 		memset(passphrase2, 0, strlen(passphrase2));
808 		xfree(passphrase2);
809 	}
810 
811 	/* Save the file using the new passphrase. */
812 	if (!key_save_private(private, identity_file, passphrase1, comment)) {
813 		printf("Saving the key failed: %s.\n", identity_file);
814 		memset(passphrase1, 0, strlen(passphrase1));
815 		xfree(passphrase1);
816 		key_free(private);
817 		xfree(comment);
818 		exit(1);
819 	}
820 	/* Destroy the passphrase and the copy of the key in memory. */
821 	memset(passphrase1, 0, strlen(passphrase1));
822 	xfree(passphrase1);
823 	key_free(private);		 /* Destroys contents */
824 	xfree(comment);
825 
826 	printf("Your identification has been saved with the new passphrase.\n");
827 	exit(0);
828 }
829 
830 /*
831  * Print the SSHFP RR.
832  */
833 static void
834 do_print_resource_record(struct passwd *pw, char *hname)
835 {
836 	Key *public;
837 	char *comment = NULL;
838 	struct stat st;
839 
840 	if (!have_identity)
841 		ask_filename(pw, "Enter file in which the key is");
842 	if (stat(identity_file, &st) < 0) {
843 		perror(identity_file);
844 		exit(1);
845 	}
846 	public = key_load_public(identity_file, &comment);
847 	if (public != NULL) {
848 		export_dns_rr(hname, public, stdout, print_generic);
849 		key_free(public);
850 		xfree(comment);
851 		exit(0);
852 	}
853 	if (comment)
854 		xfree(comment);
855 
856 	printf("failed to read v2 public key from %s.\n", identity_file);
857 	exit(1);
858 }
859 
860 /*
861  * Change the comment of a private key file.
862  */
863 static void
864 do_change_comment(struct passwd *pw)
865 {
866 	char new_comment[1024], *comment, *passphrase;
867 	Key *private;
868 	Key *public;
869 	struct stat st;
870 	FILE *f;
871 	int fd;
872 
873 	if (!have_identity)
874 		ask_filename(pw, "Enter file in which the key is");
875 	if (stat(identity_file, &st) < 0) {
876 		perror(identity_file);
877 		exit(1);
878 	}
879 	private = key_load_private(identity_file, "", &comment);
880 	if (private == NULL) {
881 		if (identity_passphrase)
882 			passphrase = xstrdup(identity_passphrase);
883 		else if (identity_new_passphrase)
884 			passphrase = xstrdup(identity_new_passphrase);
885 		else
886 			passphrase = read_passphrase("Enter passphrase: ",
887 			    RP_ALLOW_STDIN);
888 		/* Try to load using the passphrase. */
889 		private = key_load_private(identity_file, passphrase, &comment);
890 		if (private == NULL) {
891 			memset(passphrase, 0, strlen(passphrase));
892 			xfree(passphrase);
893 			printf("Bad passphrase.\n");
894 			exit(1);
895 		}
896 	} else {
897 		passphrase = xstrdup("");
898 	}
899 	if (private->type != KEY_RSA1) {
900 		fprintf(stderr, "Comments are only supported for RSA1 keys.\n");
901 		key_free(private);
902 		exit(1);
903 	}
904 	printf("Key now has comment '%s'\n", comment);
905 
906 	if (identity_comment) {
907 		strlcpy(new_comment, identity_comment, sizeof(new_comment));
908 	} else {
909 		printf("Enter new comment: ");
910 		fflush(stdout);
911 		if (!fgets(new_comment, sizeof(new_comment), stdin)) {
912 			memset(passphrase, 0, strlen(passphrase));
913 			key_free(private);
914 			exit(1);
915 		}
916 		if (strchr(new_comment, '\n'))
917 			*strchr(new_comment, '\n') = 0;
918 	}
919 
920 	/* Save the file using the new passphrase. */
921 	if (!key_save_private(private, identity_file, passphrase, new_comment)) {
922 		printf("Saving the key failed: %s.\n", identity_file);
923 		memset(passphrase, 0, strlen(passphrase));
924 		xfree(passphrase);
925 		key_free(private);
926 		xfree(comment);
927 		exit(1);
928 	}
929 	memset(passphrase, 0, strlen(passphrase));
930 	xfree(passphrase);
931 	public = key_from_private(private);
932 	key_free(private);
933 
934 	strlcat(identity_file, ".pub", sizeof(identity_file));
935 	fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
936 	if (fd == -1) {
937 		printf("Could not save your public key in %s\n", identity_file);
938 		exit(1);
939 	}
940 	f = fdopen(fd, "w");
941 	if (f == NULL) {
942 		printf("fdopen %s failed", identity_file);
943 		exit(1);
944 	}
945 	if (!key_write(public, f))
946 		fprintf(stderr, "write key failed");
947 	key_free(public);
948 	fprintf(f, " %s\n", new_comment);
949 	fclose(f);
950 
951 	xfree(comment);
952 
953 	printf("The comment in your key file has been changed.\n");
954 	exit(0);
955 }
956 
957 static void
958 usage(void)
959 {
960 	fprintf(stderr, "Usage: %s [options]\n", __progname);
961 	fprintf(stderr, "Options:\n");
962 	fprintf(stderr, "  -a trials   Number of trials for screening DH-GEX moduli.\n");
963 	fprintf(stderr, "  -B          Show bubblebabble digest of key file.\n");
964 	fprintf(stderr, "  -b bits     Number of bits in the key to create.\n");
965 	fprintf(stderr, "  -C comment  Provide new comment.\n");
966 	fprintf(stderr, "  -c          Change comment in private and public key files.\n");
967 #ifdef SMARTCARD
968 	fprintf(stderr, "  -D reader   Download public key from smartcard.\n");
969 #endif /* SMARTCARD */
970 	fprintf(stderr, "  -e          Convert OpenSSH to IETF SECSH key file.\n");
971 	fprintf(stderr, "  -F hostname Find hostname in known hosts file.\n");
972 	fprintf(stderr, "  -f filename Filename of the key file.\n");
973 	fprintf(stderr, "  -G file     Generate candidates for DH-GEX moduli.\n");
974 	fprintf(stderr, "  -g          Use generic DNS resource record format.\n");
975 	fprintf(stderr, "  -H          Hash names in known_hosts file.\n");
976 	fprintf(stderr, "  -i          Convert IETF SECSH to OpenSSH key file.\n");
977 	fprintf(stderr, "  -l          Show fingerprint of key file.\n");
978 	fprintf(stderr, "  -M memory   Amount of memory (MB) to use for generating DH-GEX moduli.\n");
979 	fprintf(stderr, "  -N phrase   Provide new passphrase.\n");
980 	fprintf(stderr, "  -P phrase   Provide old passphrase.\n");
981 	fprintf(stderr, "  -p          Change passphrase of private key file.\n");
982 	fprintf(stderr, "  -q          Quiet.\n");
983 	fprintf(stderr, "  -R hostname Remove host from known_hosts file.\n");
984 	fprintf(stderr, "  -r hostname Print DNS resource record.\n");
985 	fprintf(stderr, "  -S start    Start point (hex) for generating DH-GEX moduli.\n");
986 	fprintf(stderr, "  -T file     Screen candidates for DH-GEX moduli.\n");
987 	fprintf(stderr, "  -t type     Specify type of key to create.\n");
988 #ifdef SMARTCARD
989 	fprintf(stderr, "  -U reader   Upload private key to smartcard.\n");
990 #endif /* SMARTCARD */
991 	fprintf(stderr, "  -v          Verbose.\n");
992 	fprintf(stderr, "  -W gen      Generator to use for generating DH-GEX moduli.\n");
993 	fprintf(stderr, "  -y          Read private key file and print public key.\n");
994 
995 	exit(1);
996 }
997 
998 /*
999  * Main program for key management.
1000  */
1001 int
1002 main(int ac, char **av)
1003 {
1004 	char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2;
1005 	char out_file[MAXPATHLEN], *reader_id = NULL;
1006 	char *rr_hostname = NULL;
1007 	Key *private, *public;
1008 	struct passwd *pw;
1009 	struct stat st;
1010 	int opt, type, fd, download = 0;
1011 	u_int32_t memory = 0, generator_wanted = 0, trials = 100;
1012 	int do_gen_candidates = 0, do_screen_candidates = 0;
1013 	int log_level = SYSLOG_LEVEL_INFO;
1014 	BIGNUM *start = NULL;
1015 	FILE *f;
1016 	const char *errstr;
1017 
1018 	extern int optind;
1019 	extern char *optarg;
1020 
1021 	__progname = ssh_get_progname(av[0]);
1022 
1023 	SSLeay_add_all_algorithms();
1024 	log_init(av[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1);
1025 
1026 	init_rng();
1027 	seed_rng();
1028 
1029 	/* we need this for the home * directory.  */
1030 	pw = getpwuid(getuid());
1031 	if (!pw) {
1032 		printf("You don't exist, go away!\n");
1033 		exit(1);
1034 	}
1035 	if (gethostname(hostname, sizeof(hostname)) < 0) {
1036 		perror("gethostname");
1037 		exit(1);
1038 	}
1039 
1040 	while ((opt = getopt(ac, av,
1041 	    "degiqpclBHvxXyF:b:f:t:U:D:P:N:C:r:g:R:T:G:M:S:a:W:")) != -1) {
1042 		switch (opt) {
1043 		case 'b':
1044 			bits = strtonum(optarg, 512, 32768, &errstr);
1045 			if (errstr)
1046 				fatal("Bits has bad value %s (%s)",
1047 					optarg, errstr);
1048 			break;
1049 		case 'F':
1050 			find_host = 1;
1051 			rr_hostname = optarg;
1052 			break;
1053 		case 'H':
1054 			hash_hosts = 1;
1055 			break;
1056 		case 'R':
1057 			delete_host = 1;
1058 			rr_hostname = optarg;
1059 			break;
1060 		case 'l':
1061 			print_fingerprint = 1;
1062 			break;
1063 		case 'B':
1064 			print_bubblebabble = 1;
1065 			break;
1066 		case 'p':
1067 			change_passphrase = 1;
1068 			break;
1069 		case 'c':
1070 			change_comment = 1;
1071 			break;
1072 		case 'f':
1073 			if (strlcpy(identity_file, optarg, sizeof(identity_file)) >=
1074 			    sizeof(identity_file))
1075 				fatal("Identity filename too long");
1076 			have_identity = 1;
1077 			break;
1078 		case 'g':
1079 			print_generic = 1;
1080 			break;
1081 		case 'P':
1082 			identity_passphrase = optarg;
1083 			break;
1084 		case 'N':
1085 			identity_new_passphrase = optarg;
1086 			break;
1087 		case 'C':
1088 			identity_comment = optarg;
1089 			break;
1090 		case 'q':
1091 			quiet = 1;
1092 			break;
1093 		case 'e':
1094 		case 'x':
1095 			/* export key */
1096 			convert_to_ssh2 = 1;
1097 			break;
1098 		case 'i':
1099 		case 'X':
1100 			/* import key */
1101 			convert_from_ssh2 = 1;
1102 			break;
1103 		case 'y':
1104 			print_public = 1;
1105 			break;
1106 		case 'd':
1107 			key_type_name = "dsa";
1108 			break;
1109 		case 't':
1110 			key_type_name = optarg;
1111 			break;
1112 		case 'D':
1113 			download = 1;
1114 		case 'U':
1115 			reader_id = optarg;
1116 			break;
1117 		case 'v':
1118 			if (log_level == SYSLOG_LEVEL_INFO)
1119 				log_level = SYSLOG_LEVEL_DEBUG1;
1120 			else {
1121 				if (log_level >= SYSLOG_LEVEL_DEBUG1 &&
1122 				    log_level < SYSLOG_LEVEL_DEBUG3)
1123 					log_level++;
1124 			}
1125 			break;
1126 		case 'r':
1127 			rr_hostname = optarg;
1128 			break;
1129 		case 'W':
1130 			generator_wanted = strtonum(optarg, 1, UINT_MAX, &errstr);
1131 			if (errstr)
1132 				fatal("Desired generator has bad value: %s (%s)",
1133 					optarg, errstr);
1134 			break;
1135 		case 'a':
1136 			trials = strtonum(optarg, 1, UINT_MAX, &errstr);
1137 			if (errstr)
1138 				fatal("Invalid number of trials: %s (%s)",
1139 					optarg, errstr);
1140 			break;
1141 		case 'M':
1142 			memory = strtonum(optarg, 1, UINT_MAX, &errstr);
1143 			if (errstr) {
1144 				fatal("Memory limit is %s: %s", errstr, optarg);
1145 			}
1146 			break;
1147 		case 'G':
1148 			do_gen_candidates = 1;
1149 			if (strlcpy(out_file, optarg, sizeof(out_file)) >=
1150 			    sizeof(out_file))
1151 				fatal("Output filename too long");
1152 			break;
1153 		case 'T':
1154 			do_screen_candidates = 1;
1155 			if (strlcpy(out_file, optarg, sizeof(out_file)) >=
1156 			    sizeof(out_file))
1157 				fatal("Output filename too long");
1158 			break;
1159 		case 'S':
1160 			/* XXX - also compare length against bits */
1161 			if (BN_hex2bn(&start, optarg) == 0)
1162 				fatal("Invalid start point.");
1163 			break;
1164 		case '?':
1165 		default:
1166 			usage();
1167 		}
1168 	}
1169 
1170 	/* reinit */
1171 	log_init(av[0], log_level, SYSLOG_FACILITY_USER, 1);
1172 
1173 	if (optind < ac) {
1174 		printf("Too many arguments.\n");
1175 		usage();
1176 	}
1177 	if (change_passphrase && change_comment) {
1178 		printf("Can only have one of -p and -c.\n");
1179 		usage();
1180 	}
1181 	if (delete_host || hash_hosts || find_host)
1182 		do_known_hosts(pw, rr_hostname);
1183 	if (print_fingerprint || print_bubblebabble)
1184 		do_fingerprint(pw);
1185 	if (change_passphrase)
1186 		do_change_passphrase(pw);
1187 	if (change_comment)
1188 		do_change_comment(pw);
1189 	if (convert_to_ssh2)
1190 		do_convert_to_ssh2(pw);
1191 	if (convert_from_ssh2)
1192 		do_convert_from_ssh2(pw);
1193 	if (print_public)
1194 		do_print_public(pw);
1195 	if (rr_hostname != NULL) {
1196 		do_print_resource_record(pw, rr_hostname);
1197 	}
1198 	if (reader_id != NULL) {
1199 #ifdef SMARTCARD
1200 		if (download)
1201 			do_download(pw, reader_id);
1202 		else
1203 			do_upload(pw, reader_id);
1204 #else /* SMARTCARD */
1205 		fatal("no support for smartcards.");
1206 #endif /* SMARTCARD */
1207 	}
1208 
1209 	if (do_gen_candidates) {
1210 		FILE *out = fopen(out_file, "w");
1211 
1212 		if (out == NULL) {
1213 			error("Couldn't open modulus candidate file \"%s\": %s",
1214 			    out_file, strerror(errno));
1215 			return (1);
1216 		}
1217 		if (gen_candidates(out, memory, bits, start) != 0)
1218 			fatal("modulus candidate generation failed\n");
1219 
1220 		return (0);
1221 	}
1222 
1223 	if (do_screen_candidates) {
1224 		FILE *in;
1225 		FILE *out = fopen(out_file, "w");
1226 
1227 		if (have_identity && strcmp(identity_file, "-") != 0) {
1228 			if ((in = fopen(identity_file, "r")) == NULL) {
1229 				fatal("Couldn't open modulus candidate "
1230 				    "file \"%s\": %s", identity_file,
1231 				    strerror(errno));
1232 			}
1233 		} else
1234 			in = stdin;
1235 
1236 		if (out == NULL) {
1237 			fatal("Couldn't open moduli file \"%s\": %s",
1238 			    out_file, strerror(errno));
1239 		}
1240 		if (prime_test(in, out, trials, generator_wanted) != 0)
1241 			fatal("modulus screening failed\n");
1242 		return (0);
1243 	}
1244 
1245 	arc4random_stir();
1246 
1247 	if (key_type_name == NULL) {
1248 		printf("You must specify a key type (-t).\n");
1249 		usage();
1250 	}
1251 	type = key_type_from_name(key_type_name);
1252 	if (type == KEY_UNSPEC) {
1253 		fprintf(stderr, "unknown key type %s\n", key_type_name);
1254 		exit(1);
1255 	}
1256 	if (!quiet)
1257 		printf("Generating public/private %s key pair.\n", key_type_name);
1258 	private = key_generate(type, bits);
1259 	if (private == NULL) {
1260 		fprintf(stderr, "key_generate failed");
1261 		exit(1);
1262 	}
1263 	public  = key_from_private(private);
1264 
1265 	if (!have_identity)
1266 		ask_filename(pw, "Enter file in which to save the key");
1267 
1268 	/* Create ~/.ssh directory if it doesn\'t already exist. */
1269 	snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, _PATH_SSH_USER_DIR);
1270 	if (strstr(identity_file, dotsshdir) != NULL &&
1271 	    stat(dotsshdir, &st) < 0) {
1272 		if (mkdir(dotsshdir, 0700) < 0)
1273 			error("Could not create directory '%s'.", dotsshdir);
1274 		else if (!quiet)
1275 			printf("Created directory '%s'.\n", dotsshdir);
1276 	}
1277 	/* If the file already exists, ask the user to confirm. */
1278 	if (stat(identity_file, &st) >= 0) {
1279 		char yesno[3];
1280 		printf("%s already exists.\n", identity_file);
1281 		printf("Overwrite (y/n)? ");
1282 		fflush(stdout);
1283 		if (fgets(yesno, sizeof(yesno), stdin) == NULL)
1284 			exit(1);
1285 		if (yesno[0] != 'y' && yesno[0] != 'Y')
1286 			exit(1);
1287 	}
1288 	/* Ask for a passphrase (twice). */
1289 	if (identity_passphrase)
1290 		passphrase1 = xstrdup(identity_passphrase);
1291 	else if (identity_new_passphrase)
1292 		passphrase1 = xstrdup(identity_new_passphrase);
1293 	else {
1294 passphrase_again:
1295 		passphrase1 =
1296 			read_passphrase("Enter passphrase (empty for no "
1297 			    "passphrase): ", RP_ALLOW_STDIN);
1298 		passphrase2 = read_passphrase("Enter same passphrase again: ",
1299 		    RP_ALLOW_STDIN);
1300 		if (strcmp(passphrase1, passphrase2) != 0) {
1301 			/*
1302 			 * The passphrases do not match.  Clear them and
1303 			 * retry.
1304 			 */
1305 			memset(passphrase1, 0, strlen(passphrase1));
1306 			memset(passphrase2, 0, strlen(passphrase2));
1307 			xfree(passphrase1);
1308 			xfree(passphrase2);
1309 			printf("Passphrases do not match.  Try again.\n");
1310 			goto passphrase_again;
1311 		}
1312 		/* Clear the other copy of the passphrase. */
1313 		memset(passphrase2, 0, strlen(passphrase2));
1314 		xfree(passphrase2);
1315 	}
1316 
1317 	if (identity_comment) {
1318 		strlcpy(comment, identity_comment, sizeof(comment));
1319 	} else {
1320 		/* Create default commend field for the passphrase. */
1321 		snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname);
1322 	}
1323 
1324 	/* Save the key with the given passphrase and comment. */
1325 	if (!key_save_private(private, identity_file, passphrase1, comment)) {
1326 		printf("Saving the key failed: %s.\n", identity_file);
1327 		memset(passphrase1, 0, strlen(passphrase1));
1328 		xfree(passphrase1);
1329 		exit(1);
1330 	}
1331 	/* Clear the passphrase. */
1332 	memset(passphrase1, 0, strlen(passphrase1));
1333 	xfree(passphrase1);
1334 
1335 	/* Clear the private key and the random number generator. */
1336 	key_free(private);
1337 	arc4random_stir();
1338 
1339 	if (!quiet)
1340 		printf("Your identification has been saved in %s.\n", identity_file);
1341 
1342 	strlcat(identity_file, ".pub", sizeof(identity_file));
1343 	fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
1344 	if (fd == -1) {
1345 		printf("Could not save your public key in %s\n", identity_file);
1346 		exit(1);
1347 	}
1348 	f = fdopen(fd, "w");
1349 	if (f == NULL) {
1350 		printf("fdopen %s failed", identity_file);
1351 		exit(1);
1352 	}
1353 	if (!key_write(public, f))
1354 		fprintf(stderr, "write key failed");
1355 	fprintf(f, " %s\n", comment);
1356 	fclose(f);
1357 
1358 	if (!quiet) {
1359 		char *fp = key_fingerprint(public, SSH_FP_MD5, SSH_FP_HEX);
1360 		printf("Your public key has been saved in %s.\n",
1361 		    identity_file);
1362 		printf("The key fingerprint is:\n");
1363 		printf("%s %s\n", fp, comment);
1364 		xfree(fp);
1365 	}
1366 
1367 	key_free(public);
1368 	exit(0);
1369 }
1370