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