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