ssh-keygen.c revision 58582
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 * Created: Mon Mar 27 02:26:40 1995 ylo
6 * Identity and host key generation and maintenance.
7 */
8
9#include "includes.h"
10RCSID("$Id: ssh-keygen.c,v 1.17 2000/03/16 20:56:14 markus Exp $");
11
12#include "rsa.h"
13#include "ssh.h"
14#include "xmalloc.h"
15#include "fingerprint.h"
16
17/* Generated private key. */
18RSA *private_key;
19
20/* Generated public key. */
21RSA *public_key;
22
23/* Number of bits in the RSA key.  This value can be changed on the command line. */
24int bits = 1024;
25
26/*
27 * Flag indicating that we just want to change the passphrase.  This can be
28 * set on the command line.
29 */
30int change_passphrase = 0;
31
32/*
33 * Flag indicating that we just want to change the comment.  This can be set
34 * on the command line.
35 */
36int change_comment = 0;
37
38int quiet = 0;
39
40/* Flag indicating that we just want to see the key fingerprint */
41int print_fingerprint = 0;
42
43/* The identity file name, given on the command line or entered by the user. */
44char identity_file[1024];
45int have_identity = 0;
46
47/* This is set to the passphrase if given on the command line. */
48char *identity_passphrase = NULL;
49
50/* This is set to the new passphrase if given on the command line. */
51char *identity_new_passphrase = NULL;
52
53/* This is set to the new comment if given on the command line. */
54char *identity_comment = NULL;
55
56/* argv0 */
57extern char *__progname;
58
59void
60ask_filename(struct passwd *pw, const char *prompt)
61{
62	char buf[1024];
63	snprintf(identity_file, sizeof(identity_file), "%s/%s",
64		 pw->pw_dir, SSH_CLIENT_IDENTITY);
65	printf("%s (%s): ", prompt, identity_file);
66	fflush(stdout);
67	if (fgets(buf, sizeof(buf), stdin) == NULL)
68		exit(1);
69	if (strchr(buf, '\n'))
70		*strchr(buf, '\n') = 0;
71	if (strcmp(buf, "") != 0)
72		strlcpy(identity_file, buf, sizeof(identity_file));
73	have_identity = 1;
74}
75
76void
77do_fingerprint(struct passwd *pw)
78{
79	FILE *f;
80	BIGNUM *e, *n;
81	RSA *public_key;
82	char *comment = NULL, *cp, *ep, line[16*1024];
83	int i, skip = 0, num = 1, invalid = 1;
84	unsigned int ignore;
85	struct stat st;
86
87	if (!have_identity)
88		ask_filename(pw, "Enter file in which the key is");
89	if (stat(identity_file, &st) < 0) {
90		perror(identity_file);
91		exit(1);
92	}
93
94	public_key = RSA_new();
95	if (load_public_key(identity_file, public_key, &comment)) {
96		printf("%d %s %s\n", BN_num_bits(public_key->n),
97		    fingerprint(public_key->e, public_key->n),
98		    comment);
99		RSA_free(public_key);
100		exit(0);
101	}
102	RSA_free(public_key);
103
104	f = fopen(identity_file, "r");
105	if (f != NULL) {
106		n = BN_new();
107		e = BN_new();
108		while (fgets(line, sizeof(line), f)) {
109			i = strlen(line) - 1;
110			if (line[i] != '\n') {
111				error("line %d too long: %.40s...", num, line);
112				skip = 1;
113				continue;
114			}
115			num++;
116			if (skip) {
117				skip = 0;
118				continue;
119			}
120			line[i] = '\0';
121
122			/* Skip leading whitespace, empty and comment lines. */
123			for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
124				;
125			if (!*cp || *cp == '\n' || *cp == '#')
126				continue ;
127			i = strtol(cp, &ep, 10);
128			if (i == 0 || ep == NULL || (*ep != ' ' && *ep != '\t')) {
129				int quoted = 0;
130				comment = cp;
131				for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
132					if (*cp == '\\' && cp[1] == '"')
133						cp++;	/* Skip both */
134					else if (*cp == '"')
135						quoted = !quoted;
136				}
137				if (!*cp)
138					continue;
139				*cp++ = '\0';
140			}
141			ep = cp;
142			if (auth_rsa_read_key(&cp, &ignore, e, n)) {
143				invalid = 0;
144				comment = *cp ? cp : comment;
145				printf("%d %s %s\n", BN_num_bits(n),
146				    fingerprint(e, n),
147				    comment ? comment : "no comment");
148			}
149		}
150		BN_free(e);
151		BN_free(n);
152		fclose(f);
153	}
154	if (invalid) {
155		printf("%s is not a valid key file.\n", identity_file);
156		exit(1);
157	}
158	exit(0);
159}
160
161/*
162 * Perform changing a passphrase.  The argument is the passwd structure
163 * for the current user.
164 */
165void
166do_change_passphrase(struct passwd *pw)
167{
168	char *comment;
169	char *old_passphrase, *passphrase1, *passphrase2;
170	struct stat st;
171	RSA *private_key;
172
173	if (!have_identity)
174		ask_filename(pw, "Enter file in which the key is");
175	if (stat(identity_file, &st) < 0) {
176		perror(identity_file);
177		exit(1);
178	}
179	public_key = RSA_new();
180	if (!load_public_key(identity_file, public_key, NULL)) {
181		printf("%s is not a valid key file.\n", identity_file);
182		exit(1);
183	}
184	/* Clear the public key since we are just about to load the whole file. */
185	RSA_free(public_key);
186
187	/* Try to load the file with empty passphrase. */
188	private_key = RSA_new();
189	if (!load_private_key(identity_file, "", private_key, &comment)) {
190		if (identity_passphrase)
191			old_passphrase = xstrdup(identity_passphrase);
192		else
193			old_passphrase = read_passphrase("Enter old passphrase: ", 1);
194		if (!load_private_key(identity_file, old_passphrase, private_key, &comment)) {
195			memset(old_passphrase, 0, strlen(old_passphrase));
196			xfree(old_passphrase);
197			printf("Bad passphrase.\n");
198			exit(1);
199		}
200		memset(old_passphrase, 0, strlen(old_passphrase));
201		xfree(old_passphrase);
202	}
203	printf("Key has comment '%s'\n", comment);
204
205	/* Ask the new passphrase (twice). */
206	if (identity_new_passphrase) {
207		passphrase1 = xstrdup(identity_new_passphrase);
208		passphrase2 = NULL;
209	} else {
210		passphrase1 =
211			read_passphrase("Enter new passphrase (empty for no passphrase): ", 1);
212		passphrase2 = read_passphrase("Enter same passphrase again: ", 1);
213
214		/* Verify that they are the same. */
215		if (strcmp(passphrase1, passphrase2) != 0) {
216			memset(passphrase1, 0, strlen(passphrase1));
217			memset(passphrase2, 0, strlen(passphrase2));
218			xfree(passphrase1);
219			xfree(passphrase2);
220			printf("Pass phrases do not match.  Try again.\n");
221			exit(1);
222		}
223		/* Destroy the other copy. */
224		memset(passphrase2, 0, strlen(passphrase2));
225		xfree(passphrase2);
226	}
227
228	/* Save the file using the new passphrase. */
229	if (!save_private_key(identity_file, passphrase1, private_key, comment)) {
230		printf("Saving the key failed: %s: %s.\n",
231		       identity_file, strerror(errno));
232		memset(passphrase1, 0, strlen(passphrase1));
233		xfree(passphrase1);
234		RSA_free(private_key);
235		xfree(comment);
236		exit(1);
237	}
238	/* Destroy the passphrase and the copy of the key in memory. */
239	memset(passphrase1, 0, strlen(passphrase1));
240	xfree(passphrase1);
241	RSA_free(private_key);	/* Destroys contents */
242	xfree(comment);
243
244	printf("Your identification has been saved with the new passphrase.\n");
245	exit(0);
246}
247
248/*
249 * Change the comment of a private key file.
250 */
251void
252do_change_comment(struct passwd *pw)
253{
254	char new_comment[1024], *comment;
255	RSA *private_key;
256	char *passphrase;
257	struct stat st;
258	FILE *f;
259	char *tmpbuf;
260
261	if (!have_identity)
262		ask_filename(pw, "Enter file in which the key is");
263	if (stat(identity_file, &st) < 0) {
264		perror(identity_file);
265		exit(1);
266	}
267	/*
268	 * Try to load the public key from the file the verify that it is
269	 * readable and of the proper format.
270	 */
271	public_key = RSA_new();
272	if (!load_public_key(identity_file, public_key, NULL)) {
273		printf("%s is not a valid key file.\n", identity_file);
274		exit(1);
275	}
276	private_key = RSA_new();
277
278	if (load_private_key(identity_file, "", private_key, &comment))
279		passphrase = xstrdup("");
280	else {
281		if (identity_passphrase)
282			passphrase = xstrdup(identity_passphrase);
283		else if (identity_new_passphrase)
284			passphrase = xstrdup(identity_new_passphrase);
285		else
286			passphrase = read_passphrase("Enter passphrase: ", 1);
287		/* Try to load using the passphrase. */
288		if (!load_private_key(identity_file, passphrase, private_key, &comment)) {
289			memset(passphrase, 0, strlen(passphrase));
290			xfree(passphrase);
291			printf("Bad passphrase.\n");
292			exit(1);
293		}
294	}
295	printf("Key now has comment '%s'\n", comment);
296
297	if (identity_comment) {
298		strlcpy(new_comment, identity_comment, sizeof(new_comment));
299	} else {
300		printf("Enter new comment: ");
301		fflush(stdout);
302		if (!fgets(new_comment, sizeof(new_comment), stdin)) {
303			memset(passphrase, 0, strlen(passphrase));
304			RSA_free(private_key);
305			exit(1);
306		}
307		if (strchr(new_comment, '\n'))
308			*strchr(new_comment, '\n') = 0;
309	}
310
311	/* Save the file using the new passphrase. */
312	if (!save_private_key(identity_file, passphrase, private_key, new_comment)) {
313		printf("Saving the key failed: %s: %s.\n",
314		       identity_file, strerror(errno));
315		memset(passphrase, 0, strlen(passphrase));
316		xfree(passphrase);
317		RSA_free(private_key);
318		xfree(comment);
319		exit(1);
320	}
321	memset(passphrase, 0, strlen(passphrase));
322	xfree(passphrase);
323	RSA_free(private_key);
324
325	strlcat(identity_file, ".pub", sizeof(identity_file));
326	f = fopen(identity_file, "w");
327	if (!f) {
328		printf("Could not save your public key in %s\n", identity_file);
329		exit(1);
330	}
331	fprintf(f, "%d ", BN_num_bits(public_key->n));
332	tmpbuf = BN_bn2dec(public_key->e);
333	fprintf(f, "%s ", tmpbuf);
334	free(tmpbuf);
335	tmpbuf = BN_bn2dec(public_key->n);
336	fprintf(f, "%s %s\n", tmpbuf, new_comment);
337	free(tmpbuf);
338	fclose(f);
339
340	xfree(comment);
341
342	printf("The comment in your key file has been changed.\n");
343	exit(0);
344}
345
346void
347usage(void)
348{
349	printf("ssh-keygen version %s\n", SSH_VERSION);
350	printf("Usage: %s [-b bits] [-p] [-c] [-l] [-f file] [-P pass] [-N new-pass] [-C comment]\n", __progname);
351	exit(1);
352}
353
354/*
355 * Main program for key management.
356 */
357int
358main(int ac, char **av)
359{
360	char dotsshdir[16 * 1024], comment[1024], *passphrase1, *passphrase2;
361	struct passwd *pw;
362	char *tmpbuf;
363	int opt;
364	struct stat st;
365	FILE *f;
366	char hostname[MAXHOSTNAMELEN];
367	extern int optind;
368	extern char *optarg;
369
370	/* check if RSA support exists */
371	if (rsa_alive() == 0) {
372		fprintf(stderr,
373			"%s: no RSA support in libssl and libcrypto.  See ssl(8).\n",
374			__progname);
375		exit(1);
376	}
377	/* we need this for the home * directory.  */
378	pw = getpwuid(getuid());
379	if (!pw) {
380		printf("You don't exist, go away!\n");
381		exit(1);
382	}
383
384	while ((opt = getopt(ac, av, "qpclb:f:P:N:C:")) != EOF) {
385		switch (opt) {
386		case 'b':
387			bits = atoi(optarg);
388			if (bits < 512 || bits > 32768) {
389				printf("Bits has bad value.\n");
390				exit(1);
391			}
392			break;
393
394		case 'l':
395			print_fingerprint = 1;
396			break;
397
398		case 'p':
399			change_passphrase = 1;
400			break;
401
402		case 'c':
403			change_comment = 1;
404			break;
405
406		case 'f':
407			strlcpy(identity_file, optarg, sizeof(identity_file));
408			have_identity = 1;
409			break;
410
411		case 'P':
412			identity_passphrase = optarg;
413			break;
414
415		case 'N':
416			identity_new_passphrase = optarg;
417			break;
418
419		case 'C':
420			identity_comment = optarg;
421			break;
422
423		case 'q':
424			quiet = 1;
425			break;
426
427		case '?':
428		default:
429			usage();
430		}
431	}
432	if (optind < ac) {
433		printf("Too many arguments.\n");
434		usage();
435	}
436	if (change_passphrase && change_comment) {
437		printf("Can only have one of -p and -c.\n");
438		usage();
439	}
440	if (print_fingerprint)
441		do_fingerprint(pw);
442	if (change_passphrase)
443		do_change_passphrase(pw);
444	if (change_comment)
445		do_change_comment(pw);
446
447	arc4random_stir();
448
449	if (quiet)
450		rsa_set_verbose(0);
451
452	/* Generate the rsa key pair. */
453	private_key = RSA_new();
454	public_key = RSA_new();
455	rsa_generate_key(private_key, public_key, bits);
456
457	if (!have_identity)
458		ask_filename(pw, "Enter file in which to save the key");
459
460	/* Create ~/.ssh directory if it doesn\'t already exist. */
461	snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, SSH_USER_DIR);
462	if (strstr(identity_file, dotsshdir) != NULL &&
463	    stat(dotsshdir, &st) < 0) {
464		if (mkdir(dotsshdir, 0755) < 0)
465			error("Could not create directory '%s'.", dotsshdir);
466		else if (!quiet)
467			printf("Created directory '%s'.\n", dotsshdir);
468	}
469	/* If the file already exists, ask the user to confirm. */
470	if (stat(identity_file, &st) >= 0) {
471		char yesno[3];
472		printf("%s already exists.\n", identity_file);
473		printf("Overwrite (y/n)? ");
474		fflush(stdout);
475		if (fgets(yesno, sizeof(yesno), stdin) == NULL)
476			exit(1);
477		if (yesno[0] != 'y' && yesno[0] != 'Y')
478			exit(1);
479	}
480	/* Ask for a passphrase (twice). */
481	if (identity_passphrase)
482		passphrase1 = xstrdup(identity_passphrase);
483	else if (identity_new_passphrase)
484		passphrase1 = xstrdup(identity_new_passphrase);
485	else {
486passphrase_again:
487		passphrase1 =
488			read_passphrase("Enter passphrase (empty for no passphrase): ", 1);
489		passphrase2 = read_passphrase("Enter same passphrase again: ", 1);
490		if (strcmp(passphrase1, passphrase2) != 0) {
491			/* The passphrases do not match.  Clear them and retry. */
492			memset(passphrase1, 0, strlen(passphrase1));
493			memset(passphrase2, 0, strlen(passphrase2));
494			xfree(passphrase1);
495			xfree(passphrase2);
496			printf("Passphrases do not match.  Try again.\n");
497			goto passphrase_again;
498		}
499		/* Clear the other copy of the passphrase. */
500		memset(passphrase2, 0, strlen(passphrase2));
501		xfree(passphrase2);
502	}
503
504	if (identity_comment) {
505		strlcpy(comment, identity_comment, sizeof(comment));
506	} else {
507	  	/* Create default commend field for the passphrase. */
508		if (gethostname(hostname, sizeof(hostname)) < 0) {
509			perror("gethostname");
510			exit(1);
511		}
512		snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname);
513	}
514
515	/* Save the key with the given passphrase and comment. */
516	if (!save_private_key(identity_file, passphrase1, private_key, comment)) {
517		printf("Saving the key failed: %s: %s.\n",
518		       identity_file, strerror(errno));
519		memset(passphrase1, 0, strlen(passphrase1));
520		xfree(passphrase1);
521		exit(1);
522	}
523	/* Clear the passphrase. */
524	memset(passphrase1, 0, strlen(passphrase1));
525	xfree(passphrase1);
526
527	/* Clear the private key and the random number generator. */
528	RSA_free(private_key);
529	arc4random_stir();
530
531	if (!quiet)
532		printf("Your identification has been saved in %s.\n", identity_file);
533
534	strlcat(identity_file, ".pub", sizeof(identity_file));
535	f = fopen(identity_file, "w");
536	if (!f) {
537		printf("Could not save your public key in %s\n", identity_file);
538		exit(1);
539	}
540	fprintf(f, "%d ", BN_num_bits(public_key->n));
541	tmpbuf = BN_bn2dec(public_key->e);
542	fprintf(f, "%s ", tmpbuf);
543	free(tmpbuf);
544	tmpbuf = BN_bn2dec(public_key->n);
545	fprintf(f, "%s %s\n", tmpbuf, comment);
546	free(tmpbuf);
547	fclose(f);
548
549	if (!quiet) {
550		printf("Your public key has been saved in %s.\n", identity_file);
551		printf("The key fingerprint is:\n");
552		printf("%d %s %s\n", BN_num_bits(public_key->n),
553		       fingerprint(public_key->e, public_key->n),
554		       comment);
555	}
556	exit(0);
557}
558