ssh-keygen.c revision 65668
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"
15RCSID("$OpenBSD: ssh-keygen.c,v 1.31 2000/09/07 20:27:54 deraadt Exp $");
16
17#include <openssl/evp.h>
18#include <openssl/pem.h>
19#include <openssl/rsa.h>
20#include <openssl/dsa.h>
21
22#include "ssh.h"
23#include "xmalloc.h"
24#include "key.h"
25#include "rsa.h"
26#include "dsa.h"
27#include "authfile.h"
28#include "uuencode.h"
29
30/* Number of bits in the RSA/DSA key.  This value can be changed on the command line. */
31int bits = 1024;
32
33/*
34 * Flag indicating that we just want to change the passphrase.  This can be
35 * set on the command line.
36 */
37int change_passphrase = 0;
38
39/*
40 * Flag indicating that we just want to change the comment.  This can be set
41 * on the command line.
42 */
43int change_comment = 0;
44
45int quiet = 0;
46
47/* Flag indicating that we just want to see the key fingerprint */
48int print_fingerprint = 0;
49
50/* The identity file name, given on the command line or entered by the user. */
51char identity_file[1024];
52int have_identity = 0;
53
54/* This is set to the passphrase if given on the command line. */
55char *identity_passphrase = NULL;
56
57/* This is set to the new passphrase if given on the command line. */
58char *identity_new_passphrase = NULL;
59
60/* This is set to the new comment if given on the command line. */
61char *identity_comment = NULL;
62
63/* Dump public key file in format used by real and the original SSH 2 */
64int convert_to_ssh2 = 0;
65int convert_from_ssh2 = 0;
66int print_public = 0;
67int dsa_mode = 0;
68
69/* argv0 */
70extern char *__progname;
71
72char hostname[MAXHOSTNAMELEN];
73
74void
75ask_filename(struct passwd *pw, const char *prompt)
76{
77	char buf[1024];
78	snprintf(identity_file, sizeof(identity_file), "%s/%s",
79	    pw->pw_dir,
80	    dsa_mode ? SSH_CLIENT_ID_DSA: SSH_CLIENT_IDENTITY);
81	printf("%s (%s): ", prompt, identity_file);
82	fflush(stdout);
83	if (fgets(buf, sizeof(buf), stdin) == NULL)
84		exit(1);
85	if (strchr(buf, '\n'))
86		*strchr(buf, '\n') = 0;
87	if (strcmp(buf, "") != 0)
88		strlcpy(identity_file, buf, sizeof(identity_file));
89	have_identity = 1;
90}
91
92int
93try_load_key(char *filename, Key *k)
94{
95	int success = 1;
96	if (!load_private_key(filename, "", k, NULL)) {
97		char *pass = read_passphrase("Enter passphrase: ", 1);
98		if (!load_private_key(filename, pass, k, NULL)) {
99			success = 0;
100		}
101		memset(pass, 0, strlen(pass));
102		xfree(pass);
103	}
104	return success;
105}
106
107#define SSH_COM_MAGIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----"
108#define SSH_COM_MAGIC_END   "---- END SSH2 PUBLIC KEY ----"
109
110void
111do_convert_to_ssh2(struct passwd *pw)
112{
113	Key *k;
114	int len;
115	unsigned char *blob;
116	struct stat st;
117
118	if (!have_identity)
119		ask_filename(pw, "Enter file in which the key is");
120	if (stat(identity_file, &st) < 0) {
121		perror(identity_file);
122		exit(1);
123	}
124	k = key_new(KEY_DSA);
125	if (!try_load_key(identity_file, k)) {
126		fprintf(stderr, "load failed\n");
127		exit(1);
128	}
129	dsa_make_key_blob(k, &blob, &len);
130	fprintf(stdout, "%s\n", SSH_COM_MAGIC_BEGIN);
131	fprintf(stdout,
132	    "Comment: \"%d-bit DSA, converted from openssh by %s@%s\"\n",
133	    BN_num_bits(k->dsa->p),
134	    pw->pw_name, hostname);
135	dump_base64(stdout, blob, len);
136	fprintf(stdout, "%s\n", SSH_COM_MAGIC_END);
137	key_free(k);
138	xfree(blob);
139	exit(0);
140}
141
142void
143do_convert_from_ssh2(struct passwd *pw)
144{
145	Key *k;
146	int blen;
147	char line[1024], *p;
148	char blob[8096];
149	char encoded[8096];
150	struct stat st;
151	int escaped = 0;
152	FILE *fp;
153
154	if (!have_identity)
155		ask_filename(pw, "Enter file in which the key is");
156	if (stat(identity_file, &st) < 0) {
157		perror(identity_file);
158		exit(1);
159	}
160	fp = fopen(identity_file, "r");
161	if (fp == NULL) {
162		perror(identity_file);
163		exit(1);
164	}
165	encoded[0] = '\0';
166	while (fgets(line, sizeof(line), fp)) {
167		if (!(p = strchr(line, '\n'))) {
168			fprintf(stderr, "input line too long.\n");
169			exit(1);
170		}
171		if (p > line && p[-1] == '\\')
172			escaped++;
173		if (strncmp(line, "----", 4) == 0 ||
174		    strstr(line, ": ") != NULL) {
175			fprintf(stderr, "ignore: %s", line);
176			continue;
177		}
178		if (escaped) {
179			escaped--;
180			fprintf(stderr, "escaped: %s", line);
181			continue;
182		}
183		*p = '\0';
184		strlcat(encoded, line, sizeof(encoded));
185	}
186	blen = uudecode(encoded, (unsigned char *)blob, sizeof(blob));
187	if (blen < 0) {
188		fprintf(stderr, "uudecode failed.\n");
189		exit(1);
190	}
191	k = dsa_key_from_blob(blob, blen);
192	if (!key_write(k, stdout))
193		fprintf(stderr, "key_write failed");
194	key_free(k);
195	fprintf(stdout, "\n");
196	fclose(fp);
197	exit(0);
198}
199
200void
201do_print_public(struct passwd *pw)
202{
203	Key *k;
204	int len;
205	unsigned char *blob;
206	struct stat st;
207
208	if (!have_identity)
209		ask_filename(pw, "Enter file in which the key is");
210	if (stat(identity_file, &st) < 0) {
211		perror(identity_file);
212		exit(1);
213	}
214	k = key_new(KEY_DSA);
215	if (!try_load_key(identity_file, k)) {
216		fprintf(stderr, "load failed\n");
217		exit(1);
218	}
219	dsa_make_key_blob(k, &blob, &len);
220	if (!key_write(k, stdout))
221		fprintf(stderr, "key_write failed");
222	key_free(k);
223	xfree(blob);
224	fprintf(stdout, "\n");
225	exit(0);
226}
227
228void
229do_fingerprint(struct passwd *pw)
230{
231	/* XXX RSA1 only */
232
233	FILE *f;
234	Key *public;
235	char *comment = NULL, *cp, *ep, line[16*1024];
236	int i, skip = 0, num = 1, invalid = 1;
237	unsigned int ignore;
238	struct stat st;
239
240	if (!have_identity)
241		ask_filename(pw, "Enter file in which the key is");
242	if (stat(identity_file, &st) < 0) {
243		perror(identity_file);
244		exit(1);
245	}
246	public = key_new(KEY_RSA);
247	if (load_public_key(identity_file, public, &comment)) {
248		printf("%d %s %s\n", BN_num_bits(public->rsa->n),
249		    key_fingerprint(public), comment);
250		key_free(public);
251		exit(0);
252	}
253
254	f = fopen(identity_file, "r");
255	if (f != NULL) {
256		while (fgets(line, sizeof(line), f)) {
257			i = strlen(line) - 1;
258			if (line[i] != '\n') {
259				error("line %d too long: %.40s...", num, line);
260				skip = 1;
261				continue;
262			}
263			num++;
264			if (skip) {
265				skip = 0;
266				continue;
267			}
268			line[i] = '\0';
269
270			/* Skip leading whitespace, empty and comment lines. */
271			for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
272				;
273			if (!*cp || *cp == '\n' || *cp == '#')
274				continue ;
275			i = strtol(cp, &ep, 10);
276			if (i == 0 || ep == NULL || (*ep != ' ' && *ep != '\t')) {
277				int quoted = 0;
278				comment = cp;
279				for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
280					if (*cp == '\\' && cp[1] == '"')
281						cp++;	/* Skip both */
282					else if (*cp == '"')
283						quoted = !quoted;
284				}
285				if (!*cp)
286					continue;
287				*cp++ = '\0';
288			}
289			ep = cp;
290			if (auth_rsa_read_key(&cp, &ignore, public->rsa->e, public->rsa->n)) {
291				invalid = 0;
292				comment = *cp ? cp : comment;
293				printf("%d %s %s\n", key_size(public),
294				    key_fingerprint(public),
295				    comment ? comment : "no comment");
296			}
297		}
298		fclose(f);
299	}
300	key_free(public);
301	if (invalid) {
302		printf("%s is not a valid key file.\n", identity_file);
303		exit(1);
304	}
305	exit(0);
306}
307
308/*
309 * Perform changing a passphrase.  The argument is the passwd structure
310 * for the current user.
311 */
312void
313do_change_passphrase(struct passwd *pw)
314{
315	char *comment;
316	char *old_passphrase, *passphrase1, *passphrase2;
317	struct stat st;
318	Key *private;
319	Key *public;
320	int type = dsa_mode ? KEY_DSA : KEY_RSA;
321
322	if (!have_identity)
323		ask_filename(pw, "Enter file in which the key is");
324	if (stat(identity_file, &st) < 0) {
325		perror(identity_file);
326		exit(1);
327	}
328
329	if (type == KEY_RSA) {
330		/* XXX this works currently only for RSA */
331		public = key_new(type);
332		if (!load_public_key(identity_file, public, NULL)) {
333			printf("%s is not a valid key file.\n", identity_file);
334			exit(1);
335		}
336		/* Clear the public key since we are just about to load the whole file. */
337		key_free(public);
338	}
339
340	/* Try to load the file with empty passphrase. */
341	private = key_new(type);
342	if (!load_private_key(identity_file, "", private, &comment)) {
343		if (identity_passphrase)
344			old_passphrase = xstrdup(identity_passphrase);
345		else
346			old_passphrase = read_passphrase("Enter old passphrase: ", 1);
347		if (!load_private_key(identity_file, old_passphrase, private, &comment)) {
348			memset(old_passphrase, 0, strlen(old_passphrase));
349			xfree(old_passphrase);
350			printf("Bad passphrase.\n");
351			exit(1);
352		}
353		memset(old_passphrase, 0, strlen(old_passphrase));
354		xfree(old_passphrase);
355	}
356	printf("Key has comment '%s'\n", comment);
357
358	/* Ask the new passphrase (twice). */
359	if (identity_new_passphrase) {
360		passphrase1 = xstrdup(identity_new_passphrase);
361		passphrase2 = NULL;
362	} else {
363		passphrase1 =
364			read_passphrase("Enter new passphrase (empty for no passphrase): ", 1);
365		passphrase2 = read_passphrase("Enter same passphrase again: ", 1);
366
367		/* Verify that they are the same. */
368		if (strcmp(passphrase1, passphrase2) != 0) {
369			memset(passphrase1, 0, strlen(passphrase1));
370			memset(passphrase2, 0, strlen(passphrase2));
371			xfree(passphrase1);
372			xfree(passphrase2);
373			printf("Pass phrases do not match.  Try again.\n");
374			exit(1);
375		}
376		/* Destroy the other copy. */
377		memset(passphrase2, 0, strlen(passphrase2));
378		xfree(passphrase2);
379	}
380
381	/* Save the file using the new passphrase. */
382	if (!save_private_key(identity_file, passphrase1, private, comment)) {
383		printf("Saving the key failed: %s: %s.\n",
384		       identity_file, strerror(errno));
385		memset(passphrase1, 0, strlen(passphrase1));
386		xfree(passphrase1);
387		key_free(private);
388		xfree(comment);
389		exit(1);
390	}
391	/* Destroy the passphrase and the copy of the key in memory. */
392	memset(passphrase1, 0, strlen(passphrase1));
393	xfree(passphrase1);
394	key_free(private);		 /* Destroys contents */
395	xfree(comment);
396
397	printf("Your identification has been saved with the new passphrase.\n");
398	exit(0);
399}
400
401/*
402 * Change the comment of a private key file.
403 */
404void
405do_change_comment(struct passwd *pw)
406{
407	char new_comment[1024], *comment;
408	Key *private;
409	Key *public;
410	char *passphrase;
411	struct stat st;
412	FILE *f;
413
414	if (!have_identity)
415		ask_filename(pw, "Enter file in which the key is");
416	if (stat(identity_file, &st) < 0) {
417		perror(identity_file);
418		exit(1);
419	}
420	/*
421	 * Try to load the public key from the file the verify that it is
422	 * readable and of the proper format.
423	 */
424	public = key_new(KEY_RSA);
425	if (!load_public_key(identity_file, public, NULL)) {
426		printf("%s is not a valid key file.\n", identity_file);
427		exit(1);
428	}
429
430	private = key_new(KEY_RSA);
431	if (load_private_key(identity_file, "", private, &comment))
432		passphrase = xstrdup("");
433	else {
434		if (identity_passphrase)
435			passphrase = xstrdup(identity_passphrase);
436		else if (identity_new_passphrase)
437			passphrase = xstrdup(identity_new_passphrase);
438		else
439			passphrase = read_passphrase("Enter passphrase: ", 1);
440		/* Try to load using the passphrase. */
441		if (!load_private_key(identity_file, passphrase, private, &comment)) {
442			memset(passphrase, 0, strlen(passphrase));
443			xfree(passphrase);
444			printf("Bad passphrase.\n");
445			exit(1);
446		}
447	}
448	printf("Key now has comment '%s'\n", comment);
449
450	if (identity_comment) {
451		strlcpy(new_comment, identity_comment, sizeof(new_comment));
452	} else {
453		printf("Enter new comment: ");
454		fflush(stdout);
455		if (!fgets(new_comment, sizeof(new_comment), stdin)) {
456			memset(passphrase, 0, strlen(passphrase));
457			key_free(private);
458			exit(1);
459		}
460		if (strchr(new_comment, '\n'))
461			*strchr(new_comment, '\n') = 0;
462	}
463
464	/* Save the file using the new passphrase. */
465	if (!save_private_key(identity_file, passphrase, private, new_comment)) {
466		printf("Saving the key failed: %s: %s.\n",
467		       identity_file, strerror(errno));
468		memset(passphrase, 0, strlen(passphrase));
469		xfree(passphrase);
470		key_free(private);
471		xfree(comment);
472		exit(1);
473	}
474	memset(passphrase, 0, strlen(passphrase));
475	xfree(passphrase);
476	key_free(private);
477
478	strlcat(identity_file, ".pub", sizeof(identity_file));
479	f = fopen(identity_file, "w");
480	if (!f) {
481		printf("Could not save your public key in %s\n", identity_file);
482		exit(1);
483	}
484	if (!key_write(public, f))
485		fprintf(stderr, "write key failed");
486	key_free(public);
487	fprintf(f, " %s\n", new_comment);
488	fclose(f);
489
490	xfree(comment);
491
492	printf("The comment in your key file has been changed.\n");
493	exit(0);
494}
495
496void
497usage(void)
498{
499	printf("Usage: %s [-lpqxXydc] [-b bits] [-f file] [-C comment] [-N new-pass] [-P pass]\n", __progname);
500	exit(1);
501}
502
503/*
504 * Main program for key management.
505 */
506int
507main(int ac, char **av)
508{
509	char dotsshdir[16 * 1024], comment[1024], *passphrase1, *passphrase2;
510	struct passwd *pw;
511	int opt;
512	struct stat st;
513	FILE *f;
514	Key *private;
515	Key *public;
516	extern int optind;
517	extern char *optarg;
518
519	SSLeay_add_all_algorithms();
520
521	/* we need this for the home * directory.  */
522	pw = getpwuid(getuid());
523	if (!pw) {
524		printf("You don't exist, go away!\n");
525		exit(1);
526	}
527	if (gethostname(hostname, sizeof(hostname)) < 0) {
528		perror("gethostname");
529		exit(1);
530	}
531
532	while ((opt = getopt(ac, av, "dqpclRxXyb:f:P:N:C:")) != EOF) {
533		switch (opt) {
534		case 'b':
535			bits = atoi(optarg);
536			if (bits < 512 || bits > 32768) {
537				printf("Bits has bad value.\n");
538				exit(1);
539			}
540			break;
541
542		case 'l':
543			print_fingerprint = 1;
544			break;
545
546		case 'p':
547			change_passphrase = 1;
548			break;
549
550		case 'c':
551			change_comment = 1;
552			break;
553
554		case 'f':
555			strlcpy(identity_file, optarg, sizeof(identity_file));
556			have_identity = 1;
557			break;
558
559		case 'P':
560			identity_passphrase = optarg;
561			break;
562
563		case 'N':
564			identity_new_passphrase = optarg;
565			break;
566
567		case 'C':
568			identity_comment = optarg;
569			break;
570
571		case 'q':
572			quiet = 1;
573			break;
574
575		case 'R':
576			if (rsa_alive() == 0)
577				exit(1);
578			else
579				exit(0);
580			break;
581
582		case 'x':
583			convert_to_ssh2 = 1;
584			break;
585
586		case 'X':
587			convert_from_ssh2 = 1;
588			break;
589
590		case 'y':
591			print_public = 1;
592			break;
593
594		case 'd':
595			dsa_mode = 1;
596			break;
597
598		case '?':
599		default:
600			usage();
601		}
602	}
603	if (optind < ac) {
604		printf("Too many arguments.\n");
605		usage();
606	}
607	if (change_passphrase && change_comment) {
608		printf("Can only have one of -p and -c.\n");
609		usage();
610	}
611	/* check if RSA support is needed and exists */
612	if (dsa_mode == 0 && rsa_alive() == 0) {
613		fprintf(stderr,
614			"%s: no RSA support in libssl and libcrypto.  See ssl(8).\n",
615			__progname);
616		exit(1);
617	}
618	if (print_fingerprint)
619		do_fingerprint(pw);
620	if (change_passphrase)
621		do_change_passphrase(pw);
622	if (change_comment)
623		do_change_comment(pw);
624	if (convert_to_ssh2)
625		do_convert_to_ssh2(pw);
626	if (convert_from_ssh2)
627		do_convert_from_ssh2(pw);
628	if (print_public)
629		do_print_public(pw);
630
631	arc4random_stir();
632
633	if (dsa_mode != 0) {
634		if (!quiet)
635			printf("Generating DSA parameter and key.\n");
636		public = private = dsa_generate_key(bits);
637		if (private == NULL) {
638			fprintf(stderr, "dsa_generate_keys failed");
639			exit(1);
640		}
641	} else {
642		if (quiet)
643			rsa_set_verbose(0);
644		/* Generate the rsa key pair. */
645		public = key_new(KEY_RSA);
646		private = key_new(KEY_RSA);
647		rsa_generate_key(private->rsa, public->rsa, bits);
648	}
649
650	if (!have_identity)
651		ask_filename(pw, "Enter file in which to save the key");
652
653	/* Create ~/.ssh directory if it doesn\'t already exist. */
654	snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, SSH_USER_DIR);
655	if (strstr(identity_file, dotsshdir) != NULL &&
656	    stat(dotsshdir, &st) < 0) {
657		if (mkdir(dotsshdir, 0700) < 0)
658			error("Could not create directory '%s'.", dotsshdir);
659		else if (!quiet)
660			printf("Created directory '%s'.\n", dotsshdir);
661	}
662	/* If the file already exists, ask the user to confirm. */
663	if (stat(identity_file, &st) >= 0) {
664		char yesno[3];
665		printf("%s already exists.\n", identity_file);
666		printf("Overwrite (y/n)? ");
667		fflush(stdout);
668		if (fgets(yesno, sizeof(yesno), stdin) == NULL)
669			exit(1);
670		if (yesno[0] != 'y' && yesno[0] != 'Y')
671			exit(1);
672	}
673	/* Ask for a passphrase (twice). */
674	if (identity_passphrase)
675		passphrase1 = xstrdup(identity_passphrase);
676	else if (identity_new_passphrase)
677		passphrase1 = xstrdup(identity_new_passphrase);
678	else {
679passphrase_again:
680		passphrase1 =
681			read_passphrase("Enter passphrase (empty for no passphrase): ", 1);
682		passphrase2 = read_passphrase("Enter same passphrase again: ", 1);
683		if (strcmp(passphrase1, passphrase2) != 0) {
684			/* The passphrases do not match.  Clear them and retry. */
685			memset(passphrase1, 0, strlen(passphrase1));
686			memset(passphrase2, 0, strlen(passphrase2));
687			xfree(passphrase1);
688			xfree(passphrase2);
689			printf("Passphrases do not match.  Try again.\n");
690			goto passphrase_again;
691		}
692		/* Clear the other copy of the passphrase. */
693		memset(passphrase2, 0, strlen(passphrase2));
694		xfree(passphrase2);
695	}
696
697	if (identity_comment) {
698		strlcpy(comment, identity_comment, sizeof(comment));
699	} else {
700		/* Create default commend field for the passphrase. */
701		snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname);
702	}
703
704	/* Save the key with the given passphrase and comment. */
705	if (!save_private_key(identity_file, passphrase1, private, comment)) {
706		printf("Saving the key failed: %s: %s.\n",
707		    identity_file, strerror(errno));
708		memset(passphrase1, 0, strlen(passphrase1));
709		xfree(passphrase1);
710		exit(1);
711	}
712	/* Clear the passphrase. */
713	memset(passphrase1, 0, strlen(passphrase1));
714	xfree(passphrase1);
715
716	/* Clear the private key and the random number generator. */
717	if (private != public) {
718		key_free(private);
719	}
720	arc4random_stir();
721
722	if (!quiet)
723		printf("Your identification has been saved in %s.\n", identity_file);
724
725	strlcat(identity_file, ".pub", sizeof(identity_file));
726	f = fopen(identity_file, "w");
727	if (!f) {
728		printf("Could not save your public key in %s\n", identity_file);
729		exit(1);
730	}
731	if (!key_write(public, f))
732		fprintf(stderr, "write key failed");
733	fprintf(f, " %s\n", comment);
734	fclose(f);
735
736	if (!quiet) {
737		printf("Your public key has been saved in %s.\n",
738		    identity_file);
739		printf("The key fingerprint is:\n");
740		printf("%s %s\n", key_fingerprint(public), comment);
741	}
742
743	key_free(public);
744	exit(0);
745}
746