10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * Author: Tatu Ylonen <ylo@cs.hut.fi> 30Sstevel@tonic-gate * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 40Sstevel@tonic-gate * All rights reserved 50Sstevel@tonic-gate * Identity and host key generation and maintenance. 60Sstevel@tonic-gate * 70Sstevel@tonic-gate * As far as I am concerned, the code I have written for this software 80Sstevel@tonic-gate * can be used freely for any purpose. Any derived versions of this 90Sstevel@tonic-gate * software must be clearly marked as such, and if the derived work is 100Sstevel@tonic-gate * incompatible with the protocol description in the RFC file, it must be 110Sstevel@tonic-gate * called by a name other than "ssh" or "Secure Shell". 120Sstevel@tonic-gate */ 130Sstevel@tonic-gate 14*5243Sjp161948 /* $OpenBSD: ssh-keygen.c,v 1.160 2007/01/21 01:41:54 stevesk Exp $ */ 150Sstevel@tonic-gate 160Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 170Sstevel@tonic-gate 18*5243Sjp161948 #include "includes.h" 190Sstevel@tonic-gate #include <openssl/evp.h> 200Sstevel@tonic-gate #include <openssl/pem.h> 210Sstevel@tonic-gate 220Sstevel@tonic-gate #include "xmalloc.h" 230Sstevel@tonic-gate #include "key.h" 240Sstevel@tonic-gate #include "rsa.h" 250Sstevel@tonic-gate #include "authfile.h" 260Sstevel@tonic-gate #include "uuencode.h" 270Sstevel@tonic-gate #include "buffer.h" 280Sstevel@tonic-gate #include "bufaux.h" 290Sstevel@tonic-gate #include "pathnames.h" 300Sstevel@tonic-gate #include "log.h" 310Sstevel@tonic-gate #include "readpass.h" 322757Sjp161948 #include "misc.h" 330Sstevel@tonic-gate #include <langinfo.h> 34*5243Sjp161948 #include "match.h" 35*5243Sjp161948 #include "hostfile.h" 36*5243Sjp161948 #include "tildexpand.h" 370Sstevel@tonic-gate 380Sstevel@tonic-gate #ifdef SMARTCARD 390Sstevel@tonic-gate #include "scard.h" 400Sstevel@tonic-gate #endif 410Sstevel@tonic-gate 42*5243Sjp161948 /* Number of bits in the RSA/DSA key. This value can be set on the command line. */ 43*5243Sjp161948 u_int32_t bits = 1024; 440Sstevel@tonic-gate 450Sstevel@tonic-gate /* 460Sstevel@tonic-gate * Flag indicating that we just want to change the passphrase. This can be 470Sstevel@tonic-gate * set on the command line. 480Sstevel@tonic-gate */ 490Sstevel@tonic-gate int change_passphrase = 0; 500Sstevel@tonic-gate 510Sstevel@tonic-gate /* 520Sstevel@tonic-gate * Flag indicating that we just want to change the comment. This can be set 530Sstevel@tonic-gate * on the command line. 540Sstevel@tonic-gate */ 550Sstevel@tonic-gate int change_comment = 0; 560Sstevel@tonic-gate 570Sstevel@tonic-gate int quiet = 0; 580Sstevel@tonic-gate 59*5243Sjp161948 /* Flag indicating that we want to hash a known_hosts file */ 60*5243Sjp161948 int hash_hosts = 0; 61*5243Sjp161948 /* Flag indicating that we want to lookup a host in known_hosts file */ 62*5243Sjp161948 int find_host = 0; 63*5243Sjp161948 /* Flag indicating that we want to delete a host from a known_hosts file */ 64*5243Sjp161948 int delete_host = 0; 65*5243Sjp161948 660Sstevel@tonic-gate /* Flag indicating that we just want to see the key fingerprint */ 670Sstevel@tonic-gate int print_fingerprint = 0; 680Sstevel@tonic-gate int print_bubblebabble = 0; 690Sstevel@tonic-gate 700Sstevel@tonic-gate /* The identity file name, given on the command line or entered by the user. */ 710Sstevel@tonic-gate char identity_file[1024]; 720Sstevel@tonic-gate int have_identity = 0; 730Sstevel@tonic-gate 740Sstevel@tonic-gate /* This is set to the passphrase if given on the command line. */ 750Sstevel@tonic-gate char *identity_passphrase = NULL; 760Sstevel@tonic-gate 770Sstevel@tonic-gate /* This is set to the new passphrase if given on the command line. */ 780Sstevel@tonic-gate char *identity_new_passphrase = NULL; 790Sstevel@tonic-gate 800Sstevel@tonic-gate /* This is set to the new comment if given on the command line. */ 810Sstevel@tonic-gate char *identity_comment = NULL; 820Sstevel@tonic-gate 830Sstevel@tonic-gate /* Dump public key file in format used by real and the original SSH 2 */ 840Sstevel@tonic-gate int convert_to_ssh2 = 0; 850Sstevel@tonic-gate int convert_from_ssh2 = 0; 860Sstevel@tonic-gate int print_public = 0; 870Sstevel@tonic-gate 880Sstevel@tonic-gate char *key_type_name = NULL; 890Sstevel@tonic-gate 900Sstevel@tonic-gate /* argv0 */ 910Sstevel@tonic-gate #ifdef HAVE___PROGNAME 920Sstevel@tonic-gate extern char *__progname; 930Sstevel@tonic-gate #else 940Sstevel@tonic-gate char *__progname; 950Sstevel@tonic-gate #endif 960Sstevel@tonic-gate 970Sstevel@tonic-gate char hostname[MAXHOSTNAMELEN]; 980Sstevel@tonic-gate 990Sstevel@tonic-gate static void 1000Sstevel@tonic-gate ask_filename(struct passwd *pw, const char *prompt) 1010Sstevel@tonic-gate { 1020Sstevel@tonic-gate char buf[1024]; 1030Sstevel@tonic-gate char *name = NULL; 1040Sstevel@tonic-gate 1050Sstevel@tonic-gate if (key_type_name == NULL) 1060Sstevel@tonic-gate name = _PATH_SSH_CLIENT_ID_RSA; 107*5243Sjp161948 else { 1080Sstevel@tonic-gate switch (key_type_from_name(key_type_name)) { 1090Sstevel@tonic-gate case KEY_RSA1: 1100Sstevel@tonic-gate name = _PATH_SSH_CLIENT_IDENTITY; 1110Sstevel@tonic-gate break; 1120Sstevel@tonic-gate case KEY_DSA: 1130Sstevel@tonic-gate name = _PATH_SSH_CLIENT_ID_DSA; 1140Sstevel@tonic-gate break; 1150Sstevel@tonic-gate case KEY_RSA: 1160Sstevel@tonic-gate name = _PATH_SSH_CLIENT_ID_RSA; 1170Sstevel@tonic-gate break; 1180Sstevel@tonic-gate default: 119*5243Sjp161948 fprintf(stderr, gettext("bad key type")); 1200Sstevel@tonic-gate exit(1); 1210Sstevel@tonic-gate break; 1220Sstevel@tonic-gate } 123*5243Sjp161948 } 124*5243Sjp161948 snprintf(identity_file, sizeof(identity_file), "%s/%s", pw->pw_dir, name); 125*5243Sjp161948 fprintf(stderr, "%s (%s): ", gettext(prompt), identity_file); 1260Sstevel@tonic-gate if (fgets(buf, sizeof(buf), stdin) == NULL) 1270Sstevel@tonic-gate exit(1); 1280Sstevel@tonic-gate if (strchr(buf, '\n')) 1290Sstevel@tonic-gate *strchr(buf, '\n') = 0; 1300Sstevel@tonic-gate if (strcmp(buf, "") != 0) 131*5243Sjp161948 strlcpy(identity_file, buf, sizeof(identity_file)); 1320Sstevel@tonic-gate have_identity = 1; 1330Sstevel@tonic-gate } 1340Sstevel@tonic-gate 1350Sstevel@tonic-gate static Key * 1360Sstevel@tonic-gate load_identity(char *filename) 1370Sstevel@tonic-gate { 1380Sstevel@tonic-gate char *pass; 1390Sstevel@tonic-gate Key *prv; 1400Sstevel@tonic-gate 1410Sstevel@tonic-gate prv = key_load_private(filename, "", NULL); 1420Sstevel@tonic-gate if (prv == NULL) { 1430Sstevel@tonic-gate if (identity_passphrase) 1440Sstevel@tonic-gate pass = xstrdup(identity_passphrase); 1450Sstevel@tonic-gate else 1460Sstevel@tonic-gate pass = read_passphrase(gettext("Enter passphrase: "), 1470Sstevel@tonic-gate RP_ALLOW_STDIN); 1480Sstevel@tonic-gate prv = key_load_private(filename, pass, NULL); 149*5243Sjp161948 memset(pass, 0, strlen(pass)); 1500Sstevel@tonic-gate xfree(pass); 1510Sstevel@tonic-gate } 1520Sstevel@tonic-gate return prv; 1530Sstevel@tonic-gate } 1540Sstevel@tonic-gate 1550Sstevel@tonic-gate #define SSH_COM_PUBLIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----" 1560Sstevel@tonic-gate #define SSH_COM_PUBLIC_END "---- END SSH2 PUBLIC KEY ----" 1570Sstevel@tonic-gate #define SSH_COM_PRIVATE_BEGIN "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----" 1580Sstevel@tonic-gate #define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb 1590Sstevel@tonic-gate 1600Sstevel@tonic-gate static void 1610Sstevel@tonic-gate do_convert_to_ssh2(struct passwd *pw) 1620Sstevel@tonic-gate { 1630Sstevel@tonic-gate Key *k; 1640Sstevel@tonic-gate u_int len; 1650Sstevel@tonic-gate u_char *blob; 1660Sstevel@tonic-gate struct stat st; 1670Sstevel@tonic-gate 1680Sstevel@tonic-gate if (!have_identity) 1690Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which the key is")); 1700Sstevel@tonic-gate if (stat(identity_file, &st) < 0) { 1710Sstevel@tonic-gate perror(identity_file); 1720Sstevel@tonic-gate exit(1); 1730Sstevel@tonic-gate } 1740Sstevel@tonic-gate if ((k = key_load_public(identity_file, NULL)) == NULL) { 1750Sstevel@tonic-gate if ((k = load_identity(identity_file)) == NULL) { 176*5243Sjp161948 fprintf(stderr, gettext("load failed\n")); 1770Sstevel@tonic-gate exit(1); 1780Sstevel@tonic-gate } 1790Sstevel@tonic-gate } 1800Sstevel@tonic-gate if (key_to_blob(k, &blob, &len) <= 0) { 181*5243Sjp161948 fprintf(stderr, gettext("key_to_blob failed\n")); 1820Sstevel@tonic-gate exit(1); 1830Sstevel@tonic-gate } 184*5243Sjp161948 fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN); 185*5243Sjp161948 fprintf(stdout, gettext( 1860Sstevel@tonic-gate "Comment: \"%u-bit %s, converted from OpenSSH by %s@%s\"\n"), 1870Sstevel@tonic-gate key_size(k), key_type(k), 1880Sstevel@tonic-gate pw->pw_name, hostname); 1890Sstevel@tonic-gate dump_base64(stdout, blob, len); 190*5243Sjp161948 fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END); 1910Sstevel@tonic-gate key_free(k); 1920Sstevel@tonic-gate xfree(blob); 1930Sstevel@tonic-gate exit(0); 1940Sstevel@tonic-gate } 1950Sstevel@tonic-gate 1960Sstevel@tonic-gate static void 1970Sstevel@tonic-gate buffer_get_bignum_bits(Buffer *b, BIGNUM *value) 1980Sstevel@tonic-gate { 199*5243Sjp161948 u_int bignum_bits = buffer_get_int(b); 200*5243Sjp161948 u_int bytes = (bignum_bits + 7) / 8; 2010Sstevel@tonic-gate 2020Sstevel@tonic-gate if (buffer_len(b) < bytes) 2030Sstevel@tonic-gate fatal("buffer_get_bignum_bits: input buffer too small: " 2040Sstevel@tonic-gate "need %d have %d", bytes, buffer_len(b)); 205*5243Sjp161948 if (BN_bin2bn(buffer_ptr(b), bytes, value) == NULL) 206*5243Sjp161948 fatal("buffer_get_bignum_bits: BN_bin2bn failed"); 2070Sstevel@tonic-gate buffer_consume(b, bytes); 2080Sstevel@tonic-gate } 2090Sstevel@tonic-gate 2100Sstevel@tonic-gate static Key * 2110Sstevel@tonic-gate do_convert_private_ssh2_from_blob(u_char *blob, u_int blen) 2120Sstevel@tonic-gate { 2130Sstevel@tonic-gate Buffer b; 2140Sstevel@tonic-gate Key *key = NULL; 2150Sstevel@tonic-gate char *type, *cipher; 2160Sstevel@tonic-gate u_char *sig, data[] = "abcde12345"; 2170Sstevel@tonic-gate int magic, rlen, ktype, i1, i2, i3, i4; 2180Sstevel@tonic-gate u_int slen; 2190Sstevel@tonic-gate u_long e; 2200Sstevel@tonic-gate 2210Sstevel@tonic-gate buffer_init(&b); 2220Sstevel@tonic-gate buffer_append(&b, blob, blen); 2230Sstevel@tonic-gate 2240Sstevel@tonic-gate magic = buffer_get_int(&b); 2250Sstevel@tonic-gate if (magic != SSH_COM_PRIVATE_KEY_MAGIC) { 2260Sstevel@tonic-gate error("bad magic 0x%x != 0x%x", magic, SSH_COM_PRIVATE_KEY_MAGIC); 2270Sstevel@tonic-gate buffer_free(&b); 2280Sstevel@tonic-gate return NULL; 2290Sstevel@tonic-gate } 2300Sstevel@tonic-gate i1 = buffer_get_int(&b); 2310Sstevel@tonic-gate type = buffer_get_string(&b, NULL); 2320Sstevel@tonic-gate cipher = buffer_get_string(&b, NULL); 2330Sstevel@tonic-gate i2 = buffer_get_int(&b); 2340Sstevel@tonic-gate i3 = buffer_get_int(&b); 2350Sstevel@tonic-gate i4 = buffer_get_int(&b); 236*5243Sjp161948 debug("ignore (%d %d %d %d)", i1, i2, i3, i4); 2370Sstevel@tonic-gate if (strcmp(cipher, "none") != 0) { 2380Sstevel@tonic-gate error("unsupported cipher %s", cipher); 2390Sstevel@tonic-gate xfree(cipher); 2400Sstevel@tonic-gate buffer_free(&b); 2410Sstevel@tonic-gate xfree(type); 2420Sstevel@tonic-gate return NULL; 2430Sstevel@tonic-gate } 2440Sstevel@tonic-gate xfree(cipher); 2450Sstevel@tonic-gate 2460Sstevel@tonic-gate if (strstr(type, "dsa")) { 2470Sstevel@tonic-gate ktype = KEY_DSA; 2480Sstevel@tonic-gate } else if (strstr(type, "rsa")) { 2490Sstevel@tonic-gate ktype = KEY_RSA; 2500Sstevel@tonic-gate } else { 251*5243Sjp161948 buffer_free(&b); 2520Sstevel@tonic-gate xfree(type); 2530Sstevel@tonic-gate return NULL; 2540Sstevel@tonic-gate } 2550Sstevel@tonic-gate key = key_new_private(ktype); 2560Sstevel@tonic-gate xfree(type); 2570Sstevel@tonic-gate 2580Sstevel@tonic-gate switch (key->type) { 2590Sstevel@tonic-gate case KEY_DSA: 2600Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->dsa->p); 2610Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->dsa->g); 2620Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->dsa->q); 2630Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->dsa->pub_key); 2640Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->dsa->priv_key); 2650Sstevel@tonic-gate break; 2660Sstevel@tonic-gate case KEY_RSA: 2670Sstevel@tonic-gate e = buffer_get_char(&b); 2680Sstevel@tonic-gate debug("e %lx", e); 2690Sstevel@tonic-gate if (e < 30) { 2700Sstevel@tonic-gate e <<= 8; 2710Sstevel@tonic-gate e += buffer_get_char(&b); 2720Sstevel@tonic-gate debug("e %lx", e); 2730Sstevel@tonic-gate e <<= 8; 2740Sstevel@tonic-gate e += buffer_get_char(&b); 2750Sstevel@tonic-gate debug("e %lx", e); 2760Sstevel@tonic-gate } 2770Sstevel@tonic-gate if (!BN_set_word(key->rsa->e, e)) { 2780Sstevel@tonic-gate buffer_free(&b); 2790Sstevel@tonic-gate key_free(key); 2800Sstevel@tonic-gate return NULL; 2810Sstevel@tonic-gate } 2820Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->rsa->d); 2830Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->rsa->n); 2840Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->rsa->iqmp); 2850Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->rsa->q); 2860Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->rsa->p); 2870Sstevel@tonic-gate rsa_generate_additional_parameters(key->rsa); 2880Sstevel@tonic-gate break; 2890Sstevel@tonic-gate } 2900Sstevel@tonic-gate rlen = buffer_len(&b); 2910Sstevel@tonic-gate if (rlen != 0) 2920Sstevel@tonic-gate error("do_convert_private_ssh2_from_blob: " 2930Sstevel@tonic-gate "remaining bytes in key blob %d", rlen); 2940Sstevel@tonic-gate buffer_free(&b); 2950Sstevel@tonic-gate 2960Sstevel@tonic-gate /* try the key */ 297*5243Sjp161948 key_sign(key, &sig, &slen, data, sizeof(data)); 2980Sstevel@tonic-gate key_verify(key, sig, slen, data, sizeof(data)); 2990Sstevel@tonic-gate xfree(sig); 3000Sstevel@tonic-gate return key; 3010Sstevel@tonic-gate } 3020Sstevel@tonic-gate 303*5243Sjp161948 static int 304*5243Sjp161948 get_line(FILE *fp, char *line, size_t len) 305*5243Sjp161948 { 306*5243Sjp161948 int c; 307*5243Sjp161948 size_t pos = 0; 308*5243Sjp161948 309*5243Sjp161948 line[0] = '\0'; 310*5243Sjp161948 while ((c = fgetc(fp)) != EOF) { 311*5243Sjp161948 if (pos >= len - 1) { 312*5243Sjp161948 fprintf(stderr, "input line too long.\n"); 313*5243Sjp161948 exit(1); 314*5243Sjp161948 } 315*5243Sjp161948 switch (c) { 316*5243Sjp161948 case '\r': 317*5243Sjp161948 c = fgetc(fp); 318*5243Sjp161948 if (c != EOF && c != '\n' && ungetc(c, fp) == EOF) { 319*5243Sjp161948 fprintf(stderr, "unget: %s\n", strerror(errno)); 320*5243Sjp161948 exit(1); 321*5243Sjp161948 } 322*5243Sjp161948 return pos; 323*5243Sjp161948 case '\n': 324*5243Sjp161948 return pos; 325*5243Sjp161948 } 326*5243Sjp161948 line[pos++] = c; 327*5243Sjp161948 line[pos] = '\0'; 328*5243Sjp161948 } 329*5243Sjp161948 /* We reached EOF */ 330*5243Sjp161948 return -1; 331*5243Sjp161948 } 332*5243Sjp161948 3330Sstevel@tonic-gate static void 3340Sstevel@tonic-gate do_convert_from_ssh2(struct passwd *pw) 3350Sstevel@tonic-gate { 3360Sstevel@tonic-gate Key *k; 3370Sstevel@tonic-gate int blen; 3380Sstevel@tonic-gate u_int len; 339*5243Sjp161948 char line[1024]; 3400Sstevel@tonic-gate u_char blob[8096]; 3410Sstevel@tonic-gate char encoded[8096]; 3420Sstevel@tonic-gate struct stat st; 3430Sstevel@tonic-gate int escaped = 0, private = 0, ok; 3440Sstevel@tonic-gate FILE *fp; 3450Sstevel@tonic-gate 3460Sstevel@tonic-gate if (!have_identity) 3470Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which the key is")); 3480Sstevel@tonic-gate if (stat(identity_file, &st) < 0) { 3490Sstevel@tonic-gate perror(identity_file); 3500Sstevel@tonic-gate exit(1); 3510Sstevel@tonic-gate } 3520Sstevel@tonic-gate fp = fopen(identity_file, "r"); 3530Sstevel@tonic-gate if (fp == NULL) { 3540Sstevel@tonic-gate perror(identity_file); 3550Sstevel@tonic-gate exit(1); 3560Sstevel@tonic-gate } 3570Sstevel@tonic-gate encoded[0] = '\0'; 358*5243Sjp161948 while ((blen = get_line(fp, line, sizeof(line))) != -1) { 359*5243Sjp161948 if (line[blen - 1] == '\\') 3600Sstevel@tonic-gate escaped++; 3610Sstevel@tonic-gate if (strncmp(line, "----", 4) == 0 || 3620Sstevel@tonic-gate strstr(line, ": ") != NULL) { 3630Sstevel@tonic-gate if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL) 3640Sstevel@tonic-gate private = 1; 3650Sstevel@tonic-gate if (strstr(line, " END ") != NULL) { 3660Sstevel@tonic-gate break; 3670Sstevel@tonic-gate } 3680Sstevel@tonic-gate /* fprintf(stderr, "ignore: %s", line); */ 3690Sstevel@tonic-gate continue; 3700Sstevel@tonic-gate } 3710Sstevel@tonic-gate if (escaped) { 3720Sstevel@tonic-gate escaped--; 3730Sstevel@tonic-gate /* fprintf(stderr, "escaped: %s", line); */ 3740Sstevel@tonic-gate continue; 3750Sstevel@tonic-gate } 376*5243Sjp161948 strlcat(encoded, line, sizeof(encoded)); 3770Sstevel@tonic-gate } 3780Sstevel@tonic-gate len = strlen(encoded); 3790Sstevel@tonic-gate if (((len % 4) == 3) && 3800Sstevel@tonic-gate (encoded[len-1] == '=') && 3810Sstevel@tonic-gate (encoded[len-2] == '=') && 3820Sstevel@tonic-gate (encoded[len-3] == '=')) 3830Sstevel@tonic-gate encoded[len-3] = '\0'; 3840Sstevel@tonic-gate blen = uudecode(encoded, blob, sizeof(blob)); 3850Sstevel@tonic-gate if (blen < 0) { 386*5243Sjp161948 fprintf(stderr, gettext("uudecode failed.\n")); 3870Sstevel@tonic-gate exit(1); 3880Sstevel@tonic-gate } 3890Sstevel@tonic-gate k = private ? 3900Sstevel@tonic-gate do_convert_private_ssh2_from_blob(blob, blen) : 3910Sstevel@tonic-gate key_from_blob(blob, blen); 3920Sstevel@tonic-gate if (k == NULL) { 393*5243Sjp161948 fprintf(stderr, gettext("decode blob failed.\n")); 3940Sstevel@tonic-gate exit(1); 3950Sstevel@tonic-gate } 3960Sstevel@tonic-gate ok = private ? 3970Sstevel@tonic-gate (k->type == KEY_DSA ? 3980Sstevel@tonic-gate PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, NULL, 0, NULL, NULL) : 3990Sstevel@tonic-gate PEM_write_RSAPrivateKey(stdout, k->rsa, NULL, NULL, 0, NULL, NULL)) : 4000Sstevel@tonic-gate key_write(k, stdout); 4010Sstevel@tonic-gate if (!ok) { 402*5243Sjp161948 fprintf(stderr, gettext("key write failed")); 4030Sstevel@tonic-gate exit(1); 4040Sstevel@tonic-gate } 4050Sstevel@tonic-gate key_free(k); 4060Sstevel@tonic-gate if (!private) 407*5243Sjp161948 fprintf(stdout, "\n"); 408*5243Sjp161948 fclose(fp); 4090Sstevel@tonic-gate exit(0); 4100Sstevel@tonic-gate } 4110Sstevel@tonic-gate 4120Sstevel@tonic-gate static void 4130Sstevel@tonic-gate do_print_public(struct passwd *pw) 4140Sstevel@tonic-gate { 4150Sstevel@tonic-gate Key *prv; 4160Sstevel@tonic-gate struct stat st; 4170Sstevel@tonic-gate 4180Sstevel@tonic-gate if (!have_identity) 4190Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which the key is")); 4200Sstevel@tonic-gate if (stat(identity_file, &st) < 0) { 4210Sstevel@tonic-gate perror(identity_file); 4220Sstevel@tonic-gate exit(1); 4230Sstevel@tonic-gate } 4240Sstevel@tonic-gate prv = load_identity(identity_file); 4250Sstevel@tonic-gate if (prv == NULL) { 426*5243Sjp161948 fprintf(stderr, gettext("load failed\n")); 4270Sstevel@tonic-gate exit(1); 4280Sstevel@tonic-gate } 4290Sstevel@tonic-gate if (!key_write(prv, stdout)) 430*5243Sjp161948 fprintf(stderr, gettext("key_write failed")); 4310Sstevel@tonic-gate key_free(prv); 432*5243Sjp161948 fprintf(stdout, "\n"); 4330Sstevel@tonic-gate exit(0); 4340Sstevel@tonic-gate } 4350Sstevel@tonic-gate 4360Sstevel@tonic-gate #ifdef SMARTCARD 4370Sstevel@tonic-gate static void 4380Sstevel@tonic-gate do_upload(struct passwd *pw, const char *sc_reader_id) 4390Sstevel@tonic-gate { 4400Sstevel@tonic-gate Key *prv = NULL; 4410Sstevel@tonic-gate struct stat st; 4420Sstevel@tonic-gate int ret; 4430Sstevel@tonic-gate 4440Sstevel@tonic-gate if (!have_identity) 4450Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which the key is")); 4460Sstevel@tonic-gate if (stat(identity_file, &st) < 0) { 4470Sstevel@tonic-gate perror(identity_file); 4480Sstevel@tonic-gate exit(1); 4490Sstevel@tonic-gate } 4500Sstevel@tonic-gate prv = load_identity(identity_file); 4510Sstevel@tonic-gate if (prv == NULL) { 4520Sstevel@tonic-gate error("load failed"); 4530Sstevel@tonic-gate exit(1); 4540Sstevel@tonic-gate } 4550Sstevel@tonic-gate ret = sc_put_key(prv, sc_reader_id); 4560Sstevel@tonic-gate key_free(prv); 4570Sstevel@tonic-gate if (ret < 0) 4580Sstevel@tonic-gate exit(1); 4590Sstevel@tonic-gate log("loading key done"); 4600Sstevel@tonic-gate exit(0); 4610Sstevel@tonic-gate } 4620Sstevel@tonic-gate 4630Sstevel@tonic-gate static void 4640Sstevel@tonic-gate do_download(struct passwd *pw, const char *sc_reader_id) 4650Sstevel@tonic-gate { 4660Sstevel@tonic-gate Key **keys = NULL; 4670Sstevel@tonic-gate int i; 4680Sstevel@tonic-gate 4690Sstevel@tonic-gate keys = sc_get_keys(sc_reader_id, NULL); 4700Sstevel@tonic-gate if (keys == NULL) 4710Sstevel@tonic-gate fatal("cannot read public key from smartcard"); 4720Sstevel@tonic-gate for (i = 0; keys[i]; i++) { 4730Sstevel@tonic-gate key_write(keys[i], stdout); 4740Sstevel@tonic-gate key_free(keys[i]); 475*5243Sjp161948 fprintf(stdout, "\n"); 4760Sstevel@tonic-gate } 4770Sstevel@tonic-gate xfree(keys); 4780Sstevel@tonic-gate exit(0); 4790Sstevel@tonic-gate } 4800Sstevel@tonic-gate #endif /* SMARTCARD */ 4810Sstevel@tonic-gate 4820Sstevel@tonic-gate static void 4830Sstevel@tonic-gate do_fingerprint(struct passwd *pw) 4840Sstevel@tonic-gate { 4850Sstevel@tonic-gate FILE *f; 4860Sstevel@tonic-gate Key *public; 4870Sstevel@tonic-gate char *comment = NULL, *cp, *ep, line[16*1024], *fp; 4880Sstevel@tonic-gate int i, skip = 0, num = 1, invalid = 1; 4890Sstevel@tonic-gate enum fp_rep rep; 4900Sstevel@tonic-gate enum fp_type fptype; 4910Sstevel@tonic-gate struct stat st; 4920Sstevel@tonic-gate 4930Sstevel@tonic-gate fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5; 4940Sstevel@tonic-gate rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_HEX; 4950Sstevel@tonic-gate 4960Sstevel@tonic-gate if (!have_identity) 4970Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which the key is")); 4980Sstevel@tonic-gate if (stat(identity_file, &st) < 0) { 4990Sstevel@tonic-gate perror(identity_file); 5000Sstevel@tonic-gate exit(1); 5010Sstevel@tonic-gate } 5020Sstevel@tonic-gate public = key_load_public(identity_file, &comment); 5030Sstevel@tonic-gate if (public != NULL) { 5040Sstevel@tonic-gate fp = key_fingerprint(public, fptype, rep); 505*5243Sjp161948 printf("%u %s %s\n", key_size(public), fp, comment); 5060Sstevel@tonic-gate key_free(public); 5070Sstevel@tonic-gate xfree(comment); 5080Sstevel@tonic-gate xfree(fp); 5090Sstevel@tonic-gate exit(0); 5100Sstevel@tonic-gate } 511*5243Sjp161948 if (comment) { 5120Sstevel@tonic-gate xfree(comment); 513*5243Sjp161948 comment = NULL; 514*5243Sjp161948 } 5150Sstevel@tonic-gate 5160Sstevel@tonic-gate f = fopen(identity_file, "r"); 5170Sstevel@tonic-gate if (f != NULL) { 5180Sstevel@tonic-gate while (fgets(line, sizeof(line), f)) { 5190Sstevel@tonic-gate i = strlen(line) - 1; 5200Sstevel@tonic-gate if (line[i] != '\n') { 5210Sstevel@tonic-gate error("line %d too long: %.40s...", num, line); 5220Sstevel@tonic-gate skip = 1; 5230Sstevel@tonic-gate continue; 5240Sstevel@tonic-gate } 5250Sstevel@tonic-gate num++; 5260Sstevel@tonic-gate if (skip) { 5270Sstevel@tonic-gate skip = 0; 5280Sstevel@tonic-gate continue; 5290Sstevel@tonic-gate } 5300Sstevel@tonic-gate line[i] = '\0'; 5310Sstevel@tonic-gate 5320Sstevel@tonic-gate /* Skip leading whitespace, empty and comment lines. */ 5330Sstevel@tonic-gate for (cp = line; *cp == ' ' || *cp == '\t'; cp++) 5340Sstevel@tonic-gate ; 5350Sstevel@tonic-gate if (!*cp || *cp == '\n' || *cp == '#') 536*5243Sjp161948 continue; 5370Sstevel@tonic-gate i = strtol(cp, &ep, 10); 5380Sstevel@tonic-gate if (i == 0 || ep == NULL || (*ep != ' ' && *ep != '\t')) { 5390Sstevel@tonic-gate int quoted = 0; 5400Sstevel@tonic-gate comment = cp; 5410Sstevel@tonic-gate for (; *cp && (quoted || (*cp != ' ' && 5420Sstevel@tonic-gate *cp != '\t')); cp++) { 5430Sstevel@tonic-gate if (*cp == '\\' && cp[1] == '"') 5440Sstevel@tonic-gate cp++; /* Skip both */ 5450Sstevel@tonic-gate else if (*cp == '"') 5460Sstevel@tonic-gate quoted = !quoted; 5470Sstevel@tonic-gate } 5480Sstevel@tonic-gate if (!*cp) 5490Sstevel@tonic-gate continue; 5500Sstevel@tonic-gate *cp++ = '\0'; 5510Sstevel@tonic-gate } 5520Sstevel@tonic-gate ep = cp; 5530Sstevel@tonic-gate public = key_new(KEY_RSA1); 5540Sstevel@tonic-gate if (key_read(public, &cp) != 1) { 5550Sstevel@tonic-gate cp = ep; 5560Sstevel@tonic-gate key_free(public); 5570Sstevel@tonic-gate public = key_new(KEY_UNSPEC); 5580Sstevel@tonic-gate if (key_read(public, &cp) != 1) { 5590Sstevel@tonic-gate key_free(public); 5600Sstevel@tonic-gate continue; 5610Sstevel@tonic-gate } 5620Sstevel@tonic-gate } 5630Sstevel@tonic-gate comment = *cp ? cp : comment; 5640Sstevel@tonic-gate fp = key_fingerprint(public, fptype, rep); 565*5243Sjp161948 printf("%u %s %s\n", key_size(public), fp, 5660Sstevel@tonic-gate comment ? comment : gettext("no comment")); 5670Sstevel@tonic-gate xfree(fp); 5680Sstevel@tonic-gate key_free(public); 5690Sstevel@tonic-gate invalid = 0; 5700Sstevel@tonic-gate } 571*5243Sjp161948 fclose(f); 5720Sstevel@tonic-gate } 5730Sstevel@tonic-gate if (invalid) { 574*5243Sjp161948 printf(gettext("%s is not a public key file.\n"), 5750Sstevel@tonic-gate identity_file); 5760Sstevel@tonic-gate exit(1); 5770Sstevel@tonic-gate } 5780Sstevel@tonic-gate exit(0); 5790Sstevel@tonic-gate } 5800Sstevel@tonic-gate 581*5243Sjp161948 static void 582*5243Sjp161948 print_host(FILE *f, const char *name, Key *public, int hash) 583*5243Sjp161948 { 584*5243Sjp161948 if (hash && (name = host_hash(name, NULL, 0)) == NULL) 585*5243Sjp161948 fatal("hash_host failed"); 586*5243Sjp161948 fprintf(f, "%s ", name); 587*5243Sjp161948 if (!key_write(public, f)) 588*5243Sjp161948 fatal("key_write failed"); 589*5243Sjp161948 fprintf(f, "\n"); 590*5243Sjp161948 } 591*5243Sjp161948 592*5243Sjp161948 static void 593*5243Sjp161948 do_known_hosts(struct passwd *pw, const char *name) 594*5243Sjp161948 { 595*5243Sjp161948 FILE *in, *out = stdout; 596*5243Sjp161948 Key *public; 597*5243Sjp161948 char *cp, *cp2, *kp, *kp2; 598*5243Sjp161948 char line[16*1024], tmp[MAXPATHLEN], old[MAXPATHLEN]; 599*5243Sjp161948 int c, i, skip = 0, inplace = 0, num = 0, invalid = 0, has_unhashed = 0; 600*5243Sjp161948 601*5243Sjp161948 if (!have_identity) { 602*5243Sjp161948 cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid); 603*5243Sjp161948 if (strlcpy(identity_file, cp, sizeof(identity_file)) >= 604*5243Sjp161948 sizeof(identity_file)) 605*5243Sjp161948 fatal("Specified known hosts path too long"); 606*5243Sjp161948 xfree(cp); 607*5243Sjp161948 have_identity = 1; 608*5243Sjp161948 } 609*5243Sjp161948 if ((in = fopen(identity_file, "r")) == NULL) 610*5243Sjp161948 fatal("fopen: %s", strerror(errno)); 611*5243Sjp161948 612*5243Sjp161948 /* 613*5243Sjp161948 * Find hosts goes to stdout, hash and deletions happen in-place 614*5243Sjp161948 * A corner case is ssh-keygen -HF foo, which should go to stdout 615*5243Sjp161948 */ 616*5243Sjp161948 if (!find_host && (hash_hosts || delete_host)) { 617*5243Sjp161948 if (strlcpy(tmp, identity_file, sizeof(tmp)) >= sizeof(tmp) || 618*5243Sjp161948 strlcat(tmp, ".XXXXXXXXXX", sizeof(tmp)) >= sizeof(tmp) || 619*5243Sjp161948 strlcpy(old, identity_file, sizeof(old)) >= sizeof(old) || 620*5243Sjp161948 strlcat(old, ".old", sizeof(old)) >= sizeof(old)) 621*5243Sjp161948 fatal("known_hosts path too long"); 622*5243Sjp161948 umask(077); 623*5243Sjp161948 if ((c = mkstemp(tmp)) == -1) 624*5243Sjp161948 fatal("mkstemp: %s", strerror(errno)); 625*5243Sjp161948 if ((out = fdopen(c, "w")) == NULL) { 626*5243Sjp161948 c = errno; 627*5243Sjp161948 unlink(tmp); 628*5243Sjp161948 fatal("fdopen: %s", strerror(c)); 629*5243Sjp161948 } 630*5243Sjp161948 inplace = 1; 631*5243Sjp161948 } 632*5243Sjp161948 633*5243Sjp161948 while (fgets(line, sizeof(line), in)) { 634*5243Sjp161948 num++; 635*5243Sjp161948 i = strlen(line) - 1; 636*5243Sjp161948 if (line[i] != '\n') { 637*5243Sjp161948 error("line %d too long: %.40s...", num, line); 638*5243Sjp161948 skip = 1; 639*5243Sjp161948 invalid = 1; 640*5243Sjp161948 continue; 641*5243Sjp161948 } 642*5243Sjp161948 if (skip) { 643*5243Sjp161948 skip = 0; 644*5243Sjp161948 continue; 645*5243Sjp161948 } 646*5243Sjp161948 line[i] = '\0'; 647*5243Sjp161948 648*5243Sjp161948 /* Skip leading whitespace, empty and comment lines. */ 649*5243Sjp161948 for (cp = line; *cp == ' ' || *cp == '\t'; cp++) 650*5243Sjp161948 ; 651*5243Sjp161948 if (!*cp || *cp == '\n' || *cp == '#') { 652*5243Sjp161948 if (inplace) 653*5243Sjp161948 fprintf(out, "%s\n", cp); 654*5243Sjp161948 continue; 655*5243Sjp161948 } 656*5243Sjp161948 /* Find the end of the host name portion. */ 657*5243Sjp161948 for (kp = cp; *kp && *kp != ' ' && *kp != '\t'; kp++) 658*5243Sjp161948 ; 659*5243Sjp161948 if (*kp == '\0' || *(kp + 1) == '\0') { 660*5243Sjp161948 error("line %d missing key: %.40s...", 661*5243Sjp161948 num, line); 662*5243Sjp161948 invalid = 1; 663*5243Sjp161948 continue; 664*5243Sjp161948 } 665*5243Sjp161948 *kp++ = '\0'; 666*5243Sjp161948 kp2 = kp; 667*5243Sjp161948 668*5243Sjp161948 public = key_new(KEY_RSA1); 669*5243Sjp161948 if (key_read(public, &kp) != 1) { 670*5243Sjp161948 kp = kp2; 671*5243Sjp161948 key_free(public); 672*5243Sjp161948 public = key_new(KEY_UNSPEC); 673*5243Sjp161948 if (key_read(public, &kp) != 1) { 674*5243Sjp161948 error("line %d invalid key: %.40s...", 675*5243Sjp161948 num, line); 676*5243Sjp161948 key_free(public); 677*5243Sjp161948 invalid = 1; 678*5243Sjp161948 continue; 679*5243Sjp161948 } 680*5243Sjp161948 } 681*5243Sjp161948 682*5243Sjp161948 if (*cp == HASH_DELIM) { 683*5243Sjp161948 if (find_host || delete_host) { 684*5243Sjp161948 cp2 = host_hash(name, cp, strlen(cp)); 685*5243Sjp161948 if (cp2 == NULL) { 686*5243Sjp161948 error("line %d: invalid hashed " 687*5243Sjp161948 "name: %.64s...", num, line); 688*5243Sjp161948 invalid = 1; 689*5243Sjp161948 continue; 690*5243Sjp161948 } 691*5243Sjp161948 c = (strcmp(cp2, cp) == 0); 692*5243Sjp161948 if (find_host && c) { 693*5243Sjp161948 printf(gettext("# Host %s found: " 694*5243Sjp161948 "line %d type %s\n"), name, 695*5243Sjp161948 num, key_type(public)); 696*5243Sjp161948 print_host(out, cp, public, 0); 697*5243Sjp161948 } 698*5243Sjp161948 if (delete_host && !c) 699*5243Sjp161948 print_host(out, cp, public, 0); 700*5243Sjp161948 } else if (hash_hosts) 701*5243Sjp161948 print_host(out, cp, public, 0); 702*5243Sjp161948 } else { 703*5243Sjp161948 if (find_host || delete_host) { 704*5243Sjp161948 c = (match_hostname(name, cp, 705*5243Sjp161948 strlen(cp)) == 1); 706*5243Sjp161948 if (find_host && c) { 707*5243Sjp161948 printf(gettext("# Host %s found: " 708*5243Sjp161948 "line %d type %s\n"), name, 709*5243Sjp161948 num, key_type(public)); 710*5243Sjp161948 print_host(out, name, public, hash_hosts); 711*5243Sjp161948 } 712*5243Sjp161948 if (delete_host && !c) 713*5243Sjp161948 print_host(out, cp, public, 0); 714*5243Sjp161948 } else if (hash_hosts) { 715*5243Sjp161948 for (cp2 = strsep(&cp, ","); 716*5243Sjp161948 cp2 != NULL && *cp2 != '\0'; 717*5243Sjp161948 cp2 = strsep(&cp, ",")) { 718*5243Sjp161948 if (strcspn(cp2, "*?!") != strlen(cp2)) 719*5243Sjp161948 fprintf(stderr, gettext("Warning: " 720*5243Sjp161948 "ignoring host name with " 721*5243Sjp161948 "metacharacters: %.64s\n"), 722*5243Sjp161948 cp2); 723*5243Sjp161948 else 724*5243Sjp161948 print_host(out, cp2, public, 1); 725*5243Sjp161948 } 726*5243Sjp161948 has_unhashed = 1; 727*5243Sjp161948 } 728*5243Sjp161948 } 729*5243Sjp161948 key_free(public); 730*5243Sjp161948 } 731*5243Sjp161948 fclose(in); 732*5243Sjp161948 733*5243Sjp161948 if (invalid) { 734*5243Sjp161948 fprintf(stderr, gettext("%s is not a valid known_host file.\n"), 735*5243Sjp161948 identity_file); 736*5243Sjp161948 if (inplace) { 737*5243Sjp161948 fprintf(stderr, gettext("Not replacing existing known_hosts " 738*5243Sjp161948 "file because of errors\n")); 739*5243Sjp161948 fclose(out); 740*5243Sjp161948 unlink(tmp); 741*5243Sjp161948 } 742*5243Sjp161948 exit(1); 743*5243Sjp161948 } 744*5243Sjp161948 745*5243Sjp161948 if (inplace) { 746*5243Sjp161948 fclose(out); 747*5243Sjp161948 748*5243Sjp161948 /* Backup existing file */ 749*5243Sjp161948 if (unlink(old) == -1 && errno != ENOENT) 750*5243Sjp161948 fatal("unlink %.100s: %s", old, strerror(errno)); 751*5243Sjp161948 if (link(identity_file, old) == -1) 752*5243Sjp161948 fatal("link %.100s to %.100s: %s", identity_file, old, 753*5243Sjp161948 strerror(errno)); 754*5243Sjp161948 /* Move new one into place */ 755*5243Sjp161948 if (rename(tmp, identity_file) == -1) { 756*5243Sjp161948 error("rename\"%s\" to \"%s\": %s", tmp, identity_file, 757*5243Sjp161948 strerror(errno)); 758*5243Sjp161948 unlink(tmp); 759*5243Sjp161948 unlink(old); 760*5243Sjp161948 exit(1); 761*5243Sjp161948 } 762*5243Sjp161948 763*5243Sjp161948 fprintf(stderr, gettext("%s updated.\n"), identity_file); 764*5243Sjp161948 fprintf(stderr, gettext("Original contents retained as %s\n"), old); 765*5243Sjp161948 if (has_unhashed) { 766*5243Sjp161948 fprintf(stderr, gettext("WARNING: %s contains unhashed " 767*5243Sjp161948 "entries\n"), old); 768*5243Sjp161948 fprintf(stderr, gettext("Delete this file to ensure privacy " 769*5243Sjp161948 "of hostnames\n")); 770*5243Sjp161948 } 771*5243Sjp161948 } 772*5243Sjp161948 773*5243Sjp161948 exit(0); 774*5243Sjp161948 } 775*5243Sjp161948 7760Sstevel@tonic-gate /* 7770Sstevel@tonic-gate * Perform changing a passphrase. The argument is the passwd structure 7780Sstevel@tonic-gate * for the current user. 7790Sstevel@tonic-gate */ 7800Sstevel@tonic-gate static void 7810Sstevel@tonic-gate do_change_passphrase(struct passwd *pw) 7820Sstevel@tonic-gate { 7830Sstevel@tonic-gate char *comment; 7840Sstevel@tonic-gate char *old_passphrase, *passphrase1, *passphrase2; 7850Sstevel@tonic-gate struct stat st; 7860Sstevel@tonic-gate Key *private; 7870Sstevel@tonic-gate 7880Sstevel@tonic-gate if (!have_identity) 7890Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which the key is")); 7900Sstevel@tonic-gate if (stat(identity_file, &st) < 0) { 7910Sstevel@tonic-gate perror(identity_file); 7920Sstevel@tonic-gate exit(1); 7930Sstevel@tonic-gate } 7940Sstevel@tonic-gate /* Try to load the file with empty passphrase. */ 7950Sstevel@tonic-gate private = key_load_private(identity_file, "", &comment); 7960Sstevel@tonic-gate if (private == NULL) { 7970Sstevel@tonic-gate if (identity_passphrase) 7980Sstevel@tonic-gate old_passphrase = xstrdup(identity_passphrase); 7990Sstevel@tonic-gate else 8000Sstevel@tonic-gate old_passphrase = 8010Sstevel@tonic-gate read_passphrase(gettext("Enter old passphrase: "), 8020Sstevel@tonic-gate RP_ALLOW_STDIN); 8030Sstevel@tonic-gate private = key_load_private(identity_file, old_passphrase, 8040Sstevel@tonic-gate &comment); 805*5243Sjp161948 memset(old_passphrase, 0, strlen(old_passphrase)); 8060Sstevel@tonic-gate xfree(old_passphrase); 8070Sstevel@tonic-gate if (private == NULL) { 808*5243Sjp161948 printf(gettext("Bad passphrase.\n")); 8090Sstevel@tonic-gate exit(1); 8100Sstevel@tonic-gate } 8110Sstevel@tonic-gate } 812*5243Sjp161948 printf(gettext("Key has comment '%s'\n"), comment); 8130Sstevel@tonic-gate 8140Sstevel@tonic-gate /* Ask the new passphrase (twice). */ 8150Sstevel@tonic-gate if (identity_new_passphrase) { 8160Sstevel@tonic-gate passphrase1 = xstrdup(identity_new_passphrase); 8170Sstevel@tonic-gate passphrase2 = NULL; 8180Sstevel@tonic-gate } else { 8190Sstevel@tonic-gate passphrase1 = 8200Sstevel@tonic-gate read_passphrase(gettext("Enter new passphrase (empty" 8210Sstevel@tonic-gate " for no passphrase): "), RP_ALLOW_STDIN); 8220Sstevel@tonic-gate passphrase2 = read_passphrase(gettext("Enter same " 8230Sstevel@tonic-gate "passphrase again: "), RP_ALLOW_STDIN); 8240Sstevel@tonic-gate 8250Sstevel@tonic-gate /* Verify that they are the same. */ 8260Sstevel@tonic-gate if (strcmp(passphrase1, passphrase2) != 0) { 827*5243Sjp161948 memset(passphrase1, 0, strlen(passphrase1)); 828*5243Sjp161948 memset(passphrase2, 0, strlen(passphrase2)); 8290Sstevel@tonic-gate xfree(passphrase1); 8300Sstevel@tonic-gate xfree(passphrase2); 831*5243Sjp161948 printf(gettext("Pass phrases do not match. Try " 832*5243Sjp161948 "again.\n")); 8330Sstevel@tonic-gate exit(1); 8340Sstevel@tonic-gate } 8350Sstevel@tonic-gate /* Destroy the other copy. */ 836*5243Sjp161948 memset(passphrase2, 0, strlen(passphrase2)); 8370Sstevel@tonic-gate xfree(passphrase2); 8380Sstevel@tonic-gate } 8390Sstevel@tonic-gate 8400Sstevel@tonic-gate /* Save the file using the new passphrase. */ 8410Sstevel@tonic-gate if (!key_save_private(private, identity_file, passphrase1, comment)) { 842*5243Sjp161948 printf(gettext("Saving the key failed: %s.\n"), identity_file); 843*5243Sjp161948 memset(passphrase1, 0, strlen(passphrase1)); 8440Sstevel@tonic-gate xfree(passphrase1); 8450Sstevel@tonic-gate key_free(private); 8460Sstevel@tonic-gate xfree(comment); 8470Sstevel@tonic-gate exit(1); 8480Sstevel@tonic-gate } 8490Sstevel@tonic-gate /* Destroy the passphrase and the copy of the key in memory. */ 850*5243Sjp161948 memset(passphrase1, 0, strlen(passphrase1)); 8510Sstevel@tonic-gate xfree(passphrase1); 8520Sstevel@tonic-gate key_free(private); /* Destroys contents */ 8530Sstevel@tonic-gate xfree(comment); 8540Sstevel@tonic-gate 855*5243Sjp161948 printf(gettext("Your identification has been saved with the new " 856*5243Sjp161948 "passphrase.\n")); 8570Sstevel@tonic-gate exit(0); 8580Sstevel@tonic-gate } 8590Sstevel@tonic-gate 8600Sstevel@tonic-gate /* 8610Sstevel@tonic-gate * Change the comment of a private key file. 8620Sstevel@tonic-gate */ 8630Sstevel@tonic-gate static void 8640Sstevel@tonic-gate do_change_comment(struct passwd *pw) 8650Sstevel@tonic-gate { 8660Sstevel@tonic-gate char new_comment[1024], *comment, *passphrase; 8670Sstevel@tonic-gate Key *private; 8680Sstevel@tonic-gate Key *public; 8690Sstevel@tonic-gate struct stat st; 8700Sstevel@tonic-gate FILE *f; 8710Sstevel@tonic-gate int fd; 8720Sstevel@tonic-gate 8730Sstevel@tonic-gate if (!have_identity) 8740Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which the key is")); 8750Sstevel@tonic-gate if (stat(identity_file, &st) < 0) { 8760Sstevel@tonic-gate perror(identity_file); 8770Sstevel@tonic-gate exit(1); 8780Sstevel@tonic-gate } 8790Sstevel@tonic-gate private = key_load_private(identity_file, "", &comment); 8800Sstevel@tonic-gate if (private == NULL) { 8810Sstevel@tonic-gate if (identity_passphrase) 8820Sstevel@tonic-gate passphrase = xstrdup(identity_passphrase); 8830Sstevel@tonic-gate else if (identity_new_passphrase) 8840Sstevel@tonic-gate passphrase = xstrdup(identity_new_passphrase); 8850Sstevel@tonic-gate else 8860Sstevel@tonic-gate passphrase = 8870Sstevel@tonic-gate read_passphrase(gettext("Enter passphrase: "), 888*5243Sjp161948 RP_ALLOW_STDIN); 8890Sstevel@tonic-gate /* Try to load using the passphrase. */ 8900Sstevel@tonic-gate private = key_load_private(identity_file, passphrase, &comment); 8910Sstevel@tonic-gate if (private == NULL) { 892*5243Sjp161948 memset(passphrase, 0, strlen(passphrase)); 8930Sstevel@tonic-gate xfree(passphrase); 894*5243Sjp161948 printf(gettext("Bad passphrase.\n")); 8950Sstevel@tonic-gate exit(1); 8960Sstevel@tonic-gate } 8970Sstevel@tonic-gate } else { 8980Sstevel@tonic-gate passphrase = xstrdup(""); 8990Sstevel@tonic-gate } 9000Sstevel@tonic-gate if (private->type != KEY_RSA1) { 901*5243Sjp161948 fprintf(stderr, gettext("Comments are only supported for " 902*5243Sjp161948 "RSA1 keys.\n")); 9030Sstevel@tonic-gate key_free(private); 9040Sstevel@tonic-gate exit(1); 9050Sstevel@tonic-gate } 906*5243Sjp161948 printf(gettext("Key now has comment '%s'\n"), comment); 9070Sstevel@tonic-gate 9080Sstevel@tonic-gate if (identity_comment) { 909*5243Sjp161948 strlcpy(new_comment, identity_comment, sizeof(new_comment)); 9100Sstevel@tonic-gate } else { 911*5243Sjp161948 printf(gettext("Enter new comment: ")); 912*5243Sjp161948 fflush(stdout); 9130Sstevel@tonic-gate if (!fgets(new_comment, sizeof(new_comment), stdin)) { 914*5243Sjp161948 memset(passphrase, 0, strlen(passphrase)); 9150Sstevel@tonic-gate key_free(private); 9160Sstevel@tonic-gate exit(1); 9170Sstevel@tonic-gate } 9180Sstevel@tonic-gate if (strchr(new_comment, '\n')) 9190Sstevel@tonic-gate *strchr(new_comment, '\n') = 0; 9200Sstevel@tonic-gate } 9210Sstevel@tonic-gate 9220Sstevel@tonic-gate /* Save the file using the new passphrase. */ 9230Sstevel@tonic-gate if (!key_save_private(private, identity_file, passphrase, new_comment)) { 924*5243Sjp161948 printf(gettext("Saving the key failed: %s.\n"), identity_file); 925*5243Sjp161948 memset(passphrase, 0, strlen(passphrase)); 9260Sstevel@tonic-gate xfree(passphrase); 9270Sstevel@tonic-gate key_free(private); 9280Sstevel@tonic-gate xfree(comment); 9290Sstevel@tonic-gate exit(1); 9300Sstevel@tonic-gate } 931*5243Sjp161948 memset(passphrase, 0, strlen(passphrase)); 9320Sstevel@tonic-gate xfree(passphrase); 9330Sstevel@tonic-gate public = key_from_private(private); 9340Sstevel@tonic-gate key_free(private); 9350Sstevel@tonic-gate 936*5243Sjp161948 strlcat(identity_file, ".pub", sizeof(identity_file)); 9370Sstevel@tonic-gate fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); 9380Sstevel@tonic-gate if (fd == -1) { 939*5243Sjp161948 printf(gettext("Could not save your public key in %s\n"), 940*5243Sjp161948 identity_file); 9410Sstevel@tonic-gate exit(1); 9420Sstevel@tonic-gate } 9430Sstevel@tonic-gate f = fdopen(fd, "w"); 9440Sstevel@tonic-gate if (f == NULL) { 945*5243Sjp161948 printf(gettext("fdopen %s failed"), identity_file); 9460Sstevel@tonic-gate exit(1); 9470Sstevel@tonic-gate } 9480Sstevel@tonic-gate if (!key_write(public, f)) 949*5243Sjp161948 fprintf(stderr, gettext("write key failed")); 9500Sstevel@tonic-gate key_free(public); 951*5243Sjp161948 fprintf(f, " %s\n", new_comment); 952*5243Sjp161948 fclose(f); 9530Sstevel@tonic-gate 9540Sstevel@tonic-gate xfree(comment); 9550Sstevel@tonic-gate 956*5243Sjp161948 printf(gettext("The comment in your key file has been changed.\n")); 9570Sstevel@tonic-gate exit(0); 9580Sstevel@tonic-gate } 9590Sstevel@tonic-gate 9600Sstevel@tonic-gate static void 9610Sstevel@tonic-gate usage(void) 9620Sstevel@tonic-gate { 963*5243Sjp161948 fprintf(stderr, gettext( 9640Sstevel@tonic-gate "Usage: %s [options]\n" 9650Sstevel@tonic-gate "Options:\n" 9660Sstevel@tonic-gate " -b bits Number of bits in the key to create.\n" 967*5243Sjp161948 " -B Show bubblebabble digest of key file.\n" 9680Sstevel@tonic-gate " -c Change comment in private and public key files.\n" 969*5243Sjp161948 " -C comment Provide new comment.\n" 970*5243Sjp161948 #ifdef SMARTCARD 971*5243Sjp161948 " -D reader Download public key from smartcard.\n" 972*5243Sjp161948 #endif /* SMARTCARD */ 9730Sstevel@tonic-gate " -e Convert OpenSSH to IETF SECSH key file.\n" 9740Sstevel@tonic-gate " -f filename Filename of the key file.\n" 975*5243Sjp161948 " -F hostname Find hostname in known hosts file.\n" 976*5243Sjp161948 " -H Hash names in known_hosts file.\n" 9770Sstevel@tonic-gate " -i Convert IETF SECSH to OpenSSH key file.\n" 9780Sstevel@tonic-gate " -l Show fingerprint of key file.\n" 979*5243Sjp161948 " -N phrase Provide new passphrase.\n" 9800Sstevel@tonic-gate " -p Change passphrase of private key file.\n" 981*5243Sjp161948 " -P phrase Provide old passphrase.\n" 9820Sstevel@tonic-gate " -q Quiet.\n" 983*5243Sjp161948 " -R hostname Remove host from known_hosts file.\n" 9840Sstevel@tonic-gate " -t type Specify type of key to create.\n" 9850Sstevel@tonic-gate #ifdef SMARTCARD 9860Sstevel@tonic-gate " -U reader Upload private key to smartcard.\n" 9870Sstevel@tonic-gate #endif /* SMARTCARD */ 988*5243Sjp161948 " -y Read private key file and print public key.\n" 9890Sstevel@tonic-gate ), __progname); 9900Sstevel@tonic-gate 9910Sstevel@tonic-gate exit(1); 9920Sstevel@tonic-gate } 9930Sstevel@tonic-gate 9940Sstevel@tonic-gate /* 9950Sstevel@tonic-gate * Main program for key management. 9960Sstevel@tonic-gate */ 9970Sstevel@tonic-gate int 998*5243Sjp161948 main(int argc, char **argv) 9990Sstevel@tonic-gate { 10000Sstevel@tonic-gate char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2; 10010Sstevel@tonic-gate char *reader_id = NULL; 1002*5243Sjp161948 char *rr_hostname = NULL; 10030Sstevel@tonic-gate Key *private, *public; 10040Sstevel@tonic-gate struct passwd *pw; 10050Sstevel@tonic-gate struct stat st; 10060Sstevel@tonic-gate int opt, type, fd; 10070Sstevel@tonic-gate #ifdef SMARTCARD 10080Sstevel@tonic-gate int download = 0; 10090Sstevel@tonic-gate #endif /* SMARTCARD */ 10100Sstevel@tonic-gate FILE *f; 10110Sstevel@tonic-gate 10120Sstevel@tonic-gate extern int optind; 10130Sstevel@tonic-gate extern char *optarg; 10140Sstevel@tonic-gate 1015*5243Sjp161948 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 1016*5243Sjp161948 sanitise_stdfd(); 10170Sstevel@tonic-gate 1018*5243Sjp161948 __progname = get_progname(argv[0]); 1019*5243Sjp161948 1020*5243Sjp161948 g11n_setlocale(LC_ALL, ""); 10210Sstevel@tonic-gate 10220Sstevel@tonic-gate SSLeay_add_all_algorithms(); 10230Sstevel@tonic-gate init_rng(); 10240Sstevel@tonic-gate seed_rng(); 10250Sstevel@tonic-gate 10260Sstevel@tonic-gate /* we need this for the home * directory. */ 10270Sstevel@tonic-gate pw = getpwuid(getuid()); 10280Sstevel@tonic-gate if (!pw) { 1029*5243Sjp161948 printf(gettext("You don't exist, go away!\n")); 10300Sstevel@tonic-gate exit(1); 10310Sstevel@tonic-gate } 10320Sstevel@tonic-gate if (gethostname(hostname, sizeof(hostname)) < 0) { 10330Sstevel@tonic-gate perror("gethostname"); 10340Sstevel@tonic-gate exit(1); 10350Sstevel@tonic-gate } 10360Sstevel@tonic-gate 10370Sstevel@tonic-gate #ifdef SMARTCARD 1038*5243Sjp161948 #define GETOPT_ARGS "deiqpclBHRxXyb:f:F:t:U:D:P:N:C:" 10390Sstevel@tonic-gate #else 1040*5243Sjp161948 #define GETOPT_ARGS "BcdeHilpqxXyb:C:f:F:N:P:R:t:" 10410Sstevel@tonic-gate #endif /* SMARTCARD */ 1042*5243Sjp161948 while ((opt = getopt(argc, argv, GETOPT_ARGS)) != -1) { 10430Sstevel@tonic-gate switch (opt) { 10440Sstevel@tonic-gate case 'b': 10450Sstevel@tonic-gate bits = atoi(optarg); 10460Sstevel@tonic-gate if (bits < 512 || bits > 32768) { 1047*5243Sjp161948 printf(gettext("Bits has bad value.\n")); 10480Sstevel@tonic-gate exit(1); 10490Sstevel@tonic-gate } 10500Sstevel@tonic-gate break; 1051*5243Sjp161948 case 'F': 1052*5243Sjp161948 find_host = 1; 1053*5243Sjp161948 rr_hostname = optarg; 1054*5243Sjp161948 break; 1055*5243Sjp161948 case 'H': 1056*5243Sjp161948 hash_hosts = 1; 1057*5243Sjp161948 break; 1058*5243Sjp161948 case 'R': 1059*5243Sjp161948 delete_host = 1; 1060*5243Sjp161948 rr_hostname = optarg; 1061*5243Sjp161948 break; 10620Sstevel@tonic-gate case 'l': 10630Sstevel@tonic-gate print_fingerprint = 1; 10640Sstevel@tonic-gate break; 10650Sstevel@tonic-gate case 'B': 10660Sstevel@tonic-gate print_bubblebabble = 1; 10670Sstevel@tonic-gate break; 10680Sstevel@tonic-gate case 'p': 10690Sstevel@tonic-gate change_passphrase = 1; 10700Sstevel@tonic-gate break; 10710Sstevel@tonic-gate case 'c': 10720Sstevel@tonic-gate change_comment = 1; 10730Sstevel@tonic-gate break; 10740Sstevel@tonic-gate case 'f': 1075*5243Sjp161948 strlcpy(identity_file, optarg, sizeof(identity_file)); 10760Sstevel@tonic-gate have_identity = 1; 10770Sstevel@tonic-gate break; 10780Sstevel@tonic-gate case 'P': 10790Sstevel@tonic-gate identity_passphrase = optarg; 10800Sstevel@tonic-gate break; 10810Sstevel@tonic-gate case 'N': 10820Sstevel@tonic-gate identity_new_passphrase = optarg; 10830Sstevel@tonic-gate break; 10840Sstevel@tonic-gate case 'C': 10850Sstevel@tonic-gate identity_comment = optarg; 10860Sstevel@tonic-gate break; 10870Sstevel@tonic-gate case 'q': 10880Sstevel@tonic-gate quiet = 1; 10890Sstevel@tonic-gate break; 10900Sstevel@tonic-gate case 'e': 10910Sstevel@tonic-gate case 'x': 10920Sstevel@tonic-gate /* export key */ 10930Sstevel@tonic-gate convert_to_ssh2 = 1; 10940Sstevel@tonic-gate break; 10950Sstevel@tonic-gate case 'i': 10960Sstevel@tonic-gate case 'X': 10970Sstevel@tonic-gate /* import key */ 10980Sstevel@tonic-gate convert_from_ssh2 = 1; 10990Sstevel@tonic-gate break; 11000Sstevel@tonic-gate case 'y': 11010Sstevel@tonic-gate print_public = 1; 11020Sstevel@tonic-gate break; 11030Sstevel@tonic-gate case 'd': 11040Sstevel@tonic-gate key_type_name = "dsa"; 11050Sstevel@tonic-gate break; 11060Sstevel@tonic-gate case 't': 11070Sstevel@tonic-gate key_type_name = optarg; 11080Sstevel@tonic-gate break; 11090Sstevel@tonic-gate #ifdef SMARTCARD 11100Sstevel@tonic-gate case 'D': 11110Sstevel@tonic-gate download = 1; 11120Sstevel@tonic-gate case 'U': 11130Sstevel@tonic-gate reader_id = optarg; 11140Sstevel@tonic-gate break; 11150Sstevel@tonic-gate #endif 11160Sstevel@tonic-gate case '?': 11170Sstevel@tonic-gate default: 11180Sstevel@tonic-gate usage(); 11190Sstevel@tonic-gate } 11200Sstevel@tonic-gate } 1121*5243Sjp161948 if (optind < argc) { 1122*5243Sjp161948 printf(gettext("Too many arguments.\n")); 11230Sstevel@tonic-gate usage(); 11240Sstevel@tonic-gate } 11250Sstevel@tonic-gate if (change_passphrase && change_comment) { 1126*5243Sjp161948 printf(gettext("Can only have one of -p and -c.\n")); 11270Sstevel@tonic-gate usage(); 11280Sstevel@tonic-gate } 1129*5243Sjp161948 if (delete_host || hash_hosts || find_host) 1130*5243Sjp161948 do_known_hosts(pw, rr_hostname); 11310Sstevel@tonic-gate if (print_fingerprint || print_bubblebabble) 11320Sstevel@tonic-gate do_fingerprint(pw); 11330Sstevel@tonic-gate if (change_passphrase) 11340Sstevel@tonic-gate do_change_passphrase(pw); 11350Sstevel@tonic-gate if (change_comment) 11360Sstevel@tonic-gate do_change_comment(pw); 11370Sstevel@tonic-gate if (convert_to_ssh2) 11380Sstevel@tonic-gate do_convert_to_ssh2(pw); 11390Sstevel@tonic-gate if (convert_from_ssh2) 11400Sstevel@tonic-gate do_convert_from_ssh2(pw); 11410Sstevel@tonic-gate if (print_public) 11420Sstevel@tonic-gate do_print_public(pw); 11430Sstevel@tonic-gate if (reader_id != NULL) { 11440Sstevel@tonic-gate #ifdef SMARTCARD 11450Sstevel@tonic-gate if (download) 11460Sstevel@tonic-gate do_download(pw, reader_id); 11470Sstevel@tonic-gate else 11480Sstevel@tonic-gate do_upload(pw, reader_id); 11490Sstevel@tonic-gate #else /* SMARTCARD */ 11500Sstevel@tonic-gate fatal("no support for smartcards."); 11510Sstevel@tonic-gate #endif /* SMARTCARD */ 11520Sstevel@tonic-gate } 11530Sstevel@tonic-gate 11540Sstevel@tonic-gate arc4random_stir(); 11550Sstevel@tonic-gate 11560Sstevel@tonic-gate if (key_type_name == NULL) { 1157*5243Sjp161948 printf(gettext("You must specify a key type (-t).\n")); 11580Sstevel@tonic-gate usage(); 11590Sstevel@tonic-gate } 11600Sstevel@tonic-gate type = key_type_from_name(key_type_name); 11610Sstevel@tonic-gate if (type == KEY_UNSPEC) { 1162*5243Sjp161948 fprintf(stderr, gettext("unknown key type %s\n"), 1163*5243Sjp161948 key_type_name); 11640Sstevel@tonic-gate exit(1); 11650Sstevel@tonic-gate } 11660Sstevel@tonic-gate if (!quiet) 1167*5243Sjp161948 printf(gettext("Generating public/private %s key pair.\n"), 1168*5243Sjp161948 key_type_name); 11690Sstevel@tonic-gate private = key_generate(type, bits); 11700Sstevel@tonic-gate if (private == NULL) { 1171*5243Sjp161948 fprintf(stderr, gettext("key_generate failed")); 11720Sstevel@tonic-gate exit(1); 11730Sstevel@tonic-gate } 11740Sstevel@tonic-gate public = key_from_private(private); 11750Sstevel@tonic-gate 11760Sstevel@tonic-gate if (!have_identity) 11770Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which to save the key")); 11780Sstevel@tonic-gate 1179*5243Sjp161948 /* Create ~/.ssh directory if it doesn't already exist. */ 1180*5243Sjp161948 snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, _PATH_SSH_USER_DIR); 11810Sstevel@tonic-gate if (strstr(identity_file, dotsshdir) != NULL && 11820Sstevel@tonic-gate stat(dotsshdir, &st) < 0) { 11830Sstevel@tonic-gate if (mkdir(dotsshdir, 0700) < 0) 11840Sstevel@tonic-gate error("Could not create directory '%s'.", dotsshdir); 11850Sstevel@tonic-gate else if (!quiet) 1186*5243Sjp161948 printf(gettext("Created directory '%s'.\n"), dotsshdir); 11870Sstevel@tonic-gate } 11880Sstevel@tonic-gate /* If the file already exists, ask the user to confirm. */ 11890Sstevel@tonic-gate if (stat(identity_file, &st) >= 0) { 11900Sstevel@tonic-gate char yesno[128]; 1191*5243Sjp161948 printf(gettext("%s already exists.\n"), identity_file); 1192*5243Sjp161948 printf(gettext("Overwrite (%s/%s)? "), 1193*5243Sjp161948 nl_langinfo(YESSTR), nl_langinfo(NOSTR)); 1194*5243Sjp161948 fflush(stdout); 11950Sstevel@tonic-gate if (fgets(yesno, sizeof(yesno), stdin) == NULL) 11960Sstevel@tonic-gate exit(1); 11972757Sjp161948 if (strcasecmp(chop(yesno), nl_langinfo(YESSTR)) != 0) 11980Sstevel@tonic-gate exit(1); 11990Sstevel@tonic-gate } 12000Sstevel@tonic-gate /* Ask for a passphrase (twice). */ 12010Sstevel@tonic-gate if (identity_passphrase) 12020Sstevel@tonic-gate passphrase1 = xstrdup(identity_passphrase); 12030Sstevel@tonic-gate else if (identity_new_passphrase) 12040Sstevel@tonic-gate passphrase1 = xstrdup(identity_new_passphrase); 12050Sstevel@tonic-gate else { 12060Sstevel@tonic-gate passphrase_again: 12070Sstevel@tonic-gate passphrase1 = 12080Sstevel@tonic-gate read_passphrase(gettext("Enter passphrase (empty " 12090Sstevel@tonic-gate "for no passphrase): "), RP_ALLOW_STDIN); 12100Sstevel@tonic-gate passphrase2 = read_passphrase(gettext("Enter same " 12110Sstevel@tonic-gate "passphrase again: "), RP_ALLOW_STDIN); 12120Sstevel@tonic-gate if (strcmp(passphrase1, passphrase2) != 0) { 12130Sstevel@tonic-gate /* 12140Sstevel@tonic-gate * The passphrases do not match. Clear them and 12150Sstevel@tonic-gate * retry. 12160Sstevel@tonic-gate */ 1217*5243Sjp161948 memset(passphrase1, 0, strlen(passphrase1)); 1218*5243Sjp161948 memset(passphrase2, 0, strlen(passphrase2)); 12190Sstevel@tonic-gate xfree(passphrase1); 12200Sstevel@tonic-gate xfree(passphrase2); 1221*5243Sjp161948 printf(gettext("Passphrases do not match. Try " 1222*5243Sjp161948 "again.\n")); 12230Sstevel@tonic-gate goto passphrase_again; 12240Sstevel@tonic-gate } 12250Sstevel@tonic-gate /* Clear the other copy of the passphrase. */ 1226*5243Sjp161948 memset(passphrase2, 0, strlen(passphrase2)); 12270Sstevel@tonic-gate xfree(passphrase2); 12280Sstevel@tonic-gate } 12290Sstevel@tonic-gate 12300Sstevel@tonic-gate if (identity_comment) { 1231*5243Sjp161948 strlcpy(comment, identity_comment, sizeof(comment)); 12320Sstevel@tonic-gate } else { 12330Sstevel@tonic-gate /* Create default commend field for the passphrase. */ 1234*5243Sjp161948 snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); 12350Sstevel@tonic-gate } 12360Sstevel@tonic-gate 12370Sstevel@tonic-gate /* Save the key with the given passphrase and comment. */ 12380Sstevel@tonic-gate if (!key_save_private(private, identity_file, passphrase1, comment)) { 1239*5243Sjp161948 printf(gettext("Saving the key failed: %s.\n"), identity_file); 1240*5243Sjp161948 memset(passphrase1, 0, strlen(passphrase1)); 12410Sstevel@tonic-gate xfree(passphrase1); 12420Sstevel@tonic-gate exit(1); 12430Sstevel@tonic-gate } 12440Sstevel@tonic-gate /* Clear the passphrase. */ 1245*5243Sjp161948 memset(passphrase1, 0, strlen(passphrase1)); 12460Sstevel@tonic-gate xfree(passphrase1); 12470Sstevel@tonic-gate 12480Sstevel@tonic-gate /* Clear the private key and the random number generator. */ 12490Sstevel@tonic-gate key_free(private); 12500Sstevel@tonic-gate arc4random_stir(); 12510Sstevel@tonic-gate 12520Sstevel@tonic-gate if (!quiet) 1253*5243Sjp161948 printf(gettext("Your identification has been saved in %s.\n"), 1254*5243Sjp161948 identity_file); 12550Sstevel@tonic-gate 1256*5243Sjp161948 strlcat(identity_file, ".pub", sizeof(identity_file)); 12570Sstevel@tonic-gate fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); 12580Sstevel@tonic-gate if (fd == -1) { 1259*5243Sjp161948 printf(gettext("Could not save your public key in %s\n"), 1260*5243Sjp161948 identity_file); 12610Sstevel@tonic-gate exit(1); 12620Sstevel@tonic-gate } 12630Sstevel@tonic-gate f = fdopen(fd, "w"); 12640Sstevel@tonic-gate if (f == NULL) { 1265*5243Sjp161948 printf(gettext("fdopen %s failed"), identity_file); 12660Sstevel@tonic-gate exit(1); 12670Sstevel@tonic-gate } 12680Sstevel@tonic-gate if (!key_write(public, f)) 1269*5243Sjp161948 fprintf(stderr, gettext("write key failed")); 1270*5243Sjp161948 fprintf(f, " %s\n", comment); 1271*5243Sjp161948 fclose(f); 12720Sstevel@tonic-gate 12730Sstevel@tonic-gate if (!quiet) { 12740Sstevel@tonic-gate char *fp = key_fingerprint(public, SSH_FP_MD5, SSH_FP_HEX); 1275*5243Sjp161948 printf(gettext("Your public key has been saved in %s.\n"), 12760Sstevel@tonic-gate identity_file); 1277*5243Sjp161948 printf(gettext("The key fingerprint is:\n")); 1278*5243Sjp161948 printf("%s %s\n", fp, comment); 12790Sstevel@tonic-gate xfree(fp); 12800Sstevel@tonic-gate } 12810Sstevel@tonic-gate 12820Sstevel@tonic-gate key_free(public); 12830Sstevel@tonic-gate return(0); 12840Sstevel@tonic-gate /* NOTREACHED */ 12850Sstevel@tonic-gate } 1286