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