ssh-keygen.c revision 57429
1128032Stjr/*
2102050Stjr * Author: Tatu Ylonen <ylo@cs.hut.fi>
3102050Stjr * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
4102050Stjr *                    All rights reserved
5102050Stjr * Created: Mon Mar 27 02:26:40 1995 ylo
6102050Stjr * Identity and host key generation and maintenance.
7102050Stjr */
8102050Stjr
9102050Stjr#include "includes.h"
10102050StjrRCSID("$Id: ssh-keygen.c,v 1.16 2000/02/04 14:34:09 markus Exp $");
11102050Stjr
12102050Stjr#include "rsa.h"
13102050Stjr#include "ssh.h"
14102050Stjr#include "xmalloc.h"
15102050Stjr#include "fingerprint.h"
16102050Stjr
17102050Stjr/* Generated private key. */
18102050StjrRSA *private_key;
19102050Stjr
20102050Stjr/* Generated public key. */
21102050StjrRSA *public_key;
22102050Stjr
23102050Stjr/* Number of bits in the RSA key.  This value can be changed on the command line. */
24102050Stjrint bits = 1024;
25102050Stjr
26107392Sru/*
27132497Stjr * Flag indicating that we just want to change the passphrase.  This can be
28102050Stjr * set on the command line.
29102050Stjr */
30102050Stjrint change_passphrase = 0;
31132497Stjr
32132497Stjr/*
33102050Stjr * Flag indicating that we just want to change the comment.  This can be set
34102050Stjr * on the command line.
35102050Stjr */
36102050Stjrint change_comment = 0;
37102050Stjr
38102050Stjrint quiet = 0;
39107392Sru
40107392Sru/* Flag indicating that we just want to see the key fingerprint */
41107392Sruint print_fingerprint = 0;
42107392Sru
43132497Stjr/* The identity file name, given on the command line or entered by the user. */
44132497Stjrchar identity_file[1024];
45132497Stjrint have_identity = 0;
46132497Stjr
47132497Stjr/* This is set to the passphrase if given on the command line. */
48102050Stjrchar *identity_passphrase = NULL;
49102050Stjr
50102050Stjr/* This is set to the new passphrase if given on the command line. */
51102050Stjrchar *identity_new_passphrase = NULL;
52102050Stjr
53102050Stjr/* This is set to the new comment if given on the command line. */
54102050Stjrchar *identity_comment = NULL;
55102050Stjr
56102050Stjr/* argv0 */
57102050Stjrextern char *__progname;
58102050Stjr
59102050Stjrvoid
60102050Stjrask_filename(struct passwd *pw, const char *prompt)
61102050Stjr{
62102050Stjr	char buf[1024];
63102050Stjr	snprintf(identity_file, sizeof(identity_file), "%s/%s",
64102050Stjr		 pw->pw_dir, SSH_CLIENT_IDENTITY);
65102050Stjr	printf("%s (%s): ", prompt, identity_file);
66102050Stjr	fflush(stdout);
67102050Stjr	if (fgets(buf, sizeof(buf), stdin) == NULL)
68102050Stjr		exit(1);
69102050Stjr	if (strchr(buf, '\n'))
70102050Stjr		*strchr(buf, '\n') = 0;
71102050Stjr	if (strcmp(buf, "") != 0)
72102050Stjr		strlcpy(identity_file, buf, sizeof(identity_file));
73102050Stjr	have_identity = 1;
74102050Stjr}
75107392Sru
76102050Stjrvoid
77102050Stjrdo_fingerprint(struct passwd *pw)
78102050Stjr{
79102050Stjr	FILE *f;
80107392Sru	BIGNUM *e, *n;
81102050Stjr	RSA *public_key;
82102050Stjr	char *comment = NULL, *cp, *ep, line[16*1024];
83102050Stjr	int i, skip = 0, num = 1, invalid = 1;
84102050Stjr	struct stat st;
85102050Stjr
86102050Stjr	if (!have_identity)
87102050Stjr		ask_filename(pw, "Enter file in which the key is");
88107392Sru	if (stat(identity_file, &st) < 0) {
89128032Stjr		perror(identity_file);
90128032Stjr		exit(1);
91132497Stjr	}
92132497Stjr
93132497Stjr	public_key = RSA_new();
94132497Stjr	if (load_public_key(identity_file, public_key, &comment)) {
95132497Stjr		printf("%d %s %s\n", BN_num_bits(public_key->n),
96132497Stjr		    fingerprint(public_key->e, public_key->n),
97132497Stjr		    comment);
98132497Stjr		RSA_free(public_key);
99132497Stjr		exit(0);
100102050Stjr	}
101102050Stjr	RSA_free(public_key);
102102050Stjr
103132497Stjr	f = fopen(identity_file, "r");
104132497Stjr	if (f != NULL) {
105132497Stjr		n = BN_new();
106102050Stjr		e = BN_new();
107102050Stjr		while (fgets(line, sizeof(line), f)) {
108102050Stjr			i = strlen(line) - 1;
109107392Sru			if (line[i] != '\n') {
110102050Stjr				error("line %d too long: %.40s...", num, line);
111102050Stjr				skip = 1;
112102050Stjr				continue;
113132497Stjr			}
114132497Stjr			num++;
115132497Stjr			if (skip) {
116102050Stjr				skip = 0;
117102050Stjr				continue;
118102050Stjr			}
119128032Stjr			line[i] = '\0';
120128032Stjr
121102050Stjr			/* Skip leading whitespace, empty and comment lines. */
122102050Stjr			for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
123102050Stjr				;
124107392Sru			if (!*cp || *cp == '\n' || *cp == '#')
125107392Sru				continue ;
126102050Stjr			i = strtol(cp, &ep, 10);
127102050Stjr			if (i == 0 || ep == NULL || (*ep != ' ' && *ep != '\t')) {
128102050Stjr				int quoted = 0;
129102050Stjr				comment = cp;
130102050Stjr				for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
131132497Stjr					if (*cp == '\\' && cp[1] == '"')
132132497Stjr						cp++;	/* Skip both */
133132497Stjr					else if (*cp == '"')
134132497Stjr						quoted = !quoted;
135				}
136				if (!*cp)
137					continue;
138				*cp++ = '\0';
139			}
140			ep = cp;
141			if (auth_rsa_read_key(&cp, &i, e, n)) {
142				invalid = 0;
143				comment = *cp ? cp : comment;
144				printf("%d %s %s\n", BN_num_bits(n),
145				    fingerprint(e, n),
146				    comment ? comment : "no comment");
147			}
148		}
149		BN_free(e);
150		BN_free(n);
151		fclose(f);
152	}
153	if (invalid) {
154		printf("%s is not a valid key file.\n", identity_file);
155		exit(1);
156	}
157	exit(0);
158}
159
160/*
161 * Perform changing a passphrase.  The argument is the passwd structure
162 * for the current user.
163 */
164void
165do_change_passphrase(struct passwd *pw)
166{
167	char *comment;
168	char *old_passphrase, *passphrase1, *passphrase2;
169	struct stat st;
170	RSA *private_key;
171
172	if (!have_identity)
173		ask_filename(pw, "Enter file in which the key is");
174	if (stat(identity_file, &st) < 0) {
175		perror(identity_file);
176		exit(1);
177	}
178	public_key = RSA_new();
179	if (!load_public_key(identity_file, public_key, NULL)) {
180		printf("%s is not a valid key file.\n", identity_file);
181		exit(1);
182	}
183	/* Clear the public key since we are just about to load the whole file. */
184	RSA_free(public_key);
185
186	/* Try to load the file with empty passphrase. */
187	private_key = RSA_new();
188	if (!load_private_key(identity_file, "", private_key, &comment)) {
189		if (identity_passphrase)
190			old_passphrase = xstrdup(identity_passphrase);
191		else
192			old_passphrase = read_passphrase("Enter old passphrase: ", 1);
193		if (!load_private_key(identity_file, old_passphrase, private_key, &comment)) {
194			memset(old_passphrase, 0, strlen(old_passphrase));
195			xfree(old_passphrase);
196			printf("Bad passphrase.\n");
197			exit(1);
198		}
199		memset(old_passphrase, 0, strlen(old_passphrase));
200		xfree(old_passphrase);
201	}
202	printf("Key has comment '%s'\n", comment);
203
204	/* Ask the new passphrase (twice). */
205	if (identity_new_passphrase) {
206		passphrase1 = xstrdup(identity_new_passphrase);
207		passphrase2 = NULL;
208	} else {
209		passphrase1 =
210			read_passphrase("Enter new passphrase (empty for no passphrase): ", 1);
211		passphrase2 = read_passphrase("Enter same passphrase again: ", 1);
212
213		/* Verify that they are the same. */
214		if (strcmp(passphrase1, passphrase2) != 0) {
215			memset(passphrase1, 0, strlen(passphrase1));
216			memset(passphrase2, 0, strlen(passphrase2));
217			xfree(passphrase1);
218			xfree(passphrase2);
219			printf("Pass phrases do not match.  Try again.\n");
220			exit(1);
221		}
222		/* Destroy the other copy. */
223		memset(passphrase2, 0, strlen(passphrase2));
224		xfree(passphrase2);
225	}
226
227	/* Save the file using the new passphrase. */
228	if (!save_private_key(identity_file, passphrase1, private_key, comment)) {
229		printf("Saving the key failed: %s: %s.\n",
230		       identity_file, strerror(errno));
231		memset(passphrase1, 0, strlen(passphrase1));
232		xfree(passphrase1);
233		RSA_free(private_key);
234		xfree(comment);
235		exit(1);
236	}
237	/* Destroy the passphrase and the copy of the key in memory. */
238	memset(passphrase1, 0, strlen(passphrase1));
239	xfree(passphrase1);
240	RSA_free(private_key);	/* Destroys contents */
241	xfree(comment);
242
243	printf("Your identification has been saved with the new passphrase.\n");
244	exit(0);
245}
246
247/*
248 * Change the comment of a private key file.
249 */
250void
251do_change_comment(struct passwd *pw)
252{
253	char new_comment[1024], *comment;
254	RSA *private_key;
255	char *passphrase;
256	struct stat st;
257	FILE *f;
258	char *tmpbuf;
259
260	if (!have_identity)
261		ask_filename(pw, "Enter file in which the key is");
262	if (stat(identity_file, &st) < 0) {
263		perror(identity_file);
264		exit(1);
265	}
266	/*
267	 * Try to load the public key from the file the verify that it is
268	 * readable and of the proper format.
269	 */
270	public_key = RSA_new();
271	if (!load_public_key(identity_file, public_key, NULL)) {
272		printf("%s is not a valid key file.\n", identity_file);
273		exit(1);
274	}
275	private_key = RSA_new();
276
277	if (load_private_key(identity_file, "", private_key, &comment))
278		passphrase = xstrdup("");
279	else {
280		if (identity_passphrase)
281			passphrase = xstrdup(identity_passphrase);
282		else if (identity_new_passphrase)
283			passphrase = xstrdup(identity_new_passphrase);
284		else
285			passphrase = read_passphrase("Enter passphrase: ", 1);
286		/* Try to load using the passphrase. */
287		if (!load_private_key(identity_file, passphrase, private_key, &comment)) {
288			memset(passphrase, 0, strlen(passphrase));
289			xfree(passphrase);
290			printf("Bad passphrase.\n");
291			exit(1);
292		}
293	}
294	printf("Key now has comment '%s'\n", comment);
295
296	if (identity_comment) {
297		strlcpy(new_comment, identity_comment, sizeof(new_comment));
298	} else {
299		printf("Enter new comment: ");
300		fflush(stdout);
301		if (!fgets(new_comment, sizeof(new_comment), stdin)) {
302			memset(passphrase, 0, strlen(passphrase));
303			RSA_free(private_key);
304			exit(1);
305		}
306		if (strchr(new_comment, '\n'))
307			*strchr(new_comment, '\n') = 0;
308	}
309
310	/* Save the file using the new passphrase. */
311	if (!save_private_key(identity_file, passphrase, private_key, new_comment)) {
312		printf("Saving the key failed: %s: %s.\n",
313		       identity_file, strerror(errno));
314		memset(passphrase, 0, strlen(passphrase));
315		xfree(passphrase);
316		RSA_free(private_key);
317		xfree(comment);
318		exit(1);
319	}
320	memset(passphrase, 0, strlen(passphrase));
321	xfree(passphrase);
322	RSA_free(private_key);
323
324	strlcat(identity_file, ".pub", sizeof(identity_file));
325	f = fopen(identity_file, "w");
326	if (!f) {
327		printf("Could not save your public key in %s\n", identity_file);
328		exit(1);
329	}
330	fprintf(f, "%d ", BN_num_bits(public_key->n));
331	tmpbuf = BN_bn2dec(public_key->e);
332	fprintf(f, "%s ", tmpbuf);
333	free(tmpbuf);
334	tmpbuf = BN_bn2dec(public_key->n);
335	fprintf(f, "%s %s\n", tmpbuf, new_comment);
336	free(tmpbuf);
337	fclose(f);
338
339	xfree(comment);
340
341	printf("The comment in your key file has been changed.\n");
342	exit(0);
343}
344
345void
346usage(void)
347{
348	printf("ssh-keygen version %s\n", SSH_VERSION);
349	printf("Usage: %s [-b bits] [-p] [-c] [-l] [-f file] [-P pass] [-N new-pass] [-C comment]\n", __progname);
350	exit(1);
351}
352
353/*
354 * Main program for key management.
355 */
356int
357main(int ac, char **av)
358{
359	char dotsshdir[16 * 1024], comment[1024], *passphrase1, *passphrase2;
360	struct passwd *pw;
361	char *tmpbuf;
362	int opt;
363	struct stat st;
364	FILE *f;
365	char hostname[MAXHOSTNAMELEN];
366	extern int optind;
367	extern char *optarg;
368
369	/* check if RSA support exists */
370	if (rsa_alive() == 0) {
371		fprintf(stderr,
372			"%s: no RSA support in libssl and libcrypto.  See ssl(8).\n",
373			__progname);
374		exit(1);
375	}
376	/* we need this for the home * directory.  */
377	pw = getpwuid(getuid());
378	if (!pw) {
379		printf("You don't exist, go away!\n");
380		exit(1);
381	}
382
383	while ((opt = getopt(ac, av, "qpclb:f:P:N:C:")) != EOF) {
384		switch (opt) {
385		case 'b':
386			bits = atoi(optarg);
387			if (bits < 512 || bits > 32768) {
388				printf("Bits has bad value.\n");
389				exit(1);
390			}
391			break;
392
393		case 'l':
394			print_fingerprint = 1;
395			break;
396
397		case 'p':
398			change_passphrase = 1;
399			break;
400
401		case 'c':
402			change_comment = 1;
403			break;
404
405		case 'f':
406			strlcpy(identity_file, optarg, sizeof(identity_file));
407			have_identity = 1;
408			break;
409
410		case 'P':
411			identity_passphrase = optarg;
412			break;
413
414		case 'N':
415			identity_new_passphrase = optarg;
416			break;
417
418		case 'C':
419			identity_comment = optarg;
420			break;
421
422		case 'q':
423			quiet = 1;
424			break;
425
426		case '?':
427		default:
428			usage();
429		}
430	}
431	if (optind < ac) {
432		printf("Too many arguments.\n");
433		usage();
434	}
435	if (change_passphrase && change_comment) {
436		printf("Can only have one of -p and -c.\n");
437		usage();
438	}
439	if (print_fingerprint)
440		do_fingerprint(pw);
441	if (change_passphrase)
442		do_change_passphrase(pw);
443	if (change_comment)
444		do_change_comment(pw);
445
446	arc4random_stir();
447
448	if (quiet)
449		rsa_set_verbose(0);
450
451	/* Generate the rsa key pair. */
452	private_key = RSA_new();
453	public_key = RSA_new();
454	rsa_generate_key(private_key, public_key, bits);
455
456	if (!have_identity)
457		ask_filename(pw, "Enter file in which to save the key");
458
459	/* Create ~/.ssh directory if it doesn\'t already exist. */
460	snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, SSH_USER_DIR);
461	if (strstr(identity_file, dotsshdir) != NULL &&
462	    stat(dotsshdir, &st) < 0) {
463		if (mkdir(dotsshdir, 0755) < 0)
464			error("Could not create directory '%s'.", dotsshdir);
465		else if (!quiet)
466			printf("Created directory '%s'.\n", dotsshdir);
467	}
468	/* If the file already exists, ask the user to confirm. */
469	if (stat(identity_file, &st) >= 0) {
470		char yesno[3];
471		printf("%s already exists.\n", identity_file);
472		printf("Overwrite (y/n)? ");
473		fflush(stdout);
474		if (fgets(yesno, sizeof(yesno), stdin) == NULL)
475			exit(1);
476		if (yesno[0] != 'y' && yesno[0] != 'Y')
477			exit(1);
478	}
479	/* Ask for a passphrase (twice). */
480	if (identity_passphrase)
481		passphrase1 = xstrdup(identity_passphrase);
482	else if (identity_new_passphrase)
483		passphrase1 = xstrdup(identity_new_passphrase);
484	else {
485passphrase_again:
486		passphrase1 =
487			read_passphrase("Enter passphrase (empty for no passphrase): ", 1);
488		passphrase2 = read_passphrase("Enter same passphrase again: ", 1);
489		if (strcmp(passphrase1, passphrase2) != 0) {
490			/* The passphrases do not match.  Clear them and retry. */
491			memset(passphrase1, 0, strlen(passphrase1));
492			memset(passphrase2, 0, strlen(passphrase2));
493			xfree(passphrase1);
494			xfree(passphrase2);
495			printf("Passphrases do not match.  Try again.\n");
496			goto passphrase_again;
497		}
498		/* Clear the other copy of the passphrase. */
499		memset(passphrase2, 0, strlen(passphrase2));
500		xfree(passphrase2);
501	}
502
503	if (identity_comment) {
504		strlcpy(comment, identity_comment, sizeof(comment));
505	} else {
506	  	/* Create default commend field for the passphrase. */
507		if (gethostname(hostname, sizeof(hostname)) < 0) {
508			perror("gethostname");
509			exit(1);
510		}
511		snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname);
512	}
513
514	/* Save the key with the given passphrase and comment. */
515	if (!save_private_key(identity_file, passphrase1, private_key, comment)) {
516		printf("Saving the key failed: %s: %s.\n",
517		       identity_file, strerror(errno));
518		memset(passphrase1, 0, strlen(passphrase1));
519		xfree(passphrase1);
520		exit(1);
521	}
522	/* Clear the passphrase. */
523	memset(passphrase1, 0, strlen(passphrase1));
524	xfree(passphrase1);
525
526	/* Clear the private key and the random number generator. */
527	RSA_free(private_key);
528	arc4random_stir();
529
530	if (!quiet)
531		printf("Your identification has been saved in %s.\n", identity_file);
532
533	strlcat(identity_file, ".pub", sizeof(identity_file));
534	f = fopen(identity_file, "w");
535	if (!f) {
536		printf("Could not save your public key in %s\n", identity_file);
537		exit(1);
538	}
539	fprintf(f, "%d ", BN_num_bits(public_key->n));
540	tmpbuf = BN_bn2dec(public_key->e);
541	fprintf(f, "%s ", tmpbuf);
542	free(tmpbuf);
543	tmpbuf = BN_bn2dec(public_key->n);
544	fprintf(f, "%s %s\n", tmpbuf, comment);
545	free(tmpbuf);
546	fclose(f);
547
548	if (!quiet) {
549		printf("Your public key has been saved in %s.\n", identity_file);
550		printf("The key fingerprint is:\n");
551		printf("%d %s %s\n", BN_num_bits(public_key->n),
552		       fingerprint(public_key->e, public_key->n),
553		       comment);
554	}
555	exit(0);
556}
557