blob: 6f2d426b80f699370627021caf3e7226d9f01a01 [file] [log] [blame]
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001/*
Damien Miller95def091999-11-25 00:26:21 +11002 * 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 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +10008
9#include "includes.h"
Damien Miller95def091999-11-25 00:26:21 +110010RCSID("$Id: ssh-keygen.c,v 1.9 1999/11/24 13:26:23 damien Exp $");
Damien Millerd4a8b7e1999-10-27 13:42:43 +100011
12#include "rsa.h"
13#include "ssh.h"
14#include "xmalloc.h"
Damien Miller10f6f6b1999-11-17 17:29:08 +110015#include "fingerprint.h"
Damien Millerd4a8b7e1999-10-27 13:42:43 +100016
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
24 line. */
25int bits = 1024;
26
27/* Flag indicating that we just want to change the passphrase. This can be
28 set on the command line. */
29int change_passphrase = 0;
30
31/* Flag indicating that we just want to change the comment. This can be set
32 on the command line. */
33int change_comment = 0;
34
35int quiet = 0;
36
Damien Miller10f6f6b1999-11-17 17:29:08 +110037/* Flag indicating that we just want to see the key fingerprint */
38int print_fingerprint = 0;
39
Damien Miller431f66b1999-11-21 18:31:57 +110040/* The identity file name, given on the command line or entered by the user. */
41char identity_file[1024];
42int have_identity = 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100043
44/* This is set to the passphrase if given on the command line. */
45char *identity_passphrase = NULL;
46
47/* This is set to the new passphrase if given on the command line. */
48char *identity_new_passphrase = NULL;
49
50/* This is set to the new comment if given on the command line. */
51char *identity_comment = NULL;
52
Damien Miller431f66b1999-11-21 18:31:57 +110053/* argv0 */
Damien Miller95def091999-11-25 00:26:21 +110054#ifdef HAVE___PROGNAME
Damien Miller431f66b1999-11-21 18:31:57 +110055extern char *__progname;
Damien Miller95def091999-11-25 00:26:21 +110056#else /* HAVE___PROGNAME */
57const char *__progname = "ssh-keygen";
58#endif /* HAVE___PROGNAME */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100059
Damien Miller431f66b1999-11-21 18:31:57 +110060void
61ask_filename(struct passwd *pw, const char *prompt)
Damien Millerd4a8b7e1999-10-27 13:42:43 +100062{
Damien Miller95def091999-11-25 00:26:21 +110063 char buf[1024];
64 snprintf(identity_file, sizeof(identity_file), "%s/%s",
65 pw->pw_dir, SSH_CLIENT_IDENTITY);
66 printf("%s (%s): ", prompt, identity_file);
67 fflush(stdout);
68 if (fgets(buf, sizeof(buf), stdin) == NULL)
69 exit(1);
70 if (strchr(buf, '\n'))
71 *strchr(buf, '\n') = 0;
72 if (strcmp(buf, "") != 0)
73 strlcpy(identity_file, buf, sizeof(identity_file));
74 have_identity = 1;
Damien Miller10f6f6b1999-11-17 17:29:08 +110075}
Damien Millerd4a8b7e1999-10-27 13:42:43 +100076
Damien Miller10f6f6b1999-11-17 17:29:08 +110077void
78do_fingerprint(struct passwd *pw)
79{
Damien Miller95def091999-11-25 00:26:21 +110080 char *comment;
81 RSA *public_key;
82 struct stat st;
Damien Miller10f6f6b1999-11-17 17:29:08 +110083
Damien Miller95def091999-11-25 00:26:21 +110084 if (!have_identity)
85 ask_filename(pw, "Enter file in which the key is");
86 if (stat(identity_file, &st) < 0) {
87 perror(identity_file);
88 exit(1);
89 }
90 public_key = RSA_new();
91 if (!load_public_key(identity_file, public_key, &comment)) {
92 char *cp, line[1024];
93 BIGNUM *e, *n;
94 int dummy, invalid = 0;
95 FILE *f = fopen(identity_file, "r");
96 n = BN_new();
97 e = BN_new();
98 if (f && fgets(line, sizeof(line), f)) {
99 cp = line;
100 line[strlen(line) - 1] = '\0';
101 if (auth_rsa_read_key(&cp, &dummy, e, n)) {
102 public_key->e = e;
103 public_key->n = n;
104 comment = xstrdup(cp ? cp : "no comment");
105 } else {
106 invalid = 1;
107 }
108 } else {
109 invalid = 1;
110 }
111 if (invalid) {
112 printf("%s is not a valid key file.\n", identity_file);
113 BN_free(e);
114 BN_free(n);
115 exit(1);
116 }
117 }
118 printf("%d %s %s\n", BN_num_bits(public_key->n),
119 fingerprint(public_key->e, public_key->n),
120 comment);
121 RSA_free(public_key);
122 exit(0);
Damien Miller10f6f6b1999-11-17 17:29:08 +1100123}
124
Damien Miller95def091999-11-25 00:26:21 +1100125/*
126 * Perform changing a passphrase. The argument is the passwd structure
127 * for the current user.
128 */
Damien Miller10f6f6b1999-11-17 17:29:08 +1100129void
130do_change_passphrase(struct passwd *pw)
131{
Damien Miller95def091999-11-25 00:26:21 +1100132 char *comment;
133 char *old_passphrase, *passphrase1, *passphrase2;
134 struct stat st;
135 RSA *private_key;
Damien Miller10f6f6b1999-11-17 17:29:08 +1100136
Damien Miller95def091999-11-25 00:26:21 +1100137 if (!have_identity)
138 ask_filename(pw, "Enter file in which the key is");
139 /* Check if the file exists. */
140 if (stat(identity_file, &st) < 0) {
141 perror(identity_file);
142 exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000143 }
Damien Miller95def091999-11-25 00:26:21 +1100144 /* Try to load the public key from the file the verify that it is
145 readable and of the proper format. */
146 public_key = RSA_new();
147 if (!load_public_key(identity_file, public_key, NULL)) {
148 printf("%s is not a valid key file.\n", identity_file);
149 exit(1);
150 }
151 /* Clear the public key since we are just about to load the whole file. */
152 RSA_free(public_key);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000153
Damien Miller95def091999-11-25 00:26:21 +1100154 /* Try to load the file with empty passphrase. */
155 private_key = RSA_new();
156 if (!load_private_key(identity_file, "", private_key, &comment)) {
157 /* Read passphrase from the user. */
158 if (identity_passphrase)
159 old_passphrase = xstrdup(identity_passphrase);
160 else
161 old_passphrase = read_passphrase("Enter old passphrase: ", 1);
162 /* Try to load using the passphrase. */
163 if (!load_private_key(identity_file, old_passphrase, private_key, &comment)) {
164 memset(old_passphrase, 0, strlen(old_passphrase));
165 xfree(old_passphrase);
166 printf("Bad passphrase.\n");
167 exit(1);
168 }
169 /* Destroy the passphrase. */
170 memset(old_passphrase, 0, strlen(old_passphrase));
171 xfree(old_passphrase);
172 }
173 printf("Key has comment '%s'\n", comment);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000174
Damien Miller95def091999-11-25 00:26:21 +1100175 /* Ask the new passphrase (twice). */
176 if (identity_new_passphrase) {
177 passphrase1 = xstrdup(identity_new_passphrase);
178 passphrase2 = NULL;
179 } else {
180 passphrase1 =
181 read_passphrase("Enter new passphrase (empty for no passphrase): ", 1);
182 passphrase2 = read_passphrase("Enter same passphrase again: ", 1);
183
184 /* Verify that they are the same. */
185 if (strcmp(passphrase1, passphrase2) != 0) {
186 memset(passphrase1, 0, strlen(passphrase1));
187 memset(passphrase2, 0, strlen(passphrase2));
188 xfree(passphrase1);
189 xfree(passphrase2);
190 printf("Pass phrases do not match. Try again.\n");
191 exit(1);
192 }
193 /* Destroy the other copy. */
194 memset(passphrase2, 0, strlen(passphrase2));
195 xfree(passphrase2);
196 }
197
198 /* Save the file using the new passphrase. */
199 if (!save_private_key(identity_file, passphrase1, private_key, comment)) {
200 printf("Saving the key failed: %s: %s.\n",
201 identity_file, strerror(errno));
202 memset(passphrase1, 0, strlen(passphrase1));
203 xfree(passphrase1);
204 RSA_free(private_key);
205 xfree(comment);
206 exit(1);
207 }
208 /* Destroy the passphrase and the copy of the key in memory. */
209 memset(passphrase1, 0, strlen(passphrase1));
210 xfree(passphrase1);
211 RSA_free(private_key); /* Destroys contents */
212 xfree(comment);
213
214 printf("Your identification has been saved with the new passphrase.\n");
215 exit(0);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000216}
217
Damien Miller95def091999-11-25 00:26:21 +1100218/*
219 * Change the comment of a private key file.
220 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000221void
222do_change_comment(struct passwd *pw)
223{
Damien Miller95def091999-11-25 00:26:21 +1100224 char new_comment[1024], *comment;
225 RSA *private_key;
226 char *passphrase;
227 struct stat st;
228 FILE *f;
229 char *tmpbuf;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000230
Damien Miller95def091999-11-25 00:26:21 +1100231 if (!have_identity)
232 ask_filename(pw, "Enter file in which the key is");
233 /* Check if the file exists. */
234 if (stat(identity_file, &st) < 0) {
235 perror(identity_file);
236 exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000237 }
Damien Miller95def091999-11-25 00:26:21 +1100238 /* Try to load the public key from the file the verify that it is
239 readable and of the proper format. */
240 public_key = RSA_new();
241 if (!load_public_key(identity_file, public_key, NULL)) {
242 printf("%s is not a valid key file.\n", identity_file);
243 exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000244 }
Damien Miller95def091999-11-25 00:26:21 +1100245 private_key = RSA_new();
246 /* Try to load the file with empty passphrase. */
247 if (load_private_key(identity_file, "", private_key, &comment))
248 passphrase = xstrdup("");
249 else {
250 /* Read passphrase from the user. */
251 if (identity_passphrase)
252 passphrase = xstrdup(identity_passphrase);
253 else if (identity_new_passphrase)
254 passphrase = xstrdup(identity_new_passphrase);
255 else
256 passphrase = read_passphrase("Enter passphrase: ", 1);
257 /* Try to load using the passphrase. */
258 if (!load_private_key(identity_file, passphrase, private_key, &comment)) {
259 memset(passphrase, 0, strlen(passphrase));
260 xfree(passphrase);
261 printf("Bad passphrase.\n");
262 exit(1);
263 }
264 }
265 printf("Key now has comment '%s'\n", comment);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000266
Damien Miller95def091999-11-25 00:26:21 +1100267 if (identity_comment) {
268 strlcpy(new_comment, identity_comment, sizeof(new_comment));
269 } else {
270 printf("Enter new comment: ");
271 fflush(stdout);
272 if (!fgets(new_comment, sizeof(new_comment), stdin)) {
273 memset(passphrase, 0, strlen(passphrase));
274 RSA_free(private_key);
275 exit(1);
276 }
277 /* Remove terminating newline from comment. */
278 if (strchr(new_comment, '\n'))
279 *strchr(new_comment, '\n') = 0;
280 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000281
Damien Miller95def091999-11-25 00:26:21 +1100282 /* Save the file using the new passphrase. */
283 if (!save_private_key(identity_file, passphrase, private_key, new_comment)) {
284 printf("Saving the key failed: %s: %s.\n",
285 identity_file, strerror(errno));
286 memset(passphrase, 0, strlen(passphrase));
287 xfree(passphrase);
288 RSA_free(private_key);
289 xfree(comment);
290 exit(1);
291 }
292 /* Destroy the passphrase and the private key in memory. */
293 memset(passphrase, 0, strlen(passphrase));
294 xfree(passphrase);
295 RSA_free(private_key);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000296
Damien Miller95def091999-11-25 00:26:21 +1100297 /* Save the public key in text format in a file with the same name
298 but .pub appended. */
299 strlcat(identity_file, ".pub", sizeof(identity_file));
300 f = fopen(identity_file, "w");
301 if (!f) {
302 printf("Could not save your public key in %s\n", identity_file);
303 exit(1);
304 }
305 fprintf(f, "%d ", BN_num_bits(public_key->n));
306 tmpbuf = BN_bn2dec(public_key->e);
307 fprintf(f, "%s ", tmpbuf);
308 free(tmpbuf);
309 tmpbuf = BN_bn2dec(public_key->n);
310 fprintf(f, "%s %s\n", tmpbuf, new_comment);
311 free(tmpbuf);
312 fclose(f);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000313
Damien Miller95def091999-11-25 00:26:21 +1100314 xfree(comment);
315
316 printf("The comment in your key file has been changed.\n");
317 exit(0);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000318}
319
Damien Miller431f66b1999-11-21 18:31:57 +1100320void
321usage(void)
322{
Damien Miller95def091999-11-25 00:26:21 +1100323 printf("ssh-keygen version %s\n", SSH_VERSION);
324 printf("Usage: %s [-b bits] [-p] [-c] [-f file] [-P pass] [-N new-pass] [-C comment]\n", __progname);
325 exit(1);
Damien Miller431f66b1999-11-21 18:31:57 +1100326}
327
Damien Miller95def091999-11-25 00:26:21 +1100328/*
329 * Main program for key management.
330 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000331int
332main(int ac, char **av)
333{
Damien Miller95def091999-11-25 00:26:21 +1100334 char dotsshdir[16 * 1024], comment[1024], *passphrase1, *passphrase2;
335 struct passwd *pw;
336 char *tmpbuf;
337 int opt;
338 struct stat st;
339 FILE *f;
340 char hostname[MAXHOSTNAMELEN];
341 extern int optind;
342 extern char *optarg;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000343
Damien Miller95def091999-11-25 00:26:21 +1100344 /* check if RSA support exists */
345 if (rsa_alive() == 0) {
346 extern char *__progname;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000347
Damien Miller95def091999-11-25 00:26:21 +1100348 fprintf(stderr,
349 "%s: no RSA support in libssl and libcrypto. See ssl(8).\n",
350 __progname);
351 exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000352 }
Damien Miller95def091999-11-25 00:26:21 +1100353 /* Get user\'s passwd structure. We need this for the home
354 directory. */
355 pw = getpwuid(getuid());
356 if (!pw) {
357 printf("You don't exist, go away!\n");
358 exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000359 }
Damien Miller95def091999-11-25 00:26:21 +1100360 /* Parse command line arguments. */
361 while ((opt = getopt(ac, av, "qpclb:f:P:N:C:")) != EOF) {
362 switch (opt) {
363 case 'b':
364 bits = atoi(optarg);
365 if (bits < 512 || bits > 32768) {
366 printf("Bits has bad value.\n");
367 exit(1);
368 }
369 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000370
Damien Miller95def091999-11-25 00:26:21 +1100371 case 'l':
372 print_fingerprint = 1;
373 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000374
Damien Miller95def091999-11-25 00:26:21 +1100375 case 'p':
376 change_passphrase = 1;
377 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000378
Damien Miller95def091999-11-25 00:26:21 +1100379 case 'c':
380 change_comment = 1;
381 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000382
Damien Miller95def091999-11-25 00:26:21 +1100383 case 'f':
384 strlcpy(identity_file, optarg, sizeof(identity_file));
385 have_identity = 1;
386 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000387
Damien Miller95def091999-11-25 00:26:21 +1100388 case 'P':
389 identity_passphrase = optarg;
390 break;
391
392 case 'N':
393 identity_new_passphrase = optarg;
394 break;
395
396 case 'C':
397 identity_comment = optarg;
398 break;
399
400 case 'q':
401 quiet = 1;
402 break;
403
404 case '?':
405 default:
406 usage();
407 }
408 }
409 if (optind < ac) {
410 printf("Too many arguments.\n");
411 usage();
412 }
413 if (change_passphrase && change_comment) {
414 printf("Can only have one of -p and -c.\n");
415 usage();
416 }
417 if (print_fingerprint)
418 do_fingerprint(pw);
419
420 /* If the user requested to change the passphrase, do it now.
421 This function never returns. */
422 if (change_passphrase)
423 do_change_passphrase(pw);
424
425 /* If the user requested to change the comment, do it now. This
426 function never returns. */
427 if (change_comment)
428 do_change_comment(pw);
429
430 arc4random_stir();
431
432 if (quiet)
433 rsa_set_verbose(0);
434
435 /* Generate the rsa key pair. */
436 private_key = RSA_new();
437 public_key = RSA_new();
438 rsa_generate_key(private_key, public_key, bits);
439
440 if (!have_identity)
441 ask_filename(pw, "Enter file in which to save the key");
442
443 /* Create ~/.ssh directory if it doesn\'t already exist. */
444 snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, SSH_USER_DIR);
445 if (strstr(identity_file, dotsshdir) != NULL &&
446 stat(dotsshdir, &st) < 0) {
447 if (mkdir(dotsshdir, 0755) < 0)
448 error("Could not create directory '%s'.", dotsshdir);
449 else if (!quiet)
450 printf("Created directory '%s'.\n", dotsshdir);
451 }
452 /* If the file already exists, ask the user to confirm. */
453 if (stat(identity_file, &st) >= 0) {
454 char yesno[3];
455 printf("%s already exists.\n", identity_file);
456 printf("Overwrite (y/n)? ");
457 fflush(stdout);
458 if (fgets(yesno, sizeof(yesno), stdin) == NULL)
459 exit(1);
460 if (yesno[0] != 'y' && yesno[0] != 'Y')
461 exit(1);
462 }
463 /* Ask for a passphrase (twice). */
464 if (identity_passphrase)
465 passphrase1 = xstrdup(identity_passphrase);
466 else if (identity_new_passphrase)
467 passphrase1 = xstrdup(identity_new_passphrase);
468 else {
469passphrase_again:
470 passphrase1 =
471 read_passphrase("Enter passphrase (empty for no passphrase): ", 1);
472 passphrase2 = read_passphrase("Enter same passphrase again: ", 1);
473 if (strcmp(passphrase1, passphrase2) != 0) {
474 /* The passphrases do not match. Clear them and retry. */
475 memset(passphrase1, 0, strlen(passphrase1));
476 memset(passphrase2, 0, strlen(passphrase2));
477 xfree(passphrase1);
478 xfree(passphrase2);
479 printf("Passphrases do not match. Try again.\n");
480 goto passphrase_again;
481 }
482 /* Clear the other copy of the passphrase. */
483 memset(passphrase2, 0, strlen(passphrase2));
484 xfree(passphrase2);
485 }
486
487 /* Create default commend field for the passphrase. The user can
488 later edit this field. */
489 if (identity_comment) {
490 strlcpy(comment, identity_comment, sizeof(comment));
491 } else {
492 if (gethostname(hostname, sizeof(hostname)) < 0) {
493 perror("gethostname");
494 exit(1);
495 }
496 snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname);
497 }
498
499 /* Save the key with the given passphrase and comment. */
500 if (!save_private_key(identity_file, passphrase1, private_key, comment)) {
501 printf("Saving the key failed: %s: %s.\n",
502 identity_file, strerror(errno));
503 memset(passphrase1, 0, strlen(passphrase1));
504 xfree(passphrase1);
505 exit(1);
506 }
507 /* Clear the passphrase. */
508 memset(passphrase1, 0, strlen(passphrase1));
509 xfree(passphrase1);
510
511 /* Clear the private key and the random number generator. */
512 RSA_free(private_key);
513 arc4random_stir();
514
515 if (!quiet)
516 printf("Your identification has been saved in %s.\n", identity_file);
517
518 /* Save the public key in text format in a file with the same name
519 but .pub appended. */
520 strlcat(identity_file, ".pub", sizeof(identity_file));
521 f = fopen(identity_file, "w");
522 if (!f) {
523 printf("Could not save your public key in %s\n", identity_file);
524 exit(1);
525 }
526 fprintf(f, "%d ", BN_num_bits(public_key->n));
527 tmpbuf = BN_bn2dec(public_key->e);
528 fprintf(f, "%s ", tmpbuf);
529 free(tmpbuf);
530 tmpbuf = BN_bn2dec(public_key->n);
531 fprintf(f, "%s %s\n", tmpbuf, comment);
532 free(tmpbuf);
533 fclose(f);
534
535 if (!quiet) {
536 printf("Your public key has been saved in %s.\n", identity_file);
537 printf("The key fingerprint is:\n");
538 printf("%d %s %s\n", BN_num_bits(public_key->n),
539 fingerprint(public_key->e, public_key->n),
540 comment);
541 }
542 exit(0);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000543}