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