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