xref: /freebsd/crypto/openssh/ssh-keygen.c (revision 23f282aa31e9b6fceacd449020e936e98d6f2298)
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"
10 RCSID("$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. */
18 RSA *private_key;
19 
20 /* Generated public key. */
21 RSA *public_key;
22 
23 /* Number of bits in the RSA key.  This value can be changed on the command line. */
24 int 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  */
30 int 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  */
36 int change_comment = 0;
37 
38 int quiet = 0;
39 
40 /* Flag indicating that we just want to see the key fingerprint */
41 int print_fingerprint = 0;
42 
43 /* The identity file name, given on the command line or entered by the user. */
44 char identity_file[1024];
45 int have_identity = 0;
46 
47 /* This is set to the passphrase if given on the command line. */
48 char *identity_passphrase = NULL;
49 
50 /* This is set to the new passphrase if given on the command line. */
51 char *identity_new_passphrase = NULL;
52 
53 /* This is set to the new comment if given on the command line. */
54 char *identity_comment = NULL;
55 
56 /* argv0 */
57 extern char *__progname;
58 
59 void
60 ask_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 
76 void
77 do_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  */
165 void
166 do_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  */
251 void
252 do_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 
346 void
347 usage(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  */
357 int
358 main(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 {
486 passphrase_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