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 * Identity and host key generation and maintenance. 6 * 7 * As far as I am concerned, the code I have written for this software 8 * can be used freely for any purpose. Any derived versions of this 9 * software must be clearly marked as such, and if the derived work is 10 * incompatible with the protocol description in the RFC file, it must be 11 * called by a name other than "ssh" or "Secure Shell". 12 */ 13 14 /* $OpenBSD: ssh-keygen.c,v 1.160 2007/01/21 01:41:54 stevesk Exp $ */ 15 16 #include "includes.h" 17 #include <openssl/evp.h> 18 #include <openssl/pem.h> 19 20 #include "xmalloc.h" 21 #include "key.h" 22 #include "rsa.h" 23 #include "authfile.h" 24 #include "uuencode.h" 25 #include "buffer.h" 26 #include "bufaux.h" 27 #include "pathnames.h" 28 #include "log.h" 29 #include "readpass.h" 30 #include "misc.h" 31 #include <langinfo.h> 32 #include "match.h" 33 #include "hostfile.h" 34 #include "tildexpand.h" 35 36 /* Number of bits in the RSA/DSA key. This value can be set on the command line. */ 37 #define DEFAULT_BITS_RSA 2048 38 #define DEFAULT_BITS_DSA 1024 39 u_int32_t bits = 0; 40 41 /* 42 * Flag indicating that we just want to change the passphrase. This can be 43 * set on the command line. 44 */ 45 int change_passphrase = 0; 46 47 /* 48 * Flag indicating that we just want to change the comment. This can be set 49 * on the command line. 50 */ 51 int change_comment = 0; 52 53 int quiet = 0; 54 55 /* Flag indicating that we want to hash a known_hosts file */ 56 int hash_hosts = 0; 57 /* Flag indicating that we want to lookup a host in known_hosts file */ 58 int find_host = 0; 59 /* Flag indicating that we want to delete a host from a known_hosts file */ 60 int delete_host = 0; 61 62 /* Flag indicating that we just want to see the key fingerprint */ 63 int print_fingerprint = 0; 64 int print_bubblebabble = 0; 65 66 /* The identity file name, given on the command line or entered by the user. */ 67 char identity_file[1024]; 68 int have_identity = 0; 69 70 /* This is set to the passphrase if given on the command line. */ 71 char *identity_passphrase = NULL; 72 73 /* This is set to the new passphrase if given on the command line. */ 74 char *identity_new_passphrase = NULL; 75 76 /* This is set to the new comment if given on the command line. */ 77 char *identity_comment = NULL; 78 79 /* Dump public key file in format used by real and the original SSH 2 */ 80 int convert_to_ssh2 = 0; 81 int convert_from_ssh2 = 0; 82 int print_public = 0; 83 84 char *key_type_name = NULL; 85 86 /* argv0 */ 87 #ifdef HAVE___PROGNAME 88 extern char *__progname; 89 #else 90 char *__progname; 91 #endif 92 93 char hostname[MAXHOSTNAMELEN]; 94 95 static void 96 ask_filename(struct passwd *pw, const char *prompt) 97 { 98 char buf[1024]; 99 char *name = NULL; 100 101 if (key_type_name == NULL) 102 name = _PATH_SSH_CLIENT_ID_RSA; 103 else { 104 switch (key_type_from_name(key_type_name)) { 105 case KEY_RSA1: 106 name = _PATH_SSH_CLIENT_IDENTITY; 107 break; 108 case KEY_DSA: 109 name = _PATH_SSH_CLIENT_ID_DSA; 110 break; 111 case KEY_RSA: 112 name = _PATH_SSH_CLIENT_ID_RSA; 113 break; 114 default: 115 fprintf(stderr, gettext("bad key type")); 116 exit(1); 117 break; 118 } 119 } 120 snprintf(identity_file, sizeof(identity_file), "%s/%s", pw->pw_dir, name); 121 fprintf(stderr, "%s (%s): ", gettext(prompt), identity_file); 122 if (fgets(buf, sizeof(buf), stdin) == NULL) 123 exit(1); 124 if (strchr(buf, '\n')) 125 *strchr(buf, '\n') = 0; 126 if (strcmp(buf, "") != 0) 127 strlcpy(identity_file, buf, sizeof(identity_file)); 128 have_identity = 1; 129 } 130 131 static Key * 132 load_identity(char *filename) 133 { 134 char *pass; 135 Key *prv; 136 137 prv = key_load_private(filename, "", NULL); 138 if (prv == NULL) { 139 if (identity_passphrase) 140 pass = xstrdup(identity_passphrase); 141 else 142 pass = read_passphrase(gettext("Enter passphrase: "), 143 RP_ALLOW_STDIN); 144 prv = key_load_private(filename, pass, NULL); 145 memset(pass, 0, strlen(pass)); 146 xfree(pass); 147 } 148 return prv; 149 } 150 151 #define SSH_COM_PUBLIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----" 152 #define SSH_COM_PUBLIC_END "---- END SSH2 PUBLIC KEY ----" 153 #define SSH_COM_PRIVATE_BEGIN "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----" 154 #define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb 155 156 static void 157 do_convert_to_ssh2(struct passwd *pw) 158 { 159 Key *k; 160 u_int len; 161 u_char *blob; 162 struct stat st; 163 164 if (!have_identity) 165 ask_filename(pw, gettext("Enter file in which the key is")); 166 if (stat(identity_file, &st) < 0) { 167 perror(identity_file); 168 exit(1); 169 } 170 if ((k = key_load_public(identity_file, NULL)) == NULL) { 171 if ((k = load_identity(identity_file)) == NULL) { 172 fprintf(stderr, gettext("load failed\n")); 173 exit(1); 174 } 175 } 176 if (key_to_blob(k, &blob, &len) <= 0) { 177 fprintf(stderr, gettext("key_to_blob failed\n")); 178 exit(1); 179 } 180 fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN); 181 fprintf(stdout, gettext( 182 "Comment: \"%u-bit %s, converted from OpenSSH by %s@%s\"\n"), 183 key_size(k), key_type(k), 184 pw->pw_name, hostname); 185 dump_base64(stdout, blob, len); 186 fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END); 187 key_free(k); 188 xfree(blob); 189 exit(0); 190 } 191 192 static void 193 buffer_get_bignum_bits(Buffer *b, BIGNUM *value) 194 { 195 u_int bignum_bits = buffer_get_int(b); 196 u_int bytes = (bignum_bits + 7) / 8; 197 198 if (buffer_len(b) < bytes) 199 fatal("buffer_get_bignum_bits: input buffer too small: " 200 "need %d have %d", bytes, buffer_len(b)); 201 if (BN_bin2bn(buffer_ptr(b), bytes, value) == NULL) 202 fatal("buffer_get_bignum_bits: BN_bin2bn failed"); 203 buffer_consume(b, bytes); 204 } 205 206 static Key * 207 do_convert_private_ssh2_from_blob(u_char *blob, u_int blen) 208 { 209 Buffer b; 210 Key *key = NULL; 211 char *type, *cipher; 212 u_char *sig, data[] = "abcde12345"; 213 int magic, rlen, ktype, i1, i2, i3, i4; 214 u_int slen; 215 u_long e; 216 217 buffer_init(&b); 218 buffer_append(&b, blob, blen); 219 220 magic = buffer_get_int(&b); 221 if (magic != SSH_COM_PRIVATE_KEY_MAGIC) { 222 error("bad magic 0x%x != 0x%x", magic, SSH_COM_PRIVATE_KEY_MAGIC); 223 buffer_free(&b); 224 return NULL; 225 } 226 i1 = buffer_get_int(&b); 227 type = buffer_get_string(&b, NULL); 228 cipher = buffer_get_string(&b, NULL); 229 i2 = buffer_get_int(&b); 230 i3 = buffer_get_int(&b); 231 i4 = buffer_get_int(&b); 232 debug("ignore (%d %d %d %d)", i1, i2, i3, i4); 233 if (strcmp(cipher, "none") != 0) { 234 error("unsupported cipher %s", cipher); 235 xfree(cipher); 236 buffer_free(&b); 237 xfree(type); 238 return NULL; 239 } 240 xfree(cipher); 241 242 if (strstr(type, "dsa")) { 243 ktype = KEY_DSA; 244 } else if (strstr(type, "rsa")) { 245 ktype = KEY_RSA; 246 } else { 247 buffer_free(&b); 248 xfree(type); 249 return NULL; 250 } 251 key = key_new_private(ktype); 252 xfree(type); 253 254 switch (key->type) { 255 case KEY_DSA: 256 buffer_get_bignum_bits(&b, key->dsa->p); 257 buffer_get_bignum_bits(&b, key->dsa->g); 258 buffer_get_bignum_bits(&b, key->dsa->q); 259 buffer_get_bignum_bits(&b, key->dsa->pub_key); 260 buffer_get_bignum_bits(&b, key->dsa->priv_key); 261 break; 262 case KEY_RSA: 263 e = buffer_get_char(&b); 264 debug("e %lx", e); 265 if (e < 30) { 266 e <<= 8; 267 e += buffer_get_char(&b); 268 debug("e %lx", e); 269 e <<= 8; 270 e += buffer_get_char(&b); 271 debug("e %lx", e); 272 } 273 if (!BN_set_word(key->rsa->e, e)) { 274 buffer_free(&b); 275 key_free(key); 276 return NULL; 277 } 278 buffer_get_bignum_bits(&b, key->rsa->d); 279 buffer_get_bignum_bits(&b, key->rsa->n); 280 buffer_get_bignum_bits(&b, key->rsa->iqmp); 281 buffer_get_bignum_bits(&b, key->rsa->q); 282 buffer_get_bignum_bits(&b, key->rsa->p); 283 rsa_generate_additional_parameters(key->rsa); 284 break; 285 } 286 rlen = buffer_len(&b); 287 if (rlen != 0) 288 error("do_convert_private_ssh2_from_blob: " 289 "remaining bytes in key blob %d", rlen); 290 buffer_free(&b); 291 292 /* try the key */ 293 key_sign(key, &sig, &slen, data, sizeof(data)); 294 key_verify(key, sig, slen, data, sizeof(data)); 295 xfree(sig); 296 return key; 297 } 298 299 static int 300 get_line(FILE *fp, char *line, size_t len) 301 { 302 int c; 303 size_t pos = 0; 304 305 line[0] = '\0'; 306 while ((c = fgetc(fp)) != EOF) { 307 if (pos >= len - 1) { 308 fprintf(stderr, "input line too long.\n"); 309 exit(1); 310 } 311 switch (c) { 312 case '\r': 313 c = fgetc(fp); 314 if (c != EOF && c != '\n' && ungetc(c, fp) == EOF) { 315 fprintf(stderr, "unget: %s\n", strerror(errno)); 316 exit(1); 317 } 318 return pos; 319 case '\n': 320 return pos; 321 } 322 line[pos++] = c; 323 line[pos] = '\0'; 324 } 325 /* We reached EOF */ 326 return -1; 327 } 328 329 static void 330 do_convert_from_ssh2(struct passwd *pw) 331 { 332 Key *k; 333 int blen; 334 u_int len; 335 char line[1024]; 336 u_char blob[8096]; 337 char encoded[8096]; 338 struct stat st; 339 int escaped = 0, private = 0, ok; 340 FILE *fp; 341 342 if (!have_identity) 343 ask_filename(pw, gettext("Enter file in which the key is")); 344 if (stat(identity_file, &st) < 0) { 345 perror(identity_file); 346 exit(1); 347 } 348 fp = fopen(identity_file, "r"); 349 if (fp == NULL) { 350 perror(identity_file); 351 exit(1); 352 } 353 encoded[0] = '\0'; 354 while ((blen = get_line(fp, line, sizeof(line))) != -1) { 355 if (line[blen - 1] == '\\') 356 escaped++; 357 if (strncmp(line, "----", 4) == 0 || 358 strstr(line, ": ") != NULL) { 359 if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL) 360 private = 1; 361 if (strstr(line, " END ") != NULL) { 362 break; 363 } 364 /* fprintf(stderr, "ignore: %s", line); */ 365 continue; 366 } 367 if (escaped) { 368 escaped--; 369 /* fprintf(stderr, "escaped: %s", line); */ 370 continue; 371 } 372 strlcat(encoded, line, sizeof(encoded)); 373 } 374 len = strlen(encoded); 375 if (((len % 4) == 3) && 376 (encoded[len-1] == '=') && 377 (encoded[len-2] == '=') && 378 (encoded[len-3] == '=')) 379 encoded[len-3] = '\0'; 380 blen = uudecode(encoded, blob, sizeof(blob)); 381 if (blen < 0) { 382 fprintf(stderr, gettext("uudecode failed.\n")); 383 exit(1); 384 } 385 k = private ? 386 do_convert_private_ssh2_from_blob(blob, blen) : 387 key_from_blob(blob, blen); 388 if (k == NULL) { 389 fprintf(stderr, gettext("decode blob failed.\n")); 390 exit(1); 391 } 392 ok = private ? 393 (k->type == KEY_DSA ? 394 PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, NULL, 0, NULL, NULL) : 395 PEM_write_RSAPrivateKey(stdout, k->rsa, NULL, NULL, 0, NULL, NULL)) : 396 key_write(k, stdout); 397 if (!ok) { 398 fprintf(stderr, gettext("key write failed")); 399 exit(1); 400 } 401 key_free(k); 402 if (!private) 403 fprintf(stdout, "\n"); 404 fclose(fp); 405 exit(0); 406 } 407 408 static void 409 do_print_public(struct passwd *pw) 410 { 411 Key *prv; 412 struct stat st; 413 414 if (!have_identity) 415 ask_filename(pw, gettext("Enter file in which the key is")); 416 if (stat(identity_file, &st) < 0) { 417 perror(identity_file); 418 exit(1); 419 } 420 prv = load_identity(identity_file); 421 if (prv == NULL) { 422 fprintf(stderr, gettext("load failed\n")); 423 exit(1); 424 } 425 if (!key_write(prv, stdout)) 426 fprintf(stderr, gettext("key_write failed")); 427 key_free(prv); 428 fprintf(stdout, "\n"); 429 exit(0); 430 } 431 432 static void 433 do_fingerprint(struct passwd *pw) 434 { 435 FILE *f; 436 Key *public; 437 char *comment = NULL, *cp, *ep, line[16*1024], *fp; 438 int i, skip = 0, num = 1, invalid = 1; 439 enum fp_rep rep; 440 enum fp_type fptype; 441 struct stat st; 442 443 fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5; 444 rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_HEX; 445 446 if (!have_identity) 447 ask_filename(pw, gettext("Enter file in which the key is")); 448 if (stat(identity_file, &st) < 0) { 449 perror(identity_file); 450 exit(1); 451 } 452 public = key_load_public(identity_file, &comment); 453 if (public != NULL) { 454 fp = key_fingerprint(public, fptype, rep); 455 printf("%u %s %s\n", key_size(public), fp, comment); 456 key_free(public); 457 xfree(comment); 458 xfree(fp); 459 exit(0); 460 } 461 if (comment) { 462 xfree(comment); 463 comment = NULL; 464 } 465 466 f = fopen(identity_file, "r"); 467 if (f != NULL) { 468 while (fgets(line, sizeof(line), f)) { 469 i = strlen(line) - 1; 470 if (line[i] != '\n') { 471 error("line %d too long: %.40s...", num, line); 472 skip = 1; 473 continue; 474 } 475 num++; 476 if (skip) { 477 skip = 0; 478 continue; 479 } 480 line[i] = '\0'; 481 482 /* Skip leading whitespace, empty and comment lines. */ 483 for (cp = line; *cp == ' ' || *cp == '\t'; cp++) 484 ; 485 if (!*cp || *cp == '\n' || *cp == '#') 486 continue; 487 i = strtol(cp, &ep, 10); 488 if (i == 0 || ep == NULL || (*ep != ' ' && *ep != '\t')) { 489 int quoted = 0; 490 comment = cp; 491 for (; *cp && (quoted || (*cp != ' ' && 492 *cp != '\t')); cp++) { 493 if (*cp == '\\' && cp[1] == '"') 494 cp++; /* Skip both */ 495 else if (*cp == '"') 496 quoted = !quoted; 497 } 498 if (!*cp) 499 continue; 500 *cp++ = '\0'; 501 } 502 ep = cp; 503 public = key_new(KEY_RSA1); 504 if (key_read(public, &cp) != 1) { 505 cp = ep; 506 key_free(public); 507 public = key_new(KEY_UNSPEC); 508 if (key_read(public, &cp) != 1) { 509 key_free(public); 510 continue; 511 } 512 } 513 comment = *cp ? cp : comment; 514 fp = key_fingerprint(public, fptype, rep); 515 printf("%u %s %s\n", key_size(public), fp, 516 comment ? comment : gettext("no comment")); 517 xfree(fp); 518 key_free(public); 519 invalid = 0; 520 } 521 fclose(f); 522 } 523 if (invalid) { 524 printf(gettext("%s is not a public key file.\n"), 525 identity_file); 526 exit(1); 527 } 528 exit(0); 529 } 530 531 static void 532 print_host(FILE *f, const char *name, Key *public, int hash) 533 { 534 if (hash && (name = host_hash(name, NULL, 0)) == NULL) 535 fatal("hash_host failed"); 536 fprintf(f, "%s ", name); 537 if (!key_write(public, f)) 538 fatal("key_write failed"); 539 fprintf(f, "\n"); 540 } 541 542 static void 543 do_known_hosts(struct passwd *pw, const char *name) 544 { 545 FILE *in, *out = stdout; 546 Key *public; 547 char *cp, *cp2, *kp, *kp2; 548 char line[16*1024], tmp[MAXPATHLEN], old[MAXPATHLEN]; 549 int c, i, skip = 0, inplace = 0, num = 0, invalid = 0, has_unhashed = 0; 550 551 if (!have_identity) { 552 cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid); 553 if (strlcpy(identity_file, cp, sizeof(identity_file)) >= 554 sizeof(identity_file)) 555 fatal("Specified known hosts path too long"); 556 xfree(cp); 557 have_identity = 1; 558 } 559 if ((in = fopen(identity_file, "r")) == NULL) 560 fatal("fopen: %s", strerror(errno)); 561 562 /* 563 * Find hosts goes to stdout, hash and deletions happen in-place 564 * A corner case is ssh-keygen -HF foo, which should go to stdout 565 */ 566 if (!find_host && (hash_hosts || delete_host)) { 567 if (strlcpy(tmp, identity_file, sizeof(tmp)) >= sizeof(tmp) || 568 strlcat(tmp, ".XXXXXXXXXX", sizeof(tmp)) >= sizeof(tmp) || 569 strlcpy(old, identity_file, sizeof(old)) >= sizeof(old) || 570 strlcat(old, ".old", sizeof(old)) >= sizeof(old)) 571 fatal("known_hosts path too long"); 572 umask(077); 573 if ((c = mkstemp(tmp)) == -1) 574 fatal("mkstemp: %s", strerror(errno)); 575 if ((out = fdopen(c, "w")) == NULL) { 576 c = errno; 577 unlink(tmp); 578 fatal("fdopen: %s", strerror(c)); 579 } 580 inplace = 1; 581 } 582 583 while (fgets(line, sizeof(line), in)) { 584 num++; 585 i = strlen(line) - 1; 586 if (line[i] != '\n') { 587 error("line %d too long: %.40s...", num, line); 588 skip = 1; 589 invalid = 1; 590 continue; 591 } 592 if (skip) { 593 skip = 0; 594 continue; 595 } 596 line[i] = '\0'; 597 598 /* Skip leading whitespace, empty and comment lines. */ 599 for (cp = line; *cp == ' ' || *cp == '\t'; cp++) 600 ; 601 if (!*cp || *cp == '\n' || *cp == '#') { 602 if (inplace) 603 fprintf(out, "%s\n", cp); 604 continue; 605 } 606 /* Find the end of the host name portion. */ 607 for (kp = cp; *kp && *kp != ' ' && *kp != '\t'; kp++) 608 ; 609 if (*kp == '\0' || *(kp + 1) == '\0') { 610 error("line %d missing key: %.40s...", 611 num, line); 612 invalid = 1; 613 continue; 614 } 615 *kp++ = '\0'; 616 kp2 = kp; 617 618 public = key_new(KEY_RSA1); 619 if (key_read(public, &kp) != 1) { 620 kp = kp2; 621 key_free(public); 622 public = key_new(KEY_UNSPEC); 623 if (key_read(public, &kp) != 1) { 624 error("line %d invalid key: %.40s...", 625 num, line); 626 key_free(public); 627 invalid = 1; 628 continue; 629 } 630 } 631 632 if (*cp == HASH_DELIM) { 633 if (find_host || delete_host) { 634 cp2 = host_hash(name, cp, strlen(cp)); 635 if (cp2 == NULL) { 636 error("line %d: invalid hashed " 637 "name: %.64s...", num, line); 638 invalid = 1; 639 continue; 640 } 641 c = (strcmp(cp2, cp) == 0); 642 if (find_host && c) { 643 printf(gettext("# Host %s found: " 644 "line %d type %s\n"), name, 645 num, key_type(public)); 646 print_host(out, cp, public, 0); 647 } 648 if (delete_host && !c) 649 print_host(out, cp, public, 0); 650 } else if (hash_hosts) 651 print_host(out, cp, public, 0); 652 } else { 653 if (find_host || delete_host) { 654 c = (match_hostname(name, cp, 655 strlen(cp)) == 1); 656 if (find_host && c) { 657 printf(gettext("# Host %s found: " 658 "line %d type %s\n"), name, 659 num, key_type(public)); 660 print_host(out, name, public, hash_hosts); 661 } 662 if (delete_host && !c) 663 print_host(out, cp, public, 0); 664 } else if (hash_hosts) { 665 for (cp2 = strsep(&cp, ","); 666 cp2 != NULL && *cp2 != '\0'; 667 cp2 = strsep(&cp, ",")) { 668 if (strcspn(cp2, "*?!") != strlen(cp2)) 669 fprintf(stderr, gettext("Warning: " 670 "ignoring host name with " 671 "metacharacters: %.64s\n"), 672 cp2); 673 else 674 print_host(out, cp2, public, 1); 675 } 676 has_unhashed = 1; 677 } 678 } 679 key_free(public); 680 } 681 fclose(in); 682 683 if (invalid) { 684 fprintf(stderr, gettext("%s is not a valid known_host file.\n"), 685 identity_file); 686 if (inplace) { 687 fprintf(stderr, gettext("Not replacing existing known_hosts " 688 "file because of errors\n")); 689 fclose(out); 690 unlink(tmp); 691 } 692 exit(1); 693 } 694 695 if (inplace) { 696 fclose(out); 697 698 /* Backup existing file */ 699 if (unlink(old) == -1 && errno != ENOENT) 700 fatal("unlink %.100s: %s", old, strerror(errno)); 701 if (link(identity_file, old) == -1) 702 fatal("link %.100s to %.100s: %s", identity_file, old, 703 strerror(errno)); 704 /* Move new one into place */ 705 if (rename(tmp, identity_file) == -1) { 706 error("rename\"%s\" to \"%s\": %s", tmp, identity_file, 707 strerror(errno)); 708 unlink(tmp); 709 unlink(old); 710 exit(1); 711 } 712 713 fprintf(stderr, gettext("%s updated.\n"), identity_file); 714 fprintf(stderr, gettext("Original contents retained as %s\n"), old); 715 if (has_unhashed) { 716 fprintf(stderr, gettext("WARNING: %s contains unhashed " 717 "entries\n"), old); 718 fprintf(stderr, gettext("Delete this file to ensure privacy " 719 "of hostnames\n")); 720 } 721 } 722 723 exit(0); 724 } 725 726 /* 727 * Perform changing a passphrase. The argument is the passwd structure 728 * for the current user. 729 */ 730 static void 731 do_change_passphrase(struct passwd *pw) 732 { 733 char *comment; 734 char *old_passphrase, *passphrase1, *passphrase2; 735 struct stat st; 736 Key *private; 737 738 if (!have_identity) 739 ask_filename(pw, gettext("Enter file in which the key is")); 740 if (stat(identity_file, &st) < 0) { 741 perror(identity_file); 742 exit(1); 743 } 744 /* Try to load the file with empty passphrase. */ 745 private = key_load_private(identity_file, "", &comment); 746 if (private == NULL) { 747 if (identity_passphrase) 748 old_passphrase = xstrdup(identity_passphrase); 749 else 750 old_passphrase = 751 read_passphrase(gettext("Enter old passphrase: "), 752 RP_ALLOW_STDIN); 753 private = key_load_private(identity_file, old_passphrase, 754 &comment); 755 memset(old_passphrase, 0, strlen(old_passphrase)); 756 xfree(old_passphrase); 757 if (private == NULL) { 758 printf(gettext("Bad passphrase.\n")); 759 exit(1); 760 } 761 } 762 printf(gettext("Key has comment '%s'\n"), comment); 763 764 /* Ask the new passphrase (twice). */ 765 if (identity_new_passphrase) { 766 passphrase1 = xstrdup(identity_new_passphrase); 767 passphrase2 = NULL; 768 } else { 769 passphrase1 = 770 read_passphrase(gettext("Enter new passphrase (empty" 771 " for no passphrase): "), RP_ALLOW_STDIN); 772 passphrase2 = read_passphrase(gettext("Enter same " 773 "passphrase again: "), RP_ALLOW_STDIN); 774 775 /* Verify that they are the same. */ 776 if (strcmp(passphrase1, passphrase2) != 0) { 777 memset(passphrase1, 0, strlen(passphrase1)); 778 memset(passphrase2, 0, strlen(passphrase2)); 779 xfree(passphrase1); 780 xfree(passphrase2); 781 printf(gettext("Pass phrases do not match. Try " 782 "again.\n")); 783 exit(1); 784 } 785 /* Destroy the other copy. */ 786 memset(passphrase2, 0, strlen(passphrase2)); 787 xfree(passphrase2); 788 } 789 790 /* Save the file using the new passphrase. */ 791 if (!key_save_private(private, identity_file, passphrase1, comment)) { 792 printf(gettext("Saving the key failed: %s.\n"), identity_file); 793 memset(passphrase1, 0, strlen(passphrase1)); 794 xfree(passphrase1); 795 key_free(private); 796 xfree(comment); 797 exit(1); 798 } 799 /* Destroy the passphrase and the copy of the key in memory. */ 800 memset(passphrase1, 0, strlen(passphrase1)); 801 xfree(passphrase1); 802 key_free(private); /* Destroys contents */ 803 xfree(comment); 804 805 printf(gettext("Your identification has been saved with the new " 806 "passphrase.\n")); 807 exit(0); 808 } 809 810 /* 811 * Change the comment of a private key file. 812 */ 813 static void 814 do_change_comment(struct passwd *pw) 815 { 816 char new_comment[1024], *comment, *passphrase; 817 Key *private; 818 Key *public; 819 struct stat st; 820 FILE *f; 821 int fd; 822 823 if (!have_identity) 824 ask_filename(pw, gettext("Enter file in which the key is")); 825 if (stat(identity_file, &st) < 0) { 826 perror(identity_file); 827 exit(1); 828 } 829 private = key_load_private(identity_file, "", &comment); 830 if (private == NULL) { 831 if (identity_passphrase) 832 passphrase = xstrdup(identity_passphrase); 833 else if (identity_new_passphrase) 834 passphrase = xstrdup(identity_new_passphrase); 835 else 836 passphrase = 837 read_passphrase(gettext("Enter passphrase: "), 838 RP_ALLOW_STDIN); 839 /* Try to load using the passphrase. */ 840 private = key_load_private(identity_file, passphrase, &comment); 841 if (private == NULL) { 842 memset(passphrase, 0, strlen(passphrase)); 843 xfree(passphrase); 844 printf(gettext("Bad passphrase.\n")); 845 exit(1); 846 } 847 } else { 848 passphrase = xstrdup(""); 849 } 850 if (private->type != KEY_RSA1) { 851 fprintf(stderr, gettext("Comments are only supported for " 852 "RSA1 keys.\n")); 853 key_free(private); 854 exit(1); 855 } 856 printf(gettext("Key now has comment '%s'\n"), comment); 857 858 if (identity_comment) { 859 strlcpy(new_comment, identity_comment, sizeof(new_comment)); 860 } else { 861 printf(gettext("Enter new comment: ")); 862 fflush(stdout); 863 if (!fgets(new_comment, sizeof(new_comment), stdin)) { 864 memset(passphrase, 0, strlen(passphrase)); 865 key_free(private); 866 exit(1); 867 } 868 if (strchr(new_comment, '\n')) 869 *strchr(new_comment, '\n') = 0; 870 } 871 872 /* Save the file using the new passphrase. */ 873 if (!key_save_private(private, identity_file, passphrase, new_comment)) { 874 printf(gettext("Saving the key failed: %s.\n"), identity_file); 875 memset(passphrase, 0, strlen(passphrase)); 876 xfree(passphrase); 877 key_free(private); 878 xfree(comment); 879 exit(1); 880 } 881 memset(passphrase, 0, strlen(passphrase)); 882 xfree(passphrase); 883 public = key_from_private(private); 884 key_free(private); 885 886 strlcat(identity_file, ".pub", sizeof(identity_file)); 887 fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); 888 if (fd == -1) { 889 printf(gettext("Could not save your public key in %s\n"), 890 identity_file); 891 exit(1); 892 } 893 f = fdopen(fd, "w"); 894 if (f == NULL) { 895 printf(gettext("fdopen %s failed"), identity_file); 896 exit(1); 897 } 898 if (!key_write(public, f)) 899 fprintf(stderr, gettext("write key failed")); 900 key_free(public); 901 fprintf(f, " %s\n", new_comment); 902 fclose(f); 903 904 xfree(comment); 905 906 printf(gettext("The comment in your key file has been changed.\n")); 907 exit(0); 908 } 909 910 static void 911 usage(void) 912 { 913 fprintf(stderr, gettext( 914 "Usage: %s [options]\n" 915 "Options:\n" 916 " -b bits Number of bits in the key to create.\n" 917 " -B Show bubblebabble digest of key file.\n" 918 " -c Change comment in private and public key files.\n" 919 " -C comment Provide new comment.\n" 920 " -e Convert OpenSSH to IETF SECSH key file.\n" 921 " -f filename Filename of the key file.\n" 922 " -F hostname Find hostname in known hosts file.\n" 923 " -H Hash names in known_hosts file.\n" 924 " -i Convert IETF SECSH to OpenSSH key file.\n" 925 " -l Show fingerprint of key file.\n" 926 " -N phrase Provide new passphrase.\n" 927 " -p Change passphrase of private key file.\n" 928 " -P phrase Provide old passphrase.\n" 929 " -q Quiet.\n" 930 " -R hostname Remove host from known_hosts file.\n" 931 " -t type Specify type of key to create.\n" 932 " -y Read private key file and print public key.\n" 933 ), __progname); 934 935 exit(1); 936 } 937 938 /* 939 * Main program for key management. 940 */ 941 int 942 main(int argc, char **argv) 943 { 944 char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2; 945 char *rr_hostname = NULL; 946 Key *private, *public; 947 struct passwd *pw; 948 struct stat st; 949 int opt, type, fd; 950 FILE *f; 951 952 extern int optind; 953 extern char *optarg; 954 955 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 956 sanitise_stdfd(); 957 958 __progname = get_progname(argv[0]); 959 960 g11n_setlocale(LC_ALL, ""); 961 962 SSLeay_add_all_algorithms(); 963 init_rng(); 964 seed_rng(); 965 966 /* we need this for the home * directory. */ 967 pw = getpwuid(getuid()); 968 if (!pw) { 969 printf(gettext("You don't exist, go away!\n")); 970 exit(1); 971 } 972 if (gethostname(hostname, sizeof(hostname)) < 0) { 973 perror("gethostname"); 974 exit(1); 975 } 976 977 #define GETOPT_ARGS "BcdeHilpqxXyb:C:f:F:N:P:R:t:" 978 979 while ((opt = getopt(argc, argv, GETOPT_ARGS)) != -1) { 980 switch (opt) { 981 case 'b': 982 bits = atoi(optarg); 983 if (bits < 512 || bits > 32768) { 984 printf(gettext("Bits has bad value.\n")); 985 exit(1); 986 } 987 break; 988 case 'F': 989 find_host = 1; 990 rr_hostname = optarg; 991 break; 992 case 'H': 993 hash_hosts = 1; 994 break; 995 case 'R': 996 delete_host = 1; 997 rr_hostname = optarg; 998 break; 999 case 'l': 1000 print_fingerprint = 1; 1001 break; 1002 case 'B': 1003 print_bubblebabble = 1; 1004 break; 1005 case 'p': 1006 change_passphrase = 1; 1007 break; 1008 case 'c': 1009 change_comment = 1; 1010 break; 1011 case 'f': 1012 strlcpy(identity_file, optarg, sizeof(identity_file)); 1013 have_identity = 1; 1014 break; 1015 case 'P': 1016 identity_passphrase = optarg; 1017 break; 1018 case 'N': 1019 identity_new_passphrase = optarg; 1020 break; 1021 case 'C': 1022 identity_comment = optarg; 1023 break; 1024 case 'q': 1025 quiet = 1; 1026 break; 1027 case 'e': 1028 case 'x': 1029 /* export key */ 1030 convert_to_ssh2 = 1; 1031 break; 1032 case 'i': 1033 case 'X': 1034 /* import key */ 1035 convert_from_ssh2 = 1; 1036 break; 1037 case 'y': 1038 print_public = 1; 1039 break; 1040 case 'd': 1041 key_type_name = "dsa"; 1042 break; 1043 case 't': 1044 key_type_name = optarg; 1045 break; 1046 case '?': 1047 default: 1048 usage(); 1049 } 1050 } 1051 if (optind < argc) { 1052 printf(gettext("Too many arguments.\n")); 1053 usage(); 1054 } 1055 if (change_passphrase && change_comment) { 1056 printf(gettext("Can only have one of -p and -c.\n")); 1057 usage(); 1058 } 1059 if (delete_host || hash_hosts || find_host) 1060 do_known_hosts(pw, rr_hostname); 1061 if (print_fingerprint || print_bubblebabble) 1062 do_fingerprint(pw); 1063 if (change_passphrase) 1064 do_change_passphrase(pw); 1065 if (change_comment) 1066 do_change_comment(pw); 1067 if (convert_to_ssh2) 1068 do_convert_to_ssh2(pw); 1069 if (convert_from_ssh2) 1070 do_convert_from_ssh2(pw); 1071 if (print_public) 1072 do_print_public(pw); 1073 1074 arc4random_stir(); 1075 1076 if (key_type_name == NULL) { 1077 printf(gettext("You must specify a key type (-t).\n")); 1078 usage(); 1079 } 1080 type = key_type_from_name(key_type_name); 1081 if (type == KEY_UNSPEC) { 1082 fprintf(stderr, gettext("unknown key type %s\n"), 1083 key_type_name); 1084 exit(1); 1085 } 1086 if (bits == 0) 1087 bits = (type == KEY_DSA) ? DEFAULT_BITS_DSA : DEFAULT_BITS_RSA; 1088 1089 if (!quiet) 1090 printf(gettext("Generating public/private %s key pair.\n"), 1091 key_type_name); 1092 private = key_generate(type, bits); 1093 if (private == NULL) { 1094 fprintf(stderr, gettext("key_generate failed")); 1095 exit(1); 1096 } 1097 public = key_from_private(private); 1098 1099 if (!have_identity) 1100 ask_filename(pw, gettext("Enter file in which to save the key")); 1101 1102 /* Create ~/.ssh directory if it doesn't already exist. */ 1103 snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, _PATH_SSH_USER_DIR); 1104 if (strstr(identity_file, dotsshdir) != NULL && 1105 stat(dotsshdir, &st) < 0) { 1106 if (mkdir(dotsshdir, 0700) < 0) 1107 error("Could not create directory '%s'.", dotsshdir); 1108 else if (!quiet) 1109 printf(gettext("Created directory '%s'.\n"), dotsshdir); 1110 } 1111 /* If the file already exists, ask the user to confirm. */ 1112 if (stat(identity_file, &st) >= 0) { 1113 char yesno[128]; 1114 printf(gettext("%s already exists.\n"), identity_file); 1115 printf(gettext("Overwrite (%s/%s)? "), 1116 nl_langinfo(YESSTR), nl_langinfo(NOSTR)); 1117 fflush(stdout); 1118 if (fgets(yesno, sizeof(yesno), stdin) == NULL) 1119 exit(1); 1120 if (strcasecmp(chop(yesno), nl_langinfo(YESSTR)) != 0) 1121 exit(1); 1122 } 1123 /* Ask for a passphrase (twice). */ 1124 if (identity_passphrase) 1125 passphrase1 = xstrdup(identity_passphrase); 1126 else if (identity_new_passphrase) 1127 passphrase1 = xstrdup(identity_new_passphrase); 1128 else { 1129 passphrase_again: 1130 passphrase1 = 1131 read_passphrase(gettext("Enter passphrase (empty " 1132 "for no passphrase): "), RP_ALLOW_STDIN); 1133 passphrase2 = read_passphrase(gettext("Enter same " 1134 "passphrase again: "), RP_ALLOW_STDIN); 1135 if (strcmp(passphrase1, passphrase2) != 0) { 1136 /* 1137 * The passphrases do not match. Clear them and 1138 * retry. 1139 */ 1140 memset(passphrase1, 0, strlen(passphrase1)); 1141 memset(passphrase2, 0, strlen(passphrase2)); 1142 xfree(passphrase1); 1143 xfree(passphrase2); 1144 printf(gettext("Passphrases do not match. Try " 1145 "again.\n")); 1146 goto passphrase_again; 1147 } 1148 /* Clear the other copy of the passphrase. */ 1149 memset(passphrase2, 0, strlen(passphrase2)); 1150 xfree(passphrase2); 1151 } 1152 1153 if (identity_comment) { 1154 strlcpy(comment, identity_comment, sizeof(comment)); 1155 } else { 1156 /* Create default commend field for the passphrase. */ 1157 snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); 1158 } 1159 1160 /* Save the key with the given passphrase and comment. */ 1161 if (!key_save_private(private, identity_file, passphrase1, comment)) { 1162 printf(gettext("Saving the key failed: %s.\n"), identity_file); 1163 memset(passphrase1, 0, strlen(passphrase1)); 1164 xfree(passphrase1); 1165 exit(1); 1166 } 1167 /* Clear the passphrase. */ 1168 memset(passphrase1, 0, strlen(passphrase1)); 1169 xfree(passphrase1); 1170 1171 /* Clear the private key and the random number generator. */ 1172 key_free(private); 1173 arc4random_stir(); 1174 1175 if (!quiet) 1176 printf(gettext("Your identification has been saved in %s.\n"), 1177 identity_file); 1178 1179 strlcat(identity_file, ".pub", sizeof(identity_file)); 1180 fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); 1181 if (fd == -1) { 1182 printf(gettext("Could not save your public key in %s\n"), 1183 identity_file); 1184 exit(1); 1185 } 1186 f = fdopen(fd, "w"); 1187 if (f == NULL) { 1188 printf(gettext("fdopen %s failed"), identity_file); 1189 exit(1); 1190 } 1191 if (!key_write(public, f)) 1192 fprintf(stderr, gettext("write key failed")); 1193 fprintf(f, " %s\n", comment); 1194 fclose(f); 1195 1196 if (!quiet) { 1197 char *fp = key_fingerprint(public, SSH_FP_MD5, SSH_FP_HEX); 1198 printf(gettext("Your public key has been saved in %s.\n"), 1199 identity_file); 1200 printf(gettext("The key fingerprint is:\n")); 1201 printf("%s %s\n", fp, comment); 1202 xfree(fp); 1203 } 1204 1205 key_free(public); 1206 return(0); 1207 /* NOTREACHED */ 1208 } 1209