17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * Author: Tatu Ylonen <ylo@cs.hut.fi> 37c478bd9Sstevel@tonic-gate * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 47c478bd9Sstevel@tonic-gate * All rights reserved 57c478bd9Sstevel@tonic-gate * Identity and host key generation and maintenance. 67c478bd9Sstevel@tonic-gate * 77c478bd9Sstevel@tonic-gate * As far as I am concerned, the code I have written for this software 87c478bd9Sstevel@tonic-gate * can be used freely for any purpose. Any derived versions of this 97c478bd9Sstevel@tonic-gate * software must be clearly marked as such, and if the derived work is 107c478bd9Sstevel@tonic-gate * incompatible with the protocol description in the RFC file, it must be 117c478bd9Sstevel@tonic-gate * called by a name other than "ssh" or "Secure Shell". 127c478bd9Sstevel@tonic-gate */ 137c478bd9Sstevel@tonic-gate 14442d23f4Sjp161948 /* $OpenBSD: ssh-keygen.c,v 1.160 2007/01/21 01:41:54 stevesk Exp $ */ 157c478bd9Sstevel@tonic-gate 16442d23f4Sjp161948 #include "includes.h" 177c478bd9Sstevel@tonic-gate #include <openssl/evp.h> 187c478bd9Sstevel@tonic-gate #include <openssl/pem.h> 197c478bd9Sstevel@tonic-gate 207c478bd9Sstevel@tonic-gate #include "xmalloc.h" 217c478bd9Sstevel@tonic-gate #include "key.h" 227c478bd9Sstevel@tonic-gate #include "rsa.h" 237c478bd9Sstevel@tonic-gate #include "authfile.h" 247c478bd9Sstevel@tonic-gate #include "uuencode.h" 257c478bd9Sstevel@tonic-gate #include "buffer.h" 267c478bd9Sstevel@tonic-gate #include "bufaux.h" 277c478bd9Sstevel@tonic-gate #include "pathnames.h" 287c478bd9Sstevel@tonic-gate #include "log.h" 297c478bd9Sstevel@tonic-gate #include "readpass.h" 3026ba1984Sjp161948 #include "misc.h" 317c478bd9Sstevel@tonic-gate #include <langinfo.h> 32442d23f4Sjp161948 #include "match.h" 33442d23f4Sjp161948 #include "hostfile.h" 34442d23f4Sjp161948 #include "tildexpand.h" 357c478bd9Sstevel@tonic-gate 36442d23f4Sjp161948 /* Number of bits in the RSA/DSA key. This value can be set on the command line. */ 37442d23f4Sjp161948 u_int32_t bits = 1024; 387c478bd9Sstevel@tonic-gate 397c478bd9Sstevel@tonic-gate /* 407c478bd9Sstevel@tonic-gate * Flag indicating that we just want to change the passphrase. This can be 417c478bd9Sstevel@tonic-gate * set on the command line. 427c478bd9Sstevel@tonic-gate */ 437c478bd9Sstevel@tonic-gate int change_passphrase = 0; 447c478bd9Sstevel@tonic-gate 457c478bd9Sstevel@tonic-gate /* 467c478bd9Sstevel@tonic-gate * Flag indicating that we just want to change the comment. This can be set 477c478bd9Sstevel@tonic-gate * on the command line. 487c478bd9Sstevel@tonic-gate */ 497c478bd9Sstevel@tonic-gate int change_comment = 0; 507c478bd9Sstevel@tonic-gate 517c478bd9Sstevel@tonic-gate int quiet = 0; 527c478bd9Sstevel@tonic-gate 53442d23f4Sjp161948 /* Flag indicating that we want to hash a known_hosts file */ 54442d23f4Sjp161948 int hash_hosts = 0; 55442d23f4Sjp161948 /* Flag indicating that we want to lookup a host in known_hosts file */ 56442d23f4Sjp161948 int find_host = 0; 57442d23f4Sjp161948 /* Flag indicating that we want to delete a host from a known_hosts file */ 58442d23f4Sjp161948 int delete_host = 0; 59442d23f4Sjp161948 607c478bd9Sstevel@tonic-gate /* Flag indicating that we just want to see the key fingerprint */ 617c478bd9Sstevel@tonic-gate int print_fingerprint = 0; 627c478bd9Sstevel@tonic-gate int print_bubblebabble = 0; 637c478bd9Sstevel@tonic-gate 647c478bd9Sstevel@tonic-gate /* The identity file name, given on the command line or entered by the user. */ 657c478bd9Sstevel@tonic-gate char identity_file[1024]; 667c478bd9Sstevel@tonic-gate int have_identity = 0; 677c478bd9Sstevel@tonic-gate 687c478bd9Sstevel@tonic-gate /* This is set to the passphrase if given on the command line. */ 697c478bd9Sstevel@tonic-gate char *identity_passphrase = NULL; 707c478bd9Sstevel@tonic-gate 717c478bd9Sstevel@tonic-gate /* This is set to the new passphrase if given on the command line. */ 727c478bd9Sstevel@tonic-gate char *identity_new_passphrase = NULL; 737c478bd9Sstevel@tonic-gate 747c478bd9Sstevel@tonic-gate /* This is set to the new comment if given on the command line. */ 757c478bd9Sstevel@tonic-gate char *identity_comment = NULL; 767c478bd9Sstevel@tonic-gate 777c478bd9Sstevel@tonic-gate /* Dump public key file in format used by real and the original SSH 2 */ 787c478bd9Sstevel@tonic-gate int convert_to_ssh2 = 0; 797c478bd9Sstevel@tonic-gate int convert_from_ssh2 = 0; 807c478bd9Sstevel@tonic-gate int print_public = 0; 817c478bd9Sstevel@tonic-gate 827c478bd9Sstevel@tonic-gate char *key_type_name = NULL; 837c478bd9Sstevel@tonic-gate 847c478bd9Sstevel@tonic-gate /* argv0 */ 857c478bd9Sstevel@tonic-gate #ifdef HAVE___PROGNAME 867c478bd9Sstevel@tonic-gate extern char *__progname; 877c478bd9Sstevel@tonic-gate #else 887c478bd9Sstevel@tonic-gate char *__progname; 897c478bd9Sstevel@tonic-gate #endif 907c478bd9Sstevel@tonic-gate 917c478bd9Sstevel@tonic-gate char hostname[MAXHOSTNAMELEN]; 927c478bd9Sstevel@tonic-gate 937c478bd9Sstevel@tonic-gate static void 947c478bd9Sstevel@tonic-gate ask_filename(struct passwd *pw, const char *prompt) 957c478bd9Sstevel@tonic-gate { 967c478bd9Sstevel@tonic-gate char buf[1024]; 977c478bd9Sstevel@tonic-gate char *name = NULL; 987c478bd9Sstevel@tonic-gate 997c478bd9Sstevel@tonic-gate if (key_type_name == NULL) 1007c478bd9Sstevel@tonic-gate name = _PATH_SSH_CLIENT_ID_RSA; 101442d23f4Sjp161948 else { 1027c478bd9Sstevel@tonic-gate switch (key_type_from_name(key_type_name)) { 1037c478bd9Sstevel@tonic-gate case KEY_RSA1: 1047c478bd9Sstevel@tonic-gate name = _PATH_SSH_CLIENT_IDENTITY; 1057c478bd9Sstevel@tonic-gate break; 1067c478bd9Sstevel@tonic-gate case KEY_DSA: 1077c478bd9Sstevel@tonic-gate name = _PATH_SSH_CLIENT_ID_DSA; 1087c478bd9Sstevel@tonic-gate break; 1097c478bd9Sstevel@tonic-gate case KEY_RSA: 1107c478bd9Sstevel@tonic-gate name = _PATH_SSH_CLIENT_ID_RSA; 1117c478bd9Sstevel@tonic-gate break; 1127c478bd9Sstevel@tonic-gate default: 113442d23f4Sjp161948 fprintf(stderr, gettext("bad key type")); 1147c478bd9Sstevel@tonic-gate exit(1); 1157c478bd9Sstevel@tonic-gate break; 1167c478bd9Sstevel@tonic-gate } 117442d23f4Sjp161948 } 118442d23f4Sjp161948 snprintf(identity_file, sizeof(identity_file), "%s/%s", pw->pw_dir, name); 119442d23f4Sjp161948 fprintf(stderr, "%s (%s): ", gettext(prompt), identity_file); 1207c478bd9Sstevel@tonic-gate if (fgets(buf, sizeof(buf), stdin) == NULL) 1217c478bd9Sstevel@tonic-gate exit(1); 1227c478bd9Sstevel@tonic-gate if (strchr(buf, '\n')) 1237c478bd9Sstevel@tonic-gate *strchr(buf, '\n') = 0; 1247c478bd9Sstevel@tonic-gate if (strcmp(buf, "") != 0) 125442d23f4Sjp161948 strlcpy(identity_file, buf, sizeof(identity_file)); 1267c478bd9Sstevel@tonic-gate have_identity = 1; 1277c478bd9Sstevel@tonic-gate } 1287c478bd9Sstevel@tonic-gate 1297c478bd9Sstevel@tonic-gate static Key * 1307c478bd9Sstevel@tonic-gate load_identity(char *filename) 1317c478bd9Sstevel@tonic-gate { 1327c478bd9Sstevel@tonic-gate char *pass; 1337c478bd9Sstevel@tonic-gate Key *prv; 1347c478bd9Sstevel@tonic-gate 1357c478bd9Sstevel@tonic-gate prv = key_load_private(filename, "", NULL); 1367c478bd9Sstevel@tonic-gate if (prv == NULL) { 1377c478bd9Sstevel@tonic-gate if (identity_passphrase) 1387c478bd9Sstevel@tonic-gate pass = xstrdup(identity_passphrase); 1397c478bd9Sstevel@tonic-gate else 1407c478bd9Sstevel@tonic-gate pass = read_passphrase(gettext("Enter passphrase: "), 1417c478bd9Sstevel@tonic-gate RP_ALLOW_STDIN); 1427c478bd9Sstevel@tonic-gate prv = key_load_private(filename, pass, NULL); 143442d23f4Sjp161948 memset(pass, 0, strlen(pass)); 1447c478bd9Sstevel@tonic-gate xfree(pass); 1457c478bd9Sstevel@tonic-gate } 1467c478bd9Sstevel@tonic-gate return prv; 1477c478bd9Sstevel@tonic-gate } 1487c478bd9Sstevel@tonic-gate 1497c478bd9Sstevel@tonic-gate #define SSH_COM_PUBLIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----" 1507c478bd9Sstevel@tonic-gate #define SSH_COM_PUBLIC_END "---- END SSH2 PUBLIC KEY ----" 1517c478bd9Sstevel@tonic-gate #define SSH_COM_PRIVATE_BEGIN "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----" 1527c478bd9Sstevel@tonic-gate #define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb 1537c478bd9Sstevel@tonic-gate 1547c478bd9Sstevel@tonic-gate static void 1557c478bd9Sstevel@tonic-gate do_convert_to_ssh2(struct passwd *pw) 1567c478bd9Sstevel@tonic-gate { 1577c478bd9Sstevel@tonic-gate Key *k; 1587c478bd9Sstevel@tonic-gate u_int len; 1597c478bd9Sstevel@tonic-gate u_char *blob; 1607c478bd9Sstevel@tonic-gate struct stat st; 1617c478bd9Sstevel@tonic-gate 1627c478bd9Sstevel@tonic-gate if (!have_identity) 1637c478bd9Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which the key is")); 1647c478bd9Sstevel@tonic-gate if (stat(identity_file, &st) < 0) { 1657c478bd9Sstevel@tonic-gate perror(identity_file); 1667c478bd9Sstevel@tonic-gate exit(1); 1677c478bd9Sstevel@tonic-gate } 1687c478bd9Sstevel@tonic-gate if ((k = key_load_public(identity_file, NULL)) == NULL) { 1697c478bd9Sstevel@tonic-gate if ((k = load_identity(identity_file)) == NULL) { 170442d23f4Sjp161948 fprintf(stderr, gettext("load failed\n")); 1717c478bd9Sstevel@tonic-gate exit(1); 1727c478bd9Sstevel@tonic-gate } 1737c478bd9Sstevel@tonic-gate } 1747c478bd9Sstevel@tonic-gate if (key_to_blob(k, &blob, &len) <= 0) { 175442d23f4Sjp161948 fprintf(stderr, gettext("key_to_blob failed\n")); 1767c478bd9Sstevel@tonic-gate exit(1); 1777c478bd9Sstevel@tonic-gate } 178442d23f4Sjp161948 fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN); 179442d23f4Sjp161948 fprintf(stdout, gettext( 1807c478bd9Sstevel@tonic-gate "Comment: \"%u-bit %s, converted from OpenSSH by %s@%s\"\n"), 1817c478bd9Sstevel@tonic-gate key_size(k), key_type(k), 1827c478bd9Sstevel@tonic-gate pw->pw_name, hostname); 1837c478bd9Sstevel@tonic-gate dump_base64(stdout, blob, len); 184442d23f4Sjp161948 fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END); 1857c478bd9Sstevel@tonic-gate key_free(k); 1867c478bd9Sstevel@tonic-gate xfree(blob); 1877c478bd9Sstevel@tonic-gate exit(0); 1887c478bd9Sstevel@tonic-gate } 1897c478bd9Sstevel@tonic-gate 1907c478bd9Sstevel@tonic-gate static void 1917c478bd9Sstevel@tonic-gate buffer_get_bignum_bits(Buffer *b, BIGNUM *value) 1927c478bd9Sstevel@tonic-gate { 193442d23f4Sjp161948 u_int bignum_bits = buffer_get_int(b); 194442d23f4Sjp161948 u_int bytes = (bignum_bits + 7) / 8; 1957c478bd9Sstevel@tonic-gate 1967c478bd9Sstevel@tonic-gate if (buffer_len(b) < bytes) 1977c478bd9Sstevel@tonic-gate fatal("buffer_get_bignum_bits: input buffer too small: " 1987c478bd9Sstevel@tonic-gate "need %d have %d", bytes, buffer_len(b)); 199442d23f4Sjp161948 if (BN_bin2bn(buffer_ptr(b), bytes, value) == NULL) 200442d23f4Sjp161948 fatal("buffer_get_bignum_bits: BN_bin2bn failed"); 2017c478bd9Sstevel@tonic-gate buffer_consume(b, bytes); 2027c478bd9Sstevel@tonic-gate } 2037c478bd9Sstevel@tonic-gate 2047c478bd9Sstevel@tonic-gate static Key * 2057c478bd9Sstevel@tonic-gate do_convert_private_ssh2_from_blob(u_char *blob, u_int blen) 2067c478bd9Sstevel@tonic-gate { 2077c478bd9Sstevel@tonic-gate Buffer b; 2087c478bd9Sstevel@tonic-gate Key *key = NULL; 2097c478bd9Sstevel@tonic-gate char *type, *cipher; 2107c478bd9Sstevel@tonic-gate u_char *sig, data[] = "abcde12345"; 2117c478bd9Sstevel@tonic-gate int magic, rlen, ktype, i1, i2, i3, i4; 2127c478bd9Sstevel@tonic-gate u_int slen; 2137c478bd9Sstevel@tonic-gate u_long e; 2147c478bd9Sstevel@tonic-gate 2157c478bd9Sstevel@tonic-gate buffer_init(&b); 2167c478bd9Sstevel@tonic-gate buffer_append(&b, blob, blen); 2177c478bd9Sstevel@tonic-gate 2187c478bd9Sstevel@tonic-gate magic = buffer_get_int(&b); 2197c478bd9Sstevel@tonic-gate if (magic != SSH_COM_PRIVATE_KEY_MAGIC) { 2207c478bd9Sstevel@tonic-gate error("bad magic 0x%x != 0x%x", magic, SSH_COM_PRIVATE_KEY_MAGIC); 2217c478bd9Sstevel@tonic-gate buffer_free(&b); 2227c478bd9Sstevel@tonic-gate return NULL; 2237c478bd9Sstevel@tonic-gate } 2247c478bd9Sstevel@tonic-gate i1 = buffer_get_int(&b); 2257c478bd9Sstevel@tonic-gate type = buffer_get_string(&b, NULL); 2267c478bd9Sstevel@tonic-gate cipher = buffer_get_string(&b, NULL); 2277c478bd9Sstevel@tonic-gate i2 = buffer_get_int(&b); 2287c478bd9Sstevel@tonic-gate i3 = buffer_get_int(&b); 2297c478bd9Sstevel@tonic-gate i4 = buffer_get_int(&b); 2307c478bd9Sstevel@tonic-gate debug("ignore (%d %d %d %d)", i1, i2, i3, i4); 2317c478bd9Sstevel@tonic-gate if (strcmp(cipher, "none") != 0) { 2327c478bd9Sstevel@tonic-gate error("unsupported cipher %s", cipher); 2337c478bd9Sstevel@tonic-gate xfree(cipher); 2347c478bd9Sstevel@tonic-gate buffer_free(&b); 2357c478bd9Sstevel@tonic-gate xfree(type); 2367c478bd9Sstevel@tonic-gate return NULL; 2377c478bd9Sstevel@tonic-gate } 2387c478bd9Sstevel@tonic-gate xfree(cipher); 2397c478bd9Sstevel@tonic-gate 2407c478bd9Sstevel@tonic-gate if (strstr(type, "dsa")) { 2417c478bd9Sstevel@tonic-gate ktype = KEY_DSA; 2427c478bd9Sstevel@tonic-gate } else if (strstr(type, "rsa")) { 2437c478bd9Sstevel@tonic-gate ktype = KEY_RSA; 2447c478bd9Sstevel@tonic-gate } else { 245442d23f4Sjp161948 buffer_free(&b); 2467c478bd9Sstevel@tonic-gate xfree(type); 2477c478bd9Sstevel@tonic-gate return NULL; 2487c478bd9Sstevel@tonic-gate } 2497c478bd9Sstevel@tonic-gate key = key_new_private(ktype); 2507c478bd9Sstevel@tonic-gate xfree(type); 2517c478bd9Sstevel@tonic-gate 2527c478bd9Sstevel@tonic-gate switch (key->type) { 2537c478bd9Sstevel@tonic-gate case KEY_DSA: 2547c478bd9Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->dsa->p); 2557c478bd9Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->dsa->g); 2567c478bd9Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->dsa->q); 2577c478bd9Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->dsa->pub_key); 2587c478bd9Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->dsa->priv_key); 2597c478bd9Sstevel@tonic-gate break; 2607c478bd9Sstevel@tonic-gate case KEY_RSA: 2617c478bd9Sstevel@tonic-gate e = buffer_get_char(&b); 2627c478bd9Sstevel@tonic-gate debug("e %lx", e); 2637c478bd9Sstevel@tonic-gate if (e < 30) { 2647c478bd9Sstevel@tonic-gate e <<= 8; 2657c478bd9Sstevel@tonic-gate e += buffer_get_char(&b); 2667c478bd9Sstevel@tonic-gate debug("e %lx", e); 2677c478bd9Sstevel@tonic-gate e <<= 8; 2687c478bd9Sstevel@tonic-gate e += buffer_get_char(&b); 2697c478bd9Sstevel@tonic-gate debug("e %lx", e); 2707c478bd9Sstevel@tonic-gate } 2717c478bd9Sstevel@tonic-gate if (!BN_set_word(key->rsa->e, e)) { 2727c478bd9Sstevel@tonic-gate buffer_free(&b); 2737c478bd9Sstevel@tonic-gate key_free(key); 2747c478bd9Sstevel@tonic-gate return NULL; 2757c478bd9Sstevel@tonic-gate } 2767c478bd9Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->rsa->d); 2777c478bd9Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->rsa->n); 2787c478bd9Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->rsa->iqmp); 2797c478bd9Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->rsa->q); 2807c478bd9Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->rsa->p); 2817c478bd9Sstevel@tonic-gate rsa_generate_additional_parameters(key->rsa); 2827c478bd9Sstevel@tonic-gate break; 2837c478bd9Sstevel@tonic-gate } 2847c478bd9Sstevel@tonic-gate rlen = buffer_len(&b); 2857c478bd9Sstevel@tonic-gate if (rlen != 0) 2867c478bd9Sstevel@tonic-gate error("do_convert_private_ssh2_from_blob: " 2877c478bd9Sstevel@tonic-gate "remaining bytes in key blob %d", rlen); 2887c478bd9Sstevel@tonic-gate buffer_free(&b); 2897c478bd9Sstevel@tonic-gate 2907c478bd9Sstevel@tonic-gate /* try the key */ 291442d23f4Sjp161948 key_sign(key, &sig, &slen, data, sizeof(data)); 2927c478bd9Sstevel@tonic-gate key_verify(key, sig, slen, data, sizeof(data)); 2937c478bd9Sstevel@tonic-gate xfree(sig); 2947c478bd9Sstevel@tonic-gate return key; 2957c478bd9Sstevel@tonic-gate } 2967c478bd9Sstevel@tonic-gate 297442d23f4Sjp161948 static int 298442d23f4Sjp161948 get_line(FILE *fp, char *line, size_t len) 299442d23f4Sjp161948 { 300442d23f4Sjp161948 int c; 301442d23f4Sjp161948 size_t pos = 0; 302442d23f4Sjp161948 303442d23f4Sjp161948 line[0] = '\0'; 304442d23f4Sjp161948 while ((c = fgetc(fp)) != EOF) { 305442d23f4Sjp161948 if (pos >= len - 1) { 306442d23f4Sjp161948 fprintf(stderr, "input line too long.\n"); 307442d23f4Sjp161948 exit(1); 308442d23f4Sjp161948 } 309442d23f4Sjp161948 switch (c) { 310442d23f4Sjp161948 case '\r': 311442d23f4Sjp161948 c = fgetc(fp); 312442d23f4Sjp161948 if (c != EOF && c != '\n' && ungetc(c, fp) == EOF) { 313442d23f4Sjp161948 fprintf(stderr, "unget: %s\n", strerror(errno)); 314442d23f4Sjp161948 exit(1); 315442d23f4Sjp161948 } 316442d23f4Sjp161948 return pos; 317442d23f4Sjp161948 case '\n': 318442d23f4Sjp161948 return pos; 319442d23f4Sjp161948 } 320442d23f4Sjp161948 line[pos++] = c; 321442d23f4Sjp161948 line[pos] = '\0'; 322442d23f4Sjp161948 } 323442d23f4Sjp161948 /* We reached EOF */ 324442d23f4Sjp161948 return -1; 325442d23f4Sjp161948 } 326442d23f4Sjp161948 3277c478bd9Sstevel@tonic-gate static void 3287c478bd9Sstevel@tonic-gate do_convert_from_ssh2(struct passwd *pw) 3297c478bd9Sstevel@tonic-gate { 3307c478bd9Sstevel@tonic-gate Key *k; 3317c478bd9Sstevel@tonic-gate int blen; 3327c478bd9Sstevel@tonic-gate u_int len; 333442d23f4Sjp161948 char line[1024]; 3347c478bd9Sstevel@tonic-gate u_char blob[8096]; 3357c478bd9Sstevel@tonic-gate char encoded[8096]; 3367c478bd9Sstevel@tonic-gate struct stat st; 3377c478bd9Sstevel@tonic-gate int escaped = 0, private = 0, ok; 3387c478bd9Sstevel@tonic-gate FILE *fp; 3397c478bd9Sstevel@tonic-gate 3407c478bd9Sstevel@tonic-gate if (!have_identity) 3417c478bd9Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which the key is")); 3427c478bd9Sstevel@tonic-gate if (stat(identity_file, &st) < 0) { 3437c478bd9Sstevel@tonic-gate perror(identity_file); 3447c478bd9Sstevel@tonic-gate exit(1); 3457c478bd9Sstevel@tonic-gate } 3467c478bd9Sstevel@tonic-gate fp = fopen(identity_file, "r"); 3477c478bd9Sstevel@tonic-gate if (fp == NULL) { 3487c478bd9Sstevel@tonic-gate perror(identity_file); 3497c478bd9Sstevel@tonic-gate exit(1); 3507c478bd9Sstevel@tonic-gate } 3517c478bd9Sstevel@tonic-gate encoded[0] = '\0'; 352442d23f4Sjp161948 while ((blen = get_line(fp, line, sizeof(line))) != -1) { 353442d23f4Sjp161948 if (line[blen - 1] == '\\') 3547c478bd9Sstevel@tonic-gate escaped++; 3557c478bd9Sstevel@tonic-gate if (strncmp(line, "----", 4) == 0 || 3567c478bd9Sstevel@tonic-gate strstr(line, ": ") != NULL) { 3577c478bd9Sstevel@tonic-gate if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL) 3587c478bd9Sstevel@tonic-gate private = 1; 3597c478bd9Sstevel@tonic-gate if (strstr(line, " END ") != NULL) { 3607c478bd9Sstevel@tonic-gate break; 3617c478bd9Sstevel@tonic-gate } 3627c478bd9Sstevel@tonic-gate /* fprintf(stderr, "ignore: %s", line); */ 3637c478bd9Sstevel@tonic-gate continue; 3647c478bd9Sstevel@tonic-gate } 3657c478bd9Sstevel@tonic-gate if (escaped) { 3667c478bd9Sstevel@tonic-gate escaped--; 3677c478bd9Sstevel@tonic-gate /* fprintf(stderr, "escaped: %s", line); */ 3687c478bd9Sstevel@tonic-gate continue; 3697c478bd9Sstevel@tonic-gate } 370442d23f4Sjp161948 strlcat(encoded, line, sizeof(encoded)); 3717c478bd9Sstevel@tonic-gate } 3727c478bd9Sstevel@tonic-gate len = strlen(encoded); 3737c478bd9Sstevel@tonic-gate if (((len % 4) == 3) && 3747c478bd9Sstevel@tonic-gate (encoded[len-1] == '=') && 3757c478bd9Sstevel@tonic-gate (encoded[len-2] == '=') && 3767c478bd9Sstevel@tonic-gate (encoded[len-3] == '=')) 3777c478bd9Sstevel@tonic-gate encoded[len-3] = '\0'; 3787c478bd9Sstevel@tonic-gate blen = uudecode(encoded, blob, sizeof(blob)); 3797c478bd9Sstevel@tonic-gate if (blen < 0) { 380442d23f4Sjp161948 fprintf(stderr, gettext("uudecode failed.\n")); 3817c478bd9Sstevel@tonic-gate exit(1); 3827c478bd9Sstevel@tonic-gate } 3837c478bd9Sstevel@tonic-gate k = private ? 3847c478bd9Sstevel@tonic-gate do_convert_private_ssh2_from_blob(blob, blen) : 3857c478bd9Sstevel@tonic-gate key_from_blob(blob, blen); 3867c478bd9Sstevel@tonic-gate if (k == NULL) { 387442d23f4Sjp161948 fprintf(stderr, gettext("decode blob failed.\n")); 3887c478bd9Sstevel@tonic-gate exit(1); 3897c478bd9Sstevel@tonic-gate } 3907c478bd9Sstevel@tonic-gate ok = private ? 3917c478bd9Sstevel@tonic-gate (k->type == KEY_DSA ? 3927c478bd9Sstevel@tonic-gate PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, NULL, 0, NULL, NULL) : 3937c478bd9Sstevel@tonic-gate PEM_write_RSAPrivateKey(stdout, k->rsa, NULL, NULL, 0, NULL, NULL)) : 3947c478bd9Sstevel@tonic-gate key_write(k, stdout); 3957c478bd9Sstevel@tonic-gate if (!ok) { 396442d23f4Sjp161948 fprintf(stderr, gettext("key write failed")); 3977c478bd9Sstevel@tonic-gate exit(1); 3987c478bd9Sstevel@tonic-gate } 3997c478bd9Sstevel@tonic-gate key_free(k); 4007c478bd9Sstevel@tonic-gate if (!private) 401442d23f4Sjp161948 fprintf(stdout, "\n"); 402442d23f4Sjp161948 fclose(fp); 4037c478bd9Sstevel@tonic-gate exit(0); 4047c478bd9Sstevel@tonic-gate } 4057c478bd9Sstevel@tonic-gate 4067c478bd9Sstevel@tonic-gate static void 4077c478bd9Sstevel@tonic-gate do_print_public(struct passwd *pw) 4087c478bd9Sstevel@tonic-gate { 4097c478bd9Sstevel@tonic-gate Key *prv; 4107c478bd9Sstevel@tonic-gate struct stat st; 4117c478bd9Sstevel@tonic-gate 4127c478bd9Sstevel@tonic-gate if (!have_identity) 4137c478bd9Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which the key is")); 4147c478bd9Sstevel@tonic-gate if (stat(identity_file, &st) < 0) { 4157c478bd9Sstevel@tonic-gate perror(identity_file); 4167c478bd9Sstevel@tonic-gate exit(1); 4177c478bd9Sstevel@tonic-gate } 4187c478bd9Sstevel@tonic-gate prv = load_identity(identity_file); 4197c478bd9Sstevel@tonic-gate if (prv == NULL) { 420442d23f4Sjp161948 fprintf(stderr, gettext("load failed\n")); 4217c478bd9Sstevel@tonic-gate exit(1); 4227c478bd9Sstevel@tonic-gate } 4237c478bd9Sstevel@tonic-gate if (!key_write(prv, stdout)) 424442d23f4Sjp161948 fprintf(stderr, gettext("key_write failed")); 4257c478bd9Sstevel@tonic-gate key_free(prv); 426442d23f4Sjp161948 fprintf(stdout, "\n"); 4277c478bd9Sstevel@tonic-gate exit(0); 4287c478bd9Sstevel@tonic-gate } 4297c478bd9Sstevel@tonic-gate 4307c478bd9Sstevel@tonic-gate static void 4317c478bd9Sstevel@tonic-gate do_fingerprint(struct passwd *pw) 4327c478bd9Sstevel@tonic-gate { 4337c478bd9Sstevel@tonic-gate FILE *f; 4347c478bd9Sstevel@tonic-gate Key *public; 4357c478bd9Sstevel@tonic-gate char *comment = NULL, *cp, *ep, line[16*1024], *fp; 4367c478bd9Sstevel@tonic-gate int i, skip = 0, num = 1, invalid = 1; 4377c478bd9Sstevel@tonic-gate enum fp_rep rep; 4387c478bd9Sstevel@tonic-gate enum fp_type fptype; 4397c478bd9Sstevel@tonic-gate struct stat st; 4407c478bd9Sstevel@tonic-gate 4417c478bd9Sstevel@tonic-gate fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5; 4427c478bd9Sstevel@tonic-gate rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_HEX; 4437c478bd9Sstevel@tonic-gate 4447c478bd9Sstevel@tonic-gate if (!have_identity) 4457c478bd9Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which the key is")); 4467c478bd9Sstevel@tonic-gate if (stat(identity_file, &st) < 0) { 4477c478bd9Sstevel@tonic-gate perror(identity_file); 4487c478bd9Sstevel@tonic-gate exit(1); 4497c478bd9Sstevel@tonic-gate } 4507c478bd9Sstevel@tonic-gate public = key_load_public(identity_file, &comment); 4517c478bd9Sstevel@tonic-gate if (public != NULL) { 4527c478bd9Sstevel@tonic-gate fp = key_fingerprint(public, fptype, rep); 453442d23f4Sjp161948 printf("%u %s %s\n", key_size(public), fp, comment); 4547c478bd9Sstevel@tonic-gate key_free(public); 4557c478bd9Sstevel@tonic-gate xfree(comment); 4567c478bd9Sstevel@tonic-gate xfree(fp); 4577c478bd9Sstevel@tonic-gate exit(0); 4587c478bd9Sstevel@tonic-gate } 459442d23f4Sjp161948 if (comment) { 4607c478bd9Sstevel@tonic-gate xfree(comment); 461442d23f4Sjp161948 comment = NULL; 462442d23f4Sjp161948 } 4637c478bd9Sstevel@tonic-gate 4647c478bd9Sstevel@tonic-gate f = fopen(identity_file, "r"); 4657c478bd9Sstevel@tonic-gate if (f != NULL) { 4667c478bd9Sstevel@tonic-gate while (fgets(line, sizeof(line), f)) { 4677c478bd9Sstevel@tonic-gate i = strlen(line) - 1; 4687c478bd9Sstevel@tonic-gate if (line[i] != '\n') { 4697c478bd9Sstevel@tonic-gate error("line %d too long: %.40s...", num, line); 4707c478bd9Sstevel@tonic-gate skip = 1; 4717c478bd9Sstevel@tonic-gate continue; 4727c478bd9Sstevel@tonic-gate } 4737c478bd9Sstevel@tonic-gate num++; 4747c478bd9Sstevel@tonic-gate if (skip) { 4757c478bd9Sstevel@tonic-gate skip = 0; 4767c478bd9Sstevel@tonic-gate continue; 4777c478bd9Sstevel@tonic-gate } 4787c478bd9Sstevel@tonic-gate line[i] = '\0'; 4797c478bd9Sstevel@tonic-gate 4807c478bd9Sstevel@tonic-gate /* Skip leading whitespace, empty and comment lines. */ 4817c478bd9Sstevel@tonic-gate for (cp = line; *cp == ' ' || *cp == '\t'; cp++) 4827c478bd9Sstevel@tonic-gate ; 4837c478bd9Sstevel@tonic-gate if (!*cp || *cp == '\n' || *cp == '#') 4847c478bd9Sstevel@tonic-gate continue; 4857c478bd9Sstevel@tonic-gate i = strtol(cp, &ep, 10); 4867c478bd9Sstevel@tonic-gate if (i == 0 || ep == NULL || (*ep != ' ' && *ep != '\t')) { 4877c478bd9Sstevel@tonic-gate int quoted = 0; 4887c478bd9Sstevel@tonic-gate comment = cp; 4897c478bd9Sstevel@tonic-gate for (; *cp && (quoted || (*cp != ' ' && 4907c478bd9Sstevel@tonic-gate *cp != '\t')); cp++) { 4917c478bd9Sstevel@tonic-gate if (*cp == '\\' && cp[1] == '"') 4927c478bd9Sstevel@tonic-gate cp++; /* Skip both */ 4937c478bd9Sstevel@tonic-gate else if (*cp == '"') 4947c478bd9Sstevel@tonic-gate quoted = !quoted; 4957c478bd9Sstevel@tonic-gate } 4967c478bd9Sstevel@tonic-gate if (!*cp) 4977c478bd9Sstevel@tonic-gate continue; 4987c478bd9Sstevel@tonic-gate *cp++ = '\0'; 4997c478bd9Sstevel@tonic-gate } 5007c478bd9Sstevel@tonic-gate ep = cp; 5017c478bd9Sstevel@tonic-gate public = key_new(KEY_RSA1); 5027c478bd9Sstevel@tonic-gate if (key_read(public, &cp) != 1) { 5037c478bd9Sstevel@tonic-gate cp = ep; 5047c478bd9Sstevel@tonic-gate key_free(public); 5057c478bd9Sstevel@tonic-gate public = key_new(KEY_UNSPEC); 5067c478bd9Sstevel@tonic-gate if (key_read(public, &cp) != 1) { 5077c478bd9Sstevel@tonic-gate key_free(public); 5087c478bd9Sstevel@tonic-gate continue; 5097c478bd9Sstevel@tonic-gate } 5107c478bd9Sstevel@tonic-gate } 5117c478bd9Sstevel@tonic-gate comment = *cp ? cp : comment; 5127c478bd9Sstevel@tonic-gate fp = key_fingerprint(public, fptype, rep); 513442d23f4Sjp161948 printf("%u %s %s\n", key_size(public), fp, 5147c478bd9Sstevel@tonic-gate comment ? comment : gettext("no comment")); 5157c478bd9Sstevel@tonic-gate xfree(fp); 5167c478bd9Sstevel@tonic-gate key_free(public); 5177c478bd9Sstevel@tonic-gate invalid = 0; 5187c478bd9Sstevel@tonic-gate } 519442d23f4Sjp161948 fclose(f); 5207c478bd9Sstevel@tonic-gate } 5217c478bd9Sstevel@tonic-gate if (invalid) { 522442d23f4Sjp161948 printf(gettext("%s is not a public key file.\n"), 5237c478bd9Sstevel@tonic-gate identity_file); 5247c478bd9Sstevel@tonic-gate exit(1); 5257c478bd9Sstevel@tonic-gate } 5267c478bd9Sstevel@tonic-gate exit(0); 5277c478bd9Sstevel@tonic-gate } 5287c478bd9Sstevel@tonic-gate 529442d23f4Sjp161948 static void 530442d23f4Sjp161948 print_host(FILE *f, const char *name, Key *public, int hash) 531442d23f4Sjp161948 { 532442d23f4Sjp161948 if (hash && (name = host_hash(name, NULL, 0)) == NULL) 533442d23f4Sjp161948 fatal("hash_host failed"); 534442d23f4Sjp161948 fprintf(f, "%s ", name); 535442d23f4Sjp161948 if (!key_write(public, f)) 536442d23f4Sjp161948 fatal("key_write failed"); 537442d23f4Sjp161948 fprintf(f, "\n"); 538442d23f4Sjp161948 } 539442d23f4Sjp161948 540442d23f4Sjp161948 static void 541442d23f4Sjp161948 do_known_hosts(struct passwd *pw, const char *name) 542442d23f4Sjp161948 { 543442d23f4Sjp161948 FILE *in, *out = stdout; 544442d23f4Sjp161948 Key *public; 545442d23f4Sjp161948 char *cp, *cp2, *kp, *kp2; 546442d23f4Sjp161948 char line[16*1024], tmp[MAXPATHLEN], old[MAXPATHLEN]; 547442d23f4Sjp161948 int c, i, skip = 0, inplace = 0, num = 0, invalid = 0, has_unhashed = 0; 548442d23f4Sjp161948 549442d23f4Sjp161948 if (!have_identity) { 550442d23f4Sjp161948 cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid); 551442d23f4Sjp161948 if (strlcpy(identity_file, cp, sizeof(identity_file)) >= 552442d23f4Sjp161948 sizeof(identity_file)) 553442d23f4Sjp161948 fatal("Specified known hosts path too long"); 554442d23f4Sjp161948 xfree(cp); 555442d23f4Sjp161948 have_identity = 1; 556442d23f4Sjp161948 } 557442d23f4Sjp161948 if ((in = fopen(identity_file, "r")) == NULL) 558442d23f4Sjp161948 fatal("fopen: %s", strerror(errno)); 559442d23f4Sjp161948 560442d23f4Sjp161948 /* 561442d23f4Sjp161948 * Find hosts goes to stdout, hash and deletions happen in-place 562442d23f4Sjp161948 * A corner case is ssh-keygen -HF foo, which should go to stdout 563442d23f4Sjp161948 */ 564442d23f4Sjp161948 if (!find_host && (hash_hosts || delete_host)) { 565442d23f4Sjp161948 if (strlcpy(tmp, identity_file, sizeof(tmp)) >= sizeof(tmp) || 566442d23f4Sjp161948 strlcat(tmp, ".XXXXXXXXXX", sizeof(tmp)) >= sizeof(tmp) || 567442d23f4Sjp161948 strlcpy(old, identity_file, sizeof(old)) >= sizeof(old) || 568442d23f4Sjp161948 strlcat(old, ".old", sizeof(old)) >= sizeof(old)) 569442d23f4Sjp161948 fatal("known_hosts path too long"); 570442d23f4Sjp161948 umask(077); 571442d23f4Sjp161948 if ((c = mkstemp(tmp)) == -1) 572442d23f4Sjp161948 fatal("mkstemp: %s", strerror(errno)); 573442d23f4Sjp161948 if ((out = fdopen(c, "w")) == NULL) { 574442d23f4Sjp161948 c = errno; 575442d23f4Sjp161948 unlink(tmp); 576442d23f4Sjp161948 fatal("fdopen: %s", strerror(c)); 577442d23f4Sjp161948 } 578442d23f4Sjp161948 inplace = 1; 579442d23f4Sjp161948 } 580442d23f4Sjp161948 581442d23f4Sjp161948 while (fgets(line, sizeof(line), in)) { 582442d23f4Sjp161948 num++; 583442d23f4Sjp161948 i = strlen(line) - 1; 584442d23f4Sjp161948 if (line[i] != '\n') { 585442d23f4Sjp161948 error("line %d too long: %.40s...", num, line); 586442d23f4Sjp161948 skip = 1; 587442d23f4Sjp161948 invalid = 1; 588442d23f4Sjp161948 continue; 589442d23f4Sjp161948 } 590442d23f4Sjp161948 if (skip) { 591442d23f4Sjp161948 skip = 0; 592442d23f4Sjp161948 continue; 593442d23f4Sjp161948 } 594442d23f4Sjp161948 line[i] = '\0'; 595442d23f4Sjp161948 596442d23f4Sjp161948 /* Skip leading whitespace, empty and comment lines. */ 597442d23f4Sjp161948 for (cp = line; *cp == ' ' || *cp == '\t'; cp++) 598442d23f4Sjp161948 ; 599442d23f4Sjp161948 if (!*cp || *cp == '\n' || *cp == '#') { 600442d23f4Sjp161948 if (inplace) 601442d23f4Sjp161948 fprintf(out, "%s\n", cp); 602442d23f4Sjp161948 continue; 603442d23f4Sjp161948 } 604442d23f4Sjp161948 /* Find the end of the host name portion. */ 605442d23f4Sjp161948 for (kp = cp; *kp && *kp != ' ' && *kp != '\t'; kp++) 606442d23f4Sjp161948 ; 607442d23f4Sjp161948 if (*kp == '\0' || *(kp + 1) == '\0') { 608442d23f4Sjp161948 error("line %d missing key: %.40s...", 609442d23f4Sjp161948 num, line); 610442d23f4Sjp161948 invalid = 1; 611442d23f4Sjp161948 continue; 612442d23f4Sjp161948 } 613442d23f4Sjp161948 *kp++ = '\0'; 614442d23f4Sjp161948 kp2 = kp; 615442d23f4Sjp161948 616442d23f4Sjp161948 public = key_new(KEY_RSA1); 617442d23f4Sjp161948 if (key_read(public, &kp) != 1) { 618442d23f4Sjp161948 kp = kp2; 619442d23f4Sjp161948 key_free(public); 620442d23f4Sjp161948 public = key_new(KEY_UNSPEC); 621442d23f4Sjp161948 if (key_read(public, &kp) != 1) { 622442d23f4Sjp161948 error("line %d invalid key: %.40s...", 623442d23f4Sjp161948 num, line); 624442d23f4Sjp161948 key_free(public); 625442d23f4Sjp161948 invalid = 1; 626442d23f4Sjp161948 continue; 627442d23f4Sjp161948 } 628442d23f4Sjp161948 } 629442d23f4Sjp161948 630442d23f4Sjp161948 if (*cp == HASH_DELIM) { 631442d23f4Sjp161948 if (find_host || delete_host) { 632442d23f4Sjp161948 cp2 = host_hash(name, cp, strlen(cp)); 633442d23f4Sjp161948 if (cp2 == NULL) { 634442d23f4Sjp161948 error("line %d: invalid hashed " 635442d23f4Sjp161948 "name: %.64s...", num, line); 636442d23f4Sjp161948 invalid = 1; 637442d23f4Sjp161948 continue; 638442d23f4Sjp161948 } 639442d23f4Sjp161948 c = (strcmp(cp2, cp) == 0); 640442d23f4Sjp161948 if (find_host && c) { 641442d23f4Sjp161948 printf(gettext("# Host %s found: " 642442d23f4Sjp161948 "line %d type %s\n"), name, 643442d23f4Sjp161948 num, key_type(public)); 644442d23f4Sjp161948 print_host(out, cp, public, 0); 645442d23f4Sjp161948 } 646442d23f4Sjp161948 if (delete_host && !c) 647442d23f4Sjp161948 print_host(out, cp, public, 0); 648442d23f4Sjp161948 } else if (hash_hosts) 649442d23f4Sjp161948 print_host(out, cp, public, 0); 650442d23f4Sjp161948 } else { 651442d23f4Sjp161948 if (find_host || delete_host) { 652442d23f4Sjp161948 c = (match_hostname(name, cp, 653442d23f4Sjp161948 strlen(cp)) == 1); 654442d23f4Sjp161948 if (find_host && c) { 655442d23f4Sjp161948 printf(gettext("# Host %s found: " 656442d23f4Sjp161948 "line %d type %s\n"), name, 657442d23f4Sjp161948 num, key_type(public)); 658442d23f4Sjp161948 print_host(out, name, public, hash_hosts); 659442d23f4Sjp161948 } 660442d23f4Sjp161948 if (delete_host && !c) 661442d23f4Sjp161948 print_host(out, cp, public, 0); 662442d23f4Sjp161948 } else if (hash_hosts) { 663442d23f4Sjp161948 for (cp2 = strsep(&cp, ","); 664442d23f4Sjp161948 cp2 != NULL && *cp2 != '\0'; 665442d23f4Sjp161948 cp2 = strsep(&cp, ",")) { 666442d23f4Sjp161948 if (strcspn(cp2, "*?!") != strlen(cp2)) 667442d23f4Sjp161948 fprintf(stderr, gettext("Warning: " 668442d23f4Sjp161948 "ignoring host name with " 669442d23f4Sjp161948 "metacharacters: %.64s\n"), 670442d23f4Sjp161948 cp2); 671442d23f4Sjp161948 else 672442d23f4Sjp161948 print_host(out, cp2, public, 1); 673442d23f4Sjp161948 } 674442d23f4Sjp161948 has_unhashed = 1; 675442d23f4Sjp161948 } 676442d23f4Sjp161948 } 677442d23f4Sjp161948 key_free(public); 678442d23f4Sjp161948 } 679442d23f4Sjp161948 fclose(in); 680442d23f4Sjp161948 681442d23f4Sjp161948 if (invalid) { 682442d23f4Sjp161948 fprintf(stderr, gettext("%s is not a valid known_host file.\n"), 683442d23f4Sjp161948 identity_file); 684442d23f4Sjp161948 if (inplace) { 685442d23f4Sjp161948 fprintf(stderr, gettext("Not replacing existing known_hosts " 686442d23f4Sjp161948 "file because of errors\n")); 687442d23f4Sjp161948 fclose(out); 688442d23f4Sjp161948 unlink(tmp); 689442d23f4Sjp161948 } 690442d23f4Sjp161948 exit(1); 691442d23f4Sjp161948 } 692442d23f4Sjp161948 693442d23f4Sjp161948 if (inplace) { 694442d23f4Sjp161948 fclose(out); 695442d23f4Sjp161948 696442d23f4Sjp161948 /* Backup existing file */ 697442d23f4Sjp161948 if (unlink(old) == -1 && errno != ENOENT) 698442d23f4Sjp161948 fatal("unlink %.100s: %s", old, strerror(errno)); 699442d23f4Sjp161948 if (link(identity_file, old) == -1) 700442d23f4Sjp161948 fatal("link %.100s to %.100s: %s", identity_file, old, 701442d23f4Sjp161948 strerror(errno)); 702442d23f4Sjp161948 /* Move new one into place */ 703442d23f4Sjp161948 if (rename(tmp, identity_file) == -1) { 704442d23f4Sjp161948 error("rename\"%s\" to \"%s\": %s", tmp, identity_file, 705442d23f4Sjp161948 strerror(errno)); 706442d23f4Sjp161948 unlink(tmp); 707442d23f4Sjp161948 unlink(old); 708442d23f4Sjp161948 exit(1); 709442d23f4Sjp161948 } 710442d23f4Sjp161948 711442d23f4Sjp161948 fprintf(stderr, gettext("%s updated.\n"), identity_file); 712442d23f4Sjp161948 fprintf(stderr, gettext("Original contents retained as %s\n"), old); 713442d23f4Sjp161948 if (has_unhashed) { 714442d23f4Sjp161948 fprintf(stderr, gettext("WARNING: %s contains unhashed " 715442d23f4Sjp161948 "entries\n"), old); 716442d23f4Sjp161948 fprintf(stderr, gettext("Delete this file to ensure privacy " 717442d23f4Sjp161948 "of hostnames\n")); 718442d23f4Sjp161948 } 719442d23f4Sjp161948 } 720442d23f4Sjp161948 721442d23f4Sjp161948 exit(0); 722442d23f4Sjp161948 } 723442d23f4Sjp161948 7247c478bd9Sstevel@tonic-gate /* 7257c478bd9Sstevel@tonic-gate * Perform changing a passphrase. The argument is the passwd structure 7267c478bd9Sstevel@tonic-gate * for the current user. 7277c478bd9Sstevel@tonic-gate */ 7287c478bd9Sstevel@tonic-gate static void 7297c478bd9Sstevel@tonic-gate do_change_passphrase(struct passwd *pw) 7307c478bd9Sstevel@tonic-gate { 7317c478bd9Sstevel@tonic-gate char *comment; 7327c478bd9Sstevel@tonic-gate char *old_passphrase, *passphrase1, *passphrase2; 7337c478bd9Sstevel@tonic-gate struct stat st; 7347c478bd9Sstevel@tonic-gate Key *private; 7357c478bd9Sstevel@tonic-gate 7367c478bd9Sstevel@tonic-gate if (!have_identity) 7377c478bd9Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which the key is")); 7387c478bd9Sstevel@tonic-gate if (stat(identity_file, &st) < 0) { 7397c478bd9Sstevel@tonic-gate perror(identity_file); 7407c478bd9Sstevel@tonic-gate exit(1); 7417c478bd9Sstevel@tonic-gate } 7427c478bd9Sstevel@tonic-gate /* Try to load the file with empty passphrase. */ 7437c478bd9Sstevel@tonic-gate private = key_load_private(identity_file, "", &comment); 7447c478bd9Sstevel@tonic-gate if (private == NULL) { 7457c478bd9Sstevel@tonic-gate if (identity_passphrase) 7467c478bd9Sstevel@tonic-gate old_passphrase = xstrdup(identity_passphrase); 7477c478bd9Sstevel@tonic-gate else 7487c478bd9Sstevel@tonic-gate old_passphrase = 7497c478bd9Sstevel@tonic-gate read_passphrase(gettext("Enter old passphrase: "), 7507c478bd9Sstevel@tonic-gate RP_ALLOW_STDIN); 7517c478bd9Sstevel@tonic-gate private = key_load_private(identity_file, old_passphrase, 7527c478bd9Sstevel@tonic-gate &comment); 753442d23f4Sjp161948 memset(old_passphrase, 0, strlen(old_passphrase)); 7547c478bd9Sstevel@tonic-gate xfree(old_passphrase); 7557c478bd9Sstevel@tonic-gate if (private == NULL) { 756442d23f4Sjp161948 printf(gettext("Bad passphrase.\n")); 7577c478bd9Sstevel@tonic-gate exit(1); 7587c478bd9Sstevel@tonic-gate } 7597c478bd9Sstevel@tonic-gate } 760442d23f4Sjp161948 printf(gettext("Key has comment '%s'\n"), comment); 7617c478bd9Sstevel@tonic-gate 7627c478bd9Sstevel@tonic-gate /* Ask the new passphrase (twice). */ 7637c478bd9Sstevel@tonic-gate if (identity_new_passphrase) { 7647c478bd9Sstevel@tonic-gate passphrase1 = xstrdup(identity_new_passphrase); 7657c478bd9Sstevel@tonic-gate passphrase2 = NULL; 7667c478bd9Sstevel@tonic-gate } else { 7677c478bd9Sstevel@tonic-gate passphrase1 = 7687c478bd9Sstevel@tonic-gate read_passphrase(gettext("Enter new passphrase (empty" 7697c478bd9Sstevel@tonic-gate " for no passphrase): "), RP_ALLOW_STDIN); 7707c478bd9Sstevel@tonic-gate passphrase2 = read_passphrase(gettext("Enter same " 7717c478bd9Sstevel@tonic-gate "passphrase again: "), RP_ALLOW_STDIN); 7727c478bd9Sstevel@tonic-gate 7737c478bd9Sstevel@tonic-gate /* Verify that they are the same. */ 7747c478bd9Sstevel@tonic-gate if (strcmp(passphrase1, passphrase2) != 0) { 775442d23f4Sjp161948 memset(passphrase1, 0, strlen(passphrase1)); 776442d23f4Sjp161948 memset(passphrase2, 0, strlen(passphrase2)); 7777c478bd9Sstevel@tonic-gate xfree(passphrase1); 7787c478bd9Sstevel@tonic-gate xfree(passphrase2); 779442d23f4Sjp161948 printf(gettext("Pass phrases do not match. Try " 7807c478bd9Sstevel@tonic-gate "again.\n")); 7817c478bd9Sstevel@tonic-gate exit(1); 7827c478bd9Sstevel@tonic-gate } 7837c478bd9Sstevel@tonic-gate /* Destroy the other copy. */ 784442d23f4Sjp161948 memset(passphrase2, 0, strlen(passphrase2)); 7857c478bd9Sstevel@tonic-gate xfree(passphrase2); 7867c478bd9Sstevel@tonic-gate } 7877c478bd9Sstevel@tonic-gate 7887c478bd9Sstevel@tonic-gate /* Save the file using the new passphrase. */ 7897c478bd9Sstevel@tonic-gate if (!key_save_private(private, identity_file, passphrase1, comment)) { 790442d23f4Sjp161948 printf(gettext("Saving the key failed: %s.\n"), identity_file); 791442d23f4Sjp161948 memset(passphrase1, 0, strlen(passphrase1)); 7927c478bd9Sstevel@tonic-gate xfree(passphrase1); 7937c478bd9Sstevel@tonic-gate key_free(private); 7947c478bd9Sstevel@tonic-gate xfree(comment); 7957c478bd9Sstevel@tonic-gate exit(1); 7967c478bd9Sstevel@tonic-gate } 7977c478bd9Sstevel@tonic-gate /* Destroy the passphrase and the copy of the key in memory. */ 798442d23f4Sjp161948 memset(passphrase1, 0, strlen(passphrase1)); 7997c478bd9Sstevel@tonic-gate xfree(passphrase1); 8007c478bd9Sstevel@tonic-gate key_free(private); /* Destroys contents */ 8017c478bd9Sstevel@tonic-gate xfree(comment); 8027c478bd9Sstevel@tonic-gate 803442d23f4Sjp161948 printf(gettext("Your identification has been saved with the new " 8047c478bd9Sstevel@tonic-gate "passphrase.\n")); 8057c478bd9Sstevel@tonic-gate exit(0); 8067c478bd9Sstevel@tonic-gate } 8077c478bd9Sstevel@tonic-gate 8087c478bd9Sstevel@tonic-gate /* 8097c478bd9Sstevel@tonic-gate * Change the comment of a private key file. 8107c478bd9Sstevel@tonic-gate */ 8117c478bd9Sstevel@tonic-gate static void 8127c478bd9Sstevel@tonic-gate do_change_comment(struct passwd *pw) 8137c478bd9Sstevel@tonic-gate { 8147c478bd9Sstevel@tonic-gate char new_comment[1024], *comment, *passphrase; 8157c478bd9Sstevel@tonic-gate Key *private; 8167c478bd9Sstevel@tonic-gate Key *public; 8177c478bd9Sstevel@tonic-gate struct stat st; 8187c478bd9Sstevel@tonic-gate FILE *f; 8197c478bd9Sstevel@tonic-gate int fd; 8207c478bd9Sstevel@tonic-gate 8217c478bd9Sstevel@tonic-gate if (!have_identity) 8227c478bd9Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which the key is")); 8237c478bd9Sstevel@tonic-gate if (stat(identity_file, &st) < 0) { 8247c478bd9Sstevel@tonic-gate perror(identity_file); 8257c478bd9Sstevel@tonic-gate exit(1); 8267c478bd9Sstevel@tonic-gate } 8277c478bd9Sstevel@tonic-gate private = key_load_private(identity_file, "", &comment); 8287c478bd9Sstevel@tonic-gate if (private == NULL) { 8297c478bd9Sstevel@tonic-gate if (identity_passphrase) 8307c478bd9Sstevel@tonic-gate passphrase = xstrdup(identity_passphrase); 8317c478bd9Sstevel@tonic-gate else if (identity_new_passphrase) 8327c478bd9Sstevel@tonic-gate passphrase = xstrdup(identity_new_passphrase); 8337c478bd9Sstevel@tonic-gate else 8347c478bd9Sstevel@tonic-gate passphrase = 8357c478bd9Sstevel@tonic-gate read_passphrase(gettext("Enter passphrase: "), 8367c478bd9Sstevel@tonic-gate RP_ALLOW_STDIN); 8377c478bd9Sstevel@tonic-gate /* Try to load using the passphrase. */ 8387c478bd9Sstevel@tonic-gate private = key_load_private(identity_file, passphrase, &comment); 8397c478bd9Sstevel@tonic-gate if (private == NULL) { 840442d23f4Sjp161948 memset(passphrase, 0, strlen(passphrase)); 8417c478bd9Sstevel@tonic-gate xfree(passphrase); 842442d23f4Sjp161948 printf(gettext("Bad passphrase.\n")); 8437c478bd9Sstevel@tonic-gate exit(1); 8447c478bd9Sstevel@tonic-gate } 8457c478bd9Sstevel@tonic-gate } else { 8467c478bd9Sstevel@tonic-gate passphrase = xstrdup(""); 8477c478bd9Sstevel@tonic-gate } 8487c478bd9Sstevel@tonic-gate if (private->type != KEY_RSA1) { 849442d23f4Sjp161948 fprintf(stderr, gettext("Comments are only supported for " 8507c478bd9Sstevel@tonic-gate "RSA1 keys.\n")); 8517c478bd9Sstevel@tonic-gate key_free(private); 8527c478bd9Sstevel@tonic-gate exit(1); 8537c478bd9Sstevel@tonic-gate } 854442d23f4Sjp161948 printf(gettext("Key now has comment '%s'\n"), comment); 8557c478bd9Sstevel@tonic-gate 8567c478bd9Sstevel@tonic-gate if (identity_comment) { 857442d23f4Sjp161948 strlcpy(new_comment, identity_comment, sizeof(new_comment)); 8587c478bd9Sstevel@tonic-gate } else { 859442d23f4Sjp161948 printf(gettext("Enter new comment: ")); 860442d23f4Sjp161948 fflush(stdout); 8617c478bd9Sstevel@tonic-gate if (!fgets(new_comment, sizeof(new_comment), stdin)) { 862442d23f4Sjp161948 memset(passphrase, 0, strlen(passphrase)); 8637c478bd9Sstevel@tonic-gate key_free(private); 8647c478bd9Sstevel@tonic-gate exit(1); 8657c478bd9Sstevel@tonic-gate } 8667c478bd9Sstevel@tonic-gate if (strchr(new_comment, '\n')) 8677c478bd9Sstevel@tonic-gate *strchr(new_comment, '\n') = 0; 8687c478bd9Sstevel@tonic-gate } 8697c478bd9Sstevel@tonic-gate 8707c478bd9Sstevel@tonic-gate /* Save the file using the new passphrase. */ 8717c478bd9Sstevel@tonic-gate if (!key_save_private(private, identity_file, passphrase, new_comment)) { 872442d23f4Sjp161948 printf(gettext("Saving the key failed: %s.\n"), identity_file); 873442d23f4Sjp161948 memset(passphrase, 0, strlen(passphrase)); 8747c478bd9Sstevel@tonic-gate xfree(passphrase); 8757c478bd9Sstevel@tonic-gate key_free(private); 8767c478bd9Sstevel@tonic-gate xfree(comment); 8777c478bd9Sstevel@tonic-gate exit(1); 8787c478bd9Sstevel@tonic-gate } 879442d23f4Sjp161948 memset(passphrase, 0, strlen(passphrase)); 8807c478bd9Sstevel@tonic-gate xfree(passphrase); 8817c478bd9Sstevel@tonic-gate public = key_from_private(private); 8827c478bd9Sstevel@tonic-gate key_free(private); 8837c478bd9Sstevel@tonic-gate 884442d23f4Sjp161948 strlcat(identity_file, ".pub", sizeof(identity_file)); 8857c478bd9Sstevel@tonic-gate fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); 8867c478bd9Sstevel@tonic-gate if (fd == -1) { 887442d23f4Sjp161948 printf(gettext("Could not save your public key in %s\n"), 8887c478bd9Sstevel@tonic-gate identity_file); 8897c478bd9Sstevel@tonic-gate exit(1); 8907c478bd9Sstevel@tonic-gate } 8917c478bd9Sstevel@tonic-gate f = fdopen(fd, "w"); 8927c478bd9Sstevel@tonic-gate if (f == NULL) { 893442d23f4Sjp161948 printf(gettext("fdopen %s failed"), identity_file); 8947c478bd9Sstevel@tonic-gate exit(1); 8957c478bd9Sstevel@tonic-gate } 8967c478bd9Sstevel@tonic-gate if (!key_write(public, f)) 897442d23f4Sjp161948 fprintf(stderr, gettext("write key failed")); 8987c478bd9Sstevel@tonic-gate key_free(public); 899442d23f4Sjp161948 fprintf(f, " %s\n", new_comment); 900442d23f4Sjp161948 fclose(f); 9017c478bd9Sstevel@tonic-gate 9027c478bd9Sstevel@tonic-gate xfree(comment); 9037c478bd9Sstevel@tonic-gate 904442d23f4Sjp161948 printf(gettext("The comment in your key file has been changed.\n")); 9057c478bd9Sstevel@tonic-gate exit(0); 9067c478bd9Sstevel@tonic-gate } 9077c478bd9Sstevel@tonic-gate 9087c478bd9Sstevel@tonic-gate static void 9097c478bd9Sstevel@tonic-gate usage(void) 9107c478bd9Sstevel@tonic-gate { 911442d23f4Sjp161948 fprintf(stderr, gettext( 9127c478bd9Sstevel@tonic-gate "Usage: %s [options]\n" 9137c478bd9Sstevel@tonic-gate "Options:\n" 9147c478bd9Sstevel@tonic-gate " -b bits Number of bits in the key to create.\n" 9157c478bd9Sstevel@tonic-gate " -B Show bubblebabble digest of key file.\n" 916442d23f4Sjp161948 " -c Change comment in private and public key files.\n" 9177c478bd9Sstevel@tonic-gate " -C comment Provide new comment.\n" 918442d23f4Sjp161948 " -e Convert OpenSSH to IETF SECSH key file.\n" 919442d23f4Sjp161948 " -f filename Filename of the key file.\n" 920442d23f4Sjp161948 " -F hostname Find hostname in known hosts file.\n" 921442d23f4Sjp161948 " -H Hash names in known_hosts file.\n" 922442d23f4Sjp161948 " -i Convert IETF SECSH to OpenSSH key file.\n" 923442d23f4Sjp161948 " -l Show fingerprint of key file.\n" 924442d23f4Sjp161948 " -N phrase Provide new passphrase.\n" 925442d23f4Sjp161948 " -p Change passphrase of private key file.\n" 926442d23f4Sjp161948 " -P phrase Provide old passphrase.\n" 927442d23f4Sjp161948 " -q Quiet.\n" 928442d23f4Sjp161948 " -R hostname Remove host from known_hosts file.\n" 929442d23f4Sjp161948 " -t type Specify type of key to create.\n" 930442d23f4Sjp161948 " -y Read private key file and print public key.\n" 9317c478bd9Sstevel@tonic-gate ), __progname); 9327c478bd9Sstevel@tonic-gate 9337c478bd9Sstevel@tonic-gate exit(1); 9347c478bd9Sstevel@tonic-gate } 9357c478bd9Sstevel@tonic-gate 9367c478bd9Sstevel@tonic-gate /* 9377c478bd9Sstevel@tonic-gate * Main program for key management. 9387c478bd9Sstevel@tonic-gate */ 9397c478bd9Sstevel@tonic-gate int 940442d23f4Sjp161948 main(int argc, char **argv) 9417c478bd9Sstevel@tonic-gate { 9427c478bd9Sstevel@tonic-gate char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2; 943442d23f4Sjp161948 char *rr_hostname = NULL; 9447c478bd9Sstevel@tonic-gate Key *private, *public; 9457c478bd9Sstevel@tonic-gate struct passwd *pw; 9467c478bd9Sstevel@tonic-gate struct stat st; 9477c478bd9Sstevel@tonic-gate int opt, type, fd; 9487c478bd9Sstevel@tonic-gate FILE *f; 9497c478bd9Sstevel@tonic-gate 9507c478bd9Sstevel@tonic-gate extern int optind; 9517c478bd9Sstevel@tonic-gate extern char *optarg; 9527c478bd9Sstevel@tonic-gate 953442d23f4Sjp161948 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 954442d23f4Sjp161948 sanitise_stdfd(); 9557c478bd9Sstevel@tonic-gate 956442d23f4Sjp161948 __progname = get_progname(argv[0]); 957442d23f4Sjp161948 958442d23f4Sjp161948 g11n_setlocale(LC_ALL, ""); 9597c478bd9Sstevel@tonic-gate 9607c478bd9Sstevel@tonic-gate SSLeay_add_all_algorithms(); 9617c478bd9Sstevel@tonic-gate init_rng(); 9627c478bd9Sstevel@tonic-gate seed_rng(); 9637c478bd9Sstevel@tonic-gate 9647c478bd9Sstevel@tonic-gate /* we need this for the home * directory. */ 9657c478bd9Sstevel@tonic-gate pw = getpwuid(getuid()); 9667c478bd9Sstevel@tonic-gate if (!pw) { 967442d23f4Sjp161948 printf(gettext("You don't exist, go away!\n")); 9687c478bd9Sstevel@tonic-gate exit(1); 9697c478bd9Sstevel@tonic-gate } 9707c478bd9Sstevel@tonic-gate if (gethostname(hostname, sizeof(hostname)) < 0) { 9717c478bd9Sstevel@tonic-gate perror("gethostname"); 9727c478bd9Sstevel@tonic-gate exit(1); 9737c478bd9Sstevel@tonic-gate } 9747c478bd9Sstevel@tonic-gate 975442d23f4Sjp161948 #define GETOPT_ARGS "BcdeHilpqxXyb:C:f:F:N:P:R:t:" 976*ea01bd62SHuie-Ying Lee 977442d23f4Sjp161948 while ((opt = getopt(argc, argv, GETOPT_ARGS)) != -1) { 9787c478bd9Sstevel@tonic-gate switch (opt) { 9797c478bd9Sstevel@tonic-gate case 'b': 9807c478bd9Sstevel@tonic-gate bits = atoi(optarg); 9817c478bd9Sstevel@tonic-gate if (bits < 512 || bits > 32768) { 982442d23f4Sjp161948 printf(gettext("Bits has bad value.\n")); 9837c478bd9Sstevel@tonic-gate exit(1); 9847c478bd9Sstevel@tonic-gate } 9857c478bd9Sstevel@tonic-gate break; 986442d23f4Sjp161948 case 'F': 987442d23f4Sjp161948 find_host = 1; 988442d23f4Sjp161948 rr_hostname = optarg; 989442d23f4Sjp161948 break; 990442d23f4Sjp161948 case 'H': 991442d23f4Sjp161948 hash_hosts = 1; 992442d23f4Sjp161948 break; 993442d23f4Sjp161948 case 'R': 994442d23f4Sjp161948 delete_host = 1; 995442d23f4Sjp161948 rr_hostname = optarg; 996442d23f4Sjp161948 break; 9977c478bd9Sstevel@tonic-gate case 'l': 9987c478bd9Sstevel@tonic-gate print_fingerprint = 1; 9997c478bd9Sstevel@tonic-gate break; 10007c478bd9Sstevel@tonic-gate case 'B': 10017c478bd9Sstevel@tonic-gate print_bubblebabble = 1; 10027c478bd9Sstevel@tonic-gate break; 10037c478bd9Sstevel@tonic-gate case 'p': 10047c478bd9Sstevel@tonic-gate change_passphrase = 1; 10057c478bd9Sstevel@tonic-gate break; 10067c478bd9Sstevel@tonic-gate case 'c': 10077c478bd9Sstevel@tonic-gate change_comment = 1; 10087c478bd9Sstevel@tonic-gate break; 10097c478bd9Sstevel@tonic-gate case 'f': 1010442d23f4Sjp161948 strlcpy(identity_file, optarg, sizeof(identity_file)); 10117c478bd9Sstevel@tonic-gate have_identity = 1; 10127c478bd9Sstevel@tonic-gate break; 10137c478bd9Sstevel@tonic-gate case 'P': 10147c478bd9Sstevel@tonic-gate identity_passphrase = optarg; 10157c478bd9Sstevel@tonic-gate break; 10167c478bd9Sstevel@tonic-gate case 'N': 10177c478bd9Sstevel@tonic-gate identity_new_passphrase = optarg; 10187c478bd9Sstevel@tonic-gate break; 10197c478bd9Sstevel@tonic-gate case 'C': 10207c478bd9Sstevel@tonic-gate identity_comment = optarg; 10217c478bd9Sstevel@tonic-gate break; 10227c478bd9Sstevel@tonic-gate case 'q': 10237c478bd9Sstevel@tonic-gate quiet = 1; 10247c478bd9Sstevel@tonic-gate break; 10257c478bd9Sstevel@tonic-gate case 'e': 10267c478bd9Sstevel@tonic-gate case 'x': 10277c478bd9Sstevel@tonic-gate /* export key */ 10287c478bd9Sstevel@tonic-gate convert_to_ssh2 = 1; 10297c478bd9Sstevel@tonic-gate break; 10307c478bd9Sstevel@tonic-gate case 'i': 10317c478bd9Sstevel@tonic-gate case 'X': 10327c478bd9Sstevel@tonic-gate /* import key */ 10337c478bd9Sstevel@tonic-gate convert_from_ssh2 = 1; 10347c478bd9Sstevel@tonic-gate break; 10357c478bd9Sstevel@tonic-gate case 'y': 10367c478bd9Sstevel@tonic-gate print_public = 1; 10377c478bd9Sstevel@tonic-gate break; 10387c478bd9Sstevel@tonic-gate case 'd': 10397c478bd9Sstevel@tonic-gate key_type_name = "dsa"; 10407c478bd9Sstevel@tonic-gate break; 10417c478bd9Sstevel@tonic-gate case 't': 10427c478bd9Sstevel@tonic-gate key_type_name = optarg; 10437c478bd9Sstevel@tonic-gate break; 10447c478bd9Sstevel@tonic-gate case '?': 10457c478bd9Sstevel@tonic-gate default: 10467c478bd9Sstevel@tonic-gate usage(); 10477c478bd9Sstevel@tonic-gate } 10487c478bd9Sstevel@tonic-gate } 1049442d23f4Sjp161948 if (optind < argc) { 1050442d23f4Sjp161948 printf(gettext("Too many arguments.\n")); 10517c478bd9Sstevel@tonic-gate usage(); 10527c478bd9Sstevel@tonic-gate } 10537c478bd9Sstevel@tonic-gate if (change_passphrase && change_comment) { 1054442d23f4Sjp161948 printf(gettext("Can only have one of -p and -c.\n")); 10557c478bd9Sstevel@tonic-gate usage(); 10567c478bd9Sstevel@tonic-gate } 1057442d23f4Sjp161948 if (delete_host || hash_hosts || find_host) 1058442d23f4Sjp161948 do_known_hosts(pw, rr_hostname); 10597c478bd9Sstevel@tonic-gate if (print_fingerprint || print_bubblebabble) 10607c478bd9Sstevel@tonic-gate do_fingerprint(pw); 10617c478bd9Sstevel@tonic-gate if (change_passphrase) 10627c478bd9Sstevel@tonic-gate do_change_passphrase(pw); 10637c478bd9Sstevel@tonic-gate if (change_comment) 10647c478bd9Sstevel@tonic-gate do_change_comment(pw); 10657c478bd9Sstevel@tonic-gate if (convert_to_ssh2) 10667c478bd9Sstevel@tonic-gate do_convert_to_ssh2(pw); 10677c478bd9Sstevel@tonic-gate if (convert_from_ssh2) 10687c478bd9Sstevel@tonic-gate do_convert_from_ssh2(pw); 10697c478bd9Sstevel@tonic-gate if (print_public) 10707c478bd9Sstevel@tonic-gate do_print_public(pw); 10717c478bd9Sstevel@tonic-gate 10727c478bd9Sstevel@tonic-gate arc4random_stir(); 10737c478bd9Sstevel@tonic-gate 10747c478bd9Sstevel@tonic-gate if (key_type_name == NULL) { 1075442d23f4Sjp161948 printf(gettext("You must specify a key type (-t).\n")); 10767c478bd9Sstevel@tonic-gate usage(); 10777c478bd9Sstevel@tonic-gate } 10787c478bd9Sstevel@tonic-gate type = key_type_from_name(key_type_name); 10797c478bd9Sstevel@tonic-gate if (type == KEY_UNSPEC) { 1080442d23f4Sjp161948 fprintf(stderr, gettext("unknown key type %s\n"), 10817c478bd9Sstevel@tonic-gate key_type_name); 10827c478bd9Sstevel@tonic-gate exit(1); 10837c478bd9Sstevel@tonic-gate } 10847c478bd9Sstevel@tonic-gate if (!quiet) 1085442d23f4Sjp161948 printf(gettext("Generating public/private %s key pair.\n"), 10867c478bd9Sstevel@tonic-gate key_type_name); 10877c478bd9Sstevel@tonic-gate private = key_generate(type, bits); 10887c478bd9Sstevel@tonic-gate if (private == NULL) { 1089442d23f4Sjp161948 fprintf(stderr, gettext("key_generate failed")); 10907c478bd9Sstevel@tonic-gate exit(1); 10917c478bd9Sstevel@tonic-gate } 10927c478bd9Sstevel@tonic-gate public = key_from_private(private); 10937c478bd9Sstevel@tonic-gate 10947c478bd9Sstevel@tonic-gate if (!have_identity) 10957c478bd9Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which to save the key")); 10967c478bd9Sstevel@tonic-gate 1097442d23f4Sjp161948 /* Create ~/.ssh directory if it doesn't already exist. */ 1098442d23f4Sjp161948 snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, _PATH_SSH_USER_DIR); 10997c478bd9Sstevel@tonic-gate if (strstr(identity_file, dotsshdir) != NULL && 11007c478bd9Sstevel@tonic-gate stat(dotsshdir, &st) < 0) { 11017c478bd9Sstevel@tonic-gate if (mkdir(dotsshdir, 0700) < 0) 11027c478bd9Sstevel@tonic-gate error("Could not create directory '%s'.", dotsshdir); 11037c478bd9Sstevel@tonic-gate else if (!quiet) 1104442d23f4Sjp161948 printf(gettext("Created directory '%s'.\n"), dotsshdir); 11057c478bd9Sstevel@tonic-gate } 11067c478bd9Sstevel@tonic-gate /* If the file already exists, ask the user to confirm. */ 11077c478bd9Sstevel@tonic-gate if (stat(identity_file, &st) >= 0) { 11087c478bd9Sstevel@tonic-gate char yesno[128]; 1109442d23f4Sjp161948 printf(gettext("%s already exists.\n"), identity_file); 1110442d23f4Sjp161948 printf(gettext("Overwrite (%s/%s)? "), 11117c478bd9Sstevel@tonic-gate nl_langinfo(YESSTR), nl_langinfo(NOSTR)); 1112442d23f4Sjp161948 fflush(stdout); 11137c478bd9Sstevel@tonic-gate if (fgets(yesno, sizeof(yesno), stdin) == NULL) 11147c478bd9Sstevel@tonic-gate exit(1); 111526ba1984Sjp161948 if (strcasecmp(chop(yesno), nl_langinfo(YESSTR)) != 0) 11167c478bd9Sstevel@tonic-gate exit(1); 11177c478bd9Sstevel@tonic-gate } 11187c478bd9Sstevel@tonic-gate /* Ask for a passphrase (twice). */ 11197c478bd9Sstevel@tonic-gate if (identity_passphrase) 11207c478bd9Sstevel@tonic-gate passphrase1 = xstrdup(identity_passphrase); 11217c478bd9Sstevel@tonic-gate else if (identity_new_passphrase) 11227c478bd9Sstevel@tonic-gate passphrase1 = xstrdup(identity_new_passphrase); 11237c478bd9Sstevel@tonic-gate else { 11247c478bd9Sstevel@tonic-gate passphrase_again: 11257c478bd9Sstevel@tonic-gate passphrase1 = 11267c478bd9Sstevel@tonic-gate read_passphrase(gettext("Enter passphrase (empty " 11277c478bd9Sstevel@tonic-gate "for no passphrase): "), RP_ALLOW_STDIN); 11287c478bd9Sstevel@tonic-gate passphrase2 = read_passphrase(gettext("Enter same " 11297c478bd9Sstevel@tonic-gate "passphrase again: "), RP_ALLOW_STDIN); 11307c478bd9Sstevel@tonic-gate if (strcmp(passphrase1, passphrase2) != 0) { 11317c478bd9Sstevel@tonic-gate /* 11327c478bd9Sstevel@tonic-gate * The passphrases do not match. Clear them and 11337c478bd9Sstevel@tonic-gate * retry. 11347c478bd9Sstevel@tonic-gate */ 1135442d23f4Sjp161948 memset(passphrase1, 0, strlen(passphrase1)); 1136442d23f4Sjp161948 memset(passphrase2, 0, strlen(passphrase2)); 11377c478bd9Sstevel@tonic-gate xfree(passphrase1); 11387c478bd9Sstevel@tonic-gate xfree(passphrase2); 1139442d23f4Sjp161948 printf(gettext("Passphrases do not match. Try " 11407c478bd9Sstevel@tonic-gate "again.\n")); 11417c478bd9Sstevel@tonic-gate goto passphrase_again; 11427c478bd9Sstevel@tonic-gate } 11437c478bd9Sstevel@tonic-gate /* Clear the other copy of the passphrase. */ 1144442d23f4Sjp161948 memset(passphrase2, 0, strlen(passphrase2)); 11457c478bd9Sstevel@tonic-gate xfree(passphrase2); 11467c478bd9Sstevel@tonic-gate } 11477c478bd9Sstevel@tonic-gate 11487c478bd9Sstevel@tonic-gate if (identity_comment) { 1149442d23f4Sjp161948 strlcpy(comment, identity_comment, sizeof(comment)); 11507c478bd9Sstevel@tonic-gate } else { 11517c478bd9Sstevel@tonic-gate /* Create default commend field for the passphrase. */ 1152442d23f4Sjp161948 snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); 11537c478bd9Sstevel@tonic-gate } 11547c478bd9Sstevel@tonic-gate 11557c478bd9Sstevel@tonic-gate /* Save the key with the given passphrase and comment. */ 11567c478bd9Sstevel@tonic-gate if (!key_save_private(private, identity_file, passphrase1, comment)) { 1157442d23f4Sjp161948 printf(gettext("Saving the key failed: %s.\n"), identity_file); 1158442d23f4Sjp161948 memset(passphrase1, 0, strlen(passphrase1)); 11597c478bd9Sstevel@tonic-gate xfree(passphrase1); 11607c478bd9Sstevel@tonic-gate exit(1); 11617c478bd9Sstevel@tonic-gate } 11627c478bd9Sstevel@tonic-gate /* Clear the passphrase. */ 1163442d23f4Sjp161948 memset(passphrase1, 0, strlen(passphrase1)); 11647c478bd9Sstevel@tonic-gate xfree(passphrase1); 11657c478bd9Sstevel@tonic-gate 11667c478bd9Sstevel@tonic-gate /* Clear the private key and the random number generator. */ 11677c478bd9Sstevel@tonic-gate key_free(private); 11687c478bd9Sstevel@tonic-gate arc4random_stir(); 11697c478bd9Sstevel@tonic-gate 11707c478bd9Sstevel@tonic-gate if (!quiet) 1171442d23f4Sjp161948 printf(gettext("Your identification has been saved in %s.\n"), 11727c478bd9Sstevel@tonic-gate identity_file); 11737c478bd9Sstevel@tonic-gate 1174442d23f4Sjp161948 strlcat(identity_file, ".pub", sizeof(identity_file)); 11757c478bd9Sstevel@tonic-gate fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); 11767c478bd9Sstevel@tonic-gate if (fd == -1) { 1177442d23f4Sjp161948 printf(gettext("Could not save your public key in %s\n"), 11787c478bd9Sstevel@tonic-gate identity_file); 11797c478bd9Sstevel@tonic-gate exit(1); 11807c478bd9Sstevel@tonic-gate } 11817c478bd9Sstevel@tonic-gate f = fdopen(fd, "w"); 11827c478bd9Sstevel@tonic-gate if (f == NULL) { 1183442d23f4Sjp161948 printf(gettext("fdopen %s failed"), identity_file); 11847c478bd9Sstevel@tonic-gate exit(1); 11857c478bd9Sstevel@tonic-gate } 11867c478bd9Sstevel@tonic-gate if (!key_write(public, f)) 1187442d23f4Sjp161948 fprintf(stderr, gettext("write key failed")); 1188442d23f4Sjp161948 fprintf(f, " %s\n", comment); 1189442d23f4Sjp161948 fclose(f); 11907c478bd9Sstevel@tonic-gate 11917c478bd9Sstevel@tonic-gate if (!quiet) { 11927c478bd9Sstevel@tonic-gate char *fp = key_fingerprint(public, SSH_FP_MD5, SSH_FP_HEX); 1193442d23f4Sjp161948 printf(gettext("Your public key has been saved in %s.\n"), 11947c478bd9Sstevel@tonic-gate identity_file); 1195442d23f4Sjp161948 printf(gettext("The key fingerprint is:\n")); 1196442d23f4Sjp161948 printf("%s %s\n", fp, comment); 11977c478bd9Sstevel@tonic-gate xfree(fp); 11987c478bd9Sstevel@tonic-gate } 11997c478bd9Sstevel@tonic-gate 12007c478bd9Sstevel@tonic-gate key_free(public); 12017c478bd9Sstevel@tonic-gate return(0); 12027c478bd9Sstevel@tonic-gate /* NOTREACHED */ 12037c478bd9Sstevel@tonic-gate } 1204