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