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. */ 375243Sjp161948 u_int32_t bits = 1024; 380Sstevel@tonic-gate 390Sstevel@tonic-gate /* 400Sstevel@tonic-gate * Flag indicating that we just want to change the passphrase. This can be 410Sstevel@tonic-gate * set on the command line. 420Sstevel@tonic-gate */ 430Sstevel@tonic-gate int change_passphrase = 0; 440Sstevel@tonic-gate 450Sstevel@tonic-gate /* 460Sstevel@tonic-gate * Flag indicating that we just want to change the comment. This can be set 470Sstevel@tonic-gate * on the command line. 480Sstevel@tonic-gate */ 490Sstevel@tonic-gate int change_comment = 0; 500Sstevel@tonic-gate 510Sstevel@tonic-gate int quiet = 0; 520Sstevel@tonic-gate 535243Sjp161948 /* Flag indicating that we want to hash a known_hosts file */ 545243Sjp161948 int hash_hosts = 0; 555243Sjp161948 /* Flag indicating that we want to lookup a host in known_hosts file */ 565243Sjp161948 int find_host = 0; 575243Sjp161948 /* Flag indicating that we want to delete a host from a known_hosts file */ 585243Sjp161948 int delete_host = 0; 595243Sjp161948 600Sstevel@tonic-gate /* Flag indicating that we just want to see the key fingerprint */ 610Sstevel@tonic-gate int print_fingerprint = 0; 620Sstevel@tonic-gate int print_bubblebabble = 0; 630Sstevel@tonic-gate 640Sstevel@tonic-gate /* The identity file name, given on the command line or entered by the user. */ 650Sstevel@tonic-gate char identity_file[1024]; 660Sstevel@tonic-gate int have_identity = 0; 670Sstevel@tonic-gate 680Sstevel@tonic-gate /* This is set to the passphrase if given on the command line. */ 690Sstevel@tonic-gate char *identity_passphrase = NULL; 700Sstevel@tonic-gate 710Sstevel@tonic-gate /* This is set to the new passphrase if given on the command line. */ 720Sstevel@tonic-gate char *identity_new_passphrase = NULL; 730Sstevel@tonic-gate 740Sstevel@tonic-gate /* This is set to the new comment if given on the command line. */ 750Sstevel@tonic-gate char *identity_comment = NULL; 760Sstevel@tonic-gate 770Sstevel@tonic-gate /* Dump public key file in format used by real and the original SSH 2 */ 780Sstevel@tonic-gate int convert_to_ssh2 = 0; 790Sstevel@tonic-gate int convert_from_ssh2 = 0; 800Sstevel@tonic-gate int print_public = 0; 810Sstevel@tonic-gate 820Sstevel@tonic-gate char *key_type_name = NULL; 830Sstevel@tonic-gate 840Sstevel@tonic-gate /* argv0 */ 850Sstevel@tonic-gate #ifdef HAVE___PROGNAME 860Sstevel@tonic-gate extern char *__progname; 870Sstevel@tonic-gate #else 880Sstevel@tonic-gate char *__progname; 890Sstevel@tonic-gate #endif 900Sstevel@tonic-gate 910Sstevel@tonic-gate char hostname[MAXHOSTNAMELEN]; 920Sstevel@tonic-gate 930Sstevel@tonic-gate static void 940Sstevel@tonic-gate ask_filename(struct passwd *pw, const char *prompt) 950Sstevel@tonic-gate { 960Sstevel@tonic-gate char buf[1024]; 970Sstevel@tonic-gate char *name = NULL; 980Sstevel@tonic-gate 990Sstevel@tonic-gate if (key_type_name == NULL) 1000Sstevel@tonic-gate name = _PATH_SSH_CLIENT_ID_RSA; 1015243Sjp161948 else { 1020Sstevel@tonic-gate switch (key_type_from_name(key_type_name)) { 1030Sstevel@tonic-gate case KEY_RSA1: 1040Sstevel@tonic-gate name = _PATH_SSH_CLIENT_IDENTITY; 1050Sstevel@tonic-gate break; 1060Sstevel@tonic-gate case KEY_DSA: 1070Sstevel@tonic-gate name = _PATH_SSH_CLIENT_ID_DSA; 1080Sstevel@tonic-gate break; 1090Sstevel@tonic-gate case KEY_RSA: 1100Sstevel@tonic-gate name = _PATH_SSH_CLIENT_ID_RSA; 1110Sstevel@tonic-gate break; 1120Sstevel@tonic-gate default: 1135243Sjp161948 fprintf(stderr, gettext("bad key type")); 1140Sstevel@tonic-gate exit(1); 1150Sstevel@tonic-gate break; 1160Sstevel@tonic-gate } 1175243Sjp161948 } 1185243Sjp161948 snprintf(identity_file, sizeof(identity_file), "%s/%s", pw->pw_dir, name); 1195243Sjp161948 fprintf(stderr, "%s (%s): ", gettext(prompt), identity_file); 1200Sstevel@tonic-gate if (fgets(buf, sizeof(buf), stdin) == NULL) 1210Sstevel@tonic-gate exit(1); 1220Sstevel@tonic-gate if (strchr(buf, '\n')) 1230Sstevel@tonic-gate *strchr(buf, '\n') = 0; 1240Sstevel@tonic-gate if (strcmp(buf, "") != 0) 1255243Sjp161948 strlcpy(identity_file, buf, sizeof(identity_file)); 1260Sstevel@tonic-gate have_identity = 1; 1270Sstevel@tonic-gate } 1280Sstevel@tonic-gate 1290Sstevel@tonic-gate static Key * 1300Sstevel@tonic-gate load_identity(char *filename) 1310Sstevel@tonic-gate { 1320Sstevel@tonic-gate char *pass; 1330Sstevel@tonic-gate Key *prv; 1340Sstevel@tonic-gate 1350Sstevel@tonic-gate prv = key_load_private(filename, "", NULL); 1360Sstevel@tonic-gate if (prv == NULL) { 1370Sstevel@tonic-gate if (identity_passphrase) 1380Sstevel@tonic-gate pass = xstrdup(identity_passphrase); 1390Sstevel@tonic-gate else 1400Sstevel@tonic-gate pass = read_passphrase(gettext("Enter passphrase: "), 1410Sstevel@tonic-gate RP_ALLOW_STDIN); 1420Sstevel@tonic-gate prv = key_load_private(filename, pass, NULL); 1435243Sjp161948 memset(pass, 0, strlen(pass)); 1440Sstevel@tonic-gate xfree(pass); 1450Sstevel@tonic-gate } 1460Sstevel@tonic-gate return prv; 1470Sstevel@tonic-gate } 1480Sstevel@tonic-gate 1490Sstevel@tonic-gate #define SSH_COM_PUBLIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----" 1500Sstevel@tonic-gate #define SSH_COM_PUBLIC_END "---- END SSH2 PUBLIC KEY ----" 1510Sstevel@tonic-gate #define SSH_COM_PRIVATE_BEGIN "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----" 1520Sstevel@tonic-gate #define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb 1530Sstevel@tonic-gate 1540Sstevel@tonic-gate static void 1550Sstevel@tonic-gate do_convert_to_ssh2(struct passwd *pw) 1560Sstevel@tonic-gate { 1570Sstevel@tonic-gate Key *k; 1580Sstevel@tonic-gate u_int len; 1590Sstevel@tonic-gate u_char *blob; 1600Sstevel@tonic-gate struct stat st; 1610Sstevel@tonic-gate 1620Sstevel@tonic-gate if (!have_identity) 1630Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which the key is")); 1640Sstevel@tonic-gate if (stat(identity_file, &st) < 0) { 1650Sstevel@tonic-gate perror(identity_file); 1660Sstevel@tonic-gate exit(1); 1670Sstevel@tonic-gate } 1680Sstevel@tonic-gate if ((k = key_load_public(identity_file, NULL)) == NULL) { 1690Sstevel@tonic-gate if ((k = load_identity(identity_file)) == NULL) { 1705243Sjp161948 fprintf(stderr, gettext("load failed\n")); 1710Sstevel@tonic-gate exit(1); 1720Sstevel@tonic-gate } 1730Sstevel@tonic-gate } 1740Sstevel@tonic-gate if (key_to_blob(k, &blob, &len) <= 0) { 1755243Sjp161948 fprintf(stderr, gettext("key_to_blob failed\n")); 1760Sstevel@tonic-gate exit(1); 1770Sstevel@tonic-gate } 1785243Sjp161948 fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN); 1795243Sjp161948 fprintf(stdout, gettext( 1800Sstevel@tonic-gate "Comment: \"%u-bit %s, converted from OpenSSH by %s@%s\"\n"), 1810Sstevel@tonic-gate key_size(k), key_type(k), 1820Sstevel@tonic-gate pw->pw_name, hostname); 1830Sstevel@tonic-gate dump_base64(stdout, blob, len); 1845243Sjp161948 fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END); 1850Sstevel@tonic-gate key_free(k); 1860Sstevel@tonic-gate xfree(blob); 1870Sstevel@tonic-gate exit(0); 1880Sstevel@tonic-gate } 1890Sstevel@tonic-gate 1900Sstevel@tonic-gate static void 1910Sstevel@tonic-gate buffer_get_bignum_bits(Buffer *b, BIGNUM *value) 1920Sstevel@tonic-gate { 1935243Sjp161948 u_int bignum_bits = buffer_get_int(b); 1945243Sjp161948 u_int bytes = (bignum_bits + 7) / 8; 1950Sstevel@tonic-gate 1960Sstevel@tonic-gate if (buffer_len(b) < bytes) 1970Sstevel@tonic-gate fatal("buffer_get_bignum_bits: input buffer too small: " 1980Sstevel@tonic-gate "need %d have %d", bytes, buffer_len(b)); 1995243Sjp161948 if (BN_bin2bn(buffer_ptr(b), bytes, value) == NULL) 2005243Sjp161948 fatal("buffer_get_bignum_bits: BN_bin2bn failed"); 2010Sstevel@tonic-gate buffer_consume(b, bytes); 2020Sstevel@tonic-gate } 2030Sstevel@tonic-gate 2040Sstevel@tonic-gate static Key * 2050Sstevel@tonic-gate do_convert_private_ssh2_from_blob(u_char *blob, u_int blen) 2060Sstevel@tonic-gate { 2070Sstevel@tonic-gate Buffer b; 2080Sstevel@tonic-gate Key *key = NULL; 2090Sstevel@tonic-gate char *type, *cipher; 2100Sstevel@tonic-gate u_char *sig, data[] = "abcde12345"; 2110Sstevel@tonic-gate int magic, rlen, ktype, i1, i2, i3, i4; 2120Sstevel@tonic-gate u_int slen; 2130Sstevel@tonic-gate u_long e; 2140Sstevel@tonic-gate 2150Sstevel@tonic-gate buffer_init(&b); 2160Sstevel@tonic-gate buffer_append(&b, blob, blen); 2170Sstevel@tonic-gate 2180Sstevel@tonic-gate magic = buffer_get_int(&b); 2190Sstevel@tonic-gate if (magic != SSH_COM_PRIVATE_KEY_MAGIC) { 2200Sstevel@tonic-gate error("bad magic 0x%x != 0x%x", magic, SSH_COM_PRIVATE_KEY_MAGIC); 2210Sstevel@tonic-gate buffer_free(&b); 2220Sstevel@tonic-gate return NULL; 2230Sstevel@tonic-gate } 2240Sstevel@tonic-gate i1 = buffer_get_int(&b); 2250Sstevel@tonic-gate type = buffer_get_string(&b, NULL); 2260Sstevel@tonic-gate cipher = buffer_get_string(&b, NULL); 2270Sstevel@tonic-gate i2 = buffer_get_int(&b); 2280Sstevel@tonic-gate i3 = buffer_get_int(&b); 2290Sstevel@tonic-gate i4 = buffer_get_int(&b); 2305243Sjp161948 debug("ignore (%d %d %d %d)", i1, i2, i3, i4); 2310Sstevel@tonic-gate if (strcmp(cipher, "none") != 0) { 2320Sstevel@tonic-gate error("unsupported cipher %s", cipher); 2330Sstevel@tonic-gate xfree(cipher); 2340Sstevel@tonic-gate buffer_free(&b); 2350Sstevel@tonic-gate xfree(type); 2360Sstevel@tonic-gate return NULL; 2370Sstevel@tonic-gate } 2380Sstevel@tonic-gate xfree(cipher); 2390Sstevel@tonic-gate 2400Sstevel@tonic-gate if (strstr(type, "dsa")) { 2410Sstevel@tonic-gate ktype = KEY_DSA; 2420Sstevel@tonic-gate } else if (strstr(type, "rsa")) { 2430Sstevel@tonic-gate ktype = KEY_RSA; 2440Sstevel@tonic-gate } else { 2455243Sjp161948 buffer_free(&b); 2460Sstevel@tonic-gate xfree(type); 2470Sstevel@tonic-gate return NULL; 2480Sstevel@tonic-gate } 2490Sstevel@tonic-gate key = key_new_private(ktype); 2500Sstevel@tonic-gate xfree(type); 2510Sstevel@tonic-gate 2520Sstevel@tonic-gate switch (key->type) { 2530Sstevel@tonic-gate case KEY_DSA: 2540Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->dsa->p); 2550Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->dsa->g); 2560Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->dsa->q); 2570Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->dsa->pub_key); 2580Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->dsa->priv_key); 2590Sstevel@tonic-gate break; 2600Sstevel@tonic-gate case KEY_RSA: 2610Sstevel@tonic-gate e = buffer_get_char(&b); 2620Sstevel@tonic-gate debug("e %lx", e); 2630Sstevel@tonic-gate if (e < 30) { 2640Sstevel@tonic-gate e <<= 8; 2650Sstevel@tonic-gate e += buffer_get_char(&b); 2660Sstevel@tonic-gate debug("e %lx", e); 2670Sstevel@tonic-gate e <<= 8; 2680Sstevel@tonic-gate e += buffer_get_char(&b); 2690Sstevel@tonic-gate debug("e %lx", e); 2700Sstevel@tonic-gate } 2710Sstevel@tonic-gate if (!BN_set_word(key->rsa->e, e)) { 2720Sstevel@tonic-gate buffer_free(&b); 2730Sstevel@tonic-gate key_free(key); 2740Sstevel@tonic-gate return NULL; 2750Sstevel@tonic-gate } 2760Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->rsa->d); 2770Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->rsa->n); 2780Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->rsa->iqmp); 2790Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->rsa->q); 2800Sstevel@tonic-gate buffer_get_bignum_bits(&b, key->rsa->p); 2810Sstevel@tonic-gate rsa_generate_additional_parameters(key->rsa); 2820Sstevel@tonic-gate break; 2830Sstevel@tonic-gate } 2840Sstevel@tonic-gate rlen = buffer_len(&b); 2850Sstevel@tonic-gate if (rlen != 0) 2860Sstevel@tonic-gate error("do_convert_private_ssh2_from_blob: " 2870Sstevel@tonic-gate "remaining bytes in key blob %d", rlen); 2880Sstevel@tonic-gate buffer_free(&b); 2890Sstevel@tonic-gate 2900Sstevel@tonic-gate /* try the key */ 2915243Sjp161948 key_sign(key, &sig, &slen, data, sizeof(data)); 2920Sstevel@tonic-gate key_verify(key, sig, slen, data, sizeof(data)); 2930Sstevel@tonic-gate xfree(sig); 2940Sstevel@tonic-gate return key; 2950Sstevel@tonic-gate } 2960Sstevel@tonic-gate 2975243Sjp161948 static int 2985243Sjp161948 get_line(FILE *fp, char *line, size_t len) 2995243Sjp161948 { 3005243Sjp161948 int c; 3015243Sjp161948 size_t pos = 0; 3025243Sjp161948 3035243Sjp161948 line[0] = '\0'; 3045243Sjp161948 while ((c = fgetc(fp)) != EOF) { 3055243Sjp161948 if (pos >= len - 1) { 3065243Sjp161948 fprintf(stderr, "input line too long.\n"); 3075243Sjp161948 exit(1); 3085243Sjp161948 } 3095243Sjp161948 switch (c) { 3105243Sjp161948 case '\r': 3115243Sjp161948 c = fgetc(fp); 3125243Sjp161948 if (c != EOF && c != '\n' && ungetc(c, fp) == EOF) { 3135243Sjp161948 fprintf(stderr, "unget: %s\n", strerror(errno)); 3145243Sjp161948 exit(1); 3155243Sjp161948 } 3165243Sjp161948 return pos; 3175243Sjp161948 case '\n': 3185243Sjp161948 return pos; 3195243Sjp161948 } 3205243Sjp161948 line[pos++] = c; 3215243Sjp161948 line[pos] = '\0'; 3225243Sjp161948 } 3235243Sjp161948 /* We reached EOF */ 3245243Sjp161948 return -1; 3255243Sjp161948 } 3265243Sjp161948 3270Sstevel@tonic-gate static void 3280Sstevel@tonic-gate do_convert_from_ssh2(struct passwd *pw) 3290Sstevel@tonic-gate { 3300Sstevel@tonic-gate Key *k; 3310Sstevel@tonic-gate int blen; 3320Sstevel@tonic-gate u_int len; 3335243Sjp161948 char line[1024]; 3340Sstevel@tonic-gate u_char blob[8096]; 3350Sstevel@tonic-gate char encoded[8096]; 3360Sstevel@tonic-gate struct stat st; 3370Sstevel@tonic-gate int escaped = 0, private = 0, ok; 3380Sstevel@tonic-gate FILE *fp; 3390Sstevel@tonic-gate 3400Sstevel@tonic-gate if (!have_identity) 3410Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which the key is")); 3420Sstevel@tonic-gate if (stat(identity_file, &st) < 0) { 3430Sstevel@tonic-gate perror(identity_file); 3440Sstevel@tonic-gate exit(1); 3450Sstevel@tonic-gate } 3460Sstevel@tonic-gate fp = fopen(identity_file, "r"); 3470Sstevel@tonic-gate if (fp == NULL) { 3480Sstevel@tonic-gate perror(identity_file); 3490Sstevel@tonic-gate exit(1); 3500Sstevel@tonic-gate } 3510Sstevel@tonic-gate encoded[0] = '\0'; 3525243Sjp161948 while ((blen = get_line(fp, line, sizeof(line))) != -1) { 3535243Sjp161948 if (line[blen - 1] == '\\') 3540Sstevel@tonic-gate escaped++; 3550Sstevel@tonic-gate if (strncmp(line, "----", 4) == 0 || 3560Sstevel@tonic-gate strstr(line, ": ") != NULL) { 3570Sstevel@tonic-gate if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL) 3580Sstevel@tonic-gate private = 1; 3590Sstevel@tonic-gate if (strstr(line, " END ") != NULL) { 3600Sstevel@tonic-gate break; 3610Sstevel@tonic-gate } 3620Sstevel@tonic-gate /* fprintf(stderr, "ignore: %s", line); */ 3630Sstevel@tonic-gate continue; 3640Sstevel@tonic-gate } 3650Sstevel@tonic-gate if (escaped) { 3660Sstevel@tonic-gate escaped--; 3670Sstevel@tonic-gate /* fprintf(stderr, "escaped: %s", line); */ 3680Sstevel@tonic-gate continue; 3690Sstevel@tonic-gate } 3705243Sjp161948 strlcat(encoded, line, sizeof(encoded)); 3710Sstevel@tonic-gate } 3720Sstevel@tonic-gate len = strlen(encoded); 3730Sstevel@tonic-gate if (((len % 4) == 3) && 3740Sstevel@tonic-gate (encoded[len-1] == '=') && 3750Sstevel@tonic-gate (encoded[len-2] == '=') && 3760Sstevel@tonic-gate (encoded[len-3] == '=')) 3770Sstevel@tonic-gate encoded[len-3] = '\0'; 3780Sstevel@tonic-gate blen = uudecode(encoded, blob, sizeof(blob)); 3790Sstevel@tonic-gate if (blen < 0) { 3805243Sjp161948 fprintf(stderr, gettext("uudecode failed.\n")); 3810Sstevel@tonic-gate exit(1); 3820Sstevel@tonic-gate } 3830Sstevel@tonic-gate k = private ? 3840Sstevel@tonic-gate do_convert_private_ssh2_from_blob(blob, blen) : 3850Sstevel@tonic-gate key_from_blob(blob, blen); 3860Sstevel@tonic-gate if (k == NULL) { 3875243Sjp161948 fprintf(stderr, gettext("decode blob failed.\n")); 3880Sstevel@tonic-gate exit(1); 3890Sstevel@tonic-gate } 3900Sstevel@tonic-gate ok = private ? 3910Sstevel@tonic-gate (k->type == KEY_DSA ? 3920Sstevel@tonic-gate PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, NULL, 0, NULL, NULL) : 3930Sstevel@tonic-gate PEM_write_RSAPrivateKey(stdout, k->rsa, NULL, NULL, 0, NULL, NULL)) : 3940Sstevel@tonic-gate key_write(k, stdout); 3950Sstevel@tonic-gate if (!ok) { 3965243Sjp161948 fprintf(stderr, gettext("key write failed")); 3970Sstevel@tonic-gate exit(1); 3980Sstevel@tonic-gate } 3990Sstevel@tonic-gate key_free(k); 4000Sstevel@tonic-gate if (!private) 4015243Sjp161948 fprintf(stdout, "\n"); 4025243Sjp161948 fclose(fp); 4030Sstevel@tonic-gate exit(0); 4040Sstevel@tonic-gate } 4050Sstevel@tonic-gate 4060Sstevel@tonic-gate static void 4070Sstevel@tonic-gate do_print_public(struct passwd *pw) 4080Sstevel@tonic-gate { 4090Sstevel@tonic-gate Key *prv; 4100Sstevel@tonic-gate struct stat st; 4110Sstevel@tonic-gate 4120Sstevel@tonic-gate if (!have_identity) 4130Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which the key is")); 4140Sstevel@tonic-gate if (stat(identity_file, &st) < 0) { 4150Sstevel@tonic-gate perror(identity_file); 4160Sstevel@tonic-gate exit(1); 4170Sstevel@tonic-gate } 4180Sstevel@tonic-gate prv = load_identity(identity_file); 4190Sstevel@tonic-gate if (prv == NULL) { 4205243Sjp161948 fprintf(stderr, gettext("load failed\n")); 4210Sstevel@tonic-gate exit(1); 4220Sstevel@tonic-gate } 4230Sstevel@tonic-gate if (!key_write(prv, stdout)) 4245243Sjp161948 fprintf(stderr, gettext("key_write failed")); 4250Sstevel@tonic-gate key_free(prv); 4265243Sjp161948 fprintf(stdout, "\n"); 4270Sstevel@tonic-gate exit(0); 4280Sstevel@tonic-gate } 4290Sstevel@tonic-gate 4300Sstevel@tonic-gate static void 4310Sstevel@tonic-gate do_fingerprint(struct passwd *pw) 4320Sstevel@tonic-gate { 4330Sstevel@tonic-gate FILE *f; 4340Sstevel@tonic-gate Key *public; 4350Sstevel@tonic-gate char *comment = NULL, *cp, *ep, line[16*1024], *fp; 4360Sstevel@tonic-gate int i, skip = 0, num = 1, invalid = 1; 4370Sstevel@tonic-gate enum fp_rep rep; 4380Sstevel@tonic-gate enum fp_type fptype; 4390Sstevel@tonic-gate struct stat st; 4400Sstevel@tonic-gate 4410Sstevel@tonic-gate fptype = print_bubblebabble ? SSH_FP_SHA1 : SSH_FP_MD5; 4420Sstevel@tonic-gate rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_HEX; 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 public = key_load_public(identity_file, &comment); 4510Sstevel@tonic-gate if (public != NULL) { 4520Sstevel@tonic-gate fp = key_fingerprint(public, fptype, rep); 4535243Sjp161948 printf("%u %s %s\n", key_size(public), fp, comment); 4540Sstevel@tonic-gate key_free(public); 4550Sstevel@tonic-gate xfree(comment); 4560Sstevel@tonic-gate xfree(fp); 4570Sstevel@tonic-gate exit(0); 4580Sstevel@tonic-gate } 4595243Sjp161948 if (comment) { 4600Sstevel@tonic-gate xfree(comment); 4615243Sjp161948 comment = NULL; 4625243Sjp161948 } 4630Sstevel@tonic-gate 4640Sstevel@tonic-gate f = fopen(identity_file, "r"); 4650Sstevel@tonic-gate if (f != NULL) { 4660Sstevel@tonic-gate while (fgets(line, sizeof(line), f)) { 4670Sstevel@tonic-gate i = strlen(line) - 1; 4680Sstevel@tonic-gate if (line[i] != '\n') { 4690Sstevel@tonic-gate error("line %d too long: %.40s...", num, line); 4700Sstevel@tonic-gate skip = 1; 4710Sstevel@tonic-gate continue; 4720Sstevel@tonic-gate } 4730Sstevel@tonic-gate num++; 4740Sstevel@tonic-gate if (skip) { 4750Sstevel@tonic-gate skip = 0; 4760Sstevel@tonic-gate continue; 4770Sstevel@tonic-gate } 4780Sstevel@tonic-gate line[i] = '\0'; 4790Sstevel@tonic-gate 4800Sstevel@tonic-gate /* Skip leading whitespace, empty and comment lines. */ 4810Sstevel@tonic-gate for (cp = line; *cp == ' ' || *cp == '\t'; cp++) 4820Sstevel@tonic-gate ; 4830Sstevel@tonic-gate if (!*cp || *cp == '\n' || *cp == '#') 4845243Sjp161948 continue; 4850Sstevel@tonic-gate i = strtol(cp, &ep, 10); 4860Sstevel@tonic-gate if (i == 0 || ep == NULL || (*ep != ' ' && *ep != '\t')) { 4870Sstevel@tonic-gate int quoted = 0; 4880Sstevel@tonic-gate comment = cp; 4890Sstevel@tonic-gate for (; *cp && (quoted || (*cp != ' ' && 4900Sstevel@tonic-gate *cp != '\t')); cp++) { 4910Sstevel@tonic-gate if (*cp == '\\' && cp[1] == '"') 4920Sstevel@tonic-gate cp++; /* Skip both */ 4930Sstevel@tonic-gate else if (*cp == '"') 4940Sstevel@tonic-gate quoted = !quoted; 4950Sstevel@tonic-gate } 4960Sstevel@tonic-gate if (!*cp) 4970Sstevel@tonic-gate continue; 4980Sstevel@tonic-gate *cp++ = '\0'; 4990Sstevel@tonic-gate } 5000Sstevel@tonic-gate ep = cp; 5010Sstevel@tonic-gate public = key_new(KEY_RSA1); 5020Sstevel@tonic-gate if (key_read(public, &cp) != 1) { 5030Sstevel@tonic-gate cp = ep; 5040Sstevel@tonic-gate key_free(public); 5050Sstevel@tonic-gate public = key_new(KEY_UNSPEC); 5060Sstevel@tonic-gate if (key_read(public, &cp) != 1) { 5070Sstevel@tonic-gate key_free(public); 5080Sstevel@tonic-gate continue; 5090Sstevel@tonic-gate } 5100Sstevel@tonic-gate } 5110Sstevel@tonic-gate comment = *cp ? cp : comment; 5120Sstevel@tonic-gate fp = key_fingerprint(public, fptype, rep); 5135243Sjp161948 printf("%u %s %s\n", key_size(public), fp, 5140Sstevel@tonic-gate comment ? comment : gettext("no comment")); 5150Sstevel@tonic-gate xfree(fp); 5160Sstevel@tonic-gate key_free(public); 5170Sstevel@tonic-gate invalid = 0; 5180Sstevel@tonic-gate } 5195243Sjp161948 fclose(f); 5200Sstevel@tonic-gate } 5210Sstevel@tonic-gate if (invalid) { 5225243Sjp161948 printf(gettext("%s is not a public key file.\n"), 5230Sstevel@tonic-gate identity_file); 5240Sstevel@tonic-gate exit(1); 5250Sstevel@tonic-gate } 5260Sstevel@tonic-gate exit(0); 5270Sstevel@tonic-gate } 5280Sstevel@tonic-gate 5295243Sjp161948 static void 5305243Sjp161948 print_host(FILE *f, const char *name, Key *public, int hash) 5315243Sjp161948 { 5325243Sjp161948 if (hash && (name = host_hash(name, NULL, 0)) == NULL) 5335243Sjp161948 fatal("hash_host failed"); 5345243Sjp161948 fprintf(f, "%s ", name); 5355243Sjp161948 if (!key_write(public, f)) 5365243Sjp161948 fatal("key_write failed"); 5375243Sjp161948 fprintf(f, "\n"); 5385243Sjp161948 } 5395243Sjp161948 5405243Sjp161948 static void 5415243Sjp161948 do_known_hosts(struct passwd *pw, const char *name) 5425243Sjp161948 { 5435243Sjp161948 FILE *in, *out = stdout; 5445243Sjp161948 Key *public; 5455243Sjp161948 char *cp, *cp2, *kp, *kp2; 5465243Sjp161948 char line[16*1024], tmp[MAXPATHLEN], old[MAXPATHLEN]; 5475243Sjp161948 int c, i, skip = 0, inplace = 0, num = 0, invalid = 0, has_unhashed = 0; 5485243Sjp161948 5495243Sjp161948 if (!have_identity) { 5505243Sjp161948 cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid); 5515243Sjp161948 if (strlcpy(identity_file, cp, sizeof(identity_file)) >= 5525243Sjp161948 sizeof(identity_file)) 5535243Sjp161948 fatal("Specified known hosts path too long"); 5545243Sjp161948 xfree(cp); 5555243Sjp161948 have_identity = 1; 5565243Sjp161948 } 5575243Sjp161948 if ((in = fopen(identity_file, "r")) == NULL) 5585243Sjp161948 fatal("fopen: %s", strerror(errno)); 5595243Sjp161948 5605243Sjp161948 /* 5615243Sjp161948 * Find hosts goes to stdout, hash and deletions happen in-place 5625243Sjp161948 * A corner case is ssh-keygen -HF foo, which should go to stdout 5635243Sjp161948 */ 5645243Sjp161948 if (!find_host && (hash_hosts || delete_host)) { 5655243Sjp161948 if (strlcpy(tmp, identity_file, sizeof(tmp)) >= sizeof(tmp) || 5665243Sjp161948 strlcat(tmp, ".XXXXXXXXXX", sizeof(tmp)) >= sizeof(tmp) || 5675243Sjp161948 strlcpy(old, identity_file, sizeof(old)) >= sizeof(old) || 5685243Sjp161948 strlcat(old, ".old", sizeof(old)) >= sizeof(old)) 5695243Sjp161948 fatal("known_hosts path too long"); 5705243Sjp161948 umask(077); 5715243Sjp161948 if ((c = mkstemp(tmp)) == -1) 5725243Sjp161948 fatal("mkstemp: %s", strerror(errno)); 5735243Sjp161948 if ((out = fdopen(c, "w")) == NULL) { 5745243Sjp161948 c = errno; 5755243Sjp161948 unlink(tmp); 5765243Sjp161948 fatal("fdopen: %s", strerror(c)); 5775243Sjp161948 } 5785243Sjp161948 inplace = 1; 5795243Sjp161948 } 5805243Sjp161948 5815243Sjp161948 while (fgets(line, sizeof(line), in)) { 5825243Sjp161948 num++; 5835243Sjp161948 i = strlen(line) - 1; 5845243Sjp161948 if (line[i] != '\n') { 5855243Sjp161948 error("line %d too long: %.40s...", num, line); 5865243Sjp161948 skip = 1; 5875243Sjp161948 invalid = 1; 5885243Sjp161948 continue; 5895243Sjp161948 } 5905243Sjp161948 if (skip) { 5915243Sjp161948 skip = 0; 5925243Sjp161948 continue; 5935243Sjp161948 } 5945243Sjp161948 line[i] = '\0'; 5955243Sjp161948 5965243Sjp161948 /* Skip leading whitespace, empty and comment lines. */ 5975243Sjp161948 for (cp = line; *cp == ' ' || *cp == '\t'; cp++) 5985243Sjp161948 ; 5995243Sjp161948 if (!*cp || *cp == '\n' || *cp == '#') { 6005243Sjp161948 if (inplace) 6015243Sjp161948 fprintf(out, "%s\n", cp); 6025243Sjp161948 continue; 6035243Sjp161948 } 6045243Sjp161948 /* Find the end of the host name portion. */ 6055243Sjp161948 for (kp = cp; *kp && *kp != ' ' && *kp != '\t'; kp++) 6065243Sjp161948 ; 6075243Sjp161948 if (*kp == '\0' || *(kp + 1) == '\0') { 6085243Sjp161948 error("line %d missing key: %.40s...", 6095243Sjp161948 num, line); 6105243Sjp161948 invalid = 1; 6115243Sjp161948 continue; 6125243Sjp161948 } 6135243Sjp161948 *kp++ = '\0'; 6145243Sjp161948 kp2 = kp; 6155243Sjp161948 6165243Sjp161948 public = key_new(KEY_RSA1); 6175243Sjp161948 if (key_read(public, &kp) != 1) { 6185243Sjp161948 kp = kp2; 6195243Sjp161948 key_free(public); 6205243Sjp161948 public = key_new(KEY_UNSPEC); 6215243Sjp161948 if (key_read(public, &kp) != 1) { 6225243Sjp161948 error("line %d invalid key: %.40s...", 6235243Sjp161948 num, line); 6245243Sjp161948 key_free(public); 6255243Sjp161948 invalid = 1; 6265243Sjp161948 continue; 6275243Sjp161948 } 6285243Sjp161948 } 6295243Sjp161948 6305243Sjp161948 if (*cp == HASH_DELIM) { 6315243Sjp161948 if (find_host || delete_host) { 6325243Sjp161948 cp2 = host_hash(name, cp, strlen(cp)); 6335243Sjp161948 if (cp2 == NULL) { 6345243Sjp161948 error("line %d: invalid hashed " 6355243Sjp161948 "name: %.64s...", num, line); 6365243Sjp161948 invalid = 1; 6375243Sjp161948 continue; 6385243Sjp161948 } 6395243Sjp161948 c = (strcmp(cp2, cp) == 0); 6405243Sjp161948 if (find_host && c) { 6415243Sjp161948 printf(gettext("# Host %s found: " 6425243Sjp161948 "line %d type %s\n"), name, 6435243Sjp161948 num, key_type(public)); 6445243Sjp161948 print_host(out, cp, public, 0); 6455243Sjp161948 } 6465243Sjp161948 if (delete_host && !c) 6475243Sjp161948 print_host(out, cp, public, 0); 6485243Sjp161948 } else if (hash_hosts) 6495243Sjp161948 print_host(out, cp, public, 0); 6505243Sjp161948 } else { 6515243Sjp161948 if (find_host || delete_host) { 6525243Sjp161948 c = (match_hostname(name, cp, 6535243Sjp161948 strlen(cp)) == 1); 6545243Sjp161948 if (find_host && c) { 6555243Sjp161948 printf(gettext("# Host %s found: " 6565243Sjp161948 "line %d type %s\n"), name, 6575243Sjp161948 num, key_type(public)); 6585243Sjp161948 print_host(out, name, public, hash_hosts); 6595243Sjp161948 } 6605243Sjp161948 if (delete_host && !c) 6615243Sjp161948 print_host(out, cp, public, 0); 6625243Sjp161948 } else if (hash_hosts) { 6635243Sjp161948 for (cp2 = strsep(&cp, ","); 6645243Sjp161948 cp2 != NULL && *cp2 != '\0'; 6655243Sjp161948 cp2 = strsep(&cp, ",")) { 6665243Sjp161948 if (strcspn(cp2, "*?!") != strlen(cp2)) 6675243Sjp161948 fprintf(stderr, gettext("Warning: " 6685243Sjp161948 "ignoring host name with " 6695243Sjp161948 "metacharacters: %.64s\n"), 6705243Sjp161948 cp2); 6715243Sjp161948 else 6725243Sjp161948 print_host(out, cp2, public, 1); 6735243Sjp161948 } 6745243Sjp161948 has_unhashed = 1; 6755243Sjp161948 } 6765243Sjp161948 } 6775243Sjp161948 key_free(public); 6785243Sjp161948 } 6795243Sjp161948 fclose(in); 6805243Sjp161948 6815243Sjp161948 if (invalid) { 6825243Sjp161948 fprintf(stderr, gettext("%s is not a valid known_host file.\n"), 6835243Sjp161948 identity_file); 6845243Sjp161948 if (inplace) { 6855243Sjp161948 fprintf(stderr, gettext("Not replacing existing known_hosts " 6865243Sjp161948 "file because of errors\n")); 6875243Sjp161948 fclose(out); 6885243Sjp161948 unlink(tmp); 6895243Sjp161948 } 6905243Sjp161948 exit(1); 6915243Sjp161948 } 6925243Sjp161948 6935243Sjp161948 if (inplace) { 6945243Sjp161948 fclose(out); 6955243Sjp161948 6965243Sjp161948 /* Backup existing file */ 6975243Sjp161948 if (unlink(old) == -1 && errno != ENOENT) 6985243Sjp161948 fatal("unlink %.100s: %s", old, strerror(errno)); 6995243Sjp161948 if (link(identity_file, old) == -1) 7005243Sjp161948 fatal("link %.100s to %.100s: %s", identity_file, old, 7015243Sjp161948 strerror(errno)); 7025243Sjp161948 /* Move new one into place */ 7035243Sjp161948 if (rename(tmp, identity_file) == -1) { 7045243Sjp161948 error("rename\"%s\" to \"%s\": %s", tmp, identity_file, 7055243Sjp161948 strerror(errno)); 7065243Sjp161948 unlink(tmp); 7075243Sjp161948 unlink(old); 7085243Sjp161948 exit(1); 7095243Sjp161948 } 7105243Sjp161948 7115243Sjp161948 fprintf(stderr, gettext("%s updated.\n"), identity_file); 7125243Sjp161948 fprintf(stderr, gettext("Original contents retained as %s\n"), old); 7135243Sjp161948 if (has_unhashed) { 7145243Sjp161948 fprintf(stderr, gettext("WARNING: %s contains unhashed " 7155243Sjp161948 "entries\n"), old); 7165243Sjp161948 fprintf(stderr, gettext("Delete this file to ensure privacy " 7175243Sjp161948 "of hostnames\n")); 7185243Sjp161948 } 7195243Sjp161948 } 7205243Sjp161948 7215243Sjp161948 exit(0); 7225243Sjp161948 } 7235243Sjp161948 7240Sstevel@tonic-gate /* 7250Sstevel@tonic-gate * Perform changing a passphrase. The argument is the passwd structure 7260Sstevel@tonic-gate * for the current user. 7270Sstevel@tonic-gate */ 7280Sstevel@tonic-gate static void 7290Sstevel@tonic-gate do_change_passphrase(struct passwd *pw) 7300Sstevel@tonic-gate { 7310Sstevel@tonic-gate char *comment; 7320Sstevel@tonic-gate char *old_passphrase, *passphrase1, *passphrase2; 7330Sstevel@tonic-gate struct stat st; 7340Sstevel@tonic-gate Key *private; 7350Sstevel@tonic-gate 7360Sstevel@tonic-gate if (!have_identity) 7370Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which the key is")); 7380Sstevel@tonic-gate if (stat(identity_file, &st) < 0) { 7390Sstevel@tonic-gate perror(identity_file); 7400Sstevel@tonic-gate exit(1); 7410Sstevel@tonic-gate } 7420Sstevel@tonic-gate /* Try to load the file with empty passphrase. */ 7430Sstevel@tonic-gate private = key_load_private(identity_file, "", &comment); 7440Sstevel@tonic-gate if (private == NULL) { 7450Sstevel@tonic-gate if (identity_passphrase) 7460Sstevel@tonic-gate old_passphrase = xstrdup(identity_passphrase); 7470Sstevel@tonic-gate else 7480Sstevel@tonic-gate old_passphrase = 7490Sstevel@tonic-gate read_passphrase(gettext("Enter old passphrase: "), 7500Sstevel@tonic-gate RP_ALLOW_STDIN); 7510Sstevel@tonic-gate private = key_load_private(identity_file, old_passphrase, 7520Sstevel@tonic-gate &comment); 7535243Sjp161948 memset(old_passphrase, 0, strlen(old_passphrase)); 7540Sstevel@tonic-gate xfree(old_passphrase); 7550Sstevel@tonic-gate if (private == NULL) { 7565243Sjp161948 printf(gettext("Bad passphrase.\n")); 7570Sstevel@tonic-gate exit(1); 7580Sstevel@tonic-gate } 7590Sstevel@tonic-gate } 7605243Sjp161948 printf(gettext("Key has comment '%s'\n"), comment); 7610Sstevel@tonic-gate 7620Sstevel@tonic-gate /* Ask the new passphrase (twice). */ 7630Sstevel@tonic-gate if (identity_new_passphrase) { 7640Sstevel@tonic-gate passphrase1 = xstrdup(identity_new_passphrase); 7650Sstevel@tonic-gate passphrase2 = NULL; 7660Sstevel@tonic-gate } else { 7670Sstevel@tonic-gate passphrase1 = 7680Sstevel@tonic-gate read_passphrase(gettext("Enter new passphrase (empty" 7690Sstevel@tonic-gate " for no passphrase): "), RP_ALLOW_STDIN); 7700Sstevel@tonic-gate passphrase2 = read_passphrase(gettext("Enter same " 7710Sstevel@tonic-gate "passphrase again: "), RP_ALLOW_STDIN); 7720Sstevel@tonic-gate 7730Sstevel@tonic-gate /* Verify that they are the same. */ 7740Sstevel@tonic-gate if (strcmp(passphrase1, passphrase2) != 0) { 7755243Sjp161948 memset(passphrase1, 0, strlen(passphrase1)); 7765243Sjp161948 memset(passphrase2, 0, strlen(passphrase2)); 7770Sstevel@tonic-gate xfree(passphrase1); 7780Sstevel@tonic-gate xfree(passphrase2); 7795243Sjp161948 printf(gettext("Pass phrases do not match. Try " 7805243Sjp161948 "again.\n")); 7810Sstevel@tonic-gate exit(1); 7820Sstevel@tonic-gate } 7830Sstevel@tonic-gate /* Destroy the other copy. */ 7845243Sjp161948 memset(passphrase2, 0, strlen(passphrase2)); 7850Sstevel@tonic-gate xfree(passphrase2); 7860Sstevel@tonic-gate } 7870Sstevel@tonic-gate 7880Sstevel@tonic-gate /* Save the file using the new passphrase. */ 7890Sstevel@tonic-gate if (!key_save_private(private, identity_file, passphrase1, comment)) { 7905243Sjp161948 printf(gettext("Saving the key failed: %s.\n"), identity_file); 7915243Sjp161948 memset(passphrase1, 0, strlen(passphrase1)); 7920Sstevel@tonic-gate xfree(passphrase1); 7930Sstevel@tonic-gate key_free(private); 7940Sstevel@tonic-gate xfree(comment); 7950Sstevel@tonic-gate exit(1); 7960Sstevel@tonic-gate } 7970Sstevel@tonic-gate /* Destroy the passphrase and the copy of the key in memory. */ 7985243Sjp161948 memset(passphrase1, 0, strlen(passphrase1)); 7990Sstevel@tonic-gate xfree(passphrase1); 8000Sstevel@tonic-gate key_free(private); /* Destroys contents */ 8010Sstevel@tonic-gate xfree(comment); 8020Sstevel@tonic-gate 8035243Sjp161948 printf(gettext("Your identification has been saved with the new " 8045243Sjp161948 "passphrase.\n")); 8050Sstevel@tonic-gate exit(0); 8060Sstevel@tonic-gate } 8070Sstevel@tonic-gate 8080Sstevel@tonic-gate /* 8090Sstevel@tonic-gate * Change the comment of a private key file. 8100Sstevel@tonic-gate */ 8110Sstevel@tonic-gate static void 8120Sstevel@tonic-gate do_change_comment(struct passwd *pw) 8130Sstevel@tonic-gate { 8140Sstevel@tonic-gate char new_comment[1024], *comment, *passphrase; 8150Sstevel@tonic-gate Key *private; 8160Sstevel@tonic-gate Key *public; 8170Sstevel@tonic-gate struct stat st; 8180Sstevel@tonic-gate FILE *f; 8190Sstevel@tonic-gate int fd; 8200Sstevel@tonic-gate 8210Sstevel@tonic-gate if (!have_identity) 8220Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which the key is")); 8230Sstevel@tonic-gate if (stat(identity_file, &st) < 0) { 8240Sstevel@tonic-gate perror(identity_file); 8250Sstevel@tonic-gate exit(1); 8260Sstevel@tonic-gate } 8270Sstevel@tonic-gate private = key_load_private(identity_file, "", &comment); 8280Sstevel@tonic-gate if (private == NULL) { 8290Sstevel@tonic-gate if (identity_passphrase) 8300Sstevel@tonic-gate passphrase = xstrdup(identity_passphrase); 8310Sstevel@tonic-gate else if (identity_new_passphrase) 8320Sstevel@tonic-gate passphrase = xstrdup(identity_new_passphrase); 8330Sstevel@tonic-gate else 8340Sstevel@tonic-gate passphrase = 8350Sstevel@tonic-gate read_passphrase(gettext("Enter passphrase: "), 8365243Sjp161948 RP_ALLOW_STDIN); 8370Sstevel@tonic-gate /* Try to load using the passphrase. */ 8380Sstevel@tonic-gate private = key_load_private(identity_file, passphrase, &comment); 8390Sstevel@tonic-gate if (private == NULL) { 8405243Sjp161948 memset(passphrase, 0, strlen(passphrase)); 8410Sstevel@tonic-gate xfree(passphrase); 8425243Sjp161948 printf(gettext("Bad passphrase.\n")); 8430Sstevel@tonic-gate exit(1); 8440Sstevel@tonic-gate } 8450Sstevel@tonic-gate } else { 8460Sstevel@tonic-gate passphrase = xstrdup(""); 8470Sstevel@tonic-gate } 8480Sstevel@tonic-gate if (private->type != KEY_RSA1) { 8495243Sjp161948 fprintf(stderr, gettext("Comments are only supported for " 8505243Sjp161948 "RSA1 keys.\n")); 8510Sstevel@tonic-gate key_free(private); 8520Sstevel@tonic-gate exit(1); 8530Sstevel@tonic-gate } 8545243Sjp161948 printf(gettext("Key now has comment '%s'\n"), comment); 8550Sstevel@tonic-gate 8560Sstevel@tonic-gate if (identity_comment) { 8575243Sjp161948 strlcpy(new_comment, identity_comment, sizeof(new_comment)); 8580Sstevel@tonic-gate } else { 8595243Sjp161948 printf(gettext("Enter new comment: ")); 8605243Sjp161948 fflush(stdout); 8610Sstevel@tonic-gate if (!fgets(new_comment, sizeof(new_comment), stdin)) { 8625243Sjp161948 memset(passphrase, 0, strlen(passphrase)); 8630Sstevel@tonic-gate key_free(private); 8640Sstevel@tonic-gate exit(1); 8650Sstevel@tonic-gate } 8660Sstevel@tonic-gate if (strchr(new_comment, '\n')) 8670Sstevel@tonic-gate *strchr(new_comment, '\n') = 0; 8680Sstevel@tonic-gate } 8690Sstevel@tonic-gate 8700Sstevel@tonic-gate /* Save the file using the new passphrase. */ 8710Sstevel@tonic-gate if (!key_save_private(private, identity_file, passphrase, new_comment)) { 8725243Sjp161948 printf(gettext("Saving the key failed: %s.\n"), identity_file); 8735243Sjp161948 memset(passphrase, 0, strlen(passphrase)); 8740Sstevel@tonic-gate xfree(passphrase); 8750Sstevel@tonic-gate key_free(private); 8760Sstevel@tonic-gate xfree(comment); 8770Sstevel@tonic-gate exit(1); 8780Sstevel@tonic-gate } 8795243Sjp161948 memset(passphrase, 0, strlen(passphrase)); 8800Sstevel@tonic-gate xfree(passphrase); 8810Sstevel@tonic-gate public = key_from_private(private); 8820Sstevel@tonic-gate key_free(private); 8830Sstevel@tonic-gate 8845243Sjp161948 strlcat(identity_file, ".pub", sizeof(identity_file)); 8850Sstevel@tonic-gate fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); 8860Sstevel@tonic-gate if (fd == -1) { 8875243Sjp161948 printf(gettext("Could not save your public key in %s\n"), 8885243Sjp161948 identity_file); 8890Sstevel@tonic-gate exit(1); 8900Sstevel@tonic-gate } 8910Sstevel@tonic-gate f = fdopen(fd, "w"); 8920Sstevel@tonic-gate if (f == NULL) { 8935243Sjp161948 printf(gettext("fdopen %s failed"), identity_file); 8940Sstevel@tonic-gate exit(1); 8950Sstevel@tonic-gate } 8960Sstevel@tonic-gate if (!key_write(public, f)) 8975243Sjp161948 fprintf(stderr, gettext("write key failed")); 8980Sstevel@tonic-gate key_free(public); 8995243Sjp161948 fprintf(f, " %s\n", new_comment); 9005243Sjp161948 fclose(f); 9010Sstevel@tonic-gate 9020Sstevel@tonic-gate xfree(comment); 9030Sstevel@tonic-gate 9045243Sjp161948 printf(gettext("The comment in your key file has been changed.\n")); 9050Sstevel@tonic-gate exit(0); 9060Sstevel@tonic-gate } 9070Sstevel@tonic-gate 9080Sstevel@tonic-gate static void 9090Sstevel@tonic-gate usage(void) 9100Sstevel@tonic-gate { 9115243Sjp161948 fprintf(stderr, gettext( 9120Sstevel@tonic-gate "Usage: %s [options]\n" 9130Sstevel@tonic-gate "Options:\n" 9140Sstevel@tonic-gate " -b bits Number of bits in the key to create.\n" 9155243Sjp161948 " -B Show bubblebabble digest of key file.\n" 9160Sstevel@tonic-gate " -c Change comment in private and public key files.\n" 9175243Sjp161948 " -C comment Provide new comment.\n" 9180Sstevel@tonic-gate " -e Convert OpenSSH to IETF SECSH key file.\n" 9190Sstevel@tonic-gate " -f filename Filename of the key file.\n" 9205243Sjp161948 " -F hostname Find hostname in known hosts file.\n" 9215243Sjp161948 " -H Hash names in known_hosts file.\n" 9220Sstevel@tonic-gate " -i Convert IETF SECSH to OpenSSH key file.\n" 9230Sstevel@tonic-gate " -l Show fingerprint of key file.\n" 9245243Sjp161948 " -N phrase Provide new passphrase.\n" 9250Sstevel@tonic-gate " -p Change passphrase of private key file.\n" 9265243Sjp161948 " -P phrase Provide old passphrase.\n" 9270Sstevel@tonic-gate " -q Quiet.\n" 9285243Sjp161948 " -R hostname Remove host from known_hosts file.\n" 9290Sstevel@tonic-gate " -t type Specify type of key to create.\n" 9305243Sjp161948 " -y Read private key file and print public key.\n" 9310Sstevel@tonic-gate ), __progname); 9320Sstevel@tonic-gate 9330Sstevel@tonic-gate exit(1); 9340Sstevel@tonic-gate } 9350Sstevel@tonic-gate 9360Sstevel@tonic-gate /* 9370Sstevel@tonic-gate * Main program for key management. 9380Sstevel@tonic-gate */ 9390Sstevel@tonic-gate int 9405243Sjp161948 main(int argc, char **argv) 9410Sstevel@tonic-gate { 9420Sstevel@tonic-gate char dotsshdir[MAXPATHLEN], comment[1024], *passphrase1, *passphrase2; 9435243Sjp161948 char *rr_hostname = NULL; 9440Sstevel@tonic-gate Key *private, *public; 9450Sstevel@tonic-gate struct passwd *pw; 9460Sstevel@tonic-gate struct stat st; 9470Sstevel@tonic-gate int opt, type, fd; 9480Sstevel@tonic-gate FILE *f; 9490Sstevel@tonic-gate 9500Sstevel@tonic-gate extern int optind; 9510Sstevel@tonic-gate extern char *optarg; 9520Sstevel@tonic-gate 9535243Sjp161948 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 9545243Sjp161948 sanitise_stdfd(); 9550Sstevel@tonic-gate 9565243Sjp161948 __progname = get_progname(argv[0]); 9575243Sjp161948 9585243Sjp161948 g11n_setlocale(LC_ALL, ""); 9590Sstevel@tonic-gate 9600Sstevel@tonic-gate SSLeay_add_all_algorithms(); 9610Sstevel@tonic-gate init_rng(); 9620Sstevel@tonic-gate seed_rng(); 9630Sstevel@tonic-gate 9640Sstevel@tonic-gate /* we need this for the home * directory. */ 9650Sstevel@tonic-gate pw = getpwuid(getuid()); 9660Sstevel@tonic-gate if (!pw) { 9675243Sjp161948 printf(gettext("You don't exist, go away!\n")); 9680Sstevel@tonic-gate exit(1); 9690Sstevel@tonic-gate } 9700Sstevel@tonic-gate if (gethostname(hostname, sizeof(hostname)) < 0) { 9710Sstevel@tonic-gate perror("gethostname"); 9720Sstevel@tonic-gate exit(1); 9730Sstevel@tonic-gate } 9740Sstevel@tonic-gate 9755243Sjp161948 #define GETOPT_ARGS "BcdeHilpqxXyb:C:f:F:N:P:R:t:" 976*7947SHuie-Ying.Lee@Sun.COM 9775243Sjp161948 while ((opt = getopt(argc, argv, GETOPT_ARGS)) != -1) { 9780Sstevel@tonic-gate switch (opt) { 9790Sstevel@tonic-gate case 'b': 9800Sstevel@tonic-gate bits = atoi(optarg); 9810Sstevel@tonic-gate if (bits < 512 || bits > 32768) { 9825243Sjp161948 printf(gettext("Bits has bad value.\n")); 9830Sstevel@tonic-gate exit(1); 9840Sstevel@tonic-gate } 9850Sstevel@tonic-gate break; 9865243Sjp161948 case 'F': 9875243Sjp161948 find_host = 1; 9885243Sjp161948 rr_hostname = optarg; 9895243Sjp161948 break; 9905243Sjp161948 case 'H': 9915243Sjp161948 hash_hosts = 1; 9925243Sjp161948 break; 9935243Sjp161948 case 'R': 9945243Sjp161948 delete_host = 1; 9955243Sjp161948 rr_hostname = optarg; 9965243Sjp161948 break; 9970Sstevel@tonic-gate case 'l': 9980Sstevel@tonic-gate print_fingerprint = 1; 9990Sstevel@tonic-gate break; 10000Sstevel@tonic-gate case 'B': 10010Sstevel@tonic-gate print_bubblebabble = 1; 10020Sstevel@tonic-gate break; 10030Sstevel@tonic-gate case 'p': 10040Sstevel@tonic-gate change_passphrase = 1; 10050Sstevel@tonic-gate break; 10060Sstevel@tonic-gate case 'c': 10070Sstevel@tonic-gate change_comment = 1; 10080Sstevel@tonic-gate break; 10090Sstevel@tonic-gate case 'f': 10105243Sjp161948 strlcpy(identity_file, optarg, sizeof(identity_file)); 10110Sstevel@tonic-gate have_identity = 1; 10120Sstevel@tonic-gate break; 10130Sstevel@tonic-gate case 'P': 10140Sstevel@tonic-gate identity_passphrase = optarg; 10150Sstevel@tonic-gate break; 10160Sstevel@tonic-gate case 'N': 10170Sstevel@tonic-gate identity_new_passphrase = optarg; 10180Sstevel@tonic-gate break; 10190Sstevel@tonic-gate case 'C': 10200Sstevel@tonic-gate identity_comment = optarg; 10210Sstevel@tonic-gate break; 10220Sstevel@tonic-gate case 'q': 10230Sstevel@tonic-gate quiet = 1; 10240Sstevel@tonic-gate break; 10250Sstevel@tonic-gate case 'e': 10260Sstevel@tonic-gate case 'x': 10270Sstevel@tonic-gate /* export key */ 10280Sstevel@tonic-gate convert_to_ssh2 = 1; 10290Sstevel@tonic-gate break; 10300Sstevel@tonic-gate case 'i': 10310Sstevel@tonic-gate case 'X': 10320Sstevel@tonic-gate /* import key */ 10330Sstevel@tonic-gate convert_from_ssh2 = 1; 10340Sstevel@tonic-gate break; 10350Sstevel@tonic-gate case 'y': 10360Sstevel@tonic-gate print_public = 1; 10370Sstevel@tonic-gate break; 10380Sstevel@tonic-gate case 'd': 10390Sstevel@tonic-gate key_type_name = "dsa"; 10400Sstevel@tonic-gate break; 10410Sstevel@tonic-gate case 't': 10420Sstevel@tonic-gate key_type_name = optarg; 10430Sstevel@tonic-gate break; 10440Sstevel@tonic-gate case '?': 10450Sstevel@tonic-gate default: 10460Sstevel@tonic-gate usage(); 10470Sstevel@tonic-gate } 10480Sstevel@tonic-gate } 10495243Sjp161948 if (optind < argc) { 10505243Sjp161948 printf(gettext("Too many arguments.\n")); 10510Sstevel@tonic-gate usage(); 10520Sstevel@tonic-gate } 10530Sstevel@tonic-gate if (change_passphrase && change_comment) { 10545243Sjp161948 printf(gettext("Can only have one of -p and -c.\n")); 10550Sstevel@tonic-gate usage(); 10560Sstevel@tonic-gate } 10575243Sjp161948 if (delete_host || hash_hosts || find_host) 10585243Sjp161948 do_known_hosts(pw, rr_hostname); 10590Sstevel@tonic-gate if (print_fingerprint || print_bubblebabble) 10600Sstevel@tonic-gate do_fingerprint(pw); 10610Sstevel@tonic-gate if (change_passphrase) 10620Sstevel@tonic-gate do_change_passphrase(pw); 10630Sstevel@tonic-gate if (change_comment) 10640Sstevel@tonic-gate do_change_comment(pw); 10650Sstevel@tonic-gate if (convert_to_ssh2) 10660Sstevel@tonic-gate do_convert_to_ssh2(pw); 10670Sstevel@tonic-gate if (convert_from_ssh2) 10680Sstevel@tonic-gate do_convert_from_ssh2(pw); 10690Sstevel@tonic-gate if (print_public) 10700Sstevel@tonic-gate do_print_public(pw); 10710Sstevel@tonic-gate 10720Sstevel@tonic-gate arc4random_stir(); 10730Sstevel@tonic-gate 10740Sstevel@tonic-gate if (key_type_name == NULL) { 10755243Sjp161948 printf(gettext("You must specify a key type (-t).\n")); 10760Sstevel@tonic-gate usage(); 10770Sstevel@tonic-gate } 10780Sstevel@tonic-gate type = key_type_from_name(key_type_name); 10790Sstevel@tonic-gate if (type == KEY_UNSPEC) { 10805243Sjp161948 fprintf(stderr, gettext("unknown key type %s\n"), 10815243Sjp161948 key_type_name); 10820Sstevel@tonic-gate exit(1); 10830Sstevel@tonic-gate } 10840Sstevel@tonic-gate if (!quiet) 10855243Sjp161948 printf(gettext("Generating public/private %s key pair.\n"), 10865243Sjp161948 key_type_name); 10870Sstevel@tonic-gate private = key_generate(type, bits); 10880Sstevel@tonic-gate if (private == NULL) { 10895243Sjp161948 fprintf(stderr, gettext("key_generate failed")); 10900Sstevel@tonic-gate exit(1); 10910Sstevel@tonic-gate } 10920Sstevel@tonic-gate public = key_from_private(private); 10930Sstevel@tonic-gate 10940Sstevel@tonic-gate if (!have_identity) 10950Sstevel@tonic-gate ask_filename(pw, gettext("Enter file in which to save the key")); 10960Sstevel@tonic-gate 10975243Sjp161948 /* Create ~/.ssh directory if it doesn't already exist. */ 10985243Sjp161948 snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, _PATH_SSH_USER_DIR); 10990Sstevel@tonic-gate if (strstr(identity_file, dotsshdir) != NULL && 11000Sstevel@tonic-gate stat(dotsshdir, &st) < 0) { 11010Sstevel@tonic-gate if (mkdir(dotsshdir, 0700) < 0) 11020Sstevel@tonic-gate error("Could not create directory '%s'.", dotsshdir); 11030Sstevel@tonic-gate else if (!quiet) 11045243Sjp161948 printf(gettext("Created directory '%s'.\n"), dotsshdir); 11050Sstevel@tonic-gate } 11060Sstevel@tonic-gate /* If the file already exists, ask the user to confirm. */ 11070Sstevel@tonic-gate if (stat(identity_file, &st) >= 0) { 11080Sstevel@tonic-gate char yesno[128]; 11095243Sjp161948 printf(gettext("%s already exists.\n"), identity_file); 11105243Sjp161948 printf(gettext("Overwrite (%s/%s)? "), 11115243Sjp161948 nl_langinfo(YESSTR), nl_langinfo(NOSTR)); 11125243Sjp161948 fflush(stdout); 11130Sstevel@tonic-gate if (fgets(yesno, sizeof(yesno), stdin) == NULL) 11140Sstevel@tonic-gate exit(1); 11152757Sjp161948 if (strcasecmp(chop(yesno), nl_langinfo(YESSTR)) != 0) 11160Sstevel@tonic-gate exit(1); 11170Sstevel@tonic-gate } 11180Sstevel@tonic-gate /* Ask for a passphrase (twice). */ 11190Sstevel@tonic-gate if (identity_passphrase) 11200Sstevel@tonic-gate passphrase1 = xstrdup(identity_passphrase); 11210Sstevel@tonic-gate else if (identity_new_passphrase) 11220Sstevel@tonic-gate passphrase1 = xstrdup(identity_new_passphrase); 11230Sstevel@tonic-gate else { 11240Sstevel@tonic-gate passphrase_again: 11250Sstevel@tonic-gate passphrase1 = 11260Sstevel@tonic-gate read_passphrase(gettext("Enter passphrase (empty " 11270Sstevel@tonic-gate "for no passphrase): "), RP_ALLOW_STDIN); 11280Sstevel@tonic-gate passphrase2 = read_passphrase(gettext("Enter same " 11290Sstevel@tonic-gate "passphrase again: "), RP_ALLOW_STDIN); 11300Sstevel@tonic-gate if (strcmp(passphrase1, passphrase2) != 0) { 11310Sstevel@tonic-gate /* 11320Sstevel@tonic-gate * The passphrases do not match. Clear them and 11330Sstevel@tonic-gate * retry. 11340Sstevel@tonic-gate */ 11355243Sjp161948 memset(passphrase1, 0, strlen(passphrase1)); 11365243Sjp161948 memset(passphrase2, 0, strlen(passphrase2)); 11370Sstevel@tonic-gate xfree(passphrase1); 11380Sstevel@tonic-gate xfree(passphrase2); 11395243Sjp161948 printf(gettext("Passphrases do not match. Try " 11405243Sjp161948 "again.\n")); 11410Sstevel@tonic-gate goto passphrase_again; 11420Sstevel@tonic-gate } 11430Sstevel@tonic-gate /* Clear the other copy of the passphrase. */ 11445243Sjp161948 memset(passphrase2, 0, strlen(passphrase2)); 11450Sstevel@tonic-gate xfree(passphrase2); 11460Sstevel@tonic-gate } 11470Sstevel@tonic-gate 11480Sstevel@tonic-gate if (identity_comment) { 11495243Sjp161948 strlcpy(comment, identity_comment, sizeof(comment)); 11500Sstevel@tonic-gate } else { 11510Sstevel@tonic-gate /* Create default commend field for the passphrase. */ 11525243Sjp161948 snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); 11530Sstevel@tonic-gate } 11540Sstevel@tonic-gate 11550Sstevel@tonic-gate /* Save the key with the given passphrase and comment. */ 11560Sstevel@tonic-gate if (!key_save_private(private, identity_file, passphrase1, comment)) { 11575243Sjp161948 printf(gettext("Saving the key failed: %s.\n"), identity_file); 11585243Sjp161948 memset(passphrase1, 0, strlen(passphrase1)); 11590Sstevel@tonic-gate xfree(passphrase1); 11600Sstevel@tonic-gate exit(1); 11610Sstevel@tonic-gate } 11620Sstevel@tonic-gate /* Clear the passphrase. */ 11635243Sjp161948 memset(passphrase1, 0, strlen(passphrase1)); 11640Sstevel@tonic-gate xfree(passphrase1); 11650Sstevel@tonic-gate 11660Sstevel@tonic-gate /* Clear the private key and the random number generator. */ 11670Sstevel@tonic-gate key_free(private); 11680Sstevel@tonic-gate arc4random_stir(); 11690Sstevel@tonic-gate 11700Sstevel@tonic-gate if (!quiet) 11715243Sjp161948 printf(gettext("Your identification has been saved in %s.\n"), 11725243Sjp161948 identity_file); 11730Sstevel@tonic-gate 11745243Sjp161948 strlcat(identity_file, ".pub", sizeof(identity_file)); 11750Sstevel@tonic-gate fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); 11760Sstevel@tonic-gate if (fd == -1) { 11775243Sjp161948 printf(gettext("Could not save your public key in %s\n"), 11785243Sjp161948 identity_file); 11790Sstevel@tonic-gate exit(1); 11800Sstevel@tonic-gate } 11810Sstevel@tonic-gate f = fdopen(fd, "w"); 11820Sstevel@tonic-gate if (f == NULL) { 11835243Sjp161948 printf(gettext("fdopen %s failed"), identity_file); 11840Sstevel@tonic-gate exit(1); 11850Sstevel@tonic-gate } 11860Sstevel@tonic-gate if (!key_write(public, f)) 11875243Sjp161948 fprintf(stderr, gettext("write key failed")); 11885243Sjp161948 fprintf(f, " %s\n", comment); 11895243Sjp161948 fclose(f); 11900Sstevel@tonic-gate 11910Sstevel@tonic-gate if (!quiet) { 11920Sstevel@tonic-gate char *fp = key_fingerprint(public, SSH_FP_MD5, SSH_FP_HEX); 11935243Sjp161948 printf(gettext("Your public key has been saved in %s.\n"), 11940Sstevel@tonic-gate identity_file); 11955243Sjp161948 printf(gettext("The key fingerprint is:\n")); 11965243Sjp161948 printf("%s %s\n", fp, comment); 11970Sstevel@tonic-gate xfree(fp); 11980Sstevel@tonic-gate } 11990Sstevel@tonic-gate 12000Sstevel@tonic-gate key_free(public); 12010Sstevel@tonic-gate return(0); 12020Sstevel@tonic-gate /* NOTREACHED */ 12030Sstevel@tonic-gate } 1204