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 145243Sjp161948 /* $OpenBSD: ssh-keygen.c,v 1.160 2007/01/21 01:41:54 stevesk Exp $ */ 150Sstevel@tonic-gate 165243Sjp161948 #include "includes.h" 170Sstevel@tonic-gate #include <openssl/evp.h> 180Sstevel@tonic-gate #include <openssl/pem.h> 190Sstevel@tonic-gate 200Sstevel@tonic-gate #include "xmalloc.h" 210Sstevel@tonic-gate #include "key.h" 220Sstevel@tonic-gate #include "rsa.h" 230Sstevel@tonic-gate #include "authfile.h" 240Sstevel@tonic-gate #include "uuencode.h" 250Sstevel@tonic-gate #include "buffer.h" 260Sstevel@tonic-gate #include "bufaux.h" 270Sstevel@tonic-gate #include "pathnames.h" 280Sstevel@tonic-gate #include "log.h" 290Sstevel@tonic-gate #include "readpass.h" 302757Sjp161948 #include "misc.h" 310Sstevel@tonic-gate #include <langinfo.h> 325243Sjp161948 #include "match.h" 335243Sjp161948 #include "hostfile.h" 345243Sjp161948 #include "tildexpand.h" 350Sstevel@tonic-gate 365243Sjp161948 /* Number of bits in the RSA/DSA key. This value can be set on the command line. */ 37*12032SHai-May.Chao@Sun.COM #define DEFAULT_BITS_RSA 2048 38*12032SHai-May.Chao@Sun.COM #define DEFAULT_BITS_DSA 1024 39*12032SHai-May.Chao@Sun.COM u_int32_t bits = 0; 400Sstevel@tonic-gate 410Sstevel@tonic-gate /* 420Sstevel@tonic-gate * Flag indicating that we just want to change the passphrase. This can be 430Sstevel@tonic-gate * set on the command line. 440Sstevel@tonic-gate */ 450Sstevel@tonic-gate int change_passphrase = 0; 460Sstevel@tonic-gate 470Sstevel@tonic-gate /* 480Sstevel@tonic-gate * Flag indicating that we just want to change the comment. This can be set 490Sstevel@tonic-gate * on the command line. 500Sstevel@tonic-gate */ 510Sstevel@tonic-gate int change_comment = 0; 520Sstevel@tonic-gate 530Sstevel@tonic-gate int quiet = 0; 540Sstevel@tonic-gate 555243Sjp161948 /* Flag indicating that we want to hash a known_hosts file */ 565243Sjp161948 int hash_hosts = 0; 575243Sjp161948 /* Flag indicating that we want to lookup a host in known_hosts file */ 585243Sjp161948 int find_host = 0; 595243Sjp161948 /* Flag indicating that we want to delete a host from a known_hosts file */ 605243Sjp161948 int delete_host = 0; 615243Sjp161948 620Sstevel@tonic-gate /* Flag indicating that we just want to see the key fingerprint */ 630Sstevel@tonic-gate int print_fingerprint = 0; 640Sstevel@tonic-gate int print_bubblebabble = 0; 650Sstevel@tonic-gate 660Sstevel@tonic-gate /* The identity file name, given on the command line or entered by the user. */ 670Sstevel@tonic-gate char identity_file[1024]; 680Sstevel@tonic-gate int have_identity = 0; 690Sstevel@tonic-gate 700Sstevel@tonic-gate /* This is set to the passphrase if given on the command line. */ 710Sstevel@tonic-gate char *identity_passphrase = NULL; 720Sstevel@tonic-gate 730Sstevel@tonic-gate /* This is set to the new passphrase if given on the command line. */ 740Sstevel@tonic-gate char *identity_new_passphrase = NULL; 750Sstevel@tonic-gate 760Sstevel@tonic-gate /* This is set to the new comment if given on the command line. */ 770Sstevel@tonic-gate char *identity_comment = NULL; 780Sstevel@tonic-gate 790Sstevel@tonic-gate /* Dump public key file in format used by real and the original SSH 2 */ 800Sstevel@tonic-gate int convert_to_ssh2 = 0; 810Sstevel@tonic-gate int convert_from_ssh2 = 0; 820Sstevel@tonic-gate int print_public = 0; 830Sstevel@tonic-gate 840Sstevel@tonic-gate char *key_type_name = NULL; 850Sstevel@tonic-gate 860Sstevel@tonic-gate /* argv0 */ 870Sstevel@tonic-gate #ifdef HAVE___PROGNAME 880Sstevel@tonic-gate extern char *__progname; 890Sstevel@tonic-gate #else 900Sstevel@tonic-gate char *__progname; 910Sstevel@tonic-gate #endif 920Sstevel@tonic-gate 930Sstevel@tonic-gate char hostname[MAXHOSTNAMELEN]; 940Sstevel@tonic-gate 950Sstevel@tonic-gate static void 960Sstevel@tonic-gate ask_filename(struct passwd *pw, const char *prompt) 970Sstevel@tonic-gate { 980Sstevel@tonic-gate char buf[1024]; 990Sstevel@tonic-gate char *name = NULL; 1000Sstevel@tonic-gate 1010Sstevel@tonic-gate if (key_type_name == NULL) 1020Sstevel@tonic-gate name = _PATH_SSH_CLIENT_ID_RSA; 1035243Sjp161948 else { 1040Sstevel@tonic-gate switch (key_type_from_name(key_type_name)) { 1050Sstevel@tonic-gate case KEY_RSA1: 1060Sstevel@tonic-gate name = _PATH_SSH_CLIENT_IDENTITY; 1070Sstevel@tonic-gate break; 1080Sstevel@tonic-gate case KEY_DSA: 1090Sstevel@tonic-gate name = _PATH_SSH_CLIENT_ID_DSA; 1100Sstevel@tonic-gate break; 1110Sstevel@tonic-gate case KEY_RSA: 1120Sstevel@tonic-gate name = _PATH_SSH_CLIENT_ID_RSA; 1130Sstevel@tonic-gate break; 1140Sstevel@tonic-gate default: 1155243Sjp161948 fprintf(stderr, gettext("bad key type")); 1160Sstevel@tonic-gate exit(1); 1170Sstevel@tonic-gate break; 1180Sstevel@tonic-gate } 1195243Sjp161948 } 1205243Sjp161948 snprintf(identity_file, sizeof(identity_file), "%s/%s", pw->pw_dir, name); 1215243Sjp161948 fprintf(stderr, "%s (%s): ", gettext(prompt), identity_file); 1220Sstevel@tonic-gate if (fgets(buf, sizeof(buf), stdin) == NULL) 1230Sstevel@tonic-gate exit(1); 1240Sstevel@tonic-gate if (strchr(buf, '\n')) 1250Sstevel@tonic-gate *strchr(buf, '\n') = 0; 1260Sstevel@tonic-gate if (strcmp(buf, "") != 0) 1275243Sjp161948 strlcpy(identity_file, buf, sizeof(identity_file)); 1280Sstevel@tonic-gate have_identity = 1; 1290Sstevel@tonic-gate } 1300Sstevel@tonic-gate 1310Sstevel@tonic-gate static Key * 1320Sstevel@tonic-gate load_identity(char *filename) 1330Sstevel@tonic-gate { 1340Sstevel@tonic-gate char *pass; 1350Sstevel@tonic-gate Key *prv; 1360Sstevel@tonic-gate 1370Sstevel@tonic-gate prv = key_load_private(filename, "", NULL); 1380Sstevel@tonic-gate if (prv == NULL) { 1390Sstevel@tonic-gate if (identity_passphrase) 1400Sstevel@tonic-gate pass = xstrdup(identity_passphrase); 1410Sstevel@tonic-gate else 1420Sstevel@tonic-gate pass = read_passphrase(gettext("Enter passphrase: "), 1430Sstevel@tonic-gate RP_ALLOW_STDIN); 1440Sstevel@tonic-gate prv = key_load_private(filename, pass, NULL); 1455243Sjp161948 memset(pass, 0, strlen(pass)); 1460Sstevel@tonic-gate xfree(pass); 1470Sstevel@tonic-gate } 1480Sstevel@tonic-gate return prv; 1490Sstevel@tonic-gate } 1500Sstevel@tonic-gate 1510Sstevel@tonic-gate #define SSH_COM_PUBLIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----" 1520Sstevel@tonic-gate #define SSH_COM_PUBLIC_END "---- END SSH2 PUBLIC KEY ----" 1530Sstevel@tonic-gate #define SSH_COM_PRIVATE_BEGIN "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----" 1540Sstevel@tonic-gate #define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb 1550Sstevel@tonic-gate 1560Sstevel@tonic-gate static void 1570Sstevel@tonic-gate do_convert_to_ssh2(struct passwd *pw) 1580Sstevel@tonic-gate { 1590Sstevel@tonic-gate Key *k; 1600Sstevel@tonic-gate u_int len; 1610Sstevel@tonic-gate u_char *blob; 1620Sstevel@tonic-gate struct stat st; 1630Sstevel@tonic-gate 1640Sstevel@tonic-gate if (!have_identity) 1650Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which the key is")); 1660Sstevel@tonic-gate if (stat(identity_file, &st) < 0) { 1670Sstevel@tonic-gate perror(identity_file); 1680Sstevel@tonic-gate exit(1); 1690Sstevel@tonic-gate } 1700Sstevel@tonic-gate if ((k = key_load_public(identity_file, NULL)) == NULL) { 1710Sstevel@tonic-gate if ((k = load_identity(identity_file)) == NULL) { 1725243Sjp161948 fprintf(stderr, gettext("load failed\n")); 1730Sstevel@tonic-gate exit(1); 1740Sstevel@tonic-gate } 1750Sstevel@tonic-gate } 1760Sstevel@tonic-gate if (key_to_blob(k, &blob, &len) <= 0) { 1775243Sjp161948 fprintf(stderr, gettext("key_to_blob failed\n")); 1780Sstevel@tonic-gate exit(1); 1790Sstevel@tonic-gate } 1805243Sjp161948 fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN); 1815243Sjp161948 fprintf(stdout, gettext( 1820Sstevel@tonic-gate "Comment: \"%u-bit %s, converted from OpenSSH by %s@%s\"\n"), 1830Sstevel@tonic-gate key_size(k), key_type(k), 1840Sstevel@tonic-gate pw->pw_name, hostname); 1850Sstevel@tonic-gate dump_base64(stdout, blob, len); 1865243Sjp161948 fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END); 1870Sstevel@tonic-gate key_free(k); 1880Sstevel@tonic-gate xfree(blob); 1890Sstevel@tonic-gate exit(0); 1900Sstevel@tonic-gate } 1910Sstevel@tonic-gate 1920Sstevel@tonic-gate static void 1930Sstevel@tonic-gate buffer_get_bignum_bits(Buffer *b, BIGNUM *value) 1940Sstevel@tonic-gate { 1955243Sjp161948 u_int bignum_bits = buffer_get_int(b); 1965243Sjp161948 u_int bytes = (bignum_bits + 7) / 8; 1970Sstevel@tonic-gate 1980Sstevel@tonic-gate if (buffer_len(b) < bytes) 1990Sstevel@tonic-gate fatal("buffer_get_bignum_bits: input buffer too small: " 2000Sstevel@tonic-gate "need %d have %d", bytes, buffer_len(b)); 2015243Sjp161948 if (BN_bin2bn(buffer_ptr(b), bytes, value) == NULL) 2025243Sjp161948 fatal("buffer_get_bignum_bits: BN_bin2bn failed"); 2030Sstevel@tonic-gate buffer_consume(b, bytes); 2040Sstevel@tonic-gate } 2050Sstevel@tonic-gate 2060Sstevel@tonic-gate static Key * 2070Sstevel@tonic-gate do_convert_private_ssh2_from_blob(u_char *blob, u_int blen) 2080Sstevel@tonic-gate { 2090Sstevel@tonic-gate Buffer b; 2100Sstevel@tonic-gate Key *key = NULL; 2110Sstevel@tonic-gate char *type, *cipher; 2120Sstevel@tonic-gate u_char *sig, data[] = "abcde12345"; 2130Sstevel@tonic-gate int magic, rlen, ktype, i1, i2, i3, i4; 2140Sstevel@tonic-gate u_int slen; 2150Sstevel@tonic-gate u_long e; 2160Sstevel@tonic-gate 2170Sstevel@tonic-gate buffer_init(&b); 2180Sstevel@tonic-gate buffer_append(&b, blob, blen); 2190Sstevel@tonic-gate 2200Sstevel@tonic-gate magic = buffer_get_int(&b); 2210Sstevel@tonic-gate if (magic != SSH_COM_PRIVATE_KEY_MAGIC) { 2220Sstevel@tonic-gate error("bad magic 0x%x != 0x%x", magic, SSH_COM_PRIVATE_KEY_MAGIC); 2230Sstevel@tonic-gate buffer_free(&b); 2240Sstevel@tonic-gate return NULL; 2250Sstevel@tonic-gate } 2260Sstevel@tonic-gate i1 = buffer_get_int(&b); 2270Sstevel@tonic-gate type = buffer_get_string(&b, NULL); 2280Sstevel@tonic-gate cipher = buffer_get_string(&b, NULL); 2290Sstevel@tonic-gate i2 = buffer_get_int(&b); 2300Sstevel@tonic-gate i3 = buffer_get_int(&b); 2310Sstevel@tonic-gate i4 = buffer_get_int(&b); 2325243Sjp161948 debug("ignore (%d %d %d %d)", i1, i2, i3, i4); 2330Sstevel@tonic-gate if (strcmp(cipher, "none") != 0) { 2340Sstevel@tonic-gate error("unsupported cipher %s", cipher); 2350Sstevel@tonic-gate xfree(cipher); 2360Sstevel@tonic-gate buffer_free(&b); 2370Sstevel@tonic-gate xfree(type); 2380Sstevel@tonic-gate return NULL; 2390Sstevel@tonic-gate } 2400Sstevel@tonic-gate xfree(cipher); 2410Sstevel@tonic-gate 2420Sstevel@tonic-gate if (strstr(type, "dsa")) { 2430Sstevel@tonic-gate ktype = KEY_DSA; 2440Sstevel@tonic-gate } else if (strstr(type, "rsa")) { 2450Sstevel@tonic-gate ktype = KEY_RSA; 2460Sstevel@tonic-gate } else { 2475243Sjp161948 buffer_free(&b); 2480Sstevel@tonic-gate xfree(type); 2490Sstevel@tonic-gate return NULL; 2500Sstevel@tonic-gate } 2510Sstevel@tonic-gate key = key_new_private(ktype); 2520Sstevel@tonic-gate xfree(type); 2530Sstevel@tonic-gate 2540Sstevel@tonic-gate switch (key->type) { 2550Sstevel@tonic-gate case KEY_DSA: 2560Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->dsa->p); 2570Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->dsa->g); 2580Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->dsa->q); 2590Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->dsa->pub_key); 2600Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->dsa->priv_key); 2610Sstevel@tonic-gate break; 2620Sstevel@tonic-gate case KEY_RSA: 2630Sstevel@tonic-gate e = buffer_get_char(&b); 2640Sstevel@tonic-gate debug("e %lx", e); 2650Sstevel@tonic-gate if (e < 30) { 2660Sstevel@tonic-gate e <<= 8; 2670Sstevel@tonic-gate e += buffer_get_char(&b); 2680Sstevel@tonic-gate debug("e %lx", e); 2690Sstevel@tonic-gate e <<= 8; 2700Sstevel@tonic-gate e += buffer_get_char(&b); 2710Sstevel@tonic-gate debug("e %lx", e); 2720Sstevel@tonic-gate } 2730Sstevel@tonic-gate if (!BN_set_word(key->rsa->e, e)) { 2740Sstevel@tonic-gate buffer_free(&b); 2750Sstevel@tonic-gate key_free(key); 2760Sstevel@tonic-gate return NULL; 2770Sstevel@tonic-gate } 2780Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->rsa->d); 2790Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->rsa->n); 2800Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->rsa->iqmp); 2810Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->rsa->q); 2820Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->rsa->p); 2830Sstevel@tonic-gate rsa_generate_additional_parameters(key->rsa); 2840Sstevel@tonic-gate break; 2850Sstevel@tonic-gate } 2860Sstevel@tonic-gate rlen = buffer_len(&b); 2870Sstevel@tonic-gate if (rlen != 0) 2880Sstevel@tonic-gate error("do_convert_private_ssh2_from_blob: " 2890Sstevel@tonic-gate "remaining bytes in key blob %d", rlen); 2900Sstevel@tonic-gate buffer_free(&b); 2910Sstevel@tonic-gate 2920Sstevel@tonic-gate /* try the key */ 2935243Sjp161948 key_sign(key, &sig, &slen, data, sizeof(data)); 2940Sstevel@tonic-gate key_verify(key, sig, slen, data, sizeof(data)); 2950Sstevel@tonic-gate xfree(sig); 2960Sstevel@tonic-gate return key; 2970Sstevel@tonic-gate } 2980Sstevel@tonic-gate 2995243Sjp161948 static int 3005243Sjp161948 get_line(FILE *fp, char *line, size_t len) 3015243Sjp161948 { 3025243Sjp161948 int c; 3035243Sjp161948 size_t pos = 0; 3045243Sjp161948 3055243Sjp161948 line[0] = '\0'; 3065243Sjp161948 while ((c = fgetc(fp)) != EOF) { 3075243Sjp161948 if (pos >= len - 1) { 3085243Sjp161948 fprintf(stderr, "input line too long.\n"); 3095243Sjp161948 exit(1); 3105243Sjp161948 } 3115243Sjp161948 switch (c) { 3125243Sjp161948 case '\r': 3135243Sjp161948 c = fgetc(fp); 3145243Sjp161948 if (c != EOF && c != '\n' && ungetc(c, fp) == EOF) { 3155243Sjp161948 fprintf(stderr, "unget: %s\n", strerror(errno)); 3165243Sjp161948 exit(1); 3175243Sjp161948 } 3185243Sjp161948 return pos; 3195243Sjp161948 case '\n': 3205243Sjp161948 return pos; 3215243Sjp161948 } 3225243Sjp161948 line[pos++] = c; 3235243Sjp161948 line[pos] = '\0'; 3245243Sjp161948 } 3255243Sjp161948 /* We reached EOF */ 3265243Sjp161948 return -1; 3275243Sjp161948 } 3285243Sjp161948 3290Sstevel@tonic-gate static void 3300Sstevel@tonic-gate do_convert_from_ssh2(struct passwd *pw) 3310Sstevel@tonic-gate { 3320Sstevel@tonic-gate Key *k; 3330Sstevel@tonic-gate int blen; 3340Sstevel@tonic-gate u_int len; 3355243Sjp161948 char line[1024]; 3360Sstevel@tonic-gate u_char blob[8096]; 3370Sstevel@tonic-gate char encoded[8096]; 3380Sstevel@tonic-gate struct stat st; 3390Sstevel@tonic-gate int escaped = 0, private = 0, ok; 3400Sstevel@tonic-gate FILE *fp; 3410Sstevel@tonic-gate 3420Sstevel@tonic-gate if (!have_identity) 3430Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which the key is")); 3440Sstevel@tonic-gate if (stat(identity_file, &st) < 0) { 3450Sstevel@tonic-gate perror(identity_file); 3460Sstevel@tonic-gate exit(1); 3470Sstevel@tonic-gate } 3480Sstevel@tonic-gate fp = fopen(identity_file, "r"); 3490Sstevel@tonic-gate if (fp == NULL) { 3500Sstevel@tonic-gate perror(identity_file); 3510Sstevel@tonic-gate exit(1); 3520Sstevel@tonic-gate } 3530Sstevel@tonic-gate encoded[0] = '\0'; 3545243Sjp161948 while ((blen = get_line(fp, line, sizeof(line))) != -1) { 3555243Sjp161948 if (line[blen - 1] == '\\') 3560Sstevel@tonic-gate escaped++; 3570Sstevel@tonic-gate if (strncmp(line, "----", 4) == 0 || 3580Sstevel@tonic-gate strstr(line, ": ") != NULL) { 3590Sstevel@tonic-gate if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL) 3600Sstevel@tonic-gate private = 1; 3610Sstevel@tonic-gate if (strstr(line, " END ") != NULL) { 3620Sstevel@tonic-gate break; 3630Sstevel@tonic-gate } 3640Sstevel@tonic-gate /* fprintf(stderr, "ignore: %s", line); */ 3650Sstevel@tonic-gate continue; 3660Sstevel@tonic-gate } 3670Sstevel@tonic-gate if (escaped) { 3680Sstevel@tonic-gate escaped--; 3690Sstevel@tonic-gate /* fprintf(stderr, "escaped: %s", line); */ 3700Sstevel@tonic-gate continue; 3710Sstevel@tonic-gate } 3725243Sjp161948 strlcat(encoded, line, sizeof(encoded)); 3730Sstevel@tonic-gate } 3740Sstevel@tonic-gate len = strlen(encoded); 3750Sstevel@tonic-gate if (((len % 4) == 3) && 3760Sstevel@tonic-gate (encoded[len-1] == '=') && 3770Sstevel@tonic-gate (encoded[len-2] == '=') && 3780Sstevel@tonic-gate (encoded[len-3] == '=')) 3790Sstevel@tonic-gate encoded[len-3] = '\0'; 3800Sstevel@tonic-gate blen = uudecode(encoded, blob, sizeof(blob)); 3810Sstevel@tonic-gate if (blen < 0) { 3825243Sjp161948 fprintf(stderr, gettext("uudecode failed.\n")); 3830Sstevel@tonic-gate exit(1); 3840Sstevel@tonic-gate } 3850Sstevel@tonic-gate k = private ? 3860Sstevel@tonic-gate do_convert_private_ssh2_from_blob(blob, blen) : 3870Sstevel@tonic-gate key_from_blob(blob, blen); 3880Sstevel@tonic-gate if (k == NULL) { 3895243Sjp161948 fprintf(stderr, gettext("decode blob failed.\n")); 3900Sstevel@tonic-gate exit(1); 3910Sstevel@tonic-gate } 3920Sstevel@tonic-gate ok = private ? 3930Sstevel@tonic-gate (k->type == KEY_DSA ? 3940Sstevel@tonic-gate PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, NULL, 0, NULL, NULL) : 3950Sstevel@tonic-gate PEM_write_RSAPrivateKey(stdout, k->rsa, NULL, NULL, 0, NULL, NULL)) : 3960Sstevel@tonic-gate key_write(k, stdout); 3970Sstevel@tonic-gate if (!ok) { 3985243Sjp161948 fprintf(stderr, gettext("key write failed")); 3990Sstevel@tonic-gate exit(1); 4000Sstevel@tonic-gate } 4010Sstevel@tonic-gate key_free(k); 4020Sstevel@tonic-gate if (!private) 4035243Sjp161948 fprintf(stdout, "\n"); 4045243Sjp161948 fclose(fp); 4050Sstevel@tonic-gate exit(0); 4060Sstevel@tonic-gate } 4070Sstevel@tonic-gate 4080Sstevel@tonic-gate static void 4090Sstevel@tonic-gate do_print_public(struct passwd *pw) 4100Sstevel@tonic-gate { 4110Sstevel@tonic-gate Key *prv; 4120Sstevel@tonic-gate struct stat st; 4130Sstevel@tonic-gate 4140Sstevel@tonic-gate if (!have_identity) 4150Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which the key is")); 4160Sstevel@tonic-gate if (stat(identity_file, &st) < 0) { 4170Sstevel@tonic-gate perror(identity_file); 4180Sstevel@tonic-gate exit(1); 4190Sstevel@tonic-gate } 4200Sstevel@tonic-gate prv = load_identity(identity_file); 4210Sstevel@tonic-gate if (prv == NULL) { 4225243Sjp161948 fprintf(stderr, gettext("load failed\n")); 4230Sstevel@tonic-gate exit(1); 4240Sstevel@tonic-gate } 4250Sstevel@tonic-gate if (!key_write(prv, stdout)) 4265243Sjp161948 fprintf(stderr, gettext("key_write failed")); 4270Sstevel@tonic-gate key_free(prv); 4285243Sjp161948 fprintf(stdout, "\n"); 4290Sstevel@tonic-gate exit(0); 4300Sstevel@tonic-gate } 4310Sstevel@tonic-gate 4320Sstevel@tonic-gate static void 4330Sstevel@tonic-gate do_fingerprint(struct passwd *pw) 4340Sstevel@tonic-gate { 4350Sstevel@tonic-gate FILE *f; 4360Sstevel@tonic-gate Key *public; 4370Sstevel@tonic-gate char *comment = NULL, *cp, *ep, line[16*1024], *fp; 4380Sstevel@tonic-gate int i, skip = 0, num = 1, invalid = 1; 4390Sstevel@tonic-gate enum fp_rep rep; 4400Sstevel@tonic-gate enum fp_type fptype; 4410Sstevel@tonic-gate struct stat st; 4420Sstevel@tonic-gate 4430Sstevel@tonic-gate fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5; 4440Sstevel@tonic-gate rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_HEX; 4450Sstevel@tonic-gate 4460Sstevel@tonic-gate if (!have_identity) 4470Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which the key is")); 4480Sstevel@tonic-gate if (stat(identity_file, &st) < 0) { 4490Sstevel@tonic-gate perror(identity_file); 4500Sstevel@tonic-gate exit(1); 4510Sstevel@tonic-gate } 4520Sstevel@tonic-gate public = key_load_public(identity_file, &comment); 4530Sstevel@tonic-gate if (public != NULL) { 4540Sstevel@tonic-gate fp = key_fingerprint(public, fptype, rep); 4555243Sjp161948 printf("%u %s %s\n", key_size(public), fp, comment); 4560Sstevel@tonic-gate key_free(public); 4570Sstevel@tonic-gate xfree(comment); 4580Sstevel@tonic-gate xfree(fp); 4590Sstevel@tonic-gate exit(0); 4600Sstevel@tonic-gate } 4615243Sjp161948 if (comment) { 4620Sstevel@tonic-gate xfree(comment); 4635243Sjp161948 comment = NULL; 4645243Sjp161948 } 4650Sstevel@tonic-gate 4660Sstevel@tonic-gate f = fopen(identity_file, "r"); 4670Sstevel@tonic-gate if (f != NULL) { 4680Sstevel@tonic-gate while (fgets(line, sizeof(line), f)) { 4690Sstevel@tonic-gate i = strlen(line) - 1; 4700Sstevel@tonic-gate if (line[i] != '\n') { 4710Sstevel@tonic-gate error("line %d too long: %.40s...", num, line); 4720Sstevel@tonic-gate skip = 1; 4730Sstevel@tonic-gate continue; 4740Sstevel@tonic-gate } 4750Sstevel@tonic-gate num++; 4760Sstevel@tonic-gate if (skip) { 4770Sstevel@tonic-gate skip = 0; 4780Sstevel@tonic-gate continue; 4790Sstevel@tonic-gate } 4800Sstevel@tonic-gate line[i] = '\0'; 4810Sstevel@tonic-gate 4820Sstevel@tonic-gate /* Skip leading whitespace, empty and comment lines. */ 4830Sstevel@tonic-gate for (cp = line; *cp == ' ' || *cp == '\t'; cp++) 4840Sstevel@tonic-gate ; 4850Sstevel@tonic-gate if (!*cp || *cp == '\n' || *cp == '#') 4865243Sjp161948 continue; 4870Sstevel@tonic-gate i = strtol(cp, &ep, 10); 4880Sstevel@tonic-gate if (i == 0 || ep == NULL || (*ep != ' ' && *ep != '\t')) { 4890Sstevel@tonic-gate int quoted = 0; 4900Sstevel@tonic-gate comment = cp; 4910Sstevel@tonic-gate for (; *cp && (quoted || (*cp != ' ' && 4920Sstevel@tonic-gate *cp != '\t')); cp++) { 4930Sstevel@tonic-gate if (*cp == '\\' && cp[1] == '"') 4940Sstevel@tonic-gate cp++; /* Skip both */ 4950Sstevel@tonic-gate else if (*cp == '"') 4960Sstevel@tonic-gate quoted = !quoted; 4970Sstevel@tonic-gate } 4980Sstevel@tonic-gate if (!*cp) 4990Sstevel@tonic-gate continue; 5000Sstevel@tonic-gate *cp++ = '\0'; 5010Sstevel@tonic-gate } 5020Sstevel@tonic-gate ep = cp; 5030Sstevel@tonic-gate public = key_new(KEY_RSA1); 5040Sstevel@tonic-gate if (key_read(public, &cp) != 1) { 5050Sstevel@tonic-gate cp = ep; 5060Sstevel@tonic-gate key_free(public); 5070Sstevel@tonic-gate public = key_new(KEY_UNSPEC); 5080Sstevel@tonic-gate if (key_read(public, &cp) != 1) { 5090Sstevel@tonic-gate key_free(public); 5100Sstevel@tonic-gate continue; 5110Sstevel@tonic-gate } 5120Sstevel@tonic-gate } 5130Sstevel@tonic-gate comment = *cp ? cp : comment; 5140Sstevel@tonic-gate fp = key_fingerprint(public, fptype, rep); 5155243Sjp161948 printf("%u %s %s\n", key_size(public), fp, 5160Sstevel@tonic-gate comment ? comment : gettext("no comment")); 5170Sstevel@tonic-gate xfree(fp); 5180Sstevel@tonic-gate key_free(public); 5190Sstevel@tonic-gate invalid = 0; 5200Sstevel@tonic-gate } 5215243Sjp161948 fclose(f); 5220Sstevel@tonic-gate } 5230Sstevel@tonic-gate if (invalid) { 5245243Sjp161948 printf(gettext("%s is not a public key file.\n"), 5250Sstevel@tonic-gate identity_file); 5260Sstevel@tonic-gate exit(1); 5270Sstevel@tonic-gate } 5280Sstevel@tonic-gate exit(0); 5290Sstevel@tonic-gate } 5300Sstevel@tonic-gate 5315243Sjp161948 static void 5325243Sjp161948 print_host(FILE *f, const char *name, Key *public, int hash) 5335243Sjp161948 { 5345243Sjp161948 if (hash && (name = host_hash(name, NULL, 0)) == NULL) 5355243Sjp161948 fatal("hash_host failed"); 5365243Sjp161948 fprintf(f, "%s ", name); 5375243Sjp161948 if (!key_write(public, f)) 5385243Sjp161948 fatal("key_write failed"); 5395243Sjp161948 fprintf(f, "\n"); 5405243Sjp161948 } 5415243Sjp161948 5425243Sjp161948 static void 5435243Sjp161948 do_known_hosts(struct passwd *pw, const char *name) 5445243Sjp161948 { 5455243Sjp161948 FILE *in, *out = stdout; 5465243Sjp161948 Key *public; 5475243Sjp161948 char *cp, *cp2, *kp, *kp2; 5485243Sjp161948 char line[16*1024], tmp[MAXPATHLEN], old[MAXPATHLEN]; 5495243Sjp161948 int c, i, skip = 0, inplace = 0, num = 0, invalid = 0, has_unhashed = 0; 5505243Sjp161948 5515243Sjp161948 if (!have_identity) { 5525243Sjp161948 cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid); 5535243Sjp161948 if (strlcpy(identity_file, cp, sizeof(identity_file)) >= 5545243Sjp161948 sizeof(identity_file)) 5555243Sjp161948 fatal("Specified known hosts path too long"); 5565243Sjp161948 xfree(cp); 5575243Sjp161948 have_identity = 1; 5585243Sjp161948 } 5595243Sjp161948 if ((in = fopen(identity_file, "r")) == NULL) 5605243Sjp161948 fatal("fopen: %s", strerror(errno)); 5615243Sjp161948 5625243Sjp161948 /* 5635243Sjp161948 * Find hosts goes to stdout, hash and deletions happen in-place 5645243Sjp161948 * A corner case is ssh-keygen -HF foo, which should go to stdout 5655243Sjp161948 */ 5665243Sjp161948 if (!find_host && (hash_hosts || delete_host)) { 5675243Sjp161948 if (strlcpy(tmp, identity_file, sizeof(tmp)) >= sizeof(tmp) || 5685243Sjp161948 strlcat(tmp, ".XXXXXXXXXX", sizeof(tmp)) >= sizeof(tmp) || 5695243Sjp161948 strlcpy(old, identity_file, sizeof(old)) >= sizeof(old) || 5705243Sjp161948 strlcat(old, ".old", sizeof(old)) >= sizeof(old)) 5715243Sjp161948 fatal("known_hosts path too long"); 5725243Sjp161948 umask(077); 5735243Sjp161948 if ((c = mkstemp(tmp)) == -1) 5745243Sjp161948 fatal("mkstemp: %s", strerror(errno)); 5755243Sjp161948 if ((out = fdopen(c, "w")) == NULL) { 5765243Sjp161948 c = errno; 5775243Sjp161948 unlink(tmp); 5785243Sjp161948 fatal("fdopen: %s", strerror(c)); 5795243Sjp161948 } 5805243Sjp161948 inplace = 1; 5815243Sjp161948 } 5825243Sjp161948 5835243Sjp161948 while (fgets(line, sizeof(line), in)) { 5845243Sjp161948 num++; 5855243Sjp161948 i = strlen(line) - 1; 5865243Sjp161948 if (line[i] != '\n') { 5875243Sjp161948 error("line %d too long: %.40s...", num, line); 5885243Sjp161948 skip = 1; 5895243Sjp161948 invalid = 1; 5905243Sjp161948 continue; 5915243Sjp161948 } 5925243Sjp161948 if (skip) { 5935243Sjp161948 skip = 0; 5945243Sjp161948 continue; 5955243Sjp161948 } 5965243Sjp161948 line[i] = '\0'; 5975243Sjp161948 5985243Sjp161948 /* Skip leading whitespace, empty and comment lines. */ 5995243Sjp161948 for (cp = line; *cp == ' ' || *cp == '\t'; cp++) 6005243Sjp161948 ; 6015243Sjp161948 if (!*cp || *cp == '\n' || *cp == '#') { 6025243Sjp161948 if (inplace) 6035243Sjp161948 fprintf(out, "%s\n", cp); 6045243Sjp161948 continue; 6055243Sjp161948 } 6065243Sjp161948 /* Find the end of the host name portion. */ 6075243Sjp161948 for (kp = cp; *kp && *kp != ' ' && *kp != '\t'; kp++) 6085243Sjp161948 ; 6095243Sjp161948 if (*kp == '\0' || *(kp + 1) == '\0') { 6105243Sjp161948 error("line %d missing key: %.40s...", 6115243Sjp161948 num, line); 6125243Sjp161948 invalid = 1; 6135243Sjp161948 continue; 6145243Sjp161948 } 6155243Sjp161948 *kp++ = '\0'; 6165243Sjp161948 kp2 = kp; 6175243Sjp161948 6185243Sjp161948 public = key_new(KEY_RSA1); 6195243Sjp161948 if (key_read(public, &kp) != 1) { 6205243Sjp161948 kp = kp2; 6215243Sjp161948 key_free(public); 6225243Sjp161948 public = key_new(KEY_UNSPEC); 6235243Sjp161948 if (key_read(public, &kp) != 1) { 6245243Sjp161948 error("line %d invalid key: %.40s...", 6255243Sjp161948 num, line); 6265243Sjp161948 key_free(public); 6275243Sjp161948 invalid = 1; 6285243Sjp161948 continue; 6295243Sjp161948 } 6305243Sjp161948 } 6315243Sjp161948 6325243Sjp161948 if (*cp == HASH_DELIM) { 6335243Sjp161948 if (find_host || delete_host) { 6345243Sjp161948 cp2 = host_hash(name, cp, strlen(cp)); 6355243Sjp161948 if (cp2 == NULL) { 6365243Sjp161948 error("line %d: invalid hashed " 6375243Sjp161948 "name: %.64s...", num, line); 6385243Sjp161948 invalid = 1; 6395243Sjp161948 continue; 6405243Sjp161948 } 6415243Sjp161948 c = (strcmp(cp2, cp) == 0); 6425243Sjp161948 if (find_host && c) { 6435243Sjp161948 printf(gettext("# Host %s found: " 6445243Sjp161948 "line %d type %s\n"), name, 6455243Sjp161948 num, key_type(public)); 6465243Sjp161948 print_host(out, cp, public, 0); 6475243Sjp161948 } 6485243Sjp161948 if (delete_host && !c) 6495243Sjp161948 print_host(out, cp, public, 0); 6505243Sjp161948 } else if (hash_hosts) 6515243Sjp161948 print_host(out, cp, public, 0); 6525243Sjp161948 } else { 6535243Sjp161948 if (find_host || delete_host) { 6545243Sjp161948 c = (match_hostname(name, cp, 6555243Sjp161948 strlen(cp)) == 1); 6565243Sjp161948 if (find_host && c) { 6575243Sjp161948 printf(gettext("# Host %s found: " 6585243Sjp161948 "line %d type %s\n"), name, 6595243Sjp161948 num, key_type(public)); 6605243Sjp161948 print_host(out, name, public, hash_hosts); 6615243Sjp161948 } 6625243Sjp161948 if (delete_host && !c) 6635243Sjp161948 print_host(out, cp, public, 0); 6645243Sjp161948 } else if (hash_hosts) { 6655243Sjp161948 for (cp2 = strsep(&cp, ","); 6665243Sjp161948 cp2 != NULL && *cp2 != '\0'; 6675243Sjp161948 cp2 = strsep(&cp, ",")) { 6685243Sjp161948 if (strcspn(cp2, "*?!") != strlen(cp2)) 6695243Sjp161948 fprintf(stderr, gettext("Warning: " 6705243Sjp161948 "ignoring host name with " 6715243Sjp161948 "metacharacters: %.64s\n"), 6725243Sjp161948 cp2); 6735243Sjp161948 else 6745243Sjp161948 print_host(out, cp2, public, 1); 6755243Sjp161948 } 6765243Sjp161948 has_unhashed = 1; 6775243Sjp161948 } 6785243Sjp161948 } 6795243Sjp161948 key_free(public); 6805243Sjp161948 } 6815243Sjp161948 fclose(in); 6825243Sjp161948 6835243Sjp161948 if (invalid) { 6845243Sjp161948 fprintf(stderr, gettext("%s is not a valid known_host file.\n"), 6855243Sjp161948 identity_file); 6865243Sjp161948 if (inplace) { 6875243Sjp161948 fprintf(stderr, gettext("Not replacing existing known_hosts " 6885243Sjp161948 "file because of errors\n")); 6895243Sjp161948 fclose(out); 6905243Sjp161948 unlink(tmp); 6915243Sjp161948 } 6925243Sjp161948 exit(1); 6935243Sjp161948 } 6945243Sjp161948 6955243Sjp161948 if (inplace) { 6965243Sjp161948 fclose(out); 6975243Sjp161948 6985243Sjp161948 /* Backup existing file */ 6995243Sjp161948 if (unlink(old) == -1 && errno != ENOENT) 7005243Sjp161948 fatal("unlink %.100s: %s", old, strerror(errno)); 7015243Sjp161948 if (link(identity_file, old) == -1) 7025243Sjp161948 fatal("link %.100s to %.100s: %s", identity_file, old, 7035243Sjp161948 strerror(errno)); 7045243Sjp161948 /* Move new one into place */ 7055243Sjp161948 if (rename(tmp, identity_file) == -1) { 7065243Sjp161948 error("rename\"%s\" to \"%s\": %s", tmp, identity_file, 7075243Sjp161948 strerror(errno)); 7085243Sjp161948 unlink(tmp); 7095243Sjp161948 unlink(old); 7105243Sjp161948 exit(1); 7115243Sjp161948 } 7125243Sjp161948 7135243Sjp161948 fprintf(stderr, gettext("%s updated.\n"), identity_file); 7145243Sjp161948 fprintf(stderr, gettext("Original contents retained as %s\n"), old); 7155243Sjp161948 if (has_unhashed) { 7165243Sjp161948 fprintf(stderr, gettext("WARNING: %s contains unhashed " 7175243Sjp161948 "entries\n"), old); 7185243Sjp161948 fprintf(stderr, gettext("Delete this file to ensure privacy " 7195243Sjp161948 "of hostnames\n")); 7205243Sjp161948 } 7215243Sjp161948 } 7225243Sjp161948 7235243Sjp161948 exit(0); 7245243Sjp161948 } 7255243Sjp161948 7260Sstevel@tonic-gate /* 7270Sstevel@tonic-gate * Perform changing a passphrase. The argument is the passwd structure 7280Sstevel@tonic-gate * for the current user. 7290Sstevel@tonic-gate */ 7300Sstevel@tonic-gate static void 7310Sstevel@tonic-gate do_change_passphrase(struct passwd *pw) 7320Sstevel@tonic-gate { 7330Sstevel@tonic-gate char *comment; 7340Sstevel@tonic-gate char *old_passphrase, *passphrase1, *passphrase2; 7350Sstevel@tonic-gate struct stat st; 7360Sstevel@tonic-gate Key *private; 7370Sstevel@tonic-gate 7380Sstevel@tonic-gate if (!have_identity) 7390Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which the key is")); 7400Sstevel@tonic-gate if (stat(identity_file, &st) < 0) { 7410Sstevel@tonic-gate perror(identity_file); 7420Sstevel@tonic-gate exit(1); 7430Sstevel@tonic-gate } 7440Sstevel@tonic-gate /* Try to load the file with empty passphrase. */ 7450Sstevel@tonic-gate private = key_load_private(identity_file, "", &comment); 7460Sstevel@tonic-gate if (private == NULL) { 7470Sstevel@tonic-gate if (identity_passphrase) 7480Sstevel@tonic-gate old_passphrase = xstrdup(identity_passphrase); 7490Sstevel@tonic-gate else 7500Sstevel@tonic-gate old_passphrase = 7510Sstevel@tonic-gate read_passphrase(gettext("Enter old passphrase: "), 7520Sstevel@tonic-gate RP_ALLOW_STDIN); 7530Sstevel@tonic-gate private = key_load_private(identity_file, old_passphrase, 7540Sstevel@tonic-gate &comment); 7555243Sjp161948 memset(old_passphrase, 0, strlen(old_passphrase)); 7560Sstevel@tonic-gate xfree(old_passphrase); 7570Sstevel@tonic-gate if (private == NULL) { 7585243Sjp161948 printf(gettext("Bad passphrase.\n")); 7590Sstevel@tonic-gate exit(1); 7600Sstevel@tonic-gate } 7610Sstevel@tonic-gate } 7625243Sjp161948 printf(gettext("Key has comment '%s'\n"), comment); 7630Sstevel@tonic-gate 7640Sstevel@tonic-gate /* Ask the new passphrase (twice). */ 7650Sstevel@tonic-gate if (identity_new_passphrase) { 7660Sstevel@tonic-gate passphrase1 = xstrdup(identity_new_passphrase); 7670Sstevel@tonic-gate passphrase2 = NULL; 7680Sstevel@tonic-gate } else { 7690Sstevel@tonic-gate passphrase1 = 7700Sstevel@tonic-gate read_passphrase(gettext("Enter new passphrase (empty" 7710Sstevel@tonic-gate " for no passphrase): "), RP_ALLOW_STDIN); 7720Sstevel@tonic-gate passphrase2 = read_passphrase(gettext("Enter same " 7730Sstevel@tonic-gate "passphrase again: "), RP_ALLOW_STDIN); 7740Sstevel@tonic-gate 7750Sstevel@tonic-gate /* Verify that they are the same. */ 7760Sstevel@tonic-gate if (strcmp(passphrase1, passphrase2) != 0) { 7775243Sjp161948 memset(passphrase1, 0, strlen(passphrase1)); 7785243Sjp161948 memset(passphrase2, 0, strlen(passphrase2)); 7790Sstevel@tonic-gate xfree(passphrase1); 7800Sstevel@tonic-gate xfree(passphrase2); 7815243Sjp161948 printf(gettext("Pass phrases do not match. Try " 7825243Sjp161948 "again.\n")); 7830Sstevel@tonic-gate exit(1); 7840Sstevel@tonic-gate } 7850Sstevel@tonic-gate /* Destroy the other copy. */ 7865243Sjp161948 memset(passphrase2, 0, strlen(passphrase2)); 7870Sstevel@tonic-gate xfree(passphrase2); 7880Sstevel@tonic-gate } 7890Sstevel@tonic-gate 7900Sstevel@tonic-gate /* Save the file using the new passphrase. */ 7910Sstevel@tonic-gate if (!key_save_private(private, identity_file, passphrase1, comment)) { 7925243Sjp161948 printf(gettext("Saving the key failed: %s.\n"), identity_file); 7935243Sjp161948 memset(passphrase1, 0, strlen(passphrase1)); 7940Sstevel@tonic-gate xfree(passphrase1); 7950Sstevel@tonic-gate key_free(private); 7960Sstevel@tonic-gate xfree(comment); 7970Sstevel@tonic-gate exit(1); 7980Sstevel@tonic-gate } 7990Sstevel@tonic-gate /* Destroy the passphrase and the copy of the key in memory. */ 8005243Sjp161948 memset(passphrase1, 0, strlen(passphrase1)); 8010Sstevel@tonic-gate xfree(passphrase1); 8020Sstevel@tonic-gate key_free(private); /* Destroys contents */ 8030Sstevel@tonic-gate xfree(comment); 8040Sstevel@tonic-gate 8055243Sjp161948 printf(gettext("Your identification has been saved with the new " 8065243Sjp161948 "passphrase.\n")); 8070Sstevel@tonic-gate exit(0); 8080Sstevel@tonic-gate } 8090Sstevel@tonic-gate 8100Sstevel@tonic-gate /* 8110Sstevel@tonic-gate * Change the comment of a private key file. 8120Sstevel@tonic-gate */ 8130Sstevel@tonic-gate static void 8140Sstevel@tonic-gate do_change_comment(struct passwd *pw) 8150Sstevel@tonic-gate { 8160Sstevel@tonic-gate char new_comment[1024], *comment, *passphrase; 8170Sstevel@tonic-gate Key *private; 8180Sstevel@tonic-gate Key *public; 8190Sstevel@tonic-gate struct stat st; 8200Sstevel@tonic-gate FILE *f; 8210Sstevel@tonic-gate int fd; 8220Sstevel@tonic-gate 8230Sstevel@tonic-gate if (!have_identity) 8240Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which the key is")); 8250Sstevel@tonic-gate if (stat(identity_file, &st) < 0) { 8260Sstevel@tonic-gate perror(identity_file); 8270Sstevel@tonic-gate exit(1); 8280Sstevel@tonic-gate } 8290Sstevel@tonic-gate private = key_load_private(identity_file, "", &comment); 8300Sstevel@tonic-gate if (private == NULL) { 8310Sstevel@tonic-gate if (identity_passphrase) 8320Sstevel@tonic-gate passphrase = xstrdup(identity_passphrase); 8330Sstevel@tonic-gate else if (identity_new_passphrase) 8340Sstevel@tonic-gate passphrase = xstrdup(identity_new_passphrase); 8350Sstevel@tonic-gate else 8360Sstevel@tonic-gate passphrase = 8370Sstevel@tonic-gate read_passphrase(gettext("Enter passphrase: "), 8385243Sjp161948 RP_ALLOW_STDIN); 8390Sstevel@tonic-gate /* Try to load using the passphrase. */ 8400Sstevel@tonic-gate private = key_load_private(identity_file, passphrase, &comment); 8410Sstevel@tonic-gate if (private == NULL) { 8425243Sjp161948 memset(passphrase, 0, strlen(passphrase)); 8430Sstevel@tonic-gate xfree(passphrase); 8445243Sjp161948 printf(gettext("Bad passphrase.\n")); 8450Sstevel@tonic-gate exit(1); 8460Sstevel@tonic-gate } 8470Sstevel@tonic-gate } else { 8480Sstevel@tonic-gate passphrase = xstrdup(""); 8490Sstevel@tonic-gate } 8500Sstevel@tonic-gate if (private->type != KEY_RSA1) { 8515243Sjp161948 fprintf(stderr, gettext("Comments are only supported for " 8525243Sjp161948 "RSA1 keys.\n")); 8530Sstevel@tonic-gate key_free(private); 8540Sstevel@tonic-gate exit(1); 8550Sstevel@tonic-gate } 8565243Sjp161948 printf(gettext("Key now has comment '%s'\n"), comment); 8570Sstevel@tonic-gate 8580Sstevel@tonic-gate if (identity_comment) { 8595243Sjp161948 strlcpy(new_comment, identity_comment, sizeof(new_comment)); 8600Sstevel@tonic-gate } else { 8615243Sjp161948 printf(gettext("Enter new comment: ")); 8625243Sjp161948 fflush(stdout); 8630Sstevel@tonic-gate if (!fgets(new_comment, sizeof(new_comment), stdin)) { 8645243Sjp161948 memset(passphrase, 0, strlen(passphrase)); 8650Sstevel@tonic-gate key_free(private); 8660Sstevel@tonic-gate exit(1); 8670Sstevel@tonic-gate } 8680Sstevel@tonic-gate if (strchr(new_comment, '\n')) 8690Sstevel@tonic-gate *strchr(new_comment, '\n') = 0; 8700Sstevel@tonic-gate } 8710Sstevel@tonic-gate 8720Sstevel@tonic-gate /* Save the file using the new passphrase. */ 8730Sstevel@tonic-gate if (!key_save_private(private, identity_file, passphrase, new_comment)) { 8745243Sjp161948 printf(gettext("Saving the key failed: %s.\n"), identity_file); 8755243Sjp161948 memset(passphrase, 0, strlen(passphrase)); 8760Sstevel@tonic-gate xfree(passphrase); 8770Sstevel@tonic-gate key_free(private); 8780Sstevel@tonic-gate xfree(comment); 8790Sstevel@tonic-gate exit(1); 8800Sstevel@tonic-gate } 8815243Sjp161948 memset(passphrase, 0, strlen(passphrase)); 8820Sstevel@tonic-gate xfree(passphrase); 8830Sstevel@tonic-gate public = key_from_private(private); 8840Sstevel@tonic-gate key_free(private); 8850Sstevel@tonic-gate 8865243Sjp161948 strlcat(identity_file, ".pub", sizeof(identity_file)); 8870Sstevel@tonic-gate fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); 8880Sstevel@tonic-gate if (fd == -1) { 8895243Sjp161948 printf(gettext("Could not save your public key in %s\n"), 8905243Sjp161948 identity_file); 8910Sstevel@tonic-gate exit(1); 8920Sstevel@tonic-gate } 8930Sstevel@tonic-gate f = fdopen(fd, "w"); 8940Sstevel@tonic-gate if (f == NULL) { 8955243Sjp161948 printf(gettext("fdopen %s failed"), identity_file); 8960Sstevel@tonic-gate exit(1); 8970Sstevel@tonic-gate } 8980Sstevel@tonic-gate if (!key_write(public, f)) 8995243Sjp161948 fprintf(stderr, gettext("write key failed")); 9000Sstevel@tonic-gate key_free(public); 9015243Sjp161948 fprintf(f, " %s\n", new_comment); 9025243Sjp161948 fclose(f); 9030Sstevel@tonic-gate 9040Sstevel@tonic-gate xfree(comment); 9050Sstevel@tonic-gate 9065243Sjp161948 printf(gettext("The comment in your key file has been changed.\n")); 9070Sstevel@tonic-gate exit(0); 9080Sstevel@tonic-gate } 9090Sstevel@tonic-gate 9100Sstevel@tonic-gate static void 9110Sstevel@tonic-gate usage(void) 9120Sstevel@tonic-gate { 9135243Sjp161948 fprintf(stderr, gettext( 9140Sstevel@tonic-gate "Usage: %s [options]\n" 9150Sstevel@tonic-gate "Options:\n" 9160Sstevel@tonic-gate " -b bits Number of bits in the key to create.\n" 9175243Sjp161948 " -B Show bubblebabble digest of key file.\n" 9180Sstevel@tonic-gate " -c Change comment in private and public key files.\n" 9195243Sjp161948 " -C comment Provide new comment.\n" 9200Sstevel@tonic-gate " -e Convert OpenSSH to IETF SECSH key file.\n" 9210Sstevel@tonic-gate " -f filename Filename of the key file.\n" 9225243Sjp161948 " -F hostname Find hostname in known hosts file.\n" 9235243Sjp161948 " -H Hash names in known_hosts file.\n" 9240Sstevel@tonic-gate " -i Convert IETF SECSH to OpenSSH key file.\n" 9250Sstevel@tonic-gate " -l Show fingerprint of key file.\n" 9265243Sjp161948 " -N phrase Provide new passphrase.\n" 9270Sstevel@tonic-gate " -p Change passphrase of private key file.\n" 9285243Sjp161948 " -P phrase Provide old passphrase.\n" 9290Sstevel@tonic-gate " -q Quiet.\n" 9305243Sjp161948 " -R hostname Remove host from known_hosts file.\n" 9310Sstevel@tonic-gate " -t type Specify type of key to create.\n" 9325243Sjp161948 " -y Read private key file and print public key.\n" 9330Sstevel@tonic-gate ), __progname); 9340Sstevel@tonic-gate 9350Sstevel@tonic-gate exit(1); 9360Sstevel@tonic-gate } 9370Sstevel@tonic-gate 9380Sstevel@tonic-gate /* 9390Sstevel@tonic-gate * Main program for key management. 9400Sstevel@tonic-gate */ 9410Sstevel@tonic-gate int 9425243Sjp161948 main(int argc, char **argv) 9430Sstevel@tonic-gate { 9440Sstevel@tonic-gate char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2; 9455243Sjp161948 char *rr_hostname = NULL; 9460Sstevel@tonic-gate Key *private, *public; 9470Sstevel@tonic-gate struct passwd *pw; 9480Sstevel@tonic-gate struct stat st; 9490Sstevel@tonic-gate int opt, type, fd; 9500Sstevel@tonic-gate FILE *f; 9510Sstevel@tonic-gate 9520Sstevel@tonic-gate extern int optind; 9530Sstevel@tonic-gate extern char *optarg; 9540Sstevel@tonic-gate 9555243Sjp161948 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 9565243Sjp161948 sanitise_stdfd(); 9570Sstevel@tonic-gate 9585243Sjp161948 __progname = get_progname(argv[0]); 9595243Sjp161948 9605243Sjp161948 g11n_setlocale(LC_ALL, ""); 9610Sstevel@tonic-gate 9620Sstevel@tonic-gate SSLeay_add_all_algorithms(); 9630Sstevel@tonic-gate init_rng(); 9640Sstevel@tonic-gate seed_rng(); 9650Sstevel@tonic-gate 9660Sstevel@tonic-gate /* we need this for the home * directory. */ 9670Sstevel@tonic-gate pw = getpwuid(getuid()); 9680Sstevel@tonic-gate if (!pw) { 9695243Sjp161948 printf(gettext("You don't exist, go away!\n")); 9700Sstevel@tonic-gate exit(1); 9710Sstevel@tonic-gate } 9720Sstevel@tonic-gate if (gethostname(hostname, sizeof(hostname)) < 0) { 9730Sstevel@tonic-gate perror("gethostname"); 9740Sstevel@tonic-gate exit(1); 9750Sstevel@tonic-gate } 9760Sstevel@tonic-gate 9775243Sjp161948 #define GETOPT_ARGS "BcdeHilpqxXyb:C:f:F:N:P:R:t:" 9787947SHuie-Ying.Lee@Sun.COM 9795243Sjp161948 while ((opt = getopt(argc, argv, GETOPT_ARGS)) != -1) { 9800Sstevel@tonic-gate switch (opt) { 9810Sstevel@tonic-gate case 'b': 9820Sstevel@tonic-gate bits = atoi(optarg); 9830Sstevel@tonic-gate if (bits < 512 || bits > 32768) { 9845243Sjp161948 printf(gettext("Bits has bad value.\n")); 9850Sstevel@tonic-gate exit(1); 9860Sstevel@tonic-gate } 9870Sstevel@tonic-gate break; 9885243Sjp161948 case 'F': 9895243Sjp161948 find_host = 1; 9905243Sjp161948 rr_hostname = optarg; 9915243Sjp161948 break; 9925243Sjp161948 case 'H': 9935243Sjp161948 hash_hosts = 1; 9945243Sjp161948 break; 9955243Sjp161948 case 'R': 9965243Sjp161948 delete_host = 1; 9975243Sjp161948 rr_hostname = optarg; 9985243Sjp161948 break; 9990Sstevel@tonic-gate case 'l': 10000Sstevel@tonic-gate print_fingerprint = 1; 10010Sstevel@tonic-gate break; 10020Sstevel@tonic-gate case 'B': 10030Sstevel@tonic-gate print_bubblebabble = 1; 10040Sstevel@tonic-gate break; 10050Sstevel@tonic-gate case 'p': 10060Sstevel@tonic-gate change_passphrase = 1; 10070Sstevel@tonic-gate break; 10080Sstevel@tonic-gate case 'c': 10090Sstevel@tonic-gate change_comment = 1; 10100Sstevel@tonic-gate break; 10110Sstevel@tonic-gate case 'f': 10125243Sjp161948 strlcpy(identity_file, optarg, sizeof(identity_file)); 10130Sstevel@tonic-gate have_identity = 1; 10140Sstevel@tonic-gate break; 10150Sstevel@tonic-gate case 'P': 10160Sstevel@tonic-gate identity_passphrase = optarg; 10170Sstevel@tonic-gate break; 10180Sstevel@tonic-gate case 'N': 10190Sstevel@tonic-gate identity_new_passphrase = optarg; 10200Sstevel@tonic-gate break; 10210Sstevel@tonic-gate case 'C': 10220Sstevel@tonic-gate identity_comment = optarg; 10230Sstevel@tonic-gate break; 10240Sstevel@tonic-gate case 'q': 10250Sstevel@tonic-gate quiet = 1; 10260Sstevel@tonic-gate break; 10270Sstevel@tonic-gate case 'e': 10280Sstevel@tonic-gate case 'x': 10290Sstevel@tonic-gate /* export key */ 10300Sstevel@tonic-gate convert_to_ssh2 = 1; 10310Sstevel@tonic-gate break; 10320Sstevel@tonic-gate case 'i': 10330Sstevel@tonic-gate case 'X': 10340Sstevel@tonic-gate /* import key */ 10350Sstevel@tonic-gate convert_from_ssh2 = 1; 10360Sstevel@tonic-gate break; 10370Sstevel@tonic-gate case 'y': 10380Sstevel@tonic-gate print_public = 1; 10390Sstevel@tonic-gate break; 10400Sstevel@tonic-gate case 'd': 10410Sstevel@tonic-gate key_type_name = "dsa"; 10420Sstevel@tonic-gate break; 10430Sstevel@tonic-gate case 't': 10440Sstevel@tonic-gate key_type_name = optarg; 10450Sstevel@tonic-gate break; 10460Sstevel@tonic-gate case '?': 10470Sstevel@tonic-gate default: 10480Sstevel@tonic-gate usage(); 10490Sstevel@tonic-gate } 10500Sstevel@tonic-gate } 10515243Sjp161948 if (optind < argc) { 10525243Sjp161948 printf(gettext("Too many arguments.\n")); 10530Sstevel@tonic-gate usage(); 10540Sstevel@tonic-gate } 10550Sstevel@tonic-gate if (change_passphrase && change_comment) { 10565243Sjp161948 printf(gettext("Can only have one of -p and -c.\n")); 10570Sstevel@tonic-gate usage(); 10580Sstevel@tonic-gate } 10595243Sjp161948 if (delete_host || hash_hosts || find_host) 10605243Sjp161948 do_known_hosts(pw, rr_hostname); 10610Sstevel@tonic-gate if (print_fingerprint || print_bubblebabble) 10620Sstevel@tonic-gate do_fingerprint(pw); 10630Sstevel@tonic-gate if (change_passphrase) 10640Sstevel@tonic-gate do_change_passphrase(pw); 10650Sstevel@tonic-gate if (change_comment) 10660Sstevel@tonic-gate do_change_comment(pw); 10670Sstevel@tonic-gate if (convert_to_ssh2) 10680Sstevel@tonic-gate do_convert_to_ssh2(pw); 10690Sstevel@tonic-gate if (convert_from_ssh2) 10700Sstevel@tonic-gate do_convert_from_ssh2(pw); 10710Sstevel@tonic-gate if (print_public) 10720Sstevel@tonic-gate do_print_public(pw); 10730Sstevel@tonic-gate 10740Sstevel@tonic-gate arc4random_stir(); 10750Sstevel@tonic-gate 10760Sstevel@tonic-gate if (key_type_name == NULL) { 10775243Sjp161948 printf(gettext("You must specify a key type (-t).\n")); 10780Sstevel@tonic-gate usage(); 10790Sstevel@tonic-gate } 10800Sstevel@tonic-gate type = key_type_from_name(key_type_name); 10810Sstevel@tonic-gate if (type == KEY_UNSPEC) { 10825243Sjp161948 fprintf(stderr, gettext("unknown key type %s\n"), 10835243Sjp161948 key_type_name); 10840Sstevel@tonic-gate exit(1); 10850Sstevel@tonic-gate } 1086*12032SHai-May.Chao@Sun.COM if (bits == 0) 1087*12032SHai-May.Chao@Sun.COM bits = (type == KEY_DSA) ? DEFAULT_BITS_DSA : DEFAULT_BITS_RSA; 1088*12032SHai-May.Chao@Sun.COM 10890Sstevel@tonic-gate if (!quiet) 10905243Sjp161948 printf(gettext("Generating public/private %s key pair.\n"), 10915243Sjp161948 key_type_name); 10920Sstevel@tonic-gate private = key_generate(type, bits); 10930Sstevel@tonic-gate if (private == NULL) { 10945243Sjp161948 fprintf(stderr, gettext("key_generate failed")); 10950Sstevel@tonic-gate exit(1); 10960Sstevel@tonic-gate } 10970Sstevel@tonic-gate public = key_from_private(private); 10980Sstevel@tonic-gate 10990Sstevel@tonic-gate if (!have_identity) 11000Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which to save the key")); 11010Sstevel@tonic-gate 11025243Sjp161948 /* Create ~/.ssh directory if it doesn't already exist. */ 11035243Sjp161948 snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, _PATH_SSH_USER_DIR); 11040Sstevel@tonic-gate if (strstr(identity_file, dotsshdir) != NULL && 11050Sstevel@tonic-gate stat(dotsshdir, &st) < 0) { 11060Sstevel@tonic-gate if (mkdir(dotsshdir, 0700) < 0) 11070Sstevel@tonic-gate error("Could not create directory '%s'.", dotsshdir); 11080Sstevel@tonic-gate else if (!quiet) 11095243Sjp161948 printf(gettext("Created directory '%s'.\n"), dotsshdir); 11100Sstevel@tonic-gate } 11110Sstevel@tonic-gate /* If the file already exists, ask the user to confirm. */ 11120Sstevel@tonic-gate if (stat(identity_file, &st) >= 0) { 11130Sstevel@tonic-gate char yesno[128]; 11145243Sjp161948 printf(gettext("%s already exists.\n"), identity_file); 11155243Sjp161948 printf(gettext("Overwrite (%s/%s)? "), 11165243Sjp161948 nl_langinfo(YESSTR), nl_langinfo(NOSTR)); 11175243Sjp161948 fflush(stdout); 11180Sstevel@tonic-gate if (fgets(yesno, sizeof(yesno), stdin) == NULL) 11190Sstevel@tonic-gate exit(1); 11202757Sjp161948 if (strcasecmp(chop(yesno), nl_langinfo(YESSTR)) != 0) 11210Sstevel@tonic-gate exit(1); 11220Sstevel@tonic-gate } 11230Sstevel@tonic-gate /* Ask for a passphrase (twice). */ 11240Sstevel@tonic-gate if (identity_passphrase) 11250Sstevel@tonic-gate passphrase1 = xstrdup(identity_passphrase); 11260Sstevel@tonic-gate else if (identity_new_passphrase) 11270Sstevel@tonic-gate passphrase1 = xstrdup(identity_new_passphrase); 11280Sstevel@tonic-gate else { 11290Sstevel@tonic-gate passphrase_again: 11300Sstevel@tonic-gate passphrase1 = 11310Sstevel@tonic-gate read_passphrase(gettext("Enter passphrase (empty " 11320Sstevel@tonic-gate "for no passphrase): "), RP_ALLOW_STDIN); 11330Sstevel@tonic-gate passphrase2 = read_passphrase(gettext("Enter same " 11340Sstevel@tonic-gate "passphrase again: "), RP_ALLOW_STDIN); 11350Sstevel@tonic-gate if (strcmp(passphrase1, passphrase2) != 0) { 11360Sstevel@tonic-gate /* 11370Sstevel@tonic-gate * The passphrases do not match. Clear them and 11380Sstevel@tonic-gate * retry. 11390Sstevel@tonic-gate */ 11405243Sjp161948 memset(passphrase1, 0, strlen(passphrase1)); 11415243Sjp161948 memset(passphrase2, 0, strlen(passphrase2)); 11420Sstevel@tonic-gate xfree(passphrase1); 11430Sstevel@tonic-gate xfree(passphrase2); 11445243Sjp161948 printf(gettext("Passphrases do not match. Try " 11455243Sjp161948 "again.\n")); 11460Sstevel@tonic-gate goto passphrase_again; 11470Sstevel@tonic-gate } 11480Sstevel@tonic-gate /* Clear the other copy of the passphrase. */ 11495243Sjp161948 memset(passphrase2, 0, strlen(passphrase2)); 11500Sstevel@tonic-gate xfree(passphrase2); 11510Sstevel@tonic-gate } 11520Sstevel@tonic-gate 11530Sstevel@tonic-gate if (identity_comment) { 11545243Sjp161948 strlcpy(comment, identity_comment, sizeof(comment)); 11550Sstevel@tonic-gate } else { 11560Sstevel@tonic-gate /* Create default commend field for the passphrase. */ 11575243Sjp161948 snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); 11580Sstevel@tonic-gate } 11590Sstevel@tonic-gate 11600Sstevel@tonic-gate /* Save the key with the given passphrase and comment. */ 11610Sstevel@tonic-gate if (!key_save_private(private, identity_file, passphrase1, comment)) { 11625243Sjp161948 printf(gettext("Saving the key failed: %s.\n"), identity_file); 11635243Sjp161948 memset(passphrase1, 0, strlen(passphrase1)); 11640Sstevel@tonic-gate xfree(passphrase1); 11650Sstevel@tonic-gate exit(1); 11660Sstevel@tonic-gate } 11670Sstevel@tonic-gate /* Clear the passphrase. */ 11685243Sjp161948 memset(passphrase1, 0, strlen(passphrase1)); 11690Sstevel@tonic-gate xfree(passphrase1); 11700Sstevel@tonic-gate 11710Sstevel@tonic-gate /* Clear the private key and the random number generator. */ 11720Sstevel@tonic-gate key_free(private); 11730Sstevel@tonic-gate arc4random_stir(); 11740Sstevel@tonic-gate 11750Sstevel@tonic-gate if (!quiet) 11765243Sjp161948 printf(gettext("Your identification has been saved in %s.\n"), 11775243Sjp161948 identity_file); 11780Sstevel@tonic-gate 11795243Sjp161948 strlcat(identity_file, ".pub", sizeof(identity_file)); 11800Sstevel@tonic-gate fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); 11810Sstevel@tonic-gate if (fd == -1) { 11825243Sjp161948 printf(gettext("Could not save your public key in %s\n"), 11835243Sjp161948 identity_file); 11840Sstevel@tonic-gate exit(1); 11850Sstevel@tonic-gate } 11860Sstevel@tonic-gate f = fdopen(fd, "w"); 11870Sstevel@tonic-gate if (f == NULL) { 11885243Sjp161948 printf(gettext("fdopen %s failed"), identity_file); 11890Sstevel@tonic-gate exit(1); 11900Sstevel@tonic-gate } 11910Sstevel@tonic-gate if (!key_write(public, f)) 11925243Sjp161948 fprintf(stderr, gettext("write key failed")); 11935243Sjp161948 fprintf(f, " %s\n", comment); 11945243Sjp161948 fclose(f); 11950Sstevel@tonic-gate 11960Sstevel@tonic-gate if (!quiet) { 11970Sstevel@tonic-gate char *fp = key_fingerprint(public, SSH_FP_MD5, SSH_FP_HEX); 11985243Sjp161948 printf(gettext("Your public key has been saved in %s.\n"), 11990Sstevel@tonic-gate identity_file); 12005243Sjp161948 printf(gettext("The key fingerprint is:\n")); 12015243Sjp161948 printf("%s %s\n", fp, comment); 12020Sstevel@tonic-gate xfree(fp); 12030Sstevel@tonic-gate } 12040Sstevel@tonic-gate 12050Sstevel@tonic-gate key_free(public); 12060Sstevel@tonic-gate return(0); 12070Sstevel@tonic-gate /* NOTREACHED */ 12080Sstevel@tonic-gate } 1209