]>
Commit | Line | Data |
---|---|---|
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" | |
10 | RCSID("$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. */ | |
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 | |
24 | line. */ | |
25 | int bits = 1024; | |
26 | ||
27 | /* Flag indicating that we just want to change the passphrase. This can be | |
28 | set on the command line. */ | |
29 | int change_passphrase = 0; | |
30 | ||
31 | /* Flag indicating that we just want to change the comment. This can be set | |
32 | on the command line. */ | |
33 | int change_comment = 0; | |
34 | ||
35 | int quiet = 0; | |
36 | ||
f095fcc7 | 37 | /* Flag indicating that we just want to see the key fingerprint */ |
38 | int print_fingerprint = 0; | |
39 | ||
5ad13cd7 | 40 | /* The identity file name, given on the command line or entered by the user. */ |
41 | char identity_file[1024]; | |
42 | int have_identity = 0; | |
8efc0c15 | 43 | |
44 | /* This is set to the passphrase if given on the command line. */ | |
45 | char *identity_passphrase = NULL; | |
46 | ||
47 | /* This is set to the new passphrase if given on the command line. */ | |
48 | char *identity_new_passphrase = NULL; | |
49 | ||
50 | /* This is set to the new comment if given on the command line. */ | |
51 | char *identity_comment = NULL; | |
52 | ||
5ad13cd7 | 53 | /* argv0 */ |
5260325f | 54 | #ifdef HAVE___PROGNAME |
5ad13cd7 | 55 | extern char *__progname; |
5260325f | 56 | #else /* HAVE___PROGNAME */ |
57 | const char *__progname = "ssh-keygen"; | |
58 | #endif /* HAVE___PROGNAME */ | |
8efc0c15 | 59 | |
5ad13cd7 | 60 | void |
61 | ask_filename(struct passwd *pw, const char *prompt) | |
8efc0c15 | 62 | { |
5260325f | 63 | 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; | |
f095fcc7 | 75 | } |
8efc0c15 | 76 | |
f095fcc7 | 77 | void |
78 | do_fingerprint(struct passwd *pw) | |
79 | { | |
5260325f | 80 | char *comment; |
81 | RSA *public_key; | |
82 | struct stat st; | |
83 | ||
84 | 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); | |
f095fcc7 | 123 | } |
124 | ||
5260325f | 125 | /* |
126 | * Perform changing a passphrase. The argument is the passwd structure | |
127 | * for the current user. | |
128 | */ | |
f095fcc7 | 129 | void |
130 | do_change_passphrase(struct passwd *pw) | |
131 | { | |
5260325f | 132 | char *comment; |
133 | char *old_passphrase, *passphrase1, *passphrase2; | |
134 | struct stat st; | |
135 | RSA *private_key; | |
136 | ||
137 | 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); | |
143 | } | |
144 | /* 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); | |
153 | ||
154 | /* 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); | |
174 | ||
175 | /* 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); | |
8efc0c15 | 196 | } |
8efc0c15 | 197 | |
5260325f | 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); | |
216 | } | |
8efc0c15 | 217 | |
5260325f | 218 | /* |
219 | * Change the comment of a private key file. | |
220 | */ | |
8efc0c15 | 221 | void |
222 | do_change_comment(struct passwd *pw) | |
223 | { | |
5260325f | 224 | char new_comment[1024], *comment; |
225 | RSA *private_key; | |
226 | char *passphrase; | |
227 | struct stat st; | |
228 | FILE *f; | |
229 | char *tmpbuf; | |
230 | ||
231 | 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); | |
237 | } | |
238 | /* 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); | |
8efc0c15 | 244 | } |
5260325f | 245 | 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); | |
266 | ||
267 | 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 | } | |
281 | ||
282 | /* 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); | |
8efc0c15 | 291 | } |
5260325f | 292 | /* Destroy the passphrase and the private key in memory. */ |
293 | memset(passphrase, 0, strlen(passphrase)); | |
294 | xfree(passphrase); | |
295 | RSA_free(private_key); | |
296 | ||
297 | /* 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); | |
313 | ||
314 | xfree(comment); | |
315 | ||
316 | printf("The comment in your key file has been changed.\n"); | |
317 | exit(0); | |
8efc0c15 | 318 | } |
319 | ||
5ad13cd7 | 320 | void |
321 | usage(void) | |
322 | { | |
5260325f | 323 | 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); | |
5ad13cd7 | 326 | } |
327 | ||
5260325f | 328 | /* |
329 | * Main program for key management. | |
330 | */ | |
8efc0c15 | 331 | int |
332 | main(int ac, char **av) | |
333 | { | |
5260325f | 334 | 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; | |
343 | ||
344 | /* check if RSA support exists */ | |
345 | if (rsa_alive() == 0) { | |
346 | extern char *__progname; | |
347 | ||
348 | fprintf(stderr, | |
349 | "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", | |
350 | __progname); | |
351 | exit(1); | |
8efc0c15 | 352 | } |
5260325f | 353 | /* 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); | |
359 | } | |
360 | /* 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; | |
370 | ||
371 | case 'l': | |
372 | print_fingerprint = 1; | |
373 | break; | |
374 | ||
375 | case 'p': | |
376 | change_passphrase = 1; | |
377 | break; | |
378 | ||
379 | case 'c': | |
380 | change_comment = 1; | |
381 | break; | |
382 | ||
383 | case 'f': | |
384 | strlcpy(identity_file, optarg, sizeof(identity_file)); | |
385 | have_identity = 1; | |
386 | break; | |
387 | ||
388 | 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 { | |
469 | passphrase_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); | |
8efc0c15 | 541 | } |
5260325f | 542 | exit(0); |
8efc0c15 | 543 | } |