10cbfa66cSDaniel Fojt /* $OpenBSD: ssh-keygen.c,v 1.409.2.1 2020/05/18 19:02:13 benno Exp $ */ 218de8d7fSPeter Avalos /* 318de8d7fSPeter Avalos * Author: Tatu Ylonen <ylo@cs.hut.fi> 418de8d7fSPeter Avalos * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 518de8d7fSPeter Avalos * All rights reserved 618de8d7fSPeter Avalos * Identity and host key generation and maintenance. 718de8d7fSPeter Avalos * 818de8d7fSPeter Avalos * As far as I am concerned, the code I have written for this software 918de8d7fSPeter Avalos * can be used freely for any purpose. Any derived versions of this 1018de8d7fSPeter Avalos * software must be clearly marked as such, and if the derived work is 1118de8d7fSPeter Avalos * incompatible with the protocol description in the RFC file, it must be 1218de8d7fSPeter Avalos * called by a name other than "ssh" or "Secure Shell". 1318de8d7fSPeter Avalos */ 1418de8d7fSPeter Avalos 1518de8d7fSPeter Avalos #include "includes.h" 1618de8d7fSPeter Avalos 1718de8d7fSPeter Avalos #include <sys/types.h> 1818de8d7fSPeter Avalos #include <sys/socket.h> 1918de8d7fSPeter Avalos #include <sys/stat.h> 2018de8d7fSPeter Avalos 21e9778795SPeter Avalos #ifdef WITH_OPENSSL 2218de8d7fSPeter Avalos #include <openssl/evp.h> 2318de8d7fSPeter Avalos #include <openssl/pem.h> 2418de8d7fSPeter Avalos #include "openbsd-compat/openssl-compat.h" 25e9778795SPeter Avalos #endif 2618de8d7fSPeter Avalos 270cbfa66cSDaniel Fojt #ifdef HAVE_STDINT_H 280cbfa66cSDaniel Fojt # include <stdint.h> 290cbfa66cSDaniel Fojt #endif 3018de8d7fSPeter Avalos #include <errno.h> 3118de8d7fSPeter Avalos #include <fcntl.h> 3218de8d7fSPeter Avalos #include <netdb.h> 3318de8d7fSPeter Avalos #ifdef HAVE_PATHS_H 3418de8d7fSPeter Avalos # include <paths.h> 3518de8d7fSPeter Avalos #endif 3618de8d7fSPeter Avalos #include <pwd.h> 3718de8d7fSPeter Avalos #include <stdarg.h> 3818de8d7fSPeter Avalos #include <stdio.h> 3918de8d7fSPeter Avalos #include <stdlib.h> 4018de8d7fSPeter Avalos #include <string.h> 4118de8d7fSPeter Avalos #include <unistd.h> 42e9778795SPeter Avalos #include <limits.h> 43ce74bacaSMatthew Dillon #include <locale.h> 44664f4763Szrj #include <time.h> 4518de8d7fSPeter Avalos 4618de8d7fSPeter Avalos #include "xmalloc.h" 47e9778795SPeter Avalos #include "sshkey.h" 4818de8d7fSPeter Avalos #include "authfile.h" 49e9778795SPeter Avalos #include "sshbuf.h" 5018de8d7fSPeter Avalos #include "pathnames.h" 5118de8d7fSPeter Avalos #include "log.h" 5218de8d7fSPeter Avalos #include "misc.h" 5318de8d7fSPeter Avalos #include "match.h" 5418de8d7fSPeter Avalos #include "hostfile.h" 5518de8d7fSPeter Avalos #include "dns.h" 5636e94dc5SPeter Avalos #include "ssh.h" 57856ea928SPeter Avalos #include "ssh2.h" 58e9778795SPeter Avalos #include "ssherr.h" 59856ea928SPeter Avalos #include "ssh-pkcs11.h" 6036e94dc5SPeter Avalos #include "atomicio.h" 6136e94dc5SPeter Avalos #include "krl.h" 62e9778795SPeter Avalos #include "digest.h" 63ce74bacaSMatthew Dillon #include "utf8.h" 64ce74bacaSMatthew Dillon #include "authfd.h" 650cbfa66cSDaniel Fojt #include "sshsig.h" 660cbfa66cSDaniel Fojt #include "ssh-sk.h" 670cbfa66cSDaniel Fojt #include "sk-api.h" /* XXX for SSH_SK_USER_PRESENCE_REQD; remove */ 68e9778795SPeter Avalos 69e9778795SPeter Avalos #ifdef WITH_OPENSSL 70e9778795SPeter Avalos # define DEFAULT_KEY_TYPE_NAME "rsa" 71e9778795SPeter Avalos #else 72e9778795SPeter Avalos # define DEFAULT_KEY_TYPE_NAME "ed25519" 73e9778795SPeter Avalos #endif 7418de8d7fSPeter Avalos 75664f4763Szrj /* 76664f4763Szrj * Default number of bits in the RSA, DSA and ECDSA keys. These value can be 77664f4763Szrj * overridden on the command line. 78664f4763Szrj * 79664f4763Szrj * These values, with the exception of DSA, provide security equivalent to at 80664f4763Szrj * least 128 bits of security according to NIST Special Publication 800-57: 81664f4763Szrj * Recommendation for Key Management Part 1 rev 4 section 5.6.1. 82664f4763Szrj * For DSA it (and FIPS-186-4 section 4.2) specifies that the only size for 83664f4763Szrj * which a 160bit hash is acceptable is 1kbit, and since ssh-dss specifies only 84664f4763Szrj * SHA1 we limit the DSA key size 1k bits. 85664f4763Szrj */ 86664f4763Szrj #define DEFAULT_BITS 3072 8718de8d7fSPeter Avalos #define DEFAULT_BITS_DSA 1024 889f304aafSPeter Avalos #define DEFAULT_BITS_ECDSA 256 8918de8d7fSPeter Avalos 90664f4763Szrj static int quiet = 0; 91856ea928SPeter Avalos 9218de8d7fSPeter Avalos /* Flag indicating that we just want to see the key fingerprint */ 93664f4763Szrj static int print_fingerprint = 0; 94664f4763Szrj static int print_bubblebabble = 0; 9518de8d7fSPeter Avalos 96e9778795SPeter Avalos /* Hash algorithm to use for fingerprints. */ 97664f4763Szrj static int fingerprint_hash = SSH_FP_HASH_DEFAULT; 98e9778795SPeter Avalos 9918de8d7fSPeter Avalos /* The identity file name, given on the command line or entered by the user. */ 1000cbfa66cSDaniel Fojt static char identity_file[PATH_MAX]; 101664f4763Szrj static int have_identity = 0; 10218de8d7fSPeter Avalos 10318de8d7fSPeter Avalos /* This is set to the passphrase if given on the command line. */ 104664f4763Szrj static char *identity_passphrase = NULL; 10518de8d7fSPeter Avalos 10618de8d7fSPeter Avalos /* This is set to the new passphrase if given on the command line. */ 107664f4763Szrj static char *identity_new_passphrase = NULL; 108856ea928SPeter Avalos 109856ea928SPeter Avalos /* Key type when certifying */ 110664f4763Szrj static u_int cert_key_type = SSH2_CERT_TYPE_USER; 111856ea928SPeter Avalos 112856ea928SPeter Avalos /* "key ID" of signed key */ 113664f4763Szrj static char *cert_key_id = NULL; 114856ea928SPeter Avalos 115856ea928SPeter Avalos /* Comma-separated list of principal names for certifying keys */ 116664f4763Szrj static char *cert_principals = NULL; 117856ea928SPeter Avalos 118856ea928SPeter Avalos /* Validity period for certificates */ 119664f4763Szrj static u_int64_t cert_valid_from = 0; 120664f4763Szrj static u_int64_t cert_valid_to = ~0ULL; 121856ea928SPeter Avalos 122856ea928SPeter Avalos /* Certificate options */ 123856ea928SPeter Avalos #define CERTOPT_X_FWD (1) 124856ea928SPeter Avalos #define CERTOPT_AGENT_FWD (1<<1) 125856ea928SPeter Avalos #define CERTOPT_PORT_FWD (1<<2) 126856ea928SPeter Avalos #define CERTOPT_PTY (1<<3) 127856ea928SPeter Avalos #define CERTOPT_USER_RC (1<<4) 1280cbfa66cSDaniel Fojt #define CERTOPT_NO_REQUIRE_USER_PRESENCE (1<<5) 129856ea928SPeter Avalos #define CERTOPT_DEFAULT (CERTOPT_X_FWD|CERTOPT_AGENT_FWD| \ 130856ea928SPeter Avalos CERTOPT_PORT_FWD|CERTOPT_PTY|CERTOPT_USER_RC) 131664f4763Szrj static u_int32_t certflags_flags = CERTOPT_DEFAULT; 132664f4763Szrj static char *certflags_command = NULL; 133664f4763Szrj static char *certflags_src_addr = NULL; 134856ea928SPeter Avalos 135ce74bacaSMatthew Dillon /* Arbitrary extensions specified by user */ 136ce74bacaSMatthew Dillon struct cert_userext { 137ce74bacaSMatthew Dillon char *key; 138ce74bacaSMatthew Dillon char *val; 139ce74bacaSMatthew Dillon int crit; 140ce74bacaSMatthew Dillon }; 141664f4763Szrj static struct cert_userext *cert_userext; 142664f4763Szrj static size_t ncert_userext; 143ce74bacaSMatthew Dillon 144856ea928SPeter Avalos /* Conversion to/from various formats */ 145856ea928SPeter Avalos enum { 146856ea928SPeter Avalos FMT_RFC4716, 147856ea928SPeter Avalos FMT_PKCS8, 148856ea928SPeter Avalos FMT_PEM 149856ea928SPeter Avalos } convert_format = FMT_RFC4716; 15018de8d7fSPeter Avalos 151664f4763Szrj static char *key_type_name = NULL; 15218de8d7fSPeter Avalos 153856ea928SPeter Avalos /* Load key from this PKCS#11 provider */ 154664f4763Szrj static char *pkcs11provider = NULL; 155856ea928SPeter Avalos 1560cbfa66cSDaniel Fojt /* FIDO/U2F provider to use */ 1570cbfa66cSDaniel Fojt static char *sk_provider = NULL; 1580cbfa66cSDaniel Fojt 1590cbfa66cSDaniel Fojt /* Format for writing private keys */ 1600cbfa66cSDaniel Fojt static int private_key_format = SSHKEY_PRIVATE_OPENSSH; 16136e94dc5SPeter Avalos 16236e94dc5SPeter Avalos /* Cipher for new-format private keys */ 1630cbfa66cSDaniel Fojt static char *openssh_format_cipher = NULL; 16436e94dc5SPeter Avalos 1650cbfa66cSDaniel Fojt /* Number of KDF rounds to derive new format keys. */ 166664f4763Szrj static int rounds = 0; 16736e94dc5SPeter Avalos 16818de8d7fSPeter Avalos /* argv0 */ 16918de8d7fSPeter Avalos extern char *__progname; 17018de8d7fSPeter Avalos 171664f4763Szrj static char hostname[NI_MAXHOST]; 17218de8d7fSPeter Avalos 173e9778795SPeter Avalos #ifdef WITH_OPENSSL 17418de8d7fSPeter Avalos /* moduli.c */ 17518de8d7fSPeter Avalos int gen_candidates(FILE *, u_int32_t, u_int32_t, BIGNUM *); 17699e85e0dSPeter Avalos int prime_test(FILE *, FILE *, u_int32_t, u_int32_t, char *, unsigned long, 17799e85e0dSPeter Avalos unsigned long); 178e9778795SPeter Avalos #endif 17918de8d7fSPeter Avalos 18018de8d7fSPeter Avalos static void 181e9778795SPeter Avalos type_bits_valid(int type, const char *name, u_int32_t *bitsp) 1821c188a7fSPeter Avalos { 183e9778795SPeter Avalos if (type == KEY_UNSPEC) 184e9778795SPeter Avalos fatal("unknown key type %s", key_type_name); 1851c188a7fSPeter Avalos if (*bitsp == 0) { 186e9778795SPeter Avalos #ifdef WITH_OPENSSL 1870cbfa66cSDaniel Fojt u_int nid; 1880cbfa66cSDaniel Fojt 1890cbfa66cSDaniel Fojt switch(type) { 1900cbfa66cSDaniel Fojt case KEY_DSA: 1911c188a7fSPeter Avalos *bitsp = DEFAULT_BITS_DSA; 1920cbfa66cSDaniel Fojt break; 1930cbfa66cSDaniel Fojt case KEY_ECDSA: 194e9778795SPeter Avalos if (name != NULL && 195e9778795SPeter Avalos (nid = sshkey_ecdsa_nid_from_name(name)) > 0) 196e9778795SPeter Avalos *bitsp = sshkey_curve_nid_to_bits(nid); 197e9778795SPeter Avalos if (*bitsp == 0) 1981c188a7fSPeter Avalos *bitsp = DEFAULT_BITS_ECDSA; 1990cbfa66cSDaniel Fojt break; 2000cbfa66cSDaniel Fojt case KEY_RSA: 2011c188a7fSPeter Avalos *bitsp = DEFAULT_BITS; 2020cbfa66cSDaniel Fojt break; 2030cbfa66cSDaniel Fojt } 2040cbfa66cSDaniel Fojt #endif 2051c188a7fSPeter Avalos } 206e9778795SPeter Avalos #ifdef WITH_OPENSSL 207ce74bacaSMatthew Dillon switch (type) { 208ce74bacaSMatthew Dillon case KEY_DSA: 209ce74bacaSMatthew Dillon if (*bitsp != 1024) 210ce74bacaSMatthew Dillon fatal("Invalid DSA key length: must be 1024 bits"); 211ce74bacaSMatthew Dillon break; 212ce74bacaSMatthew Dillon case KEY_RSA: 213ce74bacaSMatthew Dillon if (*bitsp < SSH_RSA_MINIMUM_MODULUS_SIZE) 214ce74bacaSMatthew Dillon fatal("Invalid RSA key length: minimum is %d bits", 215ce74bacaSMatthew Dillon SSH_RSA_MINIMUM_MODULUS_SIZE); 2160cbfa66cSDaniel Fojt else if (*bitsp > OPENSSL_RSA_MAX_MODULUS_BITS) 2170cbfa66cSDaniel Fojt fatal("Invalid RSA key length: maximum is %d bits", 2180cbfa66cSDaniel Fojt OPENSSL_RSA_MAX_MODULUS_BITS); 219ce74bacaSMatthew Dillon break; 220ce74bacaSMatthew Dillon case KEY_ECDSA: 221ce74bacaSMatthew Dillon if (sshkey_ecdsa_bits_to_nid(*bitsp) == -1) 222ce74bacaSMatthew Dillon fatal("Invalid ECDSA key length: valid lengths are " 223664f4763Szrj #ifdef OPENSSL_HAS_NISTP521 2241c188a7fSPeter Avalos "256, 384 or 521 bits"); 225664f4763Szrj #else 226664f4763Szrj "256 or 384 bits"); 227664f4763Szrj #endif 228ce74bacaSMatthew Dillon } 22936e94dc5SPeter Avalos #endif 2301c188a7fSPeter Avalos } 2311c188a7fSPeter Avalos 2320cbfa66cSDaniel Fojt /* 2330cbfa66cSDaniel Fojt * Checks whether a file exists and, if so, asks the user whether they wish 2340cbfa66cSDaniel Fojt * to overwrite it. 2350cbfa66cSDaniel Fojt * Returns nonzero if the file does not already exist or if the user agrees to 2360cbfa66cSDaniel Fojt * overwrite, or zero otherwise. 2370cbfa66cSDaniel Fojt */ 2380cbfa66cSDaniel Fojt static int 2390cbfa66cSDaniel Fojt confirm_overwrite(const char *filename) 2400cbfa66cSDaniel Fojt { 2410cbfa66cSDaniel Fojt char yesno[3]; 2420cbfa66cSDaniel Fojt struct stat st; 2430cbfa66cSDaniel Fojt 2440cbfa66cSDaniel Fojt if (stat(filename, &st) != 0) 2450cbfa66cSDaniel Fojt return 1; 2460cbfa66cSDaniel Fojt printf("%s already exists.\n", filename); 2470cbfa66cSDaniel Fojt printf("Overwrite (y/n)? "); 2480cbfa66cSDaniel Fojt fflush(stdout); 2490cbfa66cSDaniel Fojt if (fgets(yesno, sizeof(yesno), stdin) == NULL) 2500cbfa66cSDaniel Fojt return 0; 2510cbfa66cSDaniel Fojt if (yesno[0] != 'y' && yesno[0] != 'Y') 2520cbfa66cSDaniel Fojt return 0; 2530cbfa66cSDaniel Fojt return 1; 2540cbfa66cSDaniel Fojt } 2550cbfa66cSDaniel Fojt 2561c188a7fSPeter Avalos static void 25718de8d7fSPeter Avalos ask_filename(struct passwd *pw, const char *prompt) 25818de8d7fSPeter Avalos { 25918de8d7fSPeter Avalos char buf[1024]; 26018de8d7fSPeter Avalos char *name = NULL; 26118de8d7fSPeter Avalos 26218de8d7fSPeter Avalos if (key_type_name == NULL) 26318de8d7fSPeter Avalos name = _PATH_SSH_CLIENT_ID_RSA; 26418de8d7fSPeter Avalos else { 265e9778795SPeter Avalos switch (sshkey_type_from_name(key_type_name)) { 266856ea928SPeter Avalos case KEY_DSA_CERT: 26718de8d7fSPeter Avalos case KEY_DSA: 26818de8d7fSPeter Avalos name = _PATH_SSH_CLIENT_ID_DSA; 26918de8d7fSPeter Avalos break; 2709f304aafSPeter Avalos #ifdef OPENSSL_HAS_ECC 2719f304aafSPeter Avalos case KEY_ECDSA_CERT: 2729f304aafSPeter Avalos case KEY_ECDSA: 2739f304aafSPeter Avalos name = _PATH_SSH_CLIENT_ID_ECDSA; 2749f304aafSPeter Avalos break; 2750cbfa66cSDaniel Fojt case KEY_ECDSA_SK_CERT: 2760cbfa66cSDaniel Fojt case KEY_ECDSA_SK: 2770cbfa66cSDaniel Fojt name = _PATH_SSH_CLIENT_ID_ECDSA_SK; 2780cbfa66cSDaniel Fojt break; 2799f304aafSPeter Avalos #endif 280856ea928SPeter Avalos case KEY_RSA_CERT: 28118de8d7fSPeter Avalos case KEY_RSA: 28218de8d7fSPeter Avalos name = _PATH_SSH_CLIENT_ID_RSA; 28318de8d7fSPeter Avalos break; 28436e94dc5SPeter Avalos case KEY_ED25519: 28536e94dc5SPeter Avalos case KEY_ED25519_CERT: 28636e94dc5SPeter Avalos name = _PATH_SSH_CLIENT_ID_ED25519; 28736e94dc5SPeter Avalos break; 2880cbfa66cSDaniel Fojt case KEY_ED25519_SK: 2890cbfa66cSDaniel Fojt case KEY_ED25519_SK_CERT: 2900cbfa66cSDaniel Fojt name = _PATH_SSH_CLIENT_ID_ED25519_SK; 2910cbfa66cSDaniel Fojt break; 292664f4763Szrj case KEY_XMSS: 293664f4763Szrj case KEY_XMSS_CERT: 294664f4763Szrj name = _PATH_SSH_CLIENT_ID_XMSS; 295664f4763Szrj break; 29618de8d7fSPeter Avalos default: 297e9778795SPeter Avalos fatal("bad key type"); 29818de8d7fSPeter Avalos } 29918de8d7fSPeter Avalos } 300e9778795SPeter Avalos snprintf(identity_file, sizeof(identity_file), 301e9778795SPeter Avalos "%s/%s", pw->pw_dir, name); 302e9778795SPeter Avalos printf("%s (%s): ", prompt, identity_file); 303e9778795SPeter Avalos fflush(stdout); 30418de8d7fSPeter Avalos if (fgets(buf, sizeof(buf), stdin) == NULL) 30518de8d7fSPeter Avalos exit(1); 30618de8d7fSPeter Avalos buf[strcspn(buf, "\n")] = '\0'; 30718de8d7fSPeter Avalos if (strcmp(buf, "") != 0) 30818de8d7fSPeter Avalos strlcpy(identity_file, buf, sizeof(identity_file)); 30918de8d7fSPeter Avalos have_identity = 1; 31018de8d7fSPeter Avalos } 31118de8d7fSPeter Avalos 312e9778795SPeter Avalos static struct sshkey * 3130cbfa66cSDaniel Fojt load_identity(const char *filename, char **commentp) 31418de8d7fSPeter Avalos { 31518de8d7fSPeter Avalos char *pass; 316e9778795SPeter Avalos struct sshkey *prv; 317e9778795SPeter Avalos int r; 31818de8d7fSPeter Avalos 3190cbfa66cSDaniel Fojt if (commentp != NULL) 3200cbfa66cSDaniel Fojt *commentp = NULL; 3210cbfa66cSDaniel Fojt if ((r = sshkey_load_private(filename, "", &prv, commentp)) == 0) 322e9778795SPeter Avalos return prv; 323e9778795SPeter Avalos if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) 324e9778795SPeter Avalos fatal("Load key \"%s\": %s", filename, ssh_err(r)); 32518de8d7fSPeter Avalos if (identity_passphrase) 32618de8d7fSPeter Avalos pass = xstrdup(identity_passphrase); 32718de8d7fSPeter Avalos else 328e9778795SPeter Avalos pass = read_passphrase("Enter passphrase: ", RP_ALLOW_STDIN); 3290cbfa66cSDaniel Fojt r = sshkey_load_private(filename, pass, &prv, commentp); 3300cbfa66cSDaniel Fojt freezero(pass, strlen(pass)); 331e9778795SPeter Avalos if (r != 0) 332e9778795SPeter Avalos fatal("Load key \"%s\": %s", filename, ssh_err(r)); 33318de8d7fSPeter Avalos return prv; 33418de8d7fSPeter Avalos } 33518de8d7fSPeter Avalos 33618de8d7fSPeter Avalos #define SSH_COM_PUBLIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----" 33718de8d7fSPeter Avalos #define SSH_COM_PUBLIC_END "---- END SSH2 PUBLIC KEY ----" 33818de8d7fSPeter Avalos #define SSH_COM_PRIVATE_BEGIN "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----" 33918de8d7fSPeter Avalos #define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb 34018de8d7fSPeter Avalos 34136e94dc5SPeter Avalos #ifdef WITH_OPENSSL 34218de8d7fSPeter Avalos static void 343e9778795SPeter Avalos do_convert_to_ssh2(struct passwd *pw, struct sshkey *k) 34418de8d7fSPeter Avalos { 3450cbfa66cSDaniel Fojt struct sshbuf *b; 3460cbfa66cSDaniel Fojt char comment[61], *b64; 347e9778795SPeter Avalos int r; 348856ea928SPeter Avalos 3490cbfa66cSDaniel Fojt if ((b = sshbuf_new()) == NULL) 3500cbfa66cSDaniel Fojt fatal("%s: sshbuf_new failed", __func__); 3510cbfa66cSDaniel Fojt if ((r = sshkey_putb(k, b)) != 0) 352e9778795SPeter Avalos fatal("key_to_blob failed: %s", ssh_err(r)); 3530cbfa66cSDaniel Fojt if ((b64 = sshbuf_dtob64_string(b, 1)) == NULL) 3540cbfa66cSDaniel Fojt fatal("%s: sshbuf_dtob64_string failed", __func__); 3550cbfa66cSDaniel Fojt 356856ea928SPeter Avalos /* Comment + surrounds must fit into 72 chars (RFC 4716 sec 3.3) */ 357856ea928SPeter Avalos snprintf(comment, sizeof(comment), 358856ea928SPeter Avalos "%u-bit %s, converted by %s@%s from OpenSSH", 359e9778795SPeter Avalos sshkey_size(k), sshkey_type(k), 360856ea928SPeter Avalos pw->pw_name, hostname); 361856ea928SPeter Avalos 362e9778795SPeter Avalos sshkey_free(k); 3630cbfa66cSDaniel Fojt sshbuf_free(b); 3640cbfa66cSDaniel Fojt 3650cbfa66cSDaniel Fojt fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN); 3660cbfa66cSDaniel Fojt fprintf(stdout, "Comment: \"%s\"\n%s", comment, b64); 3670cbfa66cSDaniel Fojt fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END); 3680cbfa66cSDaniel Fojt free(b64); 369856ea928SPeter Avalos exit(0); 370856ea928SPeter Avalos } 371856ea928SPeter Avalos 372856ea928SPeter Avalos static void 373e9778795SPeter Avalos do_convert_to_pkcs8(struct sshkey *k) 374856ea928SPeter Avalos { 375e9778795SPeter Avalos switch (sshkey_type_plain(k->type)) { 376856ea928SPeter Avalos case KEY_RSA: 377856ea928SPeter Avalos if (!PEM_write_RSA_PUBKEY(stdout, k->rsa)) 378856ea928SPeter Avalos fatal("PEM_write_RSA_PUBKEY failed"); 379856ea928SPeter Avalos break; 380856ea928SPeter Avalos case KEY_DSA: 381856ea928SPeter Avalos if (!PEM_write_DSA_PUBKEY(stdout, k->dsa)) 382856ea928SPeter Avalos fatal("PEM_write_DSA_PUBKEY failed"); 383856ea928SPeter Avalos break; 3849f304aafSPeter Avalos #ifdef OPENSSL_HAS_ECC 3859f304aafSPeter Avalos case KEY_ECDSA: 3869f304aafSPeter Avalos if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa)) 3879f304aafSPeter Avalos fatal("PEM_write_EC_PUBKEY failed"); 3889f304aafSPeter Avalos break; 3899f304aafSPeter Avalos #endif 390856ea928SPeter Avalos default: 391e9778795SPeter Avalos fatal("%s: unsupported key type %s", __func__, sshkey_type(k)); 392856ea928SPeter Avalos } 393856ea928SPeter Avalos exit(0); 394856ea928SPeter Avalos } 395856ea928SPeter Avalos 396856ea928SPeter Avalos static void 397e9778795SPeter Avalos do_convert_to_pem(struct sshkey *k) 398856ea928SPeter Avalos { 399e9778795SPeter Avalos switch (sshkey_type_plain(k->type)) { 400856ea928SPeter Avalos case KEY_RSA: 401856ea928SPeter Avalos if (!PEM_write_RSAPublicKey(stdout, k->rsa)) 402856ea928SPeter Avalos fatal("PEM_write_RSAPublicKey failed"); 403856ea928SPeter Avalos break; 4040cbfa66cSDaniel Fojt case KEY_DSA: 4050cbfa66cSDaniel Fojt if (!PEM_write_DSA_PUBKEY(stdout, k->dsa)) 4060cbfa66cSDaniel Fojt fatal("PEM_write_DSA_PUBKEY failed"); 4070cbfa66cSDaniel Fojt break; 4080cbfa66cSDaniel Fojt #ifdef OPENSSL_HAS_ECC 4090cbfa66cSDaniel Fojt case KEY_ECDSA: 4100cbfa66cSDaniel Fojt if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa)) 4110cbfa66cSDaniel Fojt fatal("PEM_write_EC_PUBKEY failed"); 4120cbfa66cSDaniel Fojt break; 4130cbfa66cSDaniel Fojt #endif 414856ea928SPeter Avalos default: 415e9778795SPeter Avalos fatal("%s: unsupported key type %s", __func__, sshkey_type(k)); 416856ea928SPeter Avalos } 417856ea928SPeter Avalos exit(0); 418856ea928SPeter Avalos } 419856ea928SPeter Avalos 420856ea928SPeter Avalos static void 421856ea928SPeter Avalos do_convert_to(struct passwd *pw) 422856ea928SPeter Avalos { 423e9778795SPeter Avalos struct sshkey *k; 42418de8d7fSPeter Avalos struct stat st; 425e9778795SPeter Avalos int r; 42618de8d7fSPeter Avalos 42718de8d7fSPeter Avalos if (!have_identity) 42818de8d7fSPeter Avalos ask_filename(pw, "Enter file in which the key is"); 4290cbfa66cSDaniel Fojt if (stat(identity_file, &st) == -1) 430856ea928SPeter Avalos fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); 431e9778795SPeter Avalos if ((r = sshkey_load_public(identity_file, &k, NULL)) != 0) 4320cbfa66cSDaniel Fojt k = load_identity(identity_file, NULL); 433856ea928SPeter Avalos switch (convert_format) { 434856ea928SPeter Avalos case FMT_RFC4716: 435856ea928SPeter Avalos do_convert_to_ssh2(pw, k); 436856ea928SPeter Avalos break; 437856ea928SPeter Avalos case FMT_PKCS8: 438856ea928SPeter Avalos do_convert_to_pkcs8(k); 439856ea928SPeter Avalos break; 440856ea928SPeter Avalos case FMT_PEM: 441856ea928SPeter Avalos do_convert_to_pem(k); 442856ea928SPeter Avalos break; 443856ea928SPeter Avalos default: 444856ea928SPeter Avalos fatal("%s: unknown key format %d", __func__, convert_format); 44518de8d7fSPeter Avalos } 44618de8d7fSPeter Avalos exit(0); 44718de8d7fSPeter Avalos } 44818de8d7fSPeter Avalos 449e9778795SPeter Avalos /* 450e9778795SPeter Avalos * This is almost exactly the bignum1 encoding, but with 32 bit for length 451e9778795SPeter Avalos * instead of 16. 452e9778795SPeter Avalos */ 45318de8d7fSPeter Avalos static void 454e9778795SPeter Avalos buffer_get_bignum_bits(struct sshbuf *b, BIGNUM *value) 45518de8d7fSPeter Avalos { 456e9778795SPeter Avalos u_int bytes, bignum_bits; 457e9778795SPeter Avalos int r; 45818de8d7fSPeter Avalos 459e9778795SPeter Avalos if ((r = sshbuf_get_u32(b, &bignum_bits)) != 0) 460e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 461e9778795SPeter Avalos bytes = (bignum_bits + 7) / 8; 462e9778795SPeter Avalos if (sshbuf_len(b) < bytes) 463e9778795SPeter Avalos fatal("%s: input buffer too small: need %d have %zu", 464e9778795SPeter Avalos __func__, bytes, sshbuf_len(b)); 465e9778795SPeter Avalos if (BN_bin2bn(sshbuf_ptr(b), bytes, value) == NULL) 466e9778795SPeter Avalos fatal("%s: BN_bin2bn failed", __func__); 467e9778795SPeter Avalos if ((r = sshbuf_consume(b, bytes)) != 0) 468e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 46918de8d7fSPeter Avalos } 47018de8d7fSPeter Avalos 471e9778795SPeter Avalos static struct sshkey * 4720cbfa66cSDaniel Fojt do_convert_private_ssh2(struct sshbuf *b) 47318de8d7fSPeter Avalos { 474e9778795SPeter Avalos struct sshkey *key = NULL; 47518de8d7fSPeter Avalos char *type, *cipher; 476e9778795SPeter Avalos u_char e1, e2, e3, *sig = NULL, data[] = "abcde12345"; 477e9778795SPeter Avalos int r, rlen, ktype; 478e9778795SPeter Avalos u_int magic, i1, i2, i3, i4; 479e9778795SPeter Avalos size_t slen; 48018de8d7fSPeter Avalos u_long e; 481664f4763Szrj BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL; 482664f4763Szrj BIGNUM *dsa_pub_key = NULL, *dsa_priv_key = NULL; 483664f4763Szrj BIGNUM *rsa_n = NULL, *rsa_e = NULL, *rsa_d = NULL; 484664f4763Szrj BIGNUM *rsa_p = NULL, *rsa_q = NULL, *rsa_iqmp = NULL; 4850cbfa66cSDaniel Fojt 486e9778795SPeter Avalos if ((r = sshbuf_get_u32(b, &magic)) != 0) 487e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 48818de8d7fSPeter Avalos 48918de8d7fSPeter Avalos if (magic != SSH_COM_PRIVATE_KEY_MAGIC) { 490e9778795SPeter Avalos error("bad magic 0x%x != 0x%x", magic, 491e9778795SPeter Avalos SSH_COM_PRIVATE_KEY_MAGIC); 49218de8d7fSPeter Avalos return NULL; 49318de8d7fSPeter Avalos } 494e9778795SPeter Avalos if ((r = sshbuf_get_u32(b, &i1)) != 0 || 495e9778795SPeter Avalos (r = sshbuf_get_cstring(b, &type, NULL)) != 0 || 496e9778795SPeter Avalos (r = sshbuf_get_cstring(b, &cipher, NULL)) != 0 || 497e9778795SPeter Avalos (r = sshbuf_get_u32(b, &i2)) != 0 || 498e9778795SPeter Avalos (r = sshbuf_get_u32(b, &i3)) != 0 || 499e9778795SPeter Avalos (r = sshbuf_get_u32(b, &i4)) != 0) 500e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 50118de8d7fSPeter Avalos debug("ignore (%d %d %d %d)", i1, i2, i3, i4); 50218de8d7fSPeter Avalos if (strcmp(cipher, "none") != 0) { 50318de8d7fSPeter Avalos error("unsupported cipher %s", cipher); 50436e94dc5SPeter Avalos free(cipher); 50536e94dc5SPeter Avalos free(type); 50618de8d7fSPeter Avalos return NULL; 50718de8d7fSPeter Avalos } 50836e94dc5SPeter Avalos free(cipher); 50918de8d7fSPeter Avalos 51018de8d7fSPeter Avalos if (strstr(type, "dsa")) { 51118de8d7fSPeter Avalos ktype = KEY_DSA; 51218de8d7fSPeter Avalos } else if (strstr(type, "rsa")) { 51318de8d7fSPeter Avalos ktype = KEY_RSA; 51418de8d7fSPeter Avalos } else { 51536e94dc5SPeter Avalos free(type); 51618de8d7fSPeter Avalos return NULL; 51718de8d7fSPeter Avalos } 518664f4763Szrj if ((key = sshkey_new(ktype)) == NULL) 519664f4763Szrj fatal("sshkey_new failed"); 52036e94dc5SPeter Avalos free(type); 52118de8d7fSPeter Avalos 52218de8d7fSPeter Avalos switch (key->type) { 52318de8d7fSPeter Avalos case KEY_DSA: 524664f4763Szrj if ((dsa_p = BN_new()) == NULL || 525664f4763Szrj (dsa_q = BN_new()) == NULL || 526664f4763Szrj (dsa_g = BN_new()) == NULL || 527664f4763Szrj (dsa_pub_key = BN_new()) == NULL || 528664f4763Szrj (dsa_priv_key = BN_new()) == NULL) 529664f4763Szrj fatal("%s: BN_new", __func__); 530664f4763Szrj buffer_get_bignum_bits(b, dsa_p); 531664f4763Szrj buffer_get_bignum_bits(b, dsa_g); 532664f4763Szrj buffer_get_bignum_bits(b, dsa_q); 533664f4763Szrj buffer_get_bignum_bits(b, dsa_pub_key); 534664f4763Szrj buffer_get_bignum_bits(b, dsa_priv_key); 535664f4763Szrj if (!DSA_set0_pqg(key->dsa, dsa_p, dsa_q, dsa_g)) 536664f4763Szrj fatal("%s: DSA_set0_pqg failed", __func__); 537664f4763Szrj dsa_p = dsa_q = dsa_g = NULL; /* transferred */ 538664f4763Szrj if (!DSA_set0_key(key->dsa, dsa_pub_key, dsa_priv_key)) 539664f4763Szrj fatal("%s: DSA_set0_key failed", __func__); 540664f4763Szrj dsa_pub_key = dsa_priv_key = NULL; /* transferred */ 54118de8d7fSPeter Avalos break; 54218de8d7fSPeter Avalos case KEY_RSA: 543e9778795SPeter Avalos if ((r = sshbuf_get_u8(b, &e1)) != 0 || 544e9778795SPeter Avalos (e1 < 30 && (r = sshbuf_get_u8(b, &e2)) != 0) || 545e9778795SPeter Avalos (e1 < 30 && (r = sshbuf_get_u8(b, &e3)) != 0)) 546e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 547e9778795SPeter Avalos e = e1; 54818de8d7fSPeter Avalos debug("e %lx", e); 54918de8d7fSPeter Avalos if (e < 30) { 55018de8d7fSPeter Avalos e <<= 8; 551e9778795SPeter Avalos e += e2; 55218de8d7fSPeter Avalos debug("e %lx", e); 55318de8d7fSPeter Avalos e <<= 8; 554e9778795SPeter Avalos e += e3; 55518de8d7fSPeter Avalos debug("e %lx", e); 55618de8d7fSPeter Avalos } 557664f4763Szrj if ((rsa_e = BN_new()) == NULL) 558664f4763Szrj fatal("%s: BN_new", __func__); 559664f4763Szrj if (!BN_set_word(rsa_e, e)) { 560664f4763Szrj BN_clear_free(rsa_e); 561e9778795SPeter Avalos sshkey_free(key); 56218de8d7fSPeter Avalos return NULL; 56318de8d7fSPeter Avalos } 564664f4763Szrj if ((rsa_n = BN_new()) == NULL || 565664f4763Szrj (rsa_d = BN_new()) == NULL || 566664f4763Szrj (rsa_p = BN_new()) == NULL || 567664f4763Szrj (rsa_q = BN_new()) == NULL || 568664f4763Szrj (rsa_iqmp = BN_new()) == NULL) 569664f4763Szrj fatal("%s: BN_new", __func__); 570664f4763Szrj buffer_get_bignum_bits(b, rsa_d); 571664f4763Szrj buffer_get_bignum_bits(b, rsa_n); 572664f4763Szrj buffer_get_bignum_bits(b, rsa_iqmp); 573664f4763Szrj buffer_get_bignum_bits(b, rsa_q); 574664f4763Szrj buffer_get_bignum_bits(b, rsa_p); 575664f4763Szrj if (!RSA_set0_key(key->rsa, rsa_n, rsa_e, rsa_d)) 576664f4763Szrj fatal("%s: RSA_set0_key failed", __func__); 577664f4763Szrj rsa_n = rsa_e = rsa_d = NULL; /* transferred */ 578664f4763Szrj if (!RSA_set0_factors(key->rsa, rsa_p, rsa_q)) 579664f4763Szrj fatal("%s: RSA_set0_factors failed", __func__); 580664f4763Szrj rsa_p = rsa_q = NULL; /* transferred */ 581664f4763Szrj if ((r = ssh_rsa_complete_crt_parameters(key, rsa_iqmp)) != 0) 582e9778795SPeter Avalos fatal("generate RSA parameters failed: %s", ssh_err(r)); 583664f4763Szrj BN_clear_free(rsa_iqmp); 58418de8d7fSPeter Avalos break; 58518de8d7fSPeter Avalos } 586e9778795SPeter Avalos rlen = sshbuf_len(b); 58718de8d7fSPeter Avalos if (rlen != 0) 5880cbfa66cSDaniel Fojt error("%s: remaining bytes in key blob %d", __func__, rlen); 58918de8d7fSPeter Avalos 59018de8d7fSPeter Avalos /* try the key */ 5910cbfa66cSDaniel Fojt if (sshkey_sign(key, &sig, &slen, data, sizeof(data), 5920cbfa66cSDaniel Fojt NULL, NULL, 0) != 0 || 5930cbfa66cSDaniel Fojt sshkey_verify(key, sig, slen, data, sizeof(data), 5940cbfa66cSDaniel Fojt NULL, 0, NULL) != 0) { 595e9778795SPeter Avalos sshkey_free(key); 596e9778795SPeter Avalos free(sig); 597e9778795SPeter Avalos return NULL; 598e9778795SPeter Avalos } 59936e94dc5SPeter Avalos free(sig); 60018de8d7fSPeter Avalos return key; 60118de8d7fSPeter Avalos } 60218de8d7fSPeter Avalos 60318de8d7fSPeter Avalos static int 60418de8d7fSPeter Avalos get_line(FILE *fp, char *line, size_t len) 60518de8d7fSPeter Avalos { 60618de8d7fSPeter Avalos int c; 60718de8d7fSPeter Avalos size_t pos = 0; 60818de8d7fSPeter Avalos 60918de8d7fSPeter Avalos line[0] = '\0'; 61018de8d7fSPeter Avalos while ((c = fgetc(fp)) != EOF) { 611e9778795SPeter Avalos if (pos >= len - 1) 612e9778795SPeter Avalos fatal("input line too long."); 61318de8d7fSPeter Avalos switch (c) { 61418de8d7fSPeter Avalos case '\r': 61518de8d7fSPeter Avalos c = fgetc(fp); 616e9778795SPeter Avalos if (c != EOF && c != '\n' && ungetc(c, fp) == EOF) 617e9778795SPeter Avalos fatal("unget: %s", strerror(errno)); 61818de8d7fSPeter Avalos return pos; 61918de8d7fSPeter Avalos case '\n': 62018de8d7fSPeter Avalos return pos; 62118de8d7fSPeter Avalos } 62218de8d7fSPeter Avalos line[pos++] = c; 62318de8d7fSPeter Avalos line[pos] = '\0'; 62418de8d7fSPeter Avalos } 62518de8d7fSPeter Avalos /* We reached EOF */ 62618de8d7fSPeter Avalos return -1; 62718de8d7fSPeter Avalos } 62818de8d7fSPeter Avalos 62918de8d7fSPeter Avalos static void 630e9778795SPeter Avalos do_convert_from_ssh2(struct passwd *pw, struct sshkey **k, int *private) 63118de8d7fSPeter Avalos { 632e9778795SPeter Avalos int r, blen, escaped = 0; 63318de8d7fSPeter Avalos u_int len; 63418de8d7fSPeter Avalos char line[1024]; 6350cbfa66cSDaniel Fojt struct sshbuf *buf; 63618de8d7fSPeter Avalos char encoded[8096]; 63718de8d7fSPeter Avalos FILE *fp; 63818de8d7fSPeter Avalos 6390cbfa66cSDaniel Fojt if ((buf = sshbuf_new()) == NULL) 6400cbfa66cSDaniel Fojt fatal("sshbuf_new failed"); 641856ea928SPeter Avalos if ((fp = fopen(identity_file, "r")) == NULL) 642856ea928SPeter Avalos fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); 64318de8d7fSPeter Avalos encoded[0] = '\0'; 64418de8d7fSPeter Avalos while ((blen = get_line(fp, line, sizeof(line))) != -1) { 64536e94dc5SPeter Avalos if (blen > 0 && line[blen - 1] == '\\') 64618de8d7fSPeter Avalos escaped++; 64718de8d7fSPeter Avalos if (strncmp(line, "----", 4) == 0 || 64818de8d7fSPeter Avalos strstr(line, ": ") != NULL) { 64918de8d7fSPeter Avalos if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL) 650856ea928SPeter Avalos *private = 1; 65118de8d7fSPeter Avalos if (strstr(line, " END ") != NULL) { 65218de8d7fSPeter Avalos break; 65318de8d7fSPeter Avalos } 65418de8d7fSPeter Avalos /* fprintf(stderr, "ignore: %s", line); */ 65518de8d7fSPeter Avalos continue; 65618de8d7fSPeter Avalos } 65718de8d7fSPeter Avalos if (escaped) { 65818de8d7fSPeter Avalos escaped--; 65918de8d7fSPeter Avalos /* fprintf(stderr, "escaped: %s", line); */ 66018de8d7fSPeter Avalos continue; 66118de8d7fSPeter Avalos } 66218de8d7fSPeter Avalos strlcat(encoded, line, sizeof(encoded)); 66318de8d7fSPeter Avalos } 66418de8d7fSPeter Avalos len = strlen(encoded); 66518de8d7fSPeter Avalos if (((len % 4) == 3) && 66618de8d7fSPeter Avalos (encoded[len-1] == '=') && 66718de8d7fSPeter Avalos (encoded[len-2] == '=') && 66818de8d7fSPeter Avalos (encoded[len-3] == '=')) 66918de8d7fSPeter Avalos encoded[len-3] = '\0'; 6700cbfa66cSDaniel Fojt if ((r = sshbuf_b64tod(buf, encoded)) != 0) 6710cbfa66cSDaniel Fojt fatal("%s: base64 decoding failed: %s", __func__, ssh_err(r)); 6720cbfa66cSDaniel Fojt if (*private) { 6730cbfa66cSDaniel Fojt if ((*k = do_convert_private_ssh2(buf)) == NULL) 6740cbfa66cSDaniel Fojt fatal("%s: private key conversion failed", __func__); 6750cbfa66cSDaniel Fojt } else if ((r = sshkey_fromb(buf, k)) != 0) 676e9778795SPeter Avalos fatal("decode blob failed: %s", ssh_err(r)); 6770cbfa66cSDaniel Fojt sshbuf_free(buf); 678856ea928SPeter Avalos fclose(fp); 679856ea928SPeter Avalos } 680856ea928SPeter Avalos 681856ea928SPeter Avalos static void 682e9778795SPeter Avalos do_convert_from_pkcs8(struct sshkey **k, int *private) 683856ea928SPeter Avalos { 684856ea928SPeter Avalos EVP_PKEY *pubkey; 685856ea928SPeter Avalos FILE *fp; 686856ea928SPeter Avalos 687856ea928SPeter Avalos if ((fp = fopen(identity_file, "r")) == NULL) 688856ea928SPeter Avalos fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); 689856ea928SPeter Avalos if ((pubkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) { 690856ea928SPeter Avalos fatal("%s: %s is not a recognised public key format", __func__, 691856ea928SPeter Avalos identity_file); 692856ea928SPeter Avalos } 693856ea928SPeter Avalos fclose(fp); 694664f4763Szrj switch (EVP_PKEY_base_id(pubkey)) { 695856ea928SPeter Avalos case EVP_PKEY_RSA: 696e9778795SPeter Avalos if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) 697e9778795SPeter Avalos fatal("sshkey_new failed"); 698856ea928SPeter Avalos (*k)->type = KEY_RSA; 699856ea928SPeter Avalos (*k)->rsa = EVP_PKEY_get1_RSA(pubkey); 700856ea928SPeter Avalos break; 701856ea928SPeter Avalos case EVP_PKEY_DSA: 702e9778795SPeter Avalos if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) 703e9778795SPeter Avalos fatal("sshkey_new failed"); 704856ea928SPeter Avalos (*k)->type = KEY_DSA; 705856ea928SPeter Avalos (*k)->dsa = EVP_PKEY_get1_DSA(pubkey); 706856ea928SPeter Avalos break; 7079f304aafSPeter Avalos #ifdef OPENSSL_HAS_ECC 7089f304aafSPeter Avalos case EVP_PKEY_EC: 709e9778795SPeter Avalos if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) 710e9778795SPeter Avalos fatal("sshkey_new failed"); 7119f304aafSPeter Avalos (*k)->type = KEY_ECDSA; 7129f304aafSPeter Avalos (*k)->ecdsa = EVP_PKEY_get1_EC_KEY(pubkey); 713e9778795SPeter Avalos (*k)->ecdsa_nid = sshkey_ecdsa_key_to_nid((*k)->ecdsa); 7149f304aafSPeter Avalos break; 7159f304aafSPeter Avalos #endif 716856ea928SPeter Avalos default: 717856ea928SPeter Avalos fatal("%s: unsupported pubkey type %d", __func__, 718664f4763Szrj EVP_PKEY_base_id(pubkey)); 719856ea928SPeter Avalos } 720856ea928SPeter Avalos EVP_PKEY_free(pubkey); 721856ea928SPeter Avalos return; 722856ea928SPeter Avalos } 723856ea928SPeter Avalos 724856ea928SPeter Avalos static void 725e9778795SPeter Avalos do_convert_from_pem(struct sshkey **k, int *private) 726856ea928SPeter Avalos { 727856ea928SPeter Avalos FILE *fp; 728856ea928SPeter Avalos RSA *rsa; 729856ea928SPeter Avalos 730856ea928SPeter Avalos if ((fp = fopen(identity_file, "r")) == NULL) 731856ea928SPeter Avalos fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); 732856ea928SPeter Avalos if ((rsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL)) != NULL) { 733e9778795SPeter Avalos if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) 734e9778795SPeter Avalos fatal("sshkey_new failed"); 735856ea928SPeter Avalos (*k)->type = KEY_RSA; 736856ea928SPeter Avalos (*k)->rsa = rsa; 737856ea928SPeter Avalos fclose(fp); 738856ea928SPeter Avalos return; 739856ea928SPeter Avalos } 740856ea928SPeter Avalos fatal("%s: unrecognised raw private key format", __func__); 741856ea928SPeter Avalos } 742856ea928SPeter Avalos 743856ea928SPeter Avalos static void 744856ea928SPeter Avalos do_convert_from(struct passwd *pw) 745856ea928SPeter Avalos { 746e9778795SPeter Avalos struct sshkey *k = NULL; 747e9778795SPeter Avalos int r, private = 0, ok = 0; 748856ea928SPeter Avalos struct stat st; 749856ea928SPeter Avalos 750856ea928SPeter Avalos if (!have_identity) 751856ea928SPeter Avalos ask_filename(pw, "Enter file in which the key is"); 7520cbfa66cSDaniel Fojt if (stat(identity_file, &st) == -1) 753856ea928SPeter Avalos fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); 754856ea928SPeter Avalos 755856ea928SPeter Avalos switch (convert_format) { 756856ea928SPeter Avalos case FMT_RFC4716: 757856ea928SPeter Avalos do_convert_from_ssh2(pw, &k, &private); 758856ea928SPeter Avalos break; 759856ea928SPeter Avalos case FMT_PKCS8: 760856ea928SPeter Avalos do_convert_from_pkcs8(&k, &private); 761856ea928SPeter Avalos break; 762856ea928SPeter Avalos case FMT_PEM: 763856ea928SPeter Avalos do_convert_from_pem(&k, &private); 764856ea928SPeter Avalos break; 765856ea928SPeter Avalos default: 766856ea928SPeter Avalos fatal("%s: unknown key format %d", __func__, convert_format); 767856ea928SPeter Avalos } 768856ea928SPeter Avalos 769e9778795SPeter Avalos if (!private) { 770e9778795SPeter Avalos if ((r = sshkey_write(k, stdout)) == 0) 771e9778795SPeter Avalos ok = 1; 772856ea928SPeter Avalos if (ok) 773856ea928SPeter Avalos fprintf(stdout, "\n"); 774e9778795SPeter Avalos } else { 775856ea928SPeter Avalos switch (k->type) { 776856ea928SPeter Avalos case KEY_DSA: 777856ea928SPeter Avalos ok = PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, 778856ea928SPeter Avalos NULL, 0, NULL, NULL); 779856ea928SPeter Avalos break; 7809f304aafSPeter Avalos #ifdef OPENSSL_HAS_ECC 7819f304aafSPeter Avalos case KEY_ECDSA: 7829f304aafSPeter Avalos ok = PEM_write_ECPrivateKey(stdout, k->ecdsa, NULL, 7839f304aafSPeter Avalos NULL, 0, NULL, NULL); 7849f304aafSPeter Avalos break; 7859f304aafSPeter Avalos #endif 786856ea928SPeter Avalos case KEY_RSA: 787856ea928SPeter Avalos ok = PEM_write_RSAPrivateKey(stdout, k->rsa, NULL, 788856ea928SPeter Avalos NULL, 0, NULL, NULL); 789856ea928SPeter Avalos break; 790856ea928SPeter Avalos default: 791856ea928SPeter Avalos fatal("%s: unsupported key type %s", __func__, 792e9778795SPeter Avalos sshkey_type(k)); 793856ea928SPeter Avalos } 794856ea928SPeter Avalos } 795856ea928SPeter Avalos 796e9778795SPeter Avalos if (!ok) 797e9778795SPeter Avalos fatal("key write failed"); 798e9778795SPeter Avalos sshkey_free(k); 79918de8d7fSPeter Avalos exit(0); 80018de8d7fSPeter Avalos } 80136e94dc5SPeter Avalos #endif 80218de8d7fSPeter Avalos 80318de8d7fSPeter Avalos static void 80418de8d7fSPeter Avalos do_print_public(struct passwd *pw) 80518de8d7fSPeter Avalos { 806e9778795SPeter Avalos struct sshkey *prv; 80718de8d7fSPeter Avalos struct stat st; 808e9778795SPeter Avalos int r; 8090cbfa66cSDaniel Fojt char *comment = NULL; 81018de8d7fSPeter Avalos 81118de8d7fSPeter Avalos if (!have_identity) 81218de8d7fSPeter Avalos ask_filename(pw, "Enter file in which the key is"); 8130cbfa66cSDaniel Fojt if (stat(identity_file, &st) == -1) 814e9778795SPeter Avalos fatal("%s: %s", identity_file, strerror(errno)); 8150cbfa66cSDaniel Fojt prv = load_identity(identity_file, &comment); 816e9778795SPeter Avalos if ((r = sshkey_write(prv, stdout)) != 0) 817ce74bacaSMatthew Dillon error("sshkey_write failed: %s", ssh_err(r)); 818e9778795SPeter Avalos sshkey_free(prv); 8190cbfa66cSDaniel Fojt if (comment != NULL && *comment != '\0') 8200cbfa66cSDaniel Fojt fprintf(stdout, " %s", comment); 82118de8d7fSPeter Avalos fprintf(stdout, "\n"); 8220cbfa66cSDaniel Fojt free(comment); 82318de8d7fSPeter Avalos exit(0); 82418de8d7fSPeter Avalos } 82518de8d7fSPeter Avalos 82618de8d7fSPeter Avalos static void 827856ea928SPeter Avalos do_download(struct passwd *pw) 82818de8d7fSPeter Avalos { 829856ea928SPeter Avalos #ifdef ENABLE_PKCS11 830e9778795SPeter Avalos struct sshkey **keys = NULL; 831856ea928SPeter Avalos int i, nkeys; 832e9778795SPeter Avalos enum sshkey_fp_rep rep; 833e9778795SPeter Avalos int fptype; 8340cbfa66cSDaniel Fojt char *fp, *ra, **comments = NULL; 83536e94dc5SPeter Avalos 836e9778795SPeter Avalos fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash; 837e9778795SPeter Avalos rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT; 83818de8d7fSPeter Avalos 839664f4763Szrj pkcs11_init(1); 8400cbfa66cSDaniel Fojt nkeys = pkcs11_add_provider(pkcs11provider, NULL, &keys, &comments); 841856ea928SPeter Avalos if (nkeys <= 0) 842856ea928SPeter Avalos fatal("cannot read public key from pkcs11"); 843856ea928SPeter Avalos for (i = 0; i < nkeys; i++) { 84436e94dc5SPeter Avalos if (print_fingerprint) { 845e9778795SPeter Avalos fp = sshkey_fingerprint(keys[i], fptype, rep); 846e9778795SPeter Avalos ra = sshkey_fingerprint(keys[i], fingerprint_hash, 84736e94dc5SPeter Avalos SSH_FP_RANDOMART); 848e9778795SPeter Avalos if (fp == NULL || ra == NULL) 849e9778795SPeter Avalos fatal("%s: sshkey_fingerprint fail", __func__); 850e9778795SPeter Avalos printf("%u %s %s (PKCS11 key)\n", sshkey_size(keys[i]), 851e9778795SPeter Avalos fp, sshkey_type(keys[i])); 852664f4763Szrj if (log_level_get() >= SYSLOG_LEVEL_VERBOSE) 85336e94dc5SPeter Avalos printf("%s\n", ra); 85436e94dc5SPeter Avalos free(ra); 85536e94dc5SPeter Avalos free(fp); 85636e94dc5SPeter Avalos } else { 857e9778795SPeter Avalos (void) sshkey_write(keys[i], stdout); /* XXX check */ 8580cbfa66cSDaniel Fojt fprintf(stdout, "%s%s\n", 8590cbfa66cSDaniel Fojt *(comments[i]) == '\0' ? "" : " ", comments[i]); 86018de8d7fSPeter Avalos } 8610cbfa66cSDaniel Fojt free(comments[i]); 862e9778795SPeter Avalos sshkey_free(keys[i]); 86336e94dc5SPeter Avalos } 8640cbfa66cSDaniel Fojt free(comments); 86536e94dc5SPeter Avalos free(keys); 866856ea928SPeter Avalos pkcs11_terminate(); 86718de8d7fSPeter Avalos exit(0); 868856ea928SPeter Avalos #else 869856ea928SPeter Avalos fatal("no pkcs11 support"); 870856ea928SPeter Avalos #endif /* ENABLE_PKCS11 */ 87118de8d7fSPeter Avalos } 87218de8d7fSPeter Avalos 873e9778795SPeter Avalos static struct sshkey * 874e9778795SPeter Avalos try_read_key(char **cpp) 875e9778795SPeter Avalos { 876e9778795SPeter Avalos struct sshkey *ret; 877e9778795SPeter Avalos int r; 878e9778795SPeter Avalos 879e9778795SPeter Avalos if ((ret = sshkey_new(KEY_UNSPEC)) == NULL) 880e9778795SPeter Avalos fatal("sshkey_new failed"); 881e9778795SPeter Avalos if ((r = sshkey_read(ret, cpp)) == 0) 882e9778795SPeter Avalos return ret; 883e9778795SPeter Avalos /* Not a key */ 884e9778795SPeter Avalos sshkey_free(ret); 885e9778795SPeter Avalos return NULL; 886e9778795SPeter Avalos } 887e9778795SPeter Avalos 888e9778795SPeter Avalos static void 889e9778795SPeter Avalos fingerprint_one_key(const struct sshkey *public, const char *comment) 890e9778795SPeter Avalos { 891e9778795SPeter Avalos char *fp = NULL, *ra = NULL; 892e9778795SPeter Avalos enum sshkey_fp_rep rep; 893e9778795SPeter Avalos int fptype; 894e9778795SPeter Avalos 895e9778795SPeter Avalos fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash; 896e9778795SPeter Avalos rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT; 897e9778795SPeter Avalos fp = sshkey_fingerprint(public, fptype, rep); 898e9778795SPeter Avalos ra = sshkey_fingerprint(public, fingerprint_hash, SSH_FP_RANDOMART); 899e9778795SPeter Avalos if (fp == NULL || ra == NULL) 900e9778795SPeter Avalos fatal("%s: sshkey_fingerprint failed", __func__); 901ce74bacaSMatthew Dillon mprintf("%u %s %s (%s)\n", sshkey_size(public), fp, 902e9778795SPeter Avalos comment ? comment : "no comment", sshkey_type(public)); 903664f4763Szrj if (log_level_get() >= SYSLOG_LEVEL_VERBOSE) 904e9778795SPeter Avalos printf("%s\n", ra); 905e9778795SPeter Avalos free(ra); 906e9778795SPeter Avalos free(fp); 907e9778795SPeter Avalos } 908e9778795SPeter Avalos 909e9778795SPeter Avalos static void 910e9778795SPeter Avalos fingerprint_private(const char *path) 911e9778795SPeter Avalos { 912e9778795SPeter Avalos struct stat st; 913e9778795SPeter Avalos char *comment = NULL; 9140cbfa66cSDaniel Fojt struct sshkey *privkey = NULL, *pubkey = NULL; 915e9778795SPeter Avalos int r; 916e9778795SPeter Avalos 9170cbfa66cSDaniel Fojt if (stat(identity_file, &st) == -1) 918e9778795SPeter Avalos fatal("%s: %s", path, strerror(errno)); 9190cbfa66cSDaniel Fojt if ((r = sshkey_load_public(path, &pubkey, &comment)) != 0) 920e9778795SPeter Avalos debug("load public \"%s\": %s", path, ssh_err(r)); 9210cbfa66cSDaniel Fojt if (pubkey == NULL || comment == NULL || *comment == '\0') { 9220cbfa66cSDaniel Fojt free(comment); 923e9778795SPeter Avalos if ((r = sshkey_load_private(path, NULL, 9240cbfa66cSDaniel Fojt &privkey, &comment)) != 0) 925e9778795SPeter Avalos debug("load private \"%s\": %s", path, ssh_err(r)); 9260cbfa66cSDaniel Fojt } 9270cbfa66cSDaniel Fojt if (pubkey == NULL && privkey == NULL) 928e9778795SPeter Avalos fatal("%s is not a key file.", path); 929e9778795SPeter Avalos 9300cbfa66cSDaniel Fojt fingerprint_one_key(pubkey == NULL ? privkey : pubkey, comment); 9310cbfa66cSDaniel Fojt sshkey_free(pubkey); 9320cbfa66cSDaniel Fojt sshkey_free(privkey); 933e9778795SPeter Avalos free(comment); 934e9778795SPeter Avalos } 935e9778795SPeter Avalos 93618de8d7fSPeter Avalos static void 93718de8d7fSPeter Avalos do_fingerprint(struct passwd *pw) 93818de8d7fSPeter Avalos { 93918de8d7fSPeter Avalos FILE *f; 940e9778795SPeter Avalos struct sshkey *public = NULL; 941664f4763Szrj char *comment = NULL, *cp, *ep, *line = NULL; 942664f4763Szrj size_t linesize = 0; 943e9778795SPeter Avalos int i, invalid = 1; 944e9778795SPeter Avalos const char *path; 945e9778795SPeter Avalos u_long lnum = 0; 94618de8d7fSPeter Avalos 94718de8d7fSPeter Avalos if (!have_identity) 94818de8d7fSPeter Avalos ask_filename(pw, "Enter file in which the key is"); 949e9778795SPeter Avalos path = identity_file; 950e9778795SPeter Avalos 951e9778795SPeter Avalos if (strcmp(identity_file, "-") == 0) { 952e9778795SPeter Avalos f = stdin; 953e9778795SPeter Avalos path = "(stdin)"; 954e9778795SPeter Avalos } else if ((f = fopen(path, "r")) == NULL) 955e9778795SPeter Avalos fatal("%s: %s: %s", __progname, path, strerror(errno)); 956e9778795SPeter Avalos 957664f4763Szrj while (getline(&line, &linesize, f) != -1) { 958664f4763Szrj lnum++; 959e9778795SPeter Avalos cp = line; 960e9778795SPeter Avalos cp[strcspn(cp, "\n")] = '\0'; 961e9778795SPeter Avalos /* Trim leading space and comments */ 962e9778795SPeter Avalos cp = line + strspn(line, " \t"); 963e9778795SPeter Avalos if (*cp == '#' || *cp == '\0') 964e9778795SPeter Avalos continue; 965e9778795SPeter Avalos 966e9778795SPeter Avalos /* 967e9778795SPeter Avalos * Input may be plain keys, private keys, authorized_keys 968e9778795SPeter Avalos * or known_hosts. 969e9778795SPeter Avalos */ 970e9778795SPeter Avalos 971e9778795SPeter Avalos /* 972e9778795SPeter Avalos * Try private keys first. Assume a key is private if 973e9778795SPeter Avalos * "SSH PRIVATE KEY" appears on the first line and we're 974e9778795SPeter Avalos * not reading from stdin (XXX support private keys on stdin). 975e9778795SPeter Avalos */ 976e9778795SPeter Avalos if (lnum == 1 && strcmp(identity_file, "-") != 0 && 977e9778795SPeter Avalos strstr(cp, "PRIVATE KEY") != NULL) { 978664f4763Szrj free(line); 979e9778795SPeter Avalos fclose(f); 980e9778795SPeter Avalos fingerprint_private(path); 98118de8d7fSPeter Avalos exit(0); 98218de8d7fSPeter Avalos } 98318de8d7fSPeter Avalos 984e9778795SPeter Avalos /* 985e9778795SPeter Avalos * If it's not a private key, then this must be prepared to 986e9778795SPeter Avalos * accept a public key prefixed with a hostname or options. 987e9778795SPeter Avalos * Try a bare key first, otherwise skip the leading stuff. 988e9778795SPeter Avalos */ 989e9778795SPeter Avalos if ((public = try_read_key(&cp)) == NULL) { 99018de8d7fSPeter Avalos i = strtol(cp, &ep, 10); 991e9778795SPeter Avalos if (i == 0 || ep == NULL || 992e9778795SPeter Avalos (*ep != ' ' && *ep != '\t')) { 99318de8d7fSPeter Avalos int quoted = 0; 994e9778795SPeter Avalos 99518de8d7fSPeter Avalos comment = cp; 99618de8d7fSPeter Avalos for (; *cp && (quoted || (*cp != ' ' && 99718de8d7fSPeter Avalos *cp != '\t')); cp++) { 99818de8d7fSPeter Avalos if (*cp == '\\' && cp[1] == '"') 99918de8d7fSPeter Avalos cp++; /* Skip both */ 100018de8d7fSPeter Avalos else if (*cp == '"') 100118de8d7fSPeter Avalos quoted = !quoted; 100218de8d7fSPeter Avalos } 100318de8d7fSPeter Avalos if (!*cp) 100418de8d7fSPeter Avalos continue; 100518de8d7fSPeter Avalos *cp++ = '\0'; 100618de8d7fSPeter Avalos } 1007e9778795SPeter Avalos } 1008e9778795SPeter Avalos /* Retry after parsing leading hostname/key options */ 1009e9778795SPeter Avalos if (public == NULL && (public = try_read_key(&cp)) == NULL) { 1010e9778795SPeter Avalos debug("%s:%lu: not a public key", path, lnum); 101118de8d7fSPeter Avalos continue; 101218de8d7fSPeter Avalos } 1013e9778795SPeter Avalos 1014e9778795SPeter Avalos /* Find trailing comment, if any */ 1015e9778795SPeter Avalos for (; *cp == ' ' || *cp == '\t'; cp++) 1016e9778795SPeter Avalos ; 1017e9778795SPeter Avalos if (*cp != '\0' && *cp != '#') 1018e9778795SPeter Avalos comment = cp; 1019e9778795SPeter Avalos 1020e9778795SPeter Avalos fingerprint_one_key(public, comment); 1021e9778795SPeter Avalos sshkey_free(public); 1022e9778795SPeter Avalos invalid = 0; /* One good key in the file is sufficient */ 102318de8d7fSPeter Avalos } 102418de8d7fSPeter Avalos fclose(f); 1025664f4763Szrj free(line); 1026856ea928SPeter Avalos 1027e9778795SPeter Avalos if (invalid) 1028e9778795SPeter Avalos fatal("%s is not a public key file.", path); 102918de8d7fSPeter Avalos exit(0); 103018de8d7fSPeter Avalos } 103118de8d7fSPeter Avalos 103218de8d7fSPeter Avalos static void 10331c188a7fSPeter Avalos do_gen_all_hostkeys(struct passwd *pw) 10341c188a7fSPeter Avalos { 10351c188a7fSPeter Avalos struct { 10361c188a7fSPeter Avalos char *key_type; 10371c188a7fSPeter Avalos char *key_type_display; 10381c188a7fSPeter Avalos char *path; 10391c188a7fSPeter Avalos } key_types[] = { 1040e9778795SPeter Avalos #ifdef WITH_OPENSSL 10411c188a7fSPeter Avalos { "rsa", "RSA" ,_PATH_HOST_RSA_KEY_FILE }, 10421c188a7fSPeter Avalos { "dsa", "DSA", _PATH_HOST_DSA_KEY_FILE }, 104399e85e0dSPeter Avalos #ifdef OPENSSL_HAS_ECC 10441c188a7fSPeter Avalos { "ecdsa", "ECDSA",_PATH_HOST_ECDSA_KEY_FILE }, 1045e9778795SPeter Avalos #endif /* OPENSSL_HAS_ECC */ 1046e9778795SPeter Avalos #endif /* WITH_OPENSSL */ 104736e94dc5SPeter Avalos { "ed25519", "ED25519",_PATH_HOST_ED25519_KEY_FILE }, 1048664f4763Szrj #ifdef WITH_XMSS 1049664f4763Szrj { "xmss", "XMSS",_PATH_HOST_XMSS_KEY_FILE }, 1050664f4763Szrj #endif /* WITH_XMSS */ 10511c188a7fSPeter Avalos { NULL, NULL, NULL } 10521c188a7fSPeter Avalos }; 10531c188a7fSPeter Avalos 10540cbfa66cSDaniel Fojt u_int32_t bits = 0; 10551c188a7fSPeter Avalos int first = 0; 10561c188a7fSPeter Avalos struct stat st; 1057e9778795SPeter Avalos struct sshkey *private, *public; 1058ce74bacaSMatthew Dillon char comment[1024], *prv_tmp, *pub_tmp, *prv_file, *pub_file; 1059e9778795SPeter Avalos int i, type, fd, r; 10601c188a7fSPeter Avalos 10611c188a7fSPeter Avalos for (i = 0; key_types[i].key_type; i++) { 1062ce74bacaSMatthew Dillon public = private = NULL; 1063ce74bacaSMatthew Dillon prv_tmp = pub_tmp = prv_file = pub_file = NULL; 1064ce74bacaSMatthew Dillon 1065ce74bacaSMatthew Dillon xasprintf(&prv_file, "%s%s", 1066ce74bacaSMatthew Dillon identity_file, key_types[i].path); 1067ce74bacaSMatthew Dillon 1068ce74bacaSMatthew Dillon /* Check whether private key exists and is not zero-length */ 1069ce74bacaSMatthew Dillon if (stat(prv_file, &st) == 0) { 1070ce74bacaSMatthew Dillon if (st.st_size != 0) 1071ce74bacaSMatthew Dillon goto next; 1072ce74bacaSMatthew Dillon } else if (errno != ENOENT) { 1073e9778795SPeter Avalos error("Could not stat %s: %s", key_types[i].path, 10741c188a7fSPeter Avalos strerror(errno)); 1075ce74bacaSMatthew Dillon goto failnext; 10761c188a7fSPeter Avalos } 10771c188a7fSPeter Avalos 1078ce74bacaSMatthew Dillon /* 1079ce74bacaSMatthew Dillon * Private key doesn't exist or is invalid; proceed with 1080ce74bacaSMatthew Dillon * key generation. 1081ce74bacaSMatthew Dillon */ 1082ce74bacaSMatthew Dillon xasprintf(&prv_tmp, "%s%s.XXXXXXXXXX", 1083ce74bacaSMatthew Dillon identity_file, key_types[i].path); 1084ce74bacaSMatthew Dillon xasprintf(&pub_tmp, "%s%s.pub.XXXXXXXXXX", 1085ce74bacaSMatthew Dillon identity_file, key_types[i].path); 1086ce74bacaSMatthew Dillon xasprintf(&pub_file, "%s%s.pub", 1087ce74bacaSMatthew Dillon identity_file, key_types[i].path); 1088ce74bacaSMatthew Dillon 10891c188a7fSPeter Avalos if (first == 0) { 10901c188a7fSPeter Avalos first = 1; 10911c188a7fSPeter Avalos printf("%s: generating new host keys: ", __progname); 10921c188a7fSPeter Avalos } 10931c188a7fSPeter Avalos printf("%s ", key_types[i].key_type_display); 10941c188a7fSPeter Avalos fflush(stdout); 1095e9778795SPeter Avalos type = sshkey_type_from_name(key_types[i].key_type); 1096ce74bacaSMatthew Dillon if ((fd = mkstemp(prv_tmp)) == -1) { 10970cbfa66cSDaniel Fojt error("Could not save your private key in %s: %s", 1098ce74bacaSMatthew Dillon prv_tmp, strerror(errno)); 1099ce74bacaSMatthew Dillon goto failnext; 1100ce74bacaSMatthew Dillon } 11010cbfa66cSDaniel Fojt (void)close(fd); /* just using mkstemp() to reserve a name */ 11021c188a7fSPeter Avalos bits = 0; 1103e9778795SPeter Avalos type_bits_valid(type, NULL, &bits); 1104e9778795SPeter Avalos if ((r = sshkey_generate(type, bits, &private)) != 0) { 1105ce74bacaSMatthew Dillon error("sshkey_generate failed: %s", ssh_err(r)); 1106ce74bacaSMatthew Dillon goto failnext; 11071c188a7fSPeter Avalos } 1108e9778795SPeter Avalos if ((r = sshkey_from_private(private, &public)) != 0) 1109e9778795SPeter Avalos fatal("sshkey_from_private failed: %s", ssh_err(r)); 11101c188a7fSPeter Avalos snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, 11111c188a7fSPeter Avalos hostname); 1112ce74bacaSMatthew Dillon if ((r = sshkey_save_private(private, prv_tmp, "", 11130cbfa66cSDaniel Fojt comment, private_key_format, openssh_format_cipher, 11140cbfa66cSDaniel Fojt rounds)) != 0) { 1115e9778795SPeter Avalos error("Saving key \"%s\" failed: %s", 1116ce74bacaSMatthew Dillon prv_tmp, ssh_err(r)); 1117ce74bacaSMatthew Dillon goto failnext; 11181c188a7fSPeter Avalos } 1119ce74bacaSMatthew Dillon if ((fd = mkstemp(pub_tmp)) == -1) { 1120ce74bacaSMatthew Dillon error("Could not save your public key in %s: %s", 1121ce74bacaSMatthew Dillon pub_tmp, strerror(errno)); 1122ce74bacaSMatthew Dillon goto failnext; 11231c188a7fSPeter Avalos } 1124ce74bacaSMatthew Dillon (void)fchmod(fd, 0644); 11250cbfa66cSDaniel Fojt (void)close(fd); 11260cbfa66cSDaniel Fojt if ((r = sshkey_save_public(public, pub_tmp, comment)) != 0) { 11270cbfa66cSDaniel Fojt fatal("Unable to save public key to %s: %s", 11280cbfa66cSDaniel Fojt identity_file, ssh_err(r)); 1129ce74bacaSMatthew Dillon goto failnext; 1130ce74bacaSMatthew Dillon } 11311c188a7fSPeter Avalos 1132ce74bacaSMatthew Dillon /* Rename temporary files to their permanent locations. */ 1133ce74bacaSMatthew Dillon if (rename(pub_tmp, pub_file) != 0) { 1134ce74bacaSMatthew Dillon error("Unable to move %s into position: %s", 1135ce74bacaSMatthew Dillon pub_file, strerror(errno)); 1136ce74bacaSMatthew Dillon goto failnext; 1137ce74bacaSMatthew Dillon } 1138ce74bacaSMatthew Dillon if (rename(prv_tmp, prv_file) != 0) { 1139ce74bacaSMatthew Dillon error("Unable to move %s into position: %s", 1140ce74bacaSMatthew Dillon key_types[i].path, strerror(errno)); 1141ce74bacaSMatthew Dillon failnext: 1142ce74bacaSMatthew Dillon first = 0; 1143ce74bacaSMatthew Dillon goto next; 1144ce74bacaSMatthew Dillon } 1145ce74bacaSMatthew Dillon next: 1146ce74bacaSMatthew Dillon sshkey_free(private); 1147ce74bacaSMatthew Dillon sshkey_free(public); 1148ce74bacaSMatthew Dillon free(prv_tmp); 1149ce74bacaSMatthew Dillon free(pub_tmp); 1150ce74bacaSMatthew Dillon free(prv_file); 1151ce74bacaSMatthew Dillon free(pub_file); 11521c188a7fSPeter Avalos } 11531c188a7fSPeter Avalos if (first != 0) 11541c188a7fSPeter Avalos printf("\n"); 11551c188a7fSPeter Avalos } 11561c188a7fSPeter Avalos 1157e9778795SPeter Avalos struct known_hosts_ctx { 1158e9778795SPeter Avalos const char *host; /* Hostname searched for in find/delete case */ 1159e9778795SPeter Avalos FILE *out; /* Output file, stdout for find_hosts case */ 1160e9778795SPeter Avalos int has_unhashed; /* When hashing, original had unhashed hosts */ 1161e9778795SPeter Avalos int found_key; /* For find/delete, host was found */ 1162e9778795SPeter Avalos int invalid; /* File contained invalid items; don't delete */ 1163664f4763Szrj int hash_hosts; /* Hash hostnames as we go */ 1164664f4763Szrj int find_host; /* Search for specific hostname */ 1165664f4763Szrj int delete_host; /* Delete host from known_hosts */ 1166e9778795SPeter Avalos }; 116718de8d7fSPeter Avalos 1168e9778795SPeter Avalos static int 1169e9778795SPeter Avalos known_hosts_hash(struct hostkey_foreach_line *l, void *_ctx) 1170e9778795SPeter Avalos { 1171e9778795SPeter Avalos struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx; 1172e9778795SPeter Avalos char *hashed, *cp, *hosts, *ohosts; 1173e9778795SPeter Avalos int has_wild = l->hosts && strcspn(l->hosts, "*?!") != strlen(l->hosts); 1174ce74bacaSMatthew Dillon int was_hashed = l->hosts && l->hosts[0] == HASH_DELIM; 1175e9778795SPeter Avalos 1176e9778795SPeter Avalos switch (l->status) { 1177e9778795SPeter Avalos case HKF_STATUS_OK: 1178e9778795SPeter Avalos case HKF_STATUS_MATCHED: 1179e9778795SPeter Avalos /* 1180e9778795SPeter Avalos * Don't hash hosts already already hashed, with wildcard 1181e9778795SPeter Avalos * characters or a CA/revocation marker. 1182e9778795SPeter Avalos */ 1183ce74bacaSMatthew Dillon if (was_hashed || has_wild || l->marker != MRK_NONE) { 1184e9778795SPeter Avalos fprintf(ctx->out, "%s\n", l->line); 1185664f4763Szrj if (has_wild && !ctx->find_host) { 1186ce74bacaSMatthew Dillon logit("%s:%lu: ignoring host name " 1187e9778795SPeter Avalos "with wildcard: %.64s", l->path, 1188e9778795SPeter Avalos l->linenum, l->hosts); 118918de8d7fSPeter Avalos } 1190e9778795SPeter Avalos return 0; 1191e9778795SPeter Avalos } 1192e9778795SPeter Avalos /* 1193e9778795SPeter Avalos * Split any comma-separated hostnames from the host list, 1194e9778795SPeter Avalos * hash and store separately. 1195e9778795SPeter Avalos */ 1196e9778795SPeter Avalos ohosts = hosts = xstrdup(l->hosts); 1197e9778795SPeter Avalos while ((cp = strsep(&hosts, ",")) != NULL && *cp != '\0') { 1198ce74bacaSMatthew Dillon lowercase(cp); 1199e9778795SPeter Avalos if ((hashed = host_hash(cp, NULL, 0)) == NULL) 1200e9778795SPeter Avalos fatal("hash_host failed"); 1201e9778795SPeter Avalos fprintf(ctx->out, "%s %s\n", hashed, l->rawkey); 1202e9778795SPeter Avalos ctx->has_unhashed = 1; 1203e9778795SPeter Avalos } 1204e9778795SPeter Avalos free(ohosts); 1205e9778795SPeter Avalos return 0; 1206e9778795SPeter Avalos case HKF_STATUS_INVALID: 1207e9778795SPeter Avalos /* Retain invalid lines, but mark file as invalid. */ 1208e9778795SPeter Avalos ctx->invalid = 1; 1209ce74bacaSMatthew Dillon logit("%s:%lu: invalid line", l->path, l->linenum); 1210e9778795SPeter Avalos /* FALLTHROUGH */ 1211e9778795SPeter Avalos default: 1212e9778795SPeter Avalos fprintf(ctx->out, "%s\n", l->line); 1213e9778795SPeter Avalos return 0; 1214e9778795SPeter Avalos } 1215e9778795SPeter Avalos /* NOTREACHED */ 1216e9778795SPeter Avalos return -1; 1217e9778795SPeter Avalos } 1218e9778795SPeter Avalos 1219e9778795SPeter Avalos static int 1220e9778795SPeter Avalos known_hosts_find_delete(struct hostkey_foreach_line *l, void *_ctx) 1221e9778795SPeter Avalos { 1222e9778795SPeter Avalos struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx; 1223e9778795SPeter Avalos enum sshkey_fp_rep rep; 1224e9778795SPeter Avalos int fptype; 12250cbfa66cSDaniel Fojt char *fp = NULL, *ra = NULL; 1226e9778795SPeter Avalos 1227e9778795SPeter Avalos fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash; 1228e9778795SPeter Avalos rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT; 1229e9778795SPeter Avalos 1230e9778795SPeter Avalos if (l->status == HKF_STATUS_MATCHED) { 1231664f4763Szrj if (ctx->delete_host) { 1232e9778795SPeter Avalos if (l->marker != MRK_NONE) { 1233e9778795SPeter Avalos /* Don't remove CA and revocation lines */ 1234e9778795SPeter Avalos fprintf(ctx->out, "%s\n", l->line); 1235e9778795SPeter Avalos } else { 1236e9778795SPeter Avalos /* 1237e9778795SPeter Avalos * Hostname matches and has no CA/revoke 1238e9778795SPeter Avalos * marker, delete it by *not* writing the 1239e9778795SPeter Avalos * line to ctx->out. 1240e9778795SPeter Avalos */ 1241e9778795SPeter Avalos ctx->found_key = 1; 1242e9778795SPeter Avalos if (!quiet) 1243ce74bacaSMatthew Dillon printf("# Host %s found: line %lu\n", 1244e9778795SPeter Avalos ctx->host, l->linenum); 1245e9778795SPeter Avalos } 1246e9778795SPeter Avalos return 0; 1247664f4763Szrj } else if (ctx->find_host) { 1248e9778795SPeter Avalos ctx->found_key = 1; 1249e9778795SPeter Avalos if (!quiet) { 1250ce74bacaSMatthew Dillon printf("# Host %s found: line %lu %s\n", 1251e9778795SPeter Avalos ctx->host, 1252e9778795SPeter Avalos l->linenum, l->marker == MRK_CA ? "CA" : 1253e9778795SPeter Avalos (l->marker == MRK_REVOKE ? "REVOKED" : "")); 1254e9778795SPeter Avalos } 1255664f4763Szrj if (ctx->hash_hosts) 1256e9778795SPeter Avalos known_hosts_hash(l, ctx); 1257e9778795SPeter Avalos else if (print_fingerprint) { 1258e9778795SPeter Avalos fp = sshkey_fingerprint(l->key, fptype, rep); 12590cbfa66cSDaniel Fojt ra = sshkey_fingerprint(l->key, 12600cbfa66cSDaniel Fojt fingerprint_hash, SSH_FP_RANDOMART); 12610cbfa66cSDaniel Fojt if (fp == NULL || ra == NULL) 12620cbfa66cSDaniel Fojt fatal("%s: sshkey_fingerprint failed", 12630cbfa66cSDaniel Fojt __func__); 12640cbfa66cSDaniel Fojt mprintf("%s %s %s%s%s\n", ctx->host, 12650cbfa66cSDaniel Fojt sshkey_type(l->key), fp, 12660cbfa66cSDaniel Fojt l->comment[0] ? " " : "", 12670cbfa66cSDaniel Fojt l->comment); 12680cbfa66cSDaniel Fojt if (log_level_get() >= SYSLOG_LEVEL_VERBOSE) 12690cbfa66cSDaniel Fojt printf("%s\n", ra); 12700cbfa66cSDaniel Fojt free(ra); 1271e9778795SPeter Avalos free(fp); 1272e9778795SPeter Avalos } else 1273e9778795SPeter Avalos fprintf(ctx->out, "%s\n", l->line); 1274e9778795SPeter Avalos return 0; 1275e9778795SPeter Avalos } 1276664f4763Szrj } else if (ctx->delete_host) { 1277e9778795SPeter Avalos /* Retain non-matching hosts when deleting */ 1278e9778795SPeter Avalos if (l->status == HKF_STATUS_INVALID) { 1279e9778795SPeter Avalos ctx->invalid = 1; 1280ce74bacaSMatthew Dillon logit("%s:%lu: invalid line", l->path, l->linenum); 1281e9778795SPeter Avalos } 1282e9778795SPeter Avalos fprintf(ctx->out, "%s\n", l->line); 1283e9778795SPeter Avalos } 1284e9778795SPeter Avalos return 0; 128518de8d7fSPeter Avalos } 128618de8d7fSPeter Avalos 128718de8d7fSPeter Avalos static void 1288664f4763Szrj do_known_hosts(struct passwd *pw, const char *name, int find_host, 1289664f4763Szrj int delete_host, int hash_hosts) 129018de8d7fSPeter Avalos { 1291e9778795SPeter Avalos char *cp, tmp[PATH_MAX], old[PATH_MAX]; 1292e9778795SPeter Avalos int r, fd, oerrno, inplace = 0; 1293e9778795SPeter Avalos struct known_hosts_ctx ctx; 1294e9778795SPeter Avalos u_int foreach_options; 129518de8d7fSPeter Avalos 129618de8d7fSPeter Avalos if (!have_identity) { 129718de8d7fSPeter Avalos cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid); 129818de8d7fSPeter Avalos if (strlcpy(identity_file, cp, sizeof(identity_file)) >= 129918de8d7fSPeter Avalos sizeof(identity_file)) 130018de8d7fSPeter Avalos fatal("Specified known hosts path too long"); 130136e94dc5SPeter Avalos free(cp); 130218de8d7fSPeter Avalos have_identity = 1; 130318de8d7fSPeter Avalos } 130418de8d7fSPeter Avalos 1305e9778795SPeter Avalos memset(&ctx, 0, sizeof(ctx)); 1306e9778795SPeter Avalos ctx.out = stdout; 1307e9778795SPeter Avalos ctx.host = name; 1308664f4763Szrj ctx.hash_hosts = hash_hosts; 1309664f4763Szrj ctx.find_host = find_host; 1310664f4763Szrj ctx.delete_host = delete_host; 1311e9778795SPeter Avalos 131218de8d7fSPeter Avalos /* 131318de8d7fSPeter Avalos * Find hosts goes to stdout, hash and deletions happen in-place 131418de8d7fSPeter Avalos * A corner case is ssh-keygen -HF foo, which should go to stdout 131518de8d7fSPeter Avalos */ 131618de8d7fSPeter Avalos if (!find_host && (hash_hosts || delete_host)) { 131718de8d7fSPeter Avalos if (strlcpy(tmp, identity_file, sizeof(tmp)) >= sizeof(tmp) || 131818de8d7fSPeter Avalos strlcat(tmp, ".XXXXXXXXXX", sizeof(tmp)) >= sizeof(tmp) || 131918de8d7fSPeter Avalos strlcpy(old, identity_file, sizeof(old)) >= sizeof(old) || 132018de8d7fSPeter Avalos strlcat(old, ".old", sizeof(old)) >= sizeof(old)) 132118de8d7fSPeter Avalos fatal("known_hosts path too long"); 132218de8d7fSPeter Avalos umask(077); 1323e9778795SPeter Avalos if ((fd = mkstemp(tmp)) == -1) 132418de8d7fSPeter Avalos fatal("mkstemp: %s", strerror(errno)); 1325e9778795SPeter Avalos if ((ctx.out = fdopen(fd, "w")) == NULL) { 1326e9778795SPeter Avalos oerrno = errno; 132718de8d7fSPeter Avalos unlink(tmp); 1328e9778795SPeter Avalos fatal("fdopen: %s", strerror(oerrno)); 132918de8d7fSPeter Avalos } 133018de8d7fSPeter Avalos inplace = 1; 133118de8d7fSPeter Avalos } 1332e9778795SPeter Avalos /* XXX support identity_file == "-" for stdin */ 1333e9778795SPeter Avalos foreach_options = find_host ? HKF_WANT_MATCH : 0; 1334e9778795SPeter Avalos foreach_options |= print_fingerprint ? HKF_WANT_PARSE_KEY : 0; 1335664f4763Szrj if ((r = hostkeys_foreach(identity_file, (find_host || !hash_hosts) ? 1336664f4763Szrj known_hosts_find_delete : known_hosts_hash, &ctx, name, NULL, 1337664f4763Szrj foreach_options)) != 0) { 133818de8d7fSPeter Avalos if (inplace) 1339e9778795SPeter Avalos unlink(tmp); 1340e9778795SPeter Avalos fatal("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r)); 134118de8d7fSPeter Avalos } 134218de8d7fSPeter Avalos 1343e9778795SPeter Avalos if (inplace) 1344e9778795SPeter Avalos fclose(ctx.out); 134518de8d7fSPeter Avalos 1346e9778795SPeter Avalos if (ctx.invalid) { 1347e9778795SPeter Avalos error("%s is not a valid known_hosts file.", identity_file); 134818de8d7fSPeter Avalos if (inplace) { 1349e9778795SPeter Avalos error("Not replacing existing known_hosts " 1350e9778795SPeter Avalos "file because of errors"); 135118de8d7fSPeter Avalos unlink(tmp); 135218de8d7fSPeter Avalos } 135318de8d7fSPeter Avalos exit(1); 1354e9778795SPeter Avalos } else if (delete_host && !ctx.found_key) { 1355e9778795SPeter Avalos logit("Host %s not found in %s", name, identity_file); 1356e9778795SPeter Avalos if (inplace) 1357e9778795SPeter Avalos unlink(tmp); 1358e9778795SPeter Avalos } else if (inplace) { 135918de8d7fSPeter Avalos /* Backup existing file */ 136018de8d7fSPeter Avalos if (unlink(old) == -1 && errno != ENOENT) 136118de8d7fSPeter Avalos fatal("unlink %.100s: %s", old, strerror(errno)); 136218de8d7fSPeter Avalos if (link(identity_file, old) == -1) 136318de8d7fSPeter Avalos fatal("link %.100s to %.100s: %s", identity_file, old, 136418de8d7fSPeter Avalos strerror(errno)); 136518de8d7fSPeter Avalos /* Move new one into place */ 136618de8d7fSPeter Avalos if (rename(tmp, identity_file) == -1) { 136718de8d7fSPeter Avalos error("rename\"%s\" to \"%s\": %s", tmp, identity_file, 136818de8d7fSPeter Avalos strerror(errno)); 136918de8d7fSPeter Avalos unlink(tmp); 137018de8d7fSPeter Avalos unlink(old); 137118de8d7fSPeter Avalos exit(1); 137218de8d7fSPeter Avalos } 137318de8d7fSPeter Avalos 1374e9778795SPeter Avalos printf("%s updated.\n", identity_file); 1375e9778795SPeter Avalos printf("Original contents retained as %s\n", old); 1376e9778795SPeter Avalos if (ctx.has_unhashed) { 1377e9778795SPeter Avalos logit("WARNING: %s contains unhashed entries", old); 1378e9778795SPeter Avalos logit("Delete this file to ensure privacy " 1379e9778795SPeter Avalos "of hostnames"); 138018de8d7fSPeter Avalos } 138118de8d7fSPeter Avalos } 138218de8d7fSPeter Avalos 1383e9778795SPeter Avalos exit (find_host && !ctx.found_key); 138418de8d7fSPeter Avalos } 138518de8d7fSPeter Avalos 138618de8d7fSPeter Avalos /* 138718de8d7fSPeter Avalos * Perform changing a passphrase. The argument is the passwd structure 138818de8d7fSPeter Avalos * for the current user. 138918de8d7fSPeter Avalos */ 139018de8d7fSPeter Avalos static void 139118de8d7fSPeter Avalos do_change_passphrase(struct passwd *pw) 139218de8d7fSPeter Avalos { 139318de8d7fSPeter Avalos char *comment; 139418de8d7fSPeter Avalos char *old_passphrase, *passphrase1, *passphrase2; 139518de8d7fSPeter Avalos struct stat st; 1396e9778795SPeter Avalos struct sshkey *private; 1397e9778795SPeter Avalos int r; 139818de8d7fSPeter Avalos 139918de8d7fSPeter Avalos if (!have_identity) 140018de8d7fSPeter Avalos ask_filename(pw, "Enter file in which the key is"); 14010cbfa66cSDaniel Fojt if (stat(identity_file, &st) == -1) 1402e9778795SPeter Avalos fatal("%s: %s", identity_file, strerror(errno)); 140318de8d7fSPeter Avalos /* Try to load the file with empty passphrase. */ 1404e9778795SPeter Avalos r = sshkey_load_private(identity_file, "", &private, &comment); 1405e9778795SPeter Avalos if (r == SSH_ERR_KEY_WRONG_PASSPHRASE) { 140618de8d7fSPeter Avalos if (identity_passphrase) 140718de8d7fSPeter Avalos old_passphrase = xstrdup(identity_passphrase); 140818de8d7fSPeter Avalos else 140918de8d7fSPeter Avalos old_passphrase = 141018de8d7fSPeter Avalos read_passphrase("Enter old passphrase: ", 141118de8d7fSPeter Avalos RP_ALLOW_STDIN); 1412e9778795SPeter Avalos r = sshkey_load_private(identity_file, old_passphrase, 1413e9778795SPeter Avalos &private, &comment); 14140cbfa66cSDaniel Fojt freezero(old_passphrase, strlen(old_passphrase)); 1415e9778795SPeter Avalos if (r != 0) 1416e9778795SPeter Avalos goto badkey; 1417e9778795SPeter Avalos } else if (r != 0) { 1418e9778795SPeter Avalos badkey: 1419e9778795SPeter Avalos fatal("Failed to load key %s: %s", identity_file, ssh_err(r)); 142018de8d7fSPeter Avalos } 1421e9778795SPeter Avalos if (comment) 1422ce74bacaSMatthew Dillon mprintf("Key has comment '%s'\n", comment); 142318de8d7fSPeter Avalos 142418de8d7fSPeter Avalos /* Ask the new passphrase (twice). */ 142518de8d7fSPeter Avalos if (identity_new_passphrase) { 142618de8d7fSPeter Avalos passphrase1 = xstrdup(identity_new_passphrase); 142718de8d7fSPeter Avalos passphrase2 = NULL; 142818de8d7fSPeter Avalos } else { 142918de8d7fSPeter Avalos passphrase1 = 143018de8d7fSPeter Avalos read_passphrase("Enter new passphrase (empty for no " 143118de8d7fSPeter Avalos "passphrase): ", RP_ALLOW_STDIN); 143218de8d7fSPeter Avalos passphrase2 = read_passphrase("Enter same passphrase again: ", 143318de8d7fSPeter Avalos RP_ALLOW_STDIN); 143418de8d7fSPeter Avalos 143518de8d7fSPeter Avalos /* Verify that they are the same. */ 143618de8d7fSPeter Avalos if (strcmp(passphrase1, passphrase2) != 0) { 143736e94dc5SPeter Avalos explicit_bzero(passphrase1, strlen(passphrase1)); 143836e94dc5SPeter Avalos explicit_bzero(passphrase2, strlen(passphrase2)); 143936e94dc5SPeter Avalos free(passphrase1); 144036e94dc5SPeter Avalos free(passphrase2); 144118de8d7fSPeter Avalos printf("Pass phrases do not match. Try again.\n"); 144218de8d7fSPeter Avalos exit(1); 144318de8d7fSPeter Avalos } 144418de8d7fSPeter Avalos /* Destroy the other copy. */ 14450cbfa66cSDaniel Fojt freezero(passphrase2, strlen(passphrase2)); 144618de8d7fSPeter Avalos } 144718de8d7fSPeter Avalos 144818de8d7fSPeter Avalos /* Save the file using the new passphrase. */ 1449e9778795SPeter Avalos if ((r = sshkey_save_private(private, identity_file, passphrase1, 14500cbfa66cSDaniel Fojt comment, private_key_format, openssh_format_cipher, rounds)) != 0) { 1451e9778795SPeter Avalos error("Saving key \"%s\" failed: %s.", 1452e9778795SPeter Avalos identity_file, ssh_err(r)); 14530cbfa66cSDaniel Fojt freezero(passphrase1, strlen(passphrase1)); 1454e9778795SPeter Avalos sshkey_free(private); 145536e94dc5SPeter Avalos free(comment); 145618de8d7fSPeter Avalos exit(1); 145718de8d7fSPeter Avalos } 145818de8d7fSPeter Avalos /* Destroy the passphrase and the copy of the key in memory. */ 14590cbfa66cSDaniel Fojt freezero(passphrase1, strlen(passphrase1)); 1460e9778795SPeter Avalos sshkey_free(private); /* Destroys contents */ 146136e94dc5SPeter Avalos free(comment); 146218de8d7fSPeter Avalos 146318de8d7fSPeter Avalos printf("Your identification has been saved with the new passphrase.\n"); 146418de8d7fSPeter Avalos exit(0); 146518de8d7fSPeter Avalos } 146618de8d7fSPeter Avalos 146718de8d7fSPeter Avalos /* 146818de8d7fSPeter Avalos * Print the SSHFP RR. 146918de8d7fSPeter Avalos */ 147018de8d7fSPeter Avalos static int 1471664f4763Szrj do_print_resource_record(struct passwd *pw, char *fname, char *hname, 1472664f4763Szrj int print_generic) 147318de8d7fSPeter Avalos { 1474e9778795SPeter Avalos struct sshkey *public; 147518de8d7fSPeter Avalos char *comment = NULL; 147618de8d7fSPeter Avalos struct stat st; 1477e9778795SPeter Avalos int r; 147818de8d7fSPeter Avalos 147918de8d7fSPeter Avalos if (fname == NULL) 148036e94dc5SPeter Avalos fatal("%s: no filename", __func__); 14810cbfa66cSDaniel Fojt if (stat(fname, &st) == -1) { 148218de8d7fSPeter Avalos if (errno == ENOENT) 148318de8d7fSPeter Avalos return 0; 1484e9778795SPeter Avalos fatal("%s: %s", fname, strerror(errno)); 148518de8d7fSPeter Avalos } 1486e9778795SPeter Avalos if ((r = sshkey_load_public(fname, &public, &comment)) != 0) 1487e9778795SPeter Avalos fatal("Failed to read v2 public key from \"%s\": %s.", 1488e9778795SPeter Avalos fname, ssh_err(r)); 148918de8d7fSPeter Avalos export_dns_rr(hname, public, stdout, print_generic); 1490e9778795SPeter Avalos sshkey_free(public); 149136e94dc5SPeter Avalos free(comment); 149218de8d7fSPeter Avalos return 1; 149318de8d7fSPeter Avalos } 149418de8d7fSPeter Avalos 149518de8d7fSPeter Avalos /* 149618de8d7fSPeter Avalos * Change the comment of a private key file. 149718de8d7fSPeter Avalos */ 149818de8d7fSPeter Avalos static void 1499664f4763Szrj do_change_comment(struct passwd *pw, const char *identity_comment) 150018de8d7fSPeter Avalos { 150118de8d7fSPeter Avalos char new_comment[1024], *comment, *passphrase; 1502e9778795SPeter Avalos struct sshkey *private; 1503e9778795SPeter Avalos struct sshkey *public; 150418de8d7fSPeter Avalos struct stat st; 15050cbfa66cSDaniel Fojt int r; 150618de8d7fSPeter Avalos 150718de8d7fSPeter Avalos if (!have_identity) 150818de8d7fSPeter Avalos ask_filename(pw, "Enter file in which the key is"); 15090cbfa66cSDaniel Fojt if (stat(identity_file, &st) == -1) 1510e9778795SPeter Avalos fatal("%s: %s", identity_file, strerror(errno)); 1511e9778795SPeter Avalos if ((r = sshkey_load_private(identity_file, "", 1512e9778795SPeter Avalos &private, &comment)) == 0) 1513e9778795SPeter Avalos passphrase = xstrdup(""); 1514e9778795SPeter Avalos else if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) 1515e9778795SPeter Avalos fatal("Cannot load private key \"%s\": %s.", 1516e9778795SPeter Avalos identity_file, ssh_err(r)); 1517e9778795SPeter Avalos else { 151818de8d7fSPeter Avalos if (identity_passphrase) 151918de8d7fSPeter Avalos passphrase = xstrdup(identity_passphrase); 152018de8d7fSPeter Avalos else if (identity_new_passphrase) 152118de8d7fSPeter Avalos passphrase = xstrdup(identity_new_passphrase); 152218de8d7fSPeter Avalos else 152318de8d7fSPeter Avalos passphrase = read_passphrase("Enter passphrase: ", 152418de8d7fSPeter Avalos RP_ALLOW_STDIN); 152518de8d7fSPeter Avalos /* Try to load using the passphrase. */ 1526e9778795SPeter Avalos if ((r = sshkey_load_private(identity_file, passphrase, 1527e9778795SPeter Avalos &private, &comment)) != 0) { 15280cbfa66cSDaniel Fojt freezero(passphrase, strlen(passphrase)); 1529e9778795SPeter Avalos fatal("Cannot load private key \"%s\": %s.", 1530e9778795SPeter Avalos identity_file, ssh_err(r)); 153118de8d7fSPeter Avalos } 153218de8d7fSPeter Avalos } 1533e9778795SPeter Avalos 1534664f4763Szrj if (private->type != KEY_ED25519 && private->type != KEY_XMSS && 15350cbfa66cSDaniel Fojt private_key_format != SSHKEY_PRIVATE_OPENSSH) { 1536ce74bacaSMatthew Dillon error("Comments are only supported for keys stored in " 1537e9778795SPeter Avalos "the new format (-o)."); 1538e9778795SPeter Avalos explicit_bzero(passphrase, strlen(passphrase)); 1539e9778795SPeter Avalos sshkey_free(private); 154018de8d7fSPeter Avalos exit(1); 154118de8d7fSPeter Avalos } 1542ce74bacaSMatthew Dillon if (comment) 15430cbfa66cSDaniel Fojt printf("Old comment: %s\n", comment); 1544ce74bacaSMatthew Dillon else 15450cbfa66cSDaniel Fojt printf("No existing comment\n"); 154618de8d7fSPeter Avalos 154718de8d7fSPeter Avalos if (identity_comment) { 154818de8d7fSPeter Avalos strlcpy(new_comment, identity_comment, sizeof(new_comment)); 154918de8d7fSPeter Avalos } else { 15500cbfa66cSDaniel Fojt printf("New comment: "); 155118de8d7fSPeter Avalos fflush(stdout); 155218de8d7fSPeter Avalos if (!fgets(new_comment, sizeof(new_comment), stdin)) { 155336e94dc5SPeter Avalos explicit_bzero(passphrase, strlen(passphrase)); 1554e9778795SPeter Avalos sshkey_free(private); 155518de8d7fSPeter Avalos exit(1); 155618de8d7fSPeter Avalos } 155718de8d7fSPeter Avalos new_comment[strcspn(new_comment, "\n")] = '\0'; 155818de8d7fSPeter Avalos } 15590cbfa66cSDaniel Fojt if (comment != NULL && strcmp(comment, new_comment) == 0) { 15600cbfa66cSDaniel Fojt printf("No change to comment\n"); 15610cbfa66cSDaniel Fojt free(passphrase); 15620cbfa66cSDaniel Fojt sshkey_free(private); 15630cbfa66cSDaniel Fojt free(comment); 15640cbfa66cSDaniel Fojt exit(0); 15650cbfa66cSDaniel Fojt } 156618de8d7fSPeter Avalos 156718de8d7fSPeter Avalos /* Save the file using the new passphrase. */ 1568e9778795SPeter Avalos if ((r = sshkey_save_private(private, identity_file, passphrase, 15690cbfa66cSDaniel Fojt new_comment, private_key_format, openssh_format_cipher, 15700cbfa66cSDaniel Fojt rounds)) != 0) { 1571e9778795SPeter Avalos error("Saving key \"%s\" failed: %s", 1572e9778795SPeter Avalos identity_file, ssh_err(r)); 15730cbfa66cSDaniel Fojt freezero(passphrase, strlen(passphrase)); 1574e9778795SPeter Avalos sshkey_free(private); 157536e94dc5SPeter Avalos free(comment); 157618de8d7fSPeter Avalos exit(1); 157718de8d7fSPeter Avalos } 15780cbfa66cSDaniel Fojt freezero(passphrase, strlen(passphrase)); 1579e9778795SPeter Avalos if ((r = sshkey_from_private(private, &public)) != 0) 1580ce74bacaSMatthew Dillon fatal("sshkey_from_private failed: %s", ssh_err(r)); 1581e9778795SPeter Avalos sshkey_free(private); 158218de8d7fSPeter Avalos 158318de8d7fSPeter Avalos strlcat(identity_file, ".pub", sizeof(identity_file)); 15840cbfa66cSDaniel Fojt if ((r = sshkey_save_public(public, identity_file, new_comment)) != 0) { 15850cbfa66cSDaniel Fojt fatal("Unable to save public key to %s: %s", 15860cbfa66cSDaniel Fojt identity_file, ssh_err(r)); 15870cbfa66cSDaniel Fojt } 1588e9778795SPeter Avalos sshkey_free(public); 158936e94dc5SPeter Avalos free(comment); 159018de8d7fSPeter Avalos 15910cbfa66cSDaniel Fojt if (strlen(new_comment) > 0) 15920cbfa66cSDaniel Fojt printf("Comment '%s' applied\n", new_comment); 15930cbfa66cSDaniel Fojt else 15940cbfa66cSDaniel Fojt printf("Comment removed\n"); 15950cbfa66cSDaniel Fojt 159618de8d7fSPeter Avalos exit(0); 159718de8d7fSPeter Avalos } 159818de8d7fSPeter Avalos 1599856ea928SPeter Avalos static void 1600e9778795SPeter Avalos add_flag_option(struct sshbuf *c, const char *name) 1601856ea928SPeter Avalos { 1602e9778795SPeter Avalos int r; 1603e9778795SPeter Avalos 1604856ea928SPeter Avalos debug3("%s: %s", __func__, name); 1605e9778795SPeter Avalos if ((r = sshbuf_put_cstring(c, name)) != 0 || 1606e9778795SPeter Avalos (r = sshbuf_put_string(c, NULL, 0)) != 0) 1607e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1608856ea928SPeter Avalos } 1609856ea928SPeter Avalos 1610856ea928SPeter Avalos static void 1611e9778795SPeter Avalos add_string_option(struct sshbuf *c, const char *name, const char *value) 1612856ea928SPeter Avalos { 1613e9778795SPeter Avalos struct sshbuf *b; 1614e9778795SPeter Avalos int r; 1615856ea928SPeter Avalos 1616856ea928SPeter Avalos debug3("%s: %s=%s", __func__, name, value); 1617e9778795SPeter Avalos if ((b = sshbuf_new()) == NULL) 1618e9778795SPeter Avalos fatal("%s: sshbuf_new failed", __func__); 1619e9778795SPeter Avalos if ((r = sshbuf_put_cstring(b, value)) != 0 || 1620e9778795SPeter Avalos (r = sshbuf_put_cstring(c, name)) != 0 || 1621e9778795SPeter Avalos (r = sshbuf_put_stringb(c, b)) != 0) 1622e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1623856ea928SPeter Avalos 1624e9778795SPeter Avalos sshbuf_free(b); 1625856ea928SPeter Avalos } 1626856ea928SPeter Avalos 1627856ea928SPeter Avalos #define OPTIONS_CRITICAL 1 1628856ea928SPeter Avalos #define OPTIONS_EXTENSIONS 2 1629856ea928SPeter Avalos static void 1630e9778795SPeter Avalos prepare_options_buf(struct sshbuf *c, int which) 1631856ea928SPeter Avalos { 1632ce74bacaSMatthew Dillon size_t i; 1633ce74bacaSMatthew Dillon 1634e9778795SPeter Avalos sshbuf_reset(c); 1635856ea928SPeter Avalos if ((which & OPTIONS_CRITICAL) != 0 && 1636856ea928SPeter Avalos certflags_command != NULL) 1637856ea928SPeter Avalos add_string_option(c, "force-command", certflags_command); 1638856ea928SPeter Avalos if ((which & OPTIONS_EXTENSIONS) != 0 && 16391c188a7fSPeter Avalos (certflags_flags & CERTOPT_X_FWD) != 0) 16401c188a7fSPeter Avalos add_flag_option(c, "permit-X11-forwarding"); 16411c188a7fSPeter Avalos if ((which & OPTIONS_EXTENSIONS) != 0 && 1642856ea928SPeter Avalos (certflags_flags & CERTOPT_AGENT_FWD) != 0) 1643856ea928SPeter Avalos add_flag_option(c, "permit-agent-forwarding"); 1644856ea928SPeter Avalos if ((which & OPTIONS_EXTENSIONS) != 0 && 1645856ea928SPeter Avalos (certflags_flags & CERTOPT_PORT_FWD) != 0) 1646856ea928SPeter Avalos add_flag_option(c, "permit-port-forwarding"); 1647856ea928SPeter Avalos if ((which & OPTIONS_EXTENSIONS) != 0 && 1648856ea928SPeter Avalos (certflags_flags & CERTOPT_PTY) != 0) 1649856ea928SPeter Avalos add_flag_option(c, "permit-pty"); 1650856ea928SPeter Avalos if ((which & OPTIONS_EXTENSIONS) != 0 && 1651856ea928SPeter Avalos (certflags_flags & CERTOPT_USER_RC) != 0) 1652856ea928SPeter Avalos add_flag_option(c, "permit-user-rc"); 16530cbfa66cSDaniel Fojt if ((which & OPTIONS_EXTENSIONS) != 0 && 16540cbfa66cSDaniel Fojt (certflags_flags & CERTOPT_NO_REQUIRE_USER_PRESENCE) != 0) 16550cbfa66cSDaniel Fojt add_flag_option(c, "no-touch-required"); 1656856ea928SPeter Avalos if ((which & OPTIONS_CRITICAL) != 0 && 1657856ea928SPeter Avalos certflags_src_addr != NULL) 1658856ea928SPeter Avalos add_string_option(c, "source-address", certflags_src_addr); 1659ce74bacaSMatthew Dillon for (i = 0; i < ncert_userext; i++) { 1660ce74bacaSMatthew Dillon if ((cert_userext[i].crit && (which & OPTIONS_EXTENSIONS)) || 1661ce74bacaSMatthew Dillon (!cert_userext[i].crit && (which & OPTIONS_CRITICAL))) 1662ce74bacaSMatthew Dillon continue; 1663ce74bacaSMatthew Dillon if (cert_userext[i].val == NULL) 1664ce74bacaSMatthew Dillon add_flag_option(c, cert_userext[i].key); 1665ce74bacaSMatthew Dillon else { 1666ce74bacaSMatthew Dillon add_string_option(c, cert_userext[i].key, 1667ce74bacaSMatthew Dillon cert_userext[i].val); 1668ce74bacaSMatthew Dillon } 1669ce74bacaSMatthew Dillon } 1670856ea928SPeter Avalos } 1671856ea928SPeter Avalos 1672e9778795SPeter Avalos static struct sshkey * 1673856ea928SPeter Avalos load_pkcs11_key(char *path) 1674856ea928SPeter Avalos { 1675856ea928SPeter Avalos #ifdef ENABLE_PKCS11 1676e9778795SPeter Avalos struct sshkey **keys = NULL, *public, *private = NULL; 1677e9778795SPeter Avalos int r, i, nkeys; 1678856ea928SPeter Avalos 1679e9778795SPeter Avalos if ((r = sshkey_load_public(path, &public, NULL)) != 0) 1680e9778795SPeter Avalos fatal("Couldn't load CA public key \"%s\": %s", 1681e9778795SPeter Avalos path, ssh_err(r)); 1682856ea928SPeter Avalos 16830cbfa66cSDaniel Fojt nkeys = pkcs11_add_provider(pkcs11provider, identity_passphrase, 16840cbfa66cSDaniel Fojt &keys, NULL); 1685856ea928SPeter Avalos debug3("%s: %d keys", __func__, nkeys); 1686856ea928SPeter Avalos if (nkeys <= 0) 1687856ea928SPeter Avalos fatal("cannot read public key from pkcs11"); 1688856ea928SPeter Avalos for (i = 0; i < nkeys; i++) { 1689e9778795SPeter Avalos if (sshkey_equal_public(public, keys[i])) { 1690856ea928SPeter Avalos private = keys[i]; 1691856ea928SPeter Avalos continue; 1692856ea928SPeter Avalos } 1693e9778795SPeter Avalos sshkey_free(keys[i]); 1694856ea928SPeter Avalos } 169536e94dc5SPeter Avalos free(keys); 1696e9778795SPeter Avalos sshkey_free(public); 1697856ea928SPeter Avalos return private; 1698856ea928SPeter Avalos #else 1699856ea928SPeter Avalos fatal("no pkcs11 support"); 1700856ea928SPeter Avalos #endif /* ENABLE_PKCS11 */ 1701856ea928SPeter Avalos } 1702856ea928SPeter Avalos 1703ce74bacaSMatthew Dillon /* Signer for sshkey_certify_custom that uses the agent */ 1704ce74bacaSMatthew Dillon static int 17050cbfa66cSDaniel Fojt agent_signer(struct sshkey *key, u_char **sigp, size_t *lenp, 1706ce74bacaSMatthew Dillon const u_char *data, size_t datalen, 17070cbfa66cSDaniel Fojt const char *alg, const char *provider, u_int compat, void *ctx) 1708ce74bacaSMatthew Dillon { 1709ce74bacaSMatthew Dillon int *agent_fdp = (int *)ctx; 1710ce74bacaSMatthew Dillon 1711ce74bacaSMatthew Dillon return ssh_agent_sign(*agent_fdp, key, sigp, lenp, 1712ce74bacaSMatthew Dillon data, datalen, alg, compat); 1713ce74bacaSMatthew Dillon } 1714ce74bacaSMatthew Dillon 1715856ea928SPeter Avalos static void 1716664f4763Szrj do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent, 1717664f4763Szrj unsigned long long cert_serial, int cert_serial_autoinc, 1718664f4763Szrj int argc, char **argv) 1719856ea928SPeter Avalos { 17200cbfa66cSDaniel Fojt int r, i, found, agent_fd = -1; 1721856ea928SPeter Avalos u_int n; 1722e9778795SPeter Avalos struct sshkey *ca, *public; 17230cbfa66cSDaniel Fojt char valid[64], *otmp, *tmp, *cp, *out, *comment; 17240cbfa66cSDaniel Fojt char *ca_fp = NULL, **plist = NULL; 1725ce74bacaSMatthew Dillon struct ssh_identitylist *agent_ids; 1726ce74bacaSMatthew Dillon size_t j; 17270cbfa66cSDaniel Fojt struct notifier_ctx *notifier = NULL; 1728856ea928SPeter Avalos 172936e94dc5SPeter Avalos #ifdef ENABLE_PKCS11 1730856ea928SPeter Avalos pkcs11_init(1); 173136e94dc5SPeter Avalos #endif 1732856ea928SPeter Avalos tmp = tilde_expand_filename(ca_key_path, pw->pw_uid); 1733856ea928SPeter Avalos if (pkcs11provider != NULL) { 1734ce74bacaSMatthew Dillon /* If a PKCS#11 token was specified then try to use it */ 1735856ea928SPeter Avalos if ((ca = load_pkcs11_key(tmp)) == NULL) 1736856ea928SPeter Avalos fatal("No PKCS#11 key matching %s found", ca_key_path); 1737ce74bacaSMatthew Dillon } else if (prefer_agent) { 1738ce74bacaSMatthew Dillon /* 1739ce74bacaSMatthew Dillon * Agent signature requested. Try to use agent after making 1740ce74bacaSMatthew Dillon * sure the public key specified is actually present in the 1741ce74bacaSMatthew Dillon * agent. 1742ce74bacaSMatthew Dillon */ 1743ce74bacaSMatthew Dillon if ((r = sshkey_load_public(tmp, &ca, NULL)) != 0) 1744ce74bacaSMatthew Dillon fatal("Cannot load CA public key %s: %s", 1745ce74bacaSMatthew Dillon tmp, ssh_err(r)); 1746ce74bacaSMatthew Dillon if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) 1747ce74bacaSMatthew Dillon fatal("Cannot use public key for CA signature: %s", 1748ce74bacaSMatthew Dillon ssh_err(r)); 1749ce74bacaSMatthew Dillon if ((r = ssh_fetch_identitylist(agent_fd, &agent_ids)) != 0) 1750ce74bacaSMatthew Dillon fatal("Retrieve agent key list: %s", ssh_err(r)); 1751ce74bacaSMatthew Dillon found = 0; 1752ce74bacaSMatthew Dillon for (j = 0; j < agent_ids->nkeys; j++) { 1753ce74bacaSMatthew Dillon if (sshkey_equal(ca, agent_ids->keys[j])) { 1754ce74bacaSMatthew Dillon found = 1; 1755ce74bacaSMatthew Dillon break; 1756ce74bacaSMatthew Dillon } 1757ce74bacaSMatthew Dillon } 1758ce74bacaSMatthew Dillon if (!found) 1759ce74bacaSMatthew Dillon fatal("CA key %s not found in agent", tmp); 1760ce74bacaSMatthew Dillon ssh_free_identitylist(agent_ids); 1761ce74bacaSMatthew Dillon ca->flags |= SSHKEY_FLAG_EXT; 1762ce74bacaSMatthew Dillon } else { 1763ce74bacaSMatthew Dillon /* CA key is assumed to be a private key on the filesystem */ 17640cbfa66cSDaniel Fojt ca = load_identity(tmp, NULL); 1765ce74bacaSMatthew Dillon } 176636e94dc5SPeter Avalos free(tmp); 1767856ea928SPeter Avalos 17680cbfa66cSDaniel Fojt if (key_type_name != NULL) { 17690cbfa66cSDaniel Fojt if (sshkey_type_from_name(key_type_name) != ca->type) { 1770e9778795SPeter Avalos fatal("CA key type %s doesn't match specified %s", 1771e9778795SPeter Avalos sshkey_ssh_name(ca), key_type_name); 1772e9778795SPeter Avalos } 17730cbfa66cSDaniel Fojt } else if (ca->type == KEY_RSA) { 17740cbfa66cSDaniel Fojt /* Default to a good signature algorithm */ 17750cbfa66cSDaniel Fojt key_type_name = "rsa-sha2-512"; 17760cbfa66cSDaniel Fojt } 17770cbfa66cSDaniel Fojt ca_fp = sshkey_fingerprint(ca, fingerprint_hash, SSH_FP_DEFAULT); 1778e9778795SPeter Avalos 1779856ea928SPeter Avalos for (i = 0; i < argc; i++) { 1780856ea928SPeter Avalos /* Split list of principals */ 1781856ea928SPeter Avalos n = 0; 1782856ea928SPeter Avalos if (cert_principals != NULL) { 1783856ea928SPeter Avalos otmp = tmp = xstrdup(cert_principals); 1784856ea928SPeter Avalos plist = NULL; 1785856ea928SPeter Avalos for (; (cp = strsep(&tmp, ",")) != NULL; n++) { 1786e9778795SPeter Avalos plist = xreallocarray(plist, n + 1, sizeof(*plist)); 1787856ea928SPeter Avalos if (*(plist[n] = xstrdup(cp)) == '\0') 1788856ea928SPeter Avalos fatal("Empty principal name"); 1789856ea928SPeter Avalos } 179036e94dc5SPeter Avalos free(otmp); 1791856ea928SPeter Avalos } 1792664f4763Szrj if (n > SSHKEY_CERT_MAX_PRINCIPALS) 1793664f4763Szrj fatal("Too many certificate principals specified"); 1794856ea928SPeter Avalos 1795856ea928SPeter Avalos tmp = tilde_expand_filename(argv[i], pw->pw_uid); 1796e9778795SPeter Avalos if ((r = sshkey_load_public(tmp, &public, &comment)) != 0) 1797e9778795SPeter Avalos fatal("%s: unable to open \"%s\": %s", 1798e9778795SPeter Avalos __func__, tmp, ssh_err(r)); 17990cbfa66cSDaniel Fojt if (sshkey_is_cert(public)) 1800856ea928SPeter Avalos fatal("%s: key \"%s\" type %s cannot be certified", 1801e9778795SPeter Avalos __func__, tmp, sshkey_type(public)); 1802856ea928SPeter Avalos 1803856ea928SPeter Avalos /* Prepare certificate to sign */ 1804e9778795SPeter Avalos if ((r = sshkey_to_certified(public)) != 0) 1805e9778795SPeter Avalos fatal("Could not upgrade key %s to certificate: %s", 1806e9778795SPeter Avalos tmp, ssh_err(r)); 1807856ea928SPeter Avalos public->cert->type = cert_key_type; 1808856ea928SPeter Avalos public->cert->serial = (u_int64_t)cert_serial; 1809856ea928SPeter Avalos public->cert->key_id = xstrdup(cert_key_id); 1810856ea928SPeter Avalos public->cert->nprincipals = n; 1811856ea928SPeter Avalos public->cert->principals = plist; 1812856ea928SPeter Avalos public->cert->valid_after = cert_valid_from; 1813856ea928SPeter Avalos public->cert->valid_before = cert_valid_to; 1814e9778795SPeter Avalos prepare_options_buf(public->cert->critical, OPTIONS_CRITICAL); 181536e94dc5SPeter Avalos prepare_options_buf(public->cert->extensions, 1816856ea928SPeter Avalos OPTIONS_EXTENSIONS); 1817e9778795SPeter Avalos if ((r = sshkey_from_private(ca, 1818e9778795SPeter Avalos &public->cert->signature_key)) != 0) 1819ce74bacaSMatthew Dillon fatal("sshkey_from_private (ca key): %s", ssh_err(r)); 1820856ea928SPeter Avalos 1821ce74bacaSMatthew Dillon if (agent_fd != -1 && (ca->flags & SSHKEY_FLAG_EXT) != 0) { 1822ce74bacaSMatthew Dillon if ((r = sshkey_certify_custom(public, ca, 18230cbfa66cSDaniel Fojt key_type_name, sk_provider, agent_signer, 18240cbfa66cSDaniel Fojt &agent_fd)) != 0) 1825ce74bacaSMatthew Dillon fatal("Couldn't certify key %s via agent: %s", 1826ce74bacaSMatthew Dillon tmp, ssh_err(r)); 1827ce74bacaSMatthew Dillon } else { 18280cbfa66cSDaniel Fojt if (sshkey_is_sk(ca) && 18290cbfa66cSDaniel Fojt (ca->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { 18300cbfa66cSDaniel Fojt notifier = notify_start(0, 18310cbfa66cSDaniel Fojt "Confirm user presence for key %s %s", 18320cbfa66cSDaniel Fojt sshkey_type(ca), ca_fp); 18330cbfa66cSDaniel Fojt } 18340cbfa66cSDaniel Fojt r = sshkey_certify(public, ca, key_type_name, 18350cbfa66cSDaniel Fojt sk_provider); 18360cbfa66cSDaniel Fojt notify_complete(notifier); 18370cbfa66cSDaniel Fojt if (r != 0) 1838ce74bacaSMatthew Dillon fatal("Couldn't certify key %s: %s", 1839ce74bacaSMatthew Dillon tmp, ssh_err(r)); 1840ce74bacaSMatthew Dillon } 1841856ea928SPeter Avalos 1842856ea928SPeter Avalos if ((cp = strrchr(tmp, '.')) != NULL && strcmp(cp, ".pub") == 0) 1843856ea928SPeter Avalos *cp = '\0'; 1844856ea928SPeter Avalos xasprintf(&out, "%s-cert.pub", tmp); 184536e94dc5SPeter Avalos free(tmp); 1846856ea928SPeter Avalos 18470cbfa66cSDaniel Fojt if ((r = sshkey_save_public(public, out, comment)) != 0) { 18480cbfa66cSDaniel Fojt fatal("Unable to save public key to %s: %s", 18490cbfa66cSDaniel Fojt identity_file, ssh_err(r)); 18500cbfa66cSDaniel Fojt } 1851856ea928SPeter Avalos 1852856ea928SPeter Avalos if (!quiet) { 1853e9778795SPeter Avalos sshkey_format_cert_validity(public->cert, 1854e9778795SPeter Avalos valid, sizeof(valid)); 1855856ea928SPeter Avalos logit("Signed %s key %s: id \"%s\" serial %llu%s%s " 1856e9778795SPeter Avalos "valid %s", sshkey_cert_type(public), 18579f304aafSPeter Avalos out, public->cert->key_id, 18589f304aafSPeter Avalos (unsigned long long)public->cert->serial, 1859856ea928SPeter Avalos cert_principals != NULL ? " for " : "", 1860856ea928SPeter Avalos cert_principals != NULL ? cert_principals : "", 1861e9778795SPeter Avalos valid); 1862856ea928SPeter Avalos } 1863856ea928SPeter Avalos 1864e9778795SPeter Avalos sshkey_free(public); 186536e94dc5SPeter Avalos free(out); 1866664f4763Szrj if (cert_serial_autoinc) 1867664f4763Szrj cert_serial++; 1868856ea928SPeter Avalos } 18690cbfa66cSDaniel Fojt free(ca_fp); 187036e94dc5SPeter Avalos #ifdef ENABLE_PKCS11 1871856ea928SPeter Avalos pkcs11_terminate(); 187236e94dc5SPeter Avalos #endif 1873856ea928SPeter Avalos exit(0); 1874856ea928SPeter Avalos } 1875856ea928SPeter Avalos 1876856ea928SPeter Avalos static u_int64_t 1877856ea928SPeter Avalos parse_relative_time(const char *s, time_t now) 1878856ea928SPeter Avalos { 1879856ea928SPeter Avalos int64_t mul, secs; 1880856ea928SPeter Avalos 1881856ea928SPeter Avalos mul = *s == '-' ? -1 : 1; 1882856ea928SPeter Avalos 1883856ea928SPeter Avalos if ((secs = convtime(s + 1)) == -1) 1884856ea928SPeter Avalos fatal("Invalid relative certificate time %s", s); 1885856ea928SPeter Avalos if (mul == -1 && secs > now) 1886856ea928SPeter Avalos fatal("Certificate time %s cannot be represented", s); 1887856ea928SPeter Avalos return now + (u_int64_t)(secs * mul); 1888856ea928SPeter Avalos } 1889856ea928SPeter Avalos 1890856ea928SPeter Avalos static void 1891856ea928SPeter Avalos parse_cert_times(char *timespec) 1892856ea928SPeter Avalos { 1893856ea928SPeter Avalos char *from, *to; 1894856ea928SPeter Avalos time_t now = time(NULL); 1895856ea928SPeter Avalos int64_t secs; 1896856ea928SPeter Avalos 1897856ea928SPeter Avalos /* +timespec relative to now */ 1898856ea928SPeter Avalos if (*timespec == '+' && strchr(timespec, ':') == NULL) { 1899856ea928SPeter Avalos if ((secs = convtime(timespec + 1)) == -1) 1900856ea928SPeter Avalos fatal("Invalid relative certificate life %s", timespec); 1901856ea928SPeter Avalos cert_valid_to = now + secs; 1902856ea928SPeter Avalos /* 1903856ea928SPeter Avalos * Backdate certificate one minute to avoid problems on hosts 1904856ea928SPeter Avalos * with poorly-synchronised clocks. 1905856ea928SPeter Avalos */ 1906856ea928SPeter Avalos cert_valid_from = ((now - 59)/ 60) * 60; 1907856ea928SPeter Avalos return; 1908856ea928SPeter Avalos } 1909856ea928SPeter Avalos 1910856ea928SPeter Avalos /* 1911856ea928SPeter Avalos * from:to, where 1912664f4763Szrj * from := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS | "always" 1913664f4763Szrj * to := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS | "forever" 1914856ea928SPeter Avalos */ 1915856ea928SPeter Avalos from = xstrdup(timespec); 1916856ea928SPeter Avalos to = strchr(from, ':'); 1917856ea928SPeter Avalos if (to == NULL || from == to || *(to + 1) == '\0') 1918856ea928SPeter Avalos fatal("Invalid certificate life specification %s", timespec); 1919856ea928SPeter Avalos *to++ = '\0'; 1920856ea928SPeter Avalos 1921856ea928SPeter Avalos if (*from == '-' || *from == '+') 1922856ea928SPeter Avalos cert_valid_from = parse_relative_time(from, now); 1923664f4763Szrj else if (strcmp(from, "always") == 0) 1924664f4763Szrj cert_valid_from = 0; 1925664f4763Szrj else if (parse_absolute_time(from, &cert_valid_from) != 0) 1926664f4763Szrj fatal("Invalid from time \"%s\"", from); 1927856ea928SPeter Avalos 1928856ea928SPeter Avalos if (*to == '-' || *to == '+') 192936e94dc5SPeter Avalos cert_valid_to = parse_relative_time(to, now); 1930664f4763Szrj else if (strcmp(to, "forever") == 0) 1931664f4763Szrj cert_valid_to = ~(u_int64_t)0; 1932664f4763Szrj else if (parse_absolute_time(to, &cert_valid_to) != 0) 1933664f4763Szrj fatal("Invalid to time \"%s\"", to); 1934856ea928SPeter Avalos 1935856ea928SPeter Avalos if (cert_valid_to <= cert_valid_from) 1936856ea928SPeter Avalos fatal("Empty certificate validity interval"); 193736e94dc5SPeter Avalos free(from); 1938856ea928SPeter Avalos } 1939856ea928SPeter Avalos 1940856ea928SPeter Avalos static void 1941856ea928SPeter Avalos add_cert_option(char *opt) 1942856ea928SPeter Avalos { 1943ce74bacaSMatthew Dillon char *val, *cp; 1944ce74bacaSMatthew Dillon int iscrit = 0; 1945856ea928SPeter Avalos 19461c188a7fSPeter Avalos if (strcasecmp(opt, "clear") == 0) 1947856ea928SPeter Avalos certflags_flags = 0; 1948856ea928SPeter Avalos else if (strcasecmp(opt, "no-x11-forwarding") == 0) 1949856ea928SPeter Avalos certflags_flags &= ~CERTOPT_X_FWD; 1950856ea928SPeter Avalos else if (strcasecmp(opt, "permit-x11-forwarding") == 0) 1951856ea928SPeter Avalos certflags_flags |= CERTOPT_X_FWD; 1952856ea928SPeter Avalos else if (strcasecmp(opt, "no-agent-forwarding") == 0) 1953856ea928SPeter Avalos certflags_flags &= ~CERTOPT_AGENT_FWD; 1954856ea928SPeter Avalos else if (strcasecmp(opt, "permit-agent-forwarding") == 0) 1955856ea928SPeter Avalos certflags_flags |= CERTOPT_AGENT_FWD; 1956856ea928SPeter Avalos else if (strcasecmp(opt, "no-port-forwarding") == 0) 1957856ea928SPeter Avalos certflags_flags &= ~CERTOPT_PORT_FWD; 1958856ea928SPeter Avalos else if (strcasecmp(opt, "permit-port-forwarding") == 0) 1959856ea928SPeter Avalos certflags_flags |= CERTOPT_PORT_FWD; 1960856ea928SPeter Avalos else if (strcasecmp(opt, "no-pty") == 0) 1961856ea928SPeter Avalos certflags_flags &= ~CERTOPT_PTY; 1962856ea928SPeter Avalos else if (strcasecmp(opt, "permit-pty") == 0) 1963856ea928SPeter Avalos certflags_flags |= CERTOPT_PTY; 1964856ea928SPeter Avalos else if (strcasecmp(opt, "no-user-rc") == 0) 1965856ea928SPeter Avalos certflags_flags &= ~CERTOPT_USER_RC; 1966856ea928SPeter Avalos else if (strcasecmp(opt, "permit-user-rc") == 0) 1967856ea928SPeter Avalos certflags_flags |= CERTOPT_USER_RC; 19680cbfa66cSDaniel Fojt else if (strcasecmp(opt, "touch-required") == 0) 19690cbfa66cSDaniel Fojt certflags_flags &= ~CERTOPT_NO_REQUIRE_USER_PRESENCE; 19700cbfa66cSDaniel Fojt else if (strcasecmp(opt, "no-touch-required") == 0) 19710cbfa66cSDaniel Fojt certflags_flags |= CERTOPT_NO_REQUIRE_USER_PRESENCE; 1972856ea928SPeter Avalos else if (strncasecmp(opt, "force-command=", 14) == 0) { 1973856ea928SPeter Avalos val = opt + 14; 1974856ea928SPeter Avalos if (*val == '\0') 1975856ea928SPeter Avalos fatal("Empty force-command option"); 1976856ea928SPeter Avalos if (certflags_command != NULL) 1977856ea928SPeter Avalos fatal("force-command already specified"); 1978856ea928SPeter Avalos certflags_command = xstrdup(val); 1979856ea928SPeter Avalos } else if (strncasecmp(opt, "source-address=", 15) == 0) { 1980856ea928SPeter Avalos val = opt + 15; 1981856ea928SPeter Avalos if (*val == '\0') 1982856ea928SPeter Avalos fatal("Empty source-address option"); 1983856ea928SPeter Avalos if (certflags_src_addr != NULL) 1984856ea928SPeter Avalos fatal("source-address already specified"); 1985856ea928SPeter Avalos if (addr_match_cidr_list(NULL, val) != 0) 1986856ea928SPeter Avalos fatal("Invalid source-address list"); 1987856ea928SPeter Avalos certflags_src_addr = xstrdup(val); 1988ce74bacaSMatthew Dillon } else if (strncasecmp(opt, "extension:", 10) == 0 || 1989ce74bacaSMatthew Dillon (iscrit = (strncasecmp(opt, "critical:", 9) == 0))) { 1990ce74bacaSMatthew Dillon val = xstrdup(strchr(opt, ':') + 1); 1991ce74bacaSMatthew Dillon if ((cp = strchr(val, '=')) != NULL) 1992ce74bacaSMatthew Dillon *cp++ = '\0'; 1993ce74bacaSMatthew Dillon cert_userext = xreallocarray(cert_userext, ncert_userext + 1, 1994ce74bacaSMatthew Dillon sizeof(*cert_userext)); 1995ce74bacaSMatthew Dillon cert_userext[ncert_userext].key = val; 1996ce74bacaSMatthew Dillon cert_userext[ncert_userext].val = cp == NULL ? 1997ce74bacaSMatthew Dillon NULL : xstrdup(cp); 1998ce74bacaSMatthew Dillon cert_userext[ncert_userext].crit = iscrit; 1999ce74bacaSMatthew Dillon ncert_userext++; 2000856ea928SPeter Avalos } else 2001856ea928SPeter Avalos fatal("Unsupported certificate option \"%s\"", opt); 2002856ea928SPeter Avalos } 2003856ea928SPeter Avalos 2004856ea928SPeter Avalos static void 2005e9778795SPeter Avalos show_options(struct sshbuf *optbuf, int in_critical) 2006856ea928SPeter Avalos { 200736e94dc5SPeter Avalos char *name, *arg; 2008e9778795SPeter Avalos struct sshbuf *options, *option = NULL; 2009e9778795SPeter Avalos int r; 2010856ea928SPeter Avalos 2011e9778795SPeter Avalos if ((options = sshbuf_fromb(optbuf)) == NULL) 2012e9778795SPeter Avalos fatal("%s: sshbuf_fromb failed", __func__); 2013e9778795SPeter Avalos while (sshbuf_len(options) != 0) { 2014e9778795SPeter Avalos sshbuf_free(option); 2015e9778795SPeter Avalos option = NULL; 2016e9778795SPeter Avalos if ((r = sshbuf_get_cstring(options, &name, NULL)) != 0 || 2017e9778795SPeter Avalos (r = sshbuf_froms(options, &option)) != 0) 2018e9778795SPeter Avalos fatal("%s: buffer error: %s", __func__, ssh_err(r)); 2019856ea928SPeter Avalos printf(" %s", name); 2020e9778795SPeter Avalos if (!in_critical && 2021856ea928SPeter Avalos (strcmp(name, "permit-X11-forwarding") == 0 || 2022856ea928SPeter Avalos strcmp(name, "permit-agent-forwarding") == 0 || 2023856ea928SPeter Avalos strcmp(name, "permit-port-forwarding") == 0 || 2024856ea928SPeter Avalos strcmp(name, "permit-pty") == 0 || 20250cbfa66cSDaniel Fojt strcmp(name, "permit-user-rc") == 0 || 20260cbfa66cSDaniel Fojt strcmp(name, "no-touch-required") == 0)) { 2027856ea928SPeter Avalos printf("\n"); 20280cbfa66cSDaniel Fojt } else if (in_critical && 2029856ea928SPeter Avalos (strcmp(name, "force-command") == 0 || 2030856ea928SPeter Avalos strcmp(name, "source-address") == 0)) { 2031e9778795SPeter Avalos if ((r = sshbuf_get_cstring(option, &arg, NULL)) != 0) 2032e9778795SPeter Avalos fatal("%s: buffer error: %s", 2033e9778795SPeter Avalos __func__, ssh_err(r)); 203436e94dc5SPeter Avalos printf(" %s\n", arg); 203536e94dc5SPeter Avalos free(arg); 2036856ea928SPeter Avalos } else { 2037e9778795SPeter Avalos printf(" UNKNOWN OPTION (len %zu)\n", 2038e9778795SPeter Avalos sshbuf_len(option)); 2039e9778795SPeter Avalos sshbuf_reset(option); 2040856ea928SPeter Avalos } 204136e94dc5SPeter Avalos free(name); 2042e9778795SPeter Avalos if (sshbuf_len(option) != 0) 2043856ea928SPeter Avalos fatal("Option corrupt: extra data at end"); 2044856ea928SPeter Avalos } 2045e9778795SPeter Avalos sshbuf_free(option); 2046e9778795SPeter Avalos sshbuf_free(options); 2047856ea928SPeter Avalos } 2048856ea928SPeter Avalos 2049856ea928SPeter Avalos static void 2050e9778795SPeter Avalos print_cert(struct sshkey *key) 2051856ea928SPeter Avalos { 2052e9778795SPeter Avalos char valid[64], *key_fp, *ca_fp; 2053e9778795SPeter Avalos u_int i; 2054856ea928SPeter Avalos 2055e9778795SPeter Avalos key_fp = sshkey_fingerprint(key, fingerprint_hash, SSH_FP_DEFAULT); 2056e9778795SPeter Avalos ca_fp = sshkey_fingerprint(key->cert->signature_key, 2057e9778795SPeter Avalos fingerprint_hash, SSH_FP_DEFAULT); 2058e9778795SPeter Avalos if (key_fp == NULL || ca_fp == NULL) 2059e9778795SPeter Avalos fatal("%s: sshkey_fingerprint fail", __func__); 2060e9778795SPeter Avalos sshkey_format_cert_validity(key->cert, valid, sizeof(valid)); 2061856ea928SPeter Avalos 2062e9778795SPeter Avalos printf(" Type: %s %s certificate\n", sshkey_ssh_name(key), 2063e9778795SPeter Avalos sshkey_cert_type(key)); 2064e9778795SPeter Avalos printf(" Public key: %s %s\n", sshkey_type(key), key_fp); 2065664f4763Szrj printf(" Signing CA: %s %s (using %s)\n", 2066664f4763Szrj sshkey_type(key->cert->signature_key), ca_fp, 2067664f4763Szrj key->cert->signature_type); 2068856ea928SPeter Avalos printf(" Key ID: \"%s\"\n", key->cert->key_id); 2069e9778795SPeter Avalos printf(" Serial: %llu\n", (unsigned long long)key->cert->serial); 2070e9778795SPeter Avalos printf(" Valid: %s\n", valid); 2071856ea928SPeter Avalos printf(" Principals: "); 2072856ea928SPeter Avalos if (key->cert->nprincipals == 0) 2073856ea928SPeter Avalos printf("(none)\n"); 2074856ea928SPeter Avalos else { 2075856ea928SPeter Avalos for (i = 0; i < key->cert->nprincipals; i++) 2076856ea928SPeter Avalos printf("\n %s", 2077856ea928SPeter Avalos key->cert->principals[i]); 2078856ea928SPeter Avalos printf("\n"); 2079856ea928SPeter Avalos } 2080856ea928SPeter Avalos printf(" Critical Options: "); 2081e9778795SPeter Avalos if (sshbuf_len(key->cert->critical) == 0) 2082856ea928SPeter Avalos printf("(none)\n"); 2083856ea928SPeter Avalos else { 2084856ea928SPeter Avalos printf("\n"); 2085e9778795SPeter Avalos show_options(key->cert->critical, 1); 2086856ea928SPeter Avalos } 2087856ea928SPeter Avalos printf(" Extensions: "); 2088e9778795SPeter Avalos if (sshbuf_len(key->cert->extensions) == 0) 2089856ea928SPeter Avalos printf("(none)\n"); 2090856ea928SPeter Avalos else { 2091856ea928SPeter Avalos printf("\n"); 2092e9778795SPeter Avalos show_options(key->cert->extensions, 0); 2093856ea928SPeter Avalos } 2094856ea928SPeter Avalos } 2095856ea928SPeter Avalos 2096e9778795SPeter Avalos static void 2097e9778795SPeter Avalos do_show_cert(struct passwd *pw) 2098e9778795SPeter Avalos { 2099e9778795SPeter Avalos struct sshkey *key = NULL; 2100e9778795SPeter Avalos struct stat st; 2101e9778795SPeter Avalos int r, is_stdin = 0, ok = 0; 2102e9778795SPeter Avalos FILE *f; 2103664f4763Szrj char *cp, *line = NULL; 2104e9778795SPeter Avalos const char *path; 2105664f4763Szrj size_t linesize = 0; 2106e9778795SPeter Avalos u_long lnum = 0; 2107e9778795SPeter Avalos 2108e9778795SPeter Avalos if (!have_identity) 2109e9778795SPeter Avalos ask_filename(pw, "Enter file in which the key is"); 21100cbfa66cSDaniel Fojt if (strcmp(identity_file, "-") != 0 && stat(identity_file, &st) == -1) 2111e9778795SPeter Avalos fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); 2112e9778795SPeter Avalos 2113e9778795SPeter Avalos path = identity_file; 2114e9778795SPeter Avalos if (strcmp(path, "-") == 0) { 2115e9778795SPeter Avalos f = stdin; 2116e9778795SPeter Avalos path = "(stdin)"; 2117e9778795SPeter Avalos is_stdin = 1; 2118e9778795SPeter Avalos } else if ((f = fopen(identity_file, "r")) == NULL) 2119e9778795SPeter Avalos fatal("fopen %s: %s", identity_file, strerror(errno)); 2120e9778795SPeter Avalos 2121664f4763Szrj while (getline(&line, &linesize, f) != -1) { 2122664f4763Szrj lnum++; 2123e9778795SPeter Avalos sshkey_free(key); 2124e9778795SPeter Avalos key = NULL; 2125e9778795SPeter Avalos /* Trim leading space and comments */ 2126e9778795SPeter Avalos cp = line + strspn(line, " \t"); 2127e9778795SPeter Avalos if (*cp == '#' || *cp == '\0') 2128e9778795SPeter Avalos continue; 2129e9778795SPeter Avalos if ((key = sshkey_new(KEY_UNSPEC)) == NULL) 2130ce74bacaSMatthew Dillon fatal("sshkey_new"); 2131e9778795SPeter Avalos if ((r = sshkey_read(key, &cp)) != 0) { 2132e9778795SPeter Avalos error("%s:%lu: invalid key: %s", path, 2133e9778795SPeter Avalos lnum, ssh_err(r)); 2134e9778795SPeter Avalos continue; 2135e9778795SPeter Avalos } 2136e9778795SPeter Avalos if (!sshkey_is_cert(key)) { 2137e9778795SPeter Avalos error("%s:%lu is not a certificate", path, lnum); 2138e9778795SPeter Avalos continue; 2139e9778795SPeter Avalos } 2140e9778795SPeter Avalos ok = 1; 2141e9778795SPeter Avalos if (!is_stdin && lnum == 1) 2142e9778795SPeter Avalos printf("%s:\n", path); 2143e9778795SPeter Avalos else 2144e9778795SPeter Avalos printf("%s:%lu:\n", path, lnum); 2145e9778795SPeter Avalos print_cert(key); 2146e9778795SPeter Avalos } 2147664f4763Szrj free(line); 2148e9778795SPeter Avalos sshkey_free(key); 2149e9778795SPeter Avalos fclose(f); 2150e9778795SPeter Avalos exit(ok ? 0 : 1); 2151e9778795SPeter Avalos } 2152e9778795SPeter Avalos 215336e94dc5SPeter Avalos static void 215436e94dc5SPeter Avalos load_krl(const char *path, struct ssh_krl **krlp) 215536e94dc5SPeter Avalos { 2156e9778795SPeter Avalos struct sshbuf *krlbuf; 21570cbfa66cSDaniel Fojt int r; 215836e94dc5SPeter Avalos 21590cbfa66cSDaniel Fojt if ((r = sshbuf_load_file(path, &krlbuf)) != 0) 2160e9778795SPeter Avalos fatal("Unable to load KRL: %s", ssh_err(r)); 216136e94dc5SPeter Avalos /* XXX check sigs */ 2162e9778795SPeter Avalos if ((r = ssh_krl_from_blob(krlbuf, krlp, NULL, 0)) != 0 || 216336e94dc5SPeter Avalos *krlp == NULL) 2164e9778795SPeter Avalos fatal("Invalid KRL file: %s", ssh_err(r)); 2165e9778795SPeter Avalos sshbuf_free(krlbuf); 216636e94dc5SPeter Avalos } 216736e94dc5SPeter Avalos 216836e94dc5SPeter Avalos static void 2169664f4763Szrj hash_to_blob(const char *cp, u_char **blobp, size_t *lenp, 2170664f4763Szrj const char *file, u_long lnum) 2171664f4763Szrj { 2172664f4763Szrj char *tmp; 2173664f4763Szrj size_t tlen; 2174664f4763Szrj struct sshbuf *b; 2175664f4763Szrj int r; 2176664f4763Szrj 2177664f4763Szrj if (strncmp(cp, "SHA256:", 7) != 0) 2178664f4763Szrj fatal("%s:%lu: unsupported hash algorithm", file, lnum); 2179664f4763Szrj cp += 7; 2180664f4763Szrj 2181664f4763Szrj /* 2182664f4763Szrj * OpenSSH base64 hashes omit trailing '=' 2183664f4763Szrj * characters; put them back for decode. 2184664f4763Szrj */ 2185664f4763Szrj tlen = strlen(cp); 2186664f4763Szrj tmp = xmalloc(tlen + 4 + 1); 2187664f4763Szrj strlcpy(tmp, cp, tlen + 1); 2188664f4763Szrj while ((tlen % 4) != 0) { 2189664f4763Szrj tmp[tlen++] = '='; 2190664f4763Szrj tmp[tlen] = '\0'; 2191664f4763Szrj } 2192664f4763Szrj if ((b = sshbuf_new()) == NULL) 2193664f4763Szrj fatal("%s: sshbuf_new failed", __func__); 2194664f4763Szrj if ((r = sshbuf_b64tod(b, tmp)) != 0) 2195664f4763Szrj fatal("%s:%lu: decode hash failed: %s", file, lnum, ssh_err(r)); 2196664f4763Szrj free(tmp); 2197664f4763Szrj *lenp = sshbuf_len(b); 2198664f4763Szrj *blobp = xmalloc(*lenp); 2199664f4763Szrj memcpy(*blobp, sshbuf_ptr(b), *lenp); 2200664f4763Szrj sshbuf_free(b); 2201664f4763Szrj } 2202664f4763Szrj 2203664f4763Szrj static void 2204e9778795SPeter Avalos update_krl_from_file(struct passwd *pw, const char *file, int wild_ca, 2205e9778795SPeter Avalos const struct sshkey *ca, struct ssh_krl *krl) 220636e94dc5SPeter Avalos { 2207e9778795SPeter Avalos struct sshkey *key = NULL; 220836e94dc5SPeter Avalos u_long lnum = 0; 2209664f4763Szrj char *path, *cp, *ep, *line = NULL; 2210664f4763Szrj u_char *blob = NULL; 2211664f4763Szrj size_t blen = 0, linesize = 0; 221236e94dc5SPeter Avalos unsigned long long serial, serial2; 2213664f4763Szrj int i, was_explicit_key, was_sha1, was_sha256, was_hash, r; 221436e94dc5SPeter Avalos FILE *krl_spec; 221536e94dc5SPeter Avalos 221636e94dc5SPeter Avalos path = tilde_expand_filename(file, pw->pw_uid); 221736e94dc5SPeter Avalos if (strcmp(path, "-") == 0) { 221836e94dc5SPeter Avalos krl_spec = stdin; 221936e94dc5SPeter Avalos free(path); 222036e94dc5SPeter Avalos path = xstrdup("(standard input)"); 222136e94dc5SPeter Avalos } else if ((krl_spec = fopen(path, "r")) == NULL) 222236e94dc5SPeter Avalos fatal("fopen %s: %s", path, strerror(errno)); 222336e94dc5SPeter Avalos 222436e94dc5SPeter Avalos if (!quiet) 222536e94dc5SPeter Avalos printf("Revoking from %s\n", path); 2226664f4763Szrj while (getline(&line, &linesize, krl_spec) != -1) { 2227664f4763Szrj lnum++; 2228664f4763Szrj was_explicit_key = was_sha1 = was_sha256 = was_hash = 0; 222936e94dc5SPeter Avalos cp = line + strspn(line, " \t"); 223036e94dc5SPeter Avalos /* Trim trailing space, comments and strip \n */ 223136e94dc5SPeter Avalos for (i = 0, r = -1; cp[i] != '\0'; i++) { 223236e94dc5SPeter Avalos if (cp[i] == '#' || cp[i] == '\n') { 223336e94dc5SPeter Avalos cp[i] = '\0'; 223436e94dc5SPeter Avalos break; 223536e94dc5SPeter Avalos } 223636e94dc5SPeter Avalos if (cp[i] == ' ' || cp[i] == '\t') { 223736e94dc5SPeter Avalos /* Remember the start of a span of whitespace */ 223836e94dc5SPeter Avalos if (r == -1) 223936e94dc5SPeter Avalos r = i; 224036e94dc5SPeter Avalos } else 224136e94dc5SPeter Avalos r = -1; 224236e94dc5SPeter Avalos } 224336e94dc5SPeter Avalos if (r != -1) 224436e94dc5SPeter Avalos cp[r] = '\0'; 224536e94dc5SPeter Avalos if (*cp == '\0') 224636e94dc5SPeter Avalos continue; 224736e94dc5SPeter Avalos if (strncasecmp(cp, "serial:", 7) == 0) { 2248e9778795SPeter Avalos if (ca == NULL && !wild_ca) { 224936e94dc5SPeter Avalos fatal("revoking certificates by serial number " 225036e94dc5SPeter Avalos "requires specification of a CA key"); 225136e94dc5SPeter Avalos } 225236e94dc5SPeter Avalos cp += 7; 225336e94dc5SPeter Avalos cp = cp + strspn(cp, " \t"); 225436e94dc5SPeter Avalos errno = 0; 225536e94dc5SPeter Avalos serial = strtoull(cp, &ep, 0); 225636e94dc5SPeter Avalos if (*cp == '\0' || (*ep != '\0' && *ep != '-')) 225736e94dc5SPeter Avalos fatal("%s:%lu: invalid serial \"%s\"", 225836e94dc5SPeter Avalos path, lnum, cp); 225936e94dc5SPeter Avalos if (errno == ERANGE && serial == ULLONG_MAX) 226036e94dc5SPeter Avalos fatal("%s:%lu: serial out of range", 226136e94dc5SPeter Avalos path, lnum); 226236e94dc5SPeter Avalos serial2 = serial; 226336e94dc5SPeter Avalos if (*ep == '-') { 226436e94dc5SPeter Avalos cp = ep + 1; 226536e94dc5SPeter Avalos errno = 0; 226636e94dc5SPeter Avalos serial2 = strtoull(cp, &ep, 0); 226736e94dc5SPeter Avalos if (*cp == '\0' || *ep != '\0') 226836e94dc5SPeter Avalos fatal("%s:%lu: invalid serial \"%s\"", 226936e94dc5SPeter Avalos path, lnum, cp); 227036e94dc5SPeter Avalos if (errno == ERANGE && serial2 == ULLONG_MAX) 227136e94dc5SPeter Avalos fatal("%s:%lu: serial out of range", 227236e94dc5SPeter Avalos path, lnum); 227336e94dc5SPeter Avalos if (serial2 <= serial) 227436e94dc5SPeter Avalos fatal("%s:%lu: invalid serial range " 227536e94dc5SPeter Avalos "%llu:%llu", path, lnum, 227636e94dc5SPeter Avalos (unsigned long long)serial, 227736e94dc5SPeter Avalos (unsigned long long)serial2); 227836e94dc5SPeter Avalos } 227936e94dc5SPeter Avalos if (ssh_krl_revoke_cert_by_serial_range(krl, 228036e94dc5SPeter Avalos ca, serial, serial2) != 0) { 228136e94dc5SPeter Avalos fatal("%s: revoke serial failed", 228236e94dc5SPeter Avalos __func__); 228336e94dc5SPeter Avalos } 228436e94dc5SPeter Avalos } else if (strncasecmp(cp, "id:", 3) == 0) { 2285e9778795SPeter Avalos if (ca == NULL && !wild_ca) { 228636e94dc5SPeter Avalos fatal("revoking certificates by key ID " 228736e94dc5SPeter Avalos "requires specification of a CA key"); 228836e94dc5SPeter Avalos } 228936e94dc5SPeter Avalos cp += 3; 229036e94dc5SPeter Avalos cp = cp + strspn(cp, " \t"); 229136e94dc5SPeter Avalos if (ssh_krl_revoke_cert_by_key_id(krl, ca, cp) != 0) 229236e94dc5SPeter Avalos fatal("%s: revoke key ID failed", __func__); 2293664f4763Szrj } else if (strncasecmp(cp, "hash:", 5) == 0) { 2294664f4763Szrj cp += 5; 2295664f4763Szrj cp = cp + strspn(cp, " \t"); 2296664f4763Szrj hash_to_blob(cp, &blob, &blen, file, lnum); 2297664f4763Szrj r = ssh_krl_revoke_key_sha256(krl, blob, blen); 22980cbfa66cSDaniel Fojt if (r != 0) 22990cbfa66cSDaniel Fojt fatal("%s: revoke key failed: %s", 23000cbfa66cSDaniel Fojt __func__, ssh_err(r)); 230136e94dc5SPeter Avalos } else { 230236e94dc5SPeter Avalos if (strncasecmp(cp, "key:", 4) == 0) { 230336e94dc5SPeter Avalos cp += 4; 230436e94dc5SPeter Avalos cp = cp + strspn(cp, " \t"); 230536e94dc5SPeter Avalos was_explicit_key = 1; 230636e94dc5SPeter Avalos } else if (strncasecmp(cp, "sha1:", 5) == 0) { 230736e94dc5SPeter Avalos cp += 5; 230836e94dc5SPeter Avalos cp = cp + strspn(cp, " \t"); 230936e94dc5SPeter Avalos was_sha1 = 1; 2310664f4763Szrj } else if (strncasecmp(cp, "sha256:", 7) == 0) { 2311664f4763Szrj cp += 7; 2312664f4763Szrj cp = cp + strspn(cp, " \t"); 2313664f4763Szrj was_sha256 = 1; 231436e94dc5SPeter Avalos /* 231536e94dc5SPeter Avalos * Just try to process the line as a key. 231636e94dc5SPeter Avalos * Parsing will fail if it isn't. 231736e94dc5SPeter Avalos */ 231836e94dc5SPeter Avalos } 2319e9778795SPeter Avalos if ((key = sshkey_new(KEY_UNSPEC)) == NULL) 2320ce74bacaSMatthew Dillon fatal("sshkey_new"); 2321e9778795SPeter Avalos if ((r = sshkey_read(key, &cp)) != 0) 2322e9778795SPeter Avalos fatal("%s:%lu: invalid key: %s", 2323e9778795SPeter Avalos path, lnum, ssh_err(r)); 232436e94dc5SPeter Avalos if (was_explicit_key) 232536e94dc5SPeter Avalos r = ssh_krl_revoke_key_explicit(krl, key); 2326664f4763Szrj else if (was_sha1) { 2327664f4763Szrj if (sshkey_fingerprint_raw(key, 2328664f4763Szrj SSH_DIGEST_SHA1, &blob, &blen) != 0) { 2329664f4763Szrj fatal("%s:%lu: fingerprint failed", 2330664f4763Szrj file, lnum); 2331664f4763Szrj } 2332664f4763Szrj r = ssh_krl_revoke_key_sha1(krl, blob, blen); 2333664f4763Szrj } else if (was_sha256) { 2334664f4763Szrj if (sshkey_fingerprint_raw(key, 2335664f4763Szrj SSH_DIGEST_SHA256, &blob, &blen) != 0) { 2336664f4763Szrj fatal("%s:%lu: fingerprint failed", 2337664f4763Szrj file, lnum); 2338664f4763Szrj } 2339664f4763Szrj r = ssh_krl_revoke_key_sha256(krl, blob, blen); 2340664f4763Szrj } else 234136e94dc5SPeter Avalos r = ssh_krl_revoke_key(krl, key); 234236e94dc5SPeter Avalos if (r != 0) 2343e9778795SPeter Avalos fatal("%s: revoke key failed: %s", 2344e9778795SPeter Avalos __func__, ssh_err(r)); 2345664f4763Szrj freezero(blob, blen); 2346664f4763Szrj blob = NULL; 2347664f4763Szrj blen = 0; 2348e9778795SPeter Avalos sshkey_free(key); 234936e94dc5SPeter Avalos } 235036e94dc5SPeter Avalos } 235136e94dc5SPeter Avalos if (strcmp(path, "-") != 0) 235236e94dc5SPeter Avalos fclose(krl_spec); 2353664f4763Szrj free(line); 235436e94dc5SPeter Avalos free(path); 235536e94dc5SPeter Avalos } 235636e94dc5SPeter Avalos 235736e94dc5SPeter Avalos static void 2358664f4763Szrj do_gen_krl(struct passwd *pw, int updating, const char *ca_key_path, 2359664f4763Szrj unsigned long long krl_version, const char *krl_comment, 2360664f4763Szrj int argc, char **argv) 236136e94dc5SPeter Avalos { 236236e94dc5SPeter Avalos struct ssh_krl *krl; 236336e94dc5SPeter Avalos struct stat sb; 2364e9778795SPeter Avalos struct sshkey *ca = NULL; 23650cbfa66cSDaniel Fojt int i, r, wild_ca = 0; 236636e94dc5SPeter Avalos char *tmp; 2367e9778795SPeter Avalos struct sshbuf *kbuf; 236836e94dc5SPeter Avalos 236936e94dc5SPeter Avalos if (*identity_file == '\0') 237036e94dc5SPeter Avalos fatal("KRL generation requires an output file"); 237136e94dc5SPeter Avalos if (stat(identity_file, &sb) == -1) { 237236e94dc5SPeter Avalos if (errno != ENOENT) 237336e94dc5SPeter Avalos fatal("Cannot access KRL \"%s\": %s", 237436e94dc5SPeter Avalos identity_file, strerror(errno)); 237536e94dc5SPeter Avalos if (updating) 237636e94dc5SPeter Avalos fatal("KRL \"%s\" does not exist", identity_file); 237736e94dc5SPeter Avalos } 237836e94dc5SPeter Avalos if (ca_key_path != NULL) { 2379e9778795SPeter Avalos if (strcasecmp(ca_key_path, "none") == 0) 2380e9778795SPeter Avalos wild_ca = 1; 2381e9778795SPeter Avalos else { 238236e94dc5SPeter Avalos tmp = tilde_expand_filename(ca_key_path, pw->pw_uid); 2383e9778795SPeter Avalos if ((r = sshkey_load_public(tmp, &ca, NULL)) != 0) 2384e9778795SPeter Avalos fatal("Cannot load CA public key %s: %s", 2385e9778795SPeter Avalos tmp, ssh_err(r)); 238636e94dc5SPeter Avalos free(tmp); 238736e94dc5SPeter Avalos } 2388e9778795SPeter Avalos } 238936e94dc5SPeter Avalos 239036e94dc5SPeter Avalos if (updating) 239136e94dc5SPeter Avalos load_krl(identity_file, &krl); 239236e94dc5SPeter Avalos else if ((krl = ssh_krl_init()) == NULL) 239336e94dc5SPeter Avalos fatal("couldn't create KRL"); 239436e94dc5SPeter Avalos 2395664f4763Szrj if (krl_version != 0) 2396664f4763Szrj ssh_krl_set_version(krl, krl_version); 2397664f4763Szrj if (krl_comment != NULL) 2398664f4763Szrj ssh_krl_set_comment(krl, krl_comment); 239936e94dc5SPeter Avalos 240036e94dc5SPeter Avalos for (i = 0; i < argc; i++) 2401e9778795SPeter Avalos update_krl_from_file(pw, argv[i], wild_ca, ca, krl); 240236e94dc5SPeter Avalos 2403e9778795SPeter Avalos if ((kbuf = sshbuf_new()) == NULL) 2404e9778795SPeter Avalos fatal("sshbuf_new failed"); 2405e9778795SPeter Avalos if (ssh_krl_to_blob(krl, kbuf, NULL, 0) != 0) 240636e94dc5SPeter Avalos fatal("Couldn't generate KRL"); 24070cbfa66cSDaniel Fojt if ((r = sshbuf_write_file(identity_file, kbuf)) != 0) 240836e94dc5SPeter Avalos fatal("write %s: %s", identity_file, strerror(errno)); 2409e9778795SPeter Avalos sshbuf_free(kbuf); 241036e94dc5SPeter Avalos ssh_krl_free(krl); 2411e9778795SPeter Avalos sshkey_free(ca); 241236e94dc5SPeter Avalos } 241336e94dc5SPeter Avalos 241436e94dc5SPeter Avalos static void 24150cbfa66cSDaniel Fojt do_check_krl(struct passwd *pw, int print_krl, int argc, char **argv) 241636e94dc5SPeter Avalos { 241736e94dc5SPeter Avalos int i, r, ret = 0; 241836e94dc5SPeter Avalos char *comment; 241936e94dc5SPeter Avalos struct ssh_krl *krl; 2420e9778795SPeter Avalos struct sshkey *k; 242136e94dc5SPeter Avalos 242236e94dc5SPeter Avalos if (*identity_file == '\0') 242336e94dc5SPeter Avalos fatal("KRL checking requires an input file"); 242436e94dc5SPeter Avalos load_krl(identity_file, &krl); 24250cbfa66cSDaniel Fojt if (print_krl) 24260cbfa66cSDaniel Fojt krl_dump(krl, stdout); 242736e94dc5SPeter Avalos for (i = 0; i < argc; i++) { 2428e9778795SPeter Avalos if ((r = sshkey_load_public(argv[i], &k, &comment)) != 0) 2429e9778795SPeter Avalos fatal("Cannot load public key %s: %s", 2430e9778795SPeter Avalos argv[i], ssh_err(r)); 243136e94dc5SPeter Avalos r = ssh_krl_check_key(krl, k); 243236e94dc5SPeter Avalos printf("%s%s%s%s: %s\n", argv[i], 243336e94dc5SPeter Avalos *comment ? " (" : "", comment, *comment ? ")" : "", 243436e94dc5SPeter Avalos r == 0 ? "ok" : "REVOKED"); 243536e94dc5SPeter Avalos if (r != 0) 243636e94dc5SPeter Avalos ret = 1; 2437e9778795SPeter Avalos sshkey_free(k); 243836e94dc5SPeter Avalos free(comment); 243936e94dc5SPeter Avalos } 244036e94dc5SPeter Avalos ssh_krl_free(krl); 244136e94dc5SPeter Avalos exit(ret); 244236e94dc5SPeter Avalos } 244336e94dc5SPeter Avalos 24440cbfa66cSDaniel Fojt static struct sshkey * 24450cbfa66cSDaniel Fojt load_sign_key(const char *keypath, const struct sshkey *pubkey) 24460cbfa66cSDaniel Fojt { 24470cbfa66cSDaniel Fojt size_t i, slen, plen = strlen(keypath); 24480cbfa66cSDaniel Fojt char *privpath = xstrdup(keypath); 24490cbfa66cSDaniel Fojt const char *suffixes[] = { "-cert.pub", ".pub", NULL }; 24500cbfa66cSDaniel Fojt struct sshkey *ret = NULL, *privkey = NULL; 24510cbfa66cSDaniel Fojt int r; 24520cbfa66cSDaniel Fojt 24530cbfa66cSDaniel Fojt /* 24540cbfa66cSDaniel Fojt * If passed a public key filename, then try to locate the corresponding 24550cbfa66cSDaniel Fojt * private key. This lets us specify certificates on the command-line 24560cbfa66cSDaniel Fojt * and have ssh-keygen find the appropriate private key. 24570cbfa66cSDaniel Fojt */ 24580cbfa66cSDaniel Fojt for (i = 0; suffixes[i]; i++) { 24590cbfa66cSDaniel Fojt slen = strlen(suffixes[i]); 24600cbfa66cSDaniel Fojt if (plen <= slen || 24610cbfa66cSDaniel Fojt strcmp(privpath + plen - slen, suffixes[i]) != 0) 24620cbfa66cSDaniel Fojt continue; 24630cbfa66cSDaniel Fojt privpath[plen - slen] = '\0'; 24640cbfa66cSDaniel Fojt debug("%s: %s looks like a public key, using private key " 24650cbfa66cSDaniel Fojt "path %s instead", __func__, keypath, privpath); 24660cbfa66cSDaniel Fojt } 24670cbfa66cSDaniel Fojt if ((privkey = load_identity(privpath, NULL)) == NULL) { 24680cbfa66cSDaniel Fojt error("Couldn't load identity %s", keypath); 24690cbfa66cSDaniel Fojt goto done; 24700cbfa66cSDaniel Fojt } 24710cbfa66cSDaniel Fojt if (!sshkey_equal_public(pubkey, privkey)) { 24720cbfa66cSDaniel Fojt error("Public key %s doesn't match private %s", 24730cbfa66cSDaniel Fojt keypath, privpath); 24740cbfa66cSDaniel Fojt goto done; 24750cbfa66cSDaniel Fojt } 24760cbfa66cSDaniel Fojt if (sshkey_is_cert(pubkey) && !sshkey_is_cert(privkey)) { 24770cbfa66cSDaniel Fojt /* 24780cbfa66cSDaniel Fojt * Graft the certificate onto the private key to make 24790cbfa66cSDaniel Fojt * it capable of signing. 24800cbfa66cSDaniel Fojt */ 24810cbfa66cSDaniel Fojt if ((r = sshkey_to_certified(privkey)) != 0) { 24820cbfa66cSDaniel Fojt error("%s: sshkey_to_certified: %s", __func__, 24830cbfa66cSDaniel Fojt ssh_err(r)); 24840cbfa66cSDaniel Fojt goto done; 24850cbfa66cSDaniel Fojt } 24860cbfa66cSDaniel Fojt if ((r = sshkey_cert_copy(pubkey, privkey)) != 0) { 24870cbfa66cSDaniel Fojt error("%s: sshkey_cert_copy: %s", __func__, ssh_err(r)); 24880cbfa66cSDaniel Fojt goto done; 24890cbfa66cSDaniel Fojt } 24900cbfa66cSDaniel Fojt } 24910cbfa66cSDaniel Fojt /* success */ 24920cbfa66cSDaniel Fojt ret = privkey; 24930cbfa66cSDaniel Fojt privkey = NULL; 24940cbfa66cSDaniel Fojt done: 24950cbfa66cSDaniel Fojt sshkey_free(privkey); 24960cbfa66cSDaniel Fojt free(privpath); 24970cbfa66cSDaniel Fojt return ret; 24980cbfa66cSDaniel Fojt } 24990cbfa66cSDaniel Fojt 25000cbfa66cSDaniel Fojt static int 25010cbfa66cSDaniel Fojt sign_one(struct sshkey *signkey, const char *filename, int fd, 25020cbfa66cSDaniel Fojt const char *sig_namespace, sshsig_signer *signer, void *signer_ctx) 25030cbfa66cSDaniel Fojt { 25040cbfa66cSDaniel Fojt struct sshbuf *sigbuf = NULL, *abuf = NULL; 25050cbfa66cSDaniel Fojt int r = SSH_ERR_INTERNAL_ERROR, wfd = -1, oerrno; 25060cbfa66cSDaniel Fojt char *wfile = NULL, *asig = NULL, *fp = NULL; 25070cbfa66cSDaniel Fojt 25080cbfa66cSDaniel Fojt if (!quiet) { 25090cbfa66cSDaniel Fojt if (fd == STDIN_FILENO) 25100cbfa66cSDaniel Fojt fprintf(stderr, "Signing data on standard input\n"); 25110cbfa66cSDaniel Fojt else 25120cbfa66cSDaniel Fojt fprintf(stderr, "Signing file %s\n", filename); 25130cbfa66cSDaniel Fojt } 25140cbfa66cSDaniel Fojt if (signer == NULL && sshkey_is_sk(signkey) && 25150cbfa66cSDaniel Fojt (signkey->sk_flags & SSH_SK_USER_PRESENCE_REQD)) { 25160cbfa66cSDaniel Fojt if ((fp = sshkey_fingerprint(signkey, fingerprint_hash, 25170cbfa66cSDaniel Fojt SSH_FP_DEFAULT)) == NULL) 25180cbfa66cSDaniel Fojt fatal("%s: sshkey_fingerprint failed", __func__); 25190cbfa66cSDaniel Fojt fprintf(stderr, "Confirm user presence for key %s %s\n", 25200cbfa66cSDaniel Fojt sshkey_type(signkey), fp); 25210cbfa66cSDaniel Fojt free(fp); 25220cbfa66cSDaniel Fojt } 25230cbfa66cSDaniel Fojt if ((r = sshsig_sign_fd(signkey, NULL, sk_provider, fd, sig_namespace, 25240cbfa66cSDaniel Fojt &sigbuf, signer, signer_ctx)) != 0) { 25250cbfa66cSDaniel Fojt error("Signing %s failed: %s", filename, ssh_err(r)); 25260cbfa66cSDaniel Fojt goto out; 25270cbfa66cSDaniel Fojt } 25280cbfa66cSDaniel Fojt if ((r = sshsig_armor(sigbuf, &abuf)) != 0) { 25290cbfa66cSDaniel Fojt error("%s: sshsig_armor: %s", __func__, ssh_err(r)); 25300cbfa66cSDaniel Fojt goto out; 25310cbfa66cSDaniel Fojt } 25320cbfa66cSDaniel Fojt if ((asig = sshbuf_dup_string(abuf)) == NULL) { 25330cbfa66cSDaniel Fojt error("%s: buffer error", __func__); 25340cbfa66cSDaniel Fojt r = SSH_ERR_ALLOC_FAIL; 25350cbfa66cSDaniel Fojt goto out; 25360cbfa66cSDaniel Fojt } 25370cbfa66cSDaniel Fojt 25380cbfa66cSDaniel Fojt if (fd == STDIN_FILENO) { 25390cbfa66cSDaniel Fojt fputs(asig, stdout); 25400cbfa66cSDaniel Fojt fflush(stdout); 25410cbfa66cSDaniel Fojt } else { 25420cbfa66cSDaniel Fojt xasprintf(&wfile, "%s.sig", filename); 25430cbfa66cSDaniel Fojt if (confirm_overwrite(wfile)) { 25440cbfa66cSDaniel Fojt if ((wfd = open(wfile, O_WRONLY|O_CREAT|O_TRUNC, 25450cbfa66cSDaniel Fojt 0666)) == -1) { 25460cbfa66cSDaniel Fojt oerrno = errno; 25470cbfa66cSDaniel Fojt error("Cannot open %s: %s", 25480cbfa66cSDaniel Fojt wfile, strerror(errno)); 25490cbfa66cSDaniel Fojt errno = oerrno; 25500cbfa66cSDaniel Fojt r = SSH_ERR_SYSTEM_ERROR; 25510cbfa66cSDaniel Fojt goto out; 25520cbfa66cSDaniel Fojt } 25530cbfa66cSDaniel Fojt if (atomicio(vwrite, wfd, asig, 25540cbfa66cSDaniel Fojt strlen(asig)) != strlen(asig)) { 25550cbfa66cSDaniel Fojt oerrno = errno; 25560cbfa66cSDaniel Fojt error("Cannot write to %s: %s", 25570cbfa66cSDaniel Fojt wfile, strerror(errno)); 25580cbfa66cSDaniel Fojt errno = oerrno; 25590cbfa66cSDaniel Fojt r = SSH_ERR_SYSTEM_ERROR; 25600cbfa66cSDaniel Fojt goto out; 25610cbfa66cSDaniel Fojt } 25620cbfa66cSDaniel Fojt if (!quiet) { 25630cbfa66cSDaniel Fojt fprintf(stderr, "Write signature to %s\n", 25640cbfa66cSDaniel Fojt wfile); 25650cbfa66cSDaniel Fojt } 25660cbfa66cSDaniel Fojt } 25670cbfa66cSDaniel Fojt } 25680cbfa66cSDaniel Fojt /* success */ 25690cbfa66cSDaniel Fojt r = 0; 25700cbfa66cSDaniel Fojt out: 25710cbfa66cSDaniel Fojt free(wfile); 25720cbfa66cSDaniel Fojt free(asig); 25730cbfa66cSDaniel Fojt sshbuf_free(abuf); 25740cbfa66cSDaniel Fojt sshbuf_free(sigbuf); 25750cbfa66cSDaniel Fojt if (wfd != -1) 25760cbfa66cSDaniel Fojt close(wfd); 25770cbfa66cSDaniel Fojt return r; 25780cbfa66cSDaniel Fojt } 25790cbfa66cSDaniel Fojt 25800cbfa66cSDaniel Fojt static int 25810cbfa66cSDaniel Fojt sig_sign(const char *keypath, const char *sig_namespace, int argc, char **argv) 25820cbfa66cSDaniel Fojt { 25830cbfa66cSDaniel Fojt int i, fd = -1, r, ret = -1; 25840cbfa66cSDaniel Fojt int agent_fd = -1; 25850cbfa66cSDaniel Fojt struct sshkey *pubkey = NULL, *privkey = NULL, *signkey = NULL; 25860cbfa66cSDaniel Fojt sshsig_signer *signer = NULL; 25870cbfa66cSDaniel Fojt 25880cbfa66cSDaniel Fojt /* Check file arguments. */ 25890cbfa66cSDaniel Fojt for (i = 0; i < argc; i++) { 25900cbfa66cSDaniel Fojt if (strcmp(argv[i], "-") != 0) 25910cbfa66cSDaniel Fojt continue; 25920cbfa66cSDaniel Fojt if (i > 0 || argc > 1) 25930cbfa66cSDaniel Fojt fatal("Cannot sign mix of paths and standard input"); 25940cbfa66cSDaniel Fojt } 25950cbfa66cSDaniel Fojt 25960cbfa66cSDaniel Fojt if ((r = sshkey_load_public(keypath, &pubkey, NULL)) != 0) { 25970cbfa66cSDaniel Fojt error("Couldn't load public key %s: %s", keypath, ssh_err(r)); 25980cbfa66cSDaniel Fojt goto done; 25990cbfa66cSDaniel Fojt } 26000cbfa66cSDaniel Fojt 26010cbfa66cSDaniel Fojt if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) 26020cbfa66cSDaniel Fojt debug("Couldn't get agent socket: %s", ssh_err(r)); 26030cbfa66cSDaniel Fojt else { 26040cbfa66cSDaniel Fojt if ((r = ssh_agent_has_key(agent_fd, pubkey)) == 0) 26050cbfa66cSDaniel Fojt signer = agent_signer; 26060cbfa66cSDaniel Fojt else 26070cbfa66cSDaniel Fojt debug("Couldn't find key in agent: %s", ssh_err(r)); 26080cbfa66cSDaniel Fojt } 26090cbfa66cSDaniel Fojt 26100cbfa66cSDaniel Fojt if (signer == NULL) { 26110cbfa66cSDaniel Fojt /* Not using agent - try to load private key */ 26120cbfa66cSDaniel Fojt if ((privkey = load_sign_key(keypath, pubkey)) == NULL) 26130cbfa66cSDaniel Fojt goto done; 26140cbfa66cSDaniel Fojt signkey = privkey; 26150cbfa66cSDaniel Fojt } else { 26160cbfa66cSDaniel Fojt /* Will use key in agent */ 26170cbfa66cSDaniel Fojt signkey = pubkey; 26180cbfa66cSDaniel Fojt } 26190cbfa66cSDaniel Fojt 26200cbfa66cSDaniel Fojt if (argc == 0) { 26210cbfa66cSDaniel Fojt if ((r = sign_one(signkey, "(stdin)", STDIN_FILENO, 26220cbfa66cSDaniel Fojt sig_namespace, signer, &agent_fd)) != 0) 26230cbfa66cSDaniel Fojt goto done; 26240cbfa66cSDaniel Fojt } else { 26250cbfa66cSDaniel Fojt for (i = 0; i < argc; i++) { 26260cbfa66cSDaniel Fojt if (strcmp(argv[i], "-") == 0) 26270cbfa66cSDaniel Fojt fd = STDIN_FILENO; 26280cbfa66cSDaniel Fojt else if ((fd = open(argv[i], O_RDONLY)) == -1) { 26290cbfa66cSDaniel Fojt error("Cannot open %s for signing: %s", 26300cbfa66cSDaniel Fojt argv[i], strerror(errno)); 26310cbfa66cSDaniel Fojt goto done; 26320cbfa66cSDaniel Fojt } 26330cbfa66cSDaniel Fojt if ((r = sign_one(signkey, argv[i], fd, sig_namespace, 26340cbfa66cSDaniel Fojt signer, &agent_fd)) != 0) 26350cbfa66cSDaniel Fojt goto done; 26360cbfa66cSDaniel Fojt if (fd != STDIN_FILENO) 26370cbfa66cSDaniel Fojt close(fd); 26380cbfa66cSDaniel Fojt fd = -1; 26390cbfa66cSDaniel Fojt } 26400cbfa66cSDaniel Fojt } 26410cbfa66cSDaniel Fojt 26420cbfa66cSDaniel Fojt ret = 0; 26430cbfa66cSDaniel Fojt done: 26440cbfa66cSDaniel Fojt if (fd != -1 && fd != STDIN_FILENO) 26450cbfa66cSDaniel Fojt close(fd); 26460cbfa66cSDaniel Fojt sshkey_free(pubkey); 26470cbfa66cSDaniel Fojt sshkey_free(privkey); 26480cbfa66cSDaniel Fojt return ret; 26490cbfa66cSDaniel Fojt } 26500cbfa66cSDaniel Fojt 26510cbfa66cSDaniel Fojt static int 26520cbfa66cSDaniel Fojt sig_verify(const char *signature, const char *sig_namespace, 26530cbfa66cSDaniel Fojt const char *principal, const char *allowed_keys, const char *revoked_keys) 26540cbfa66cSDaniel Fojt { 26550cbfa66cSDaniel Fojt int r, ret = -1; 26560cbfa66cSDaniel Fojt struct sshbuf *sigbuf = NULL, *abuf = NULL; 26570cbfa66cSDaniel Fojt struct sshkey *sign_key = NULL; 26580cbfa66cSDaniel Fojt char *fp = NULL; 26590cbfa66cSDaniel Fojt struct sshkey_sig_details *sig_details = NULL; 26600cbfa66cSDaniel Fojt 26610cbfa66cSDaniel Fojt memset(&sig_details, 0, sizeof(sig_details)); 26620cbfa66cSDaniel Fojt if ((r = sshbuf_load_file(signature, &abuf)) != 0) { 26630cbfa66cSDaniel Fojt error("Couldn't read signature file: %s", ssh_err(r)); 26640cbfa66cSDaniel Fojt goto done; 26650cbfa66cSDaniel Fojt } 26660cbfa66cSDaniel Fojt 26670cbfa66cSDaniel Fojt if ((r = sshsig_dearmor(abuf, &sigbuf)) != 0) { 26680cbfa66cSDaniel Fojt error("%s: sshsig_armor: %s", __func__, ssh_err(r)); 26690cbfa66cSDaniel Fojt goto done; 26700cbfa66cSDaniel Fojt } 26710cbfa66cSDaniel Fojt if ((r = sshsig_verify_fd(sigbuf, STDIN_FILENO, sig_namespace, 26720cbfa66cSDaniel Fojt &sign_key, &sig_details)) != 0) 26730cbfa66cSDaniel Fojt goto done; /* sshsig_verify() prints error */ 26740cbfa66cSDaniel Fojt 26750cbfa66cSDaniel Fojt if ((fp = sshkey_fingerprint(sign_key, fingerprint_hash, 26760cbfa66cSDaniel Fojt SSH_FP_DEFAULT)) == NULL) 26770cbfa66cSDaniel Fojt fatal("%s: sshkey_fingerprint failed", __func__); 26780cbfa66cSDaniel Fojt debug("Valid (unverified) signature from key %s", fp); 26790cbfa66cSDaniel Fojt if (sig_details != NULL) { 26800cbfa66cSDaniel Fojt debug2("%s: signature details: counter = %u, flags = 0x%02x", 26810cbfa66cSDaniel Fojt __func__, sig_details->sk_counter, sig_details->sk_flags); 26820cbfa66cSDaniel Fojt } 26830cbfa66cSDaniel Fojt free(fp); 26840cbfa66cSDaniel Fojt fp = NULL; 26850cbfa66cSDaniel Fojt 26860cbfa66cSDaniel Fojt if (revoked_keys != NULL) { 26870cbfa66cSDaniel Fojt if ((r = sshkey_check_revoked(sign_key, revoked_keys)) != 0) { 26880cbfa66cSDaniel Fojt debug3("sshkey_check_revoked failed: %s", ssh_err(r)); 26890cbfa66cSDaniel Fojt goto done; 26900cbfa66cSDaniel Fojt } 26910cbfa66cSDaniel Fojt } 26920cbfa66cSDaniel Fojt 26930cbfa66cSDaniel Fojt if (allowed_keys != NULL && 26940cbfa66cSDaniel Fojt (r = sshsig_check_allowed_keys(allowed_keys, sign_key, 26950cbfa66cSDaniel Fojt principal, sig_namespace)) != 0) { 26960cbfa66cSDaniel Fojt debug3("sshsig_check_allowed_keys failed: %s", ssh_err(r)); 26970cbfa66cSDaniel Fojt goto done; 26980cbfa66cSDaniel Fojt } 26990cbfa66cSDaniel Fojt /* success */ 27000cbfa66cSDaniel Fojt ret = 0; 27010cbfa66cSDaniel Fojt done: 27020cbfa66cSDaniel Fojt if (!quiet) { 27030cbfa66cSDaniel Fojt if (ret == 0) { 27040cbfa66cSDaniel Fojt if ((fp = sshkey_fingerprint(sign_key, fingerprint_hash, 27050cbfa66cSDaniel Fojt SSH_FP_DEFAULT)) == NULL) { 27060cbfa66cSDaniel Fojt fatal("%s: sshkey_fingerprint failed", 27070cbfa66cSDaniel Fojt __func__); 27080cbfa66cSDaniel Fojt } 27090cbfa66cSDaniel Fojt if (principal == NULL) { 27100cbfa66cSDaniel Fojt printf("Good \"%s\" signature with %s key %s\n", 27110cbfa66cSDaniel Fojt sig_namespace, sshkey_type(sign_key), fp); 27120cbfa66cSDaniel Fojt 27130cbfa66cSDaniel Fojt } else { 27140cbfa66cSDaniel Fojt printf("Good \"%s\" signature for %s with %s key %s\n", 27150cbfa66cSDaniel Fojt sig_namespace, principal, 27160cbfa66cSDaniel Fojt sshkey_type(sign_key), fp); 27170cbfa66cSDaniel Fojt } 27180cbfa66cSDaniel Fojt } else { 27190cbfa66cSDaniel Fojt printf("Could not verify signature.\n"); 27200cbfa66cSDaniel Fojt } 27210cbfa66cSDaniel Fojt } 27220cbfa66cSDaniel Fojt sshbuf_free(sigbuf); 27230cbfa66cSDaniel Fojt sshbuf_free(abuf); 27240cbfa66cSDaniel Fojt sshkey_free(sign_key); 27250cbfa66cSDaniel Fojt sshkey_sig_details_free(sig_details); 27260cbfa66cSDaniel Fojt free(fp); 27270cbfa66cSDaniel Fojt return ret; 27280cbfa66cSDaniel Fojt } 27290cbfa66cSDaniel Fojt 27300cbfa66cSDaniel Fojt static int 27310cbfa66cSDaniel Fojt sig_find_principals(const char *signature, const char *allowed_keys) { 27320cbfa66cSDaniel Fojt int r, ret = -1; 27330cbfa66cSDaniel Fojt struct sshbuf *sigbuf = NULL, *abuf = NULL; 27340cbfa66cSDaniel Fojt struct sshkey *sign_key = NULL; 27350cbfa66cSDaniel Fojt char *principals = NULL, *cp, *tmp; 27360cbfa66cSDaniel Fojt 27370cbfa66cSDaniel Fojt if ((r = sshbuf_load_file(signature, &abuf)) != 0) { 27380cbfa66cSDaniel Fojt error("Couldn't read signature file: %s", ssh_err(r)); 27390cbfa66cSDaniel Fojt goto done; 27400cbfa66cSDaniel Fojt } 27410cbfa66cSDaniel Fojt if ((r = sshsig_dearmor(abuf, &sigbuf)) != 0) { 27420cbfa66cSDaniel Fojt error("%s: sshsig_armor: %s", __func__, ssh_err(r)); 27430cbfa66cSDaniel Fojt goto done; 27440cbfa66cSDaniel Fojt } 27450cbfa66cSDaniel Fojt if ((r = sshsig_get_pubkey(sigbuf, &sign_key)) != 0) { 27460cbfa66cSDaniel Fojt error("%s: sshsig_get_pubkey: %s", 27470cbfa66cSDaniel Fojt __func__, ssh_err(r)); 27480cbfa66cSDaniel Fojt goto done; 27490cbfa66cSDaniel Fojt } 27500cbfa66cSDaniel Fojt if ((r = sshsig_find_principals(allowed_keys, sign_key, 27510cbfa66cSDaniel Fojt &principals)) != 0) { 27520cbfa66cSDaniel Fojt error("%s: sshsig_get_principal: %s", 27530cbfa66cSDaniel Fojt __func__, ssh_err(r)); 27540cbfa66cSDaniel Fojt goto done; 27550cbfa66cSDaniel Fojt } 27560cbfa66cSDaniel Fojt ret = 0; 27570cbfa66cSDaniel Fojt done: 27580cbfa66cSDaniel Fojt if (ret == 0 ) { 27590cbfa66cSDaniel Fojt /* Emit matching principals one per line */ 27600cbfa66cSDaniel Fojt tmp = principals; 27610cbfa66cSDaniel Fojt while ((cp = strsep(&tmp, ",")) != NULL && *cp != '\0') 27620cbfa66cSDaniel Fojt puts(cp); 27630cbfa66cSDaniel Fojt } else { 27640cbfa66cSDaniel Fojt fprintf(stderr, "No principal matched.\n"); 27650cbfa66cSDaniel Fojt } 27660cbfa66cSDaniel Fojt sshbuf_free(sigbuf); 27670cbfa66cSDaniel Fojt sshbuf_free(abuf); 27680cbfa66cSDaniel Fojt sshkey_free(sign_key); 27690cbfa66cSDaniel Fojt free(principals); 27700cbfa66cSDaniel Fojt return ret; 27710cbfa66cSDaniel Fojt } 27720cbfa66cSDaniel Fojt 27730cbfa66cSDaniel Fojt static void 27740cbfa66cSDaniel Fojt do_moduli_gen(const char *out_file, char **opts, size_t nopts) 27750cbfa66cSDaniel Fojt { 27760cbfa66cSDaniel Fojt #ifdef WITH_OPENSSL 27770cbfa66cSDaniel Fojt /* Moduli generation/screening */ 27780cbfa66cSDaniel Fojt u_int32_t memory = 0; 27790cbfa66cSDaniel Fojt BIGNUM *start = NULL; 27800cbfa66cSDaniel Fojt int moduli_bits = 0; 27810cbfa66cSDaniel Fojt FILE *out; 27820cbfa66cSDaniel Fojt size_t i; 27830cbfa66cSDaniel Fojt const char *errstr; 27840cbfa66cSDaniel Fojt 27850cbfa66cSDaniel Fojt /* Parse options */ 27860cbfa66cSDaniel Fojt for (i = 0; i < nopts; i++) { 27870cbfa66cSDaniel Fojt if (strncmp(opts[i], "memory=", 7) == 0) { 27880cbfa66cSDaniel Fojt memory = (u_int32_t)strtonum(opts[i]+7, 1, 27890cbfa66cSDaniel Fojt UINT_MAX, &errstr); 27900cbfa66cSDaniel Fojt if (errstr) { 27910cbfa66cSDaniel Fojt fatal("Memory limit is %s: %s", 27920cbfa66cSDaniel Fojt errstr, opts[i]+7); 27930cbfa66cSDaniel Fojt } 27940cbfa66cSDaniel Fojt } else if (strncmp(opts[i], "start=", 6) == 0) { 27950cbfa66cSDaniel Fojt /* XXX - also compare length against bits */ 27960cbfa66cSDaniel Fojt if (BN_hex2bn(&start, opts[i]+6) == 0) 27970cbfa66cSDaniel Fojt fatal("Invalid start point."); 27980cbfa66cSDaniel Fojt } else if (strncmp(opts[i], "bits=", 5) == 0) { 27990cbfa66cSDaniel Fojt moduli_bits = (int)strtonum(opts[i]+5, 1, 28000cbfa66cSDaniel Fojt INT_MAX, &errstr); 28010cbfa66cSDaniel Fojt if (errstr) { 28020cbfa66cSDaniel Fojt fatal("Invalid number: %s (%s)", 28030cbfa66cSDaniel Fojt opts[i]+12, errstr); 28040cbfa66cSDaniel Fojt } 28050cbfa66cSDaniel Fojt } else { 28060cbfa66cSDaniel Fojt fatal("Option \"%s\" is unsupported for moduli " 28070cbfa66cSDaniel Fojt "generation", opts[i]); 28080cbfa66cSDaniel Fojt } 28090cbfa66cSDaniel Fojt } 28100cbfa66cSDaniel Fojt 28110cbfa66cSDaniel Fojt if ((out = fopen(out_file, "w")) == NULL) { 28120cbfa66cSDaniel Fojt fatal("Couldn't open modulus candidate file \"%s\": %s", 28130cbfa66cSDaniel Fojt out_file, strerror(errno)); 28140cbfa66cSDaniel Fojt } 28150cbfa66cSDaniel Fojt setvbuf(out, NULL, _IOLBF, 0); 28160cbfa66cSDaniel Fojt 28170cbfa66cSDaniel Fojt if (moduli_bits == 0) 28180cbfa66cSDaniel Fojt moduli_bits = DEFAULT_BITS; 28190cbfa66cSDaniel Fojt if (gen_candidates(out, memory, moduli_bits, start) != 0) 28200cbfa66cSDaniel Fojt fatal("modulus candidate generation failed"); 28210cbfa66cSDaniel Fojt #else /* WITH_OPENSSL */ 28220cbfa66cSDaniel Fojt fatal("Moduli generation is not supported"); 28230cbfa66cSDaniel Fojt #endif /* WITH_OPENSSL */ 28240cbfa66cSDaniel Fojt } 28250cbfa66cSDaniel Fojt 28260cbfa66cSDaniel Fojt static void 28270cbfa66cSDaniel Fojt do_moduli_screen(const char *out_file, char **opts, size_t nopts) 28280cbfa66cSDaniel Fojt { 28290cbfa66cSDaniel Fojt #ifdef WITH_OPENSSL 28300cbfa66cSDaniel Fojt /* Moduli generation/screening */ 28310cbfa66cSDaniel Fojt char *checkpoint = NULL; 28320cbfa66cSDaniel Fojt u_int32_t generator_wanted = 0; 28330cbfa66cSDaniel Fojt unsigned long start_lineno = 0, lines_to_process = 0; 28340cbfa66cSDaniel Fojt int prime_tests = 0; 28350cbfa66cSDaniel Fojt FILE *out, *in = stdin; 28360cbfa66cSDaniel Fojt size_t i; 28370cbfa66cSDaniel Fojt const char *errstr; 28380cbfa66cSDaniel Fojt 28390cbfa66cSDaniel Fojt /* Parse options */ 28400cbfa66cSDaniel Fojt for (i = 0; i < nopts; i++) { 28410cbfa66cSDaniel Fojt if (strncmp(opts[i], "lines=", 6) == 0) { 28420cbfa66cSDaniel Fojt lines_to_process = strtoul(opts[i]+6, NULL, 10); 28430cbfa66cSDaniel Fojt } else if (strncmp(opts[i], "start-line=", 11) == 0) { 28440cbfa66cSDaniel Fojt start_lineno = strtoul(opts[i]+11, NULL, 10); 28450cbfa66cSDaniel Fojt } else if (strncmp(opts[i], "checkpoint=", 11) == 0) { 28460cbfa66cSDaniel Fojt checkpoint = xstrdup(opts[i]+11); 28470cbfa66cSDaniel Fojt } else if (strncmp(opts[i], "generator=", 10) == 0) { 28480cbfa66cSDaniel Fojt generator_wanted = (u_int32_t)strtonum( 28490cbfa66cSDaniel Fojt opts[i]+10, 1, UINT_MAX, &errstr); 28500cbfa66cSDaniel Fojt if (errstr != NULL) { 28510cbfa66cSDaniel Fojt fatal("Generator invalid: %s (%s)", 28520cbfa66cSDaniel Fojt opts[i]+10, errstr); 28530cbfa66cSDaniel Fojt } 28540cbfa66cSDaniel Fojt } else if (strncmp(opts[i], "prime-tests=", 12) == 0) { 28550cbfa66cSDaniel Fojt prime_tests = (int)strtonum(opts[i]+12, 1, 28560cbfa66cSDaniel Fojt INT_MAX, &errstr); 28570cbfa66cSDaniel Fojt if (errstr) { 28580cbfa66cSDaniel Fojt fatal("Invalid number: %s (%s)", 28590cbfa66cSDaniel Fojt opts[i]+12, errstr); 28600cbfa66cSDaniel Fojt } 28610cbfa66cSDaniel Fojt } else { 28620cbfa66cSDaniel Fojt fatal("Option \"%s\" is unsupported for moduli " 28630cbfa66cSDaniel Fojt "screening", opts[i]); 28640cbfa66cSDaniel Fojt } 28650cbfa66cSDaniel Fojt } 28660cbfa66cSDaniel Fojt 28670cbfa66cSDaniel Fojt if (have_identity && strcmp(identity_file, "-") != 0) { 28680cbfa66cSDaniel Fojt if ((in = fopen(identity_file, "r")) == NULL) { 28690cbfa66cSDaniel Fojt fatal("Couldn't open modulus candidate " 28700cbfa66cSDaniel Fojt "file \"%s\": %s", identity_file, 28710cbfa66cSDaniel Fojt strerror(errno)); 28720cbfa66cSDaniel Fojt } 28730cbfa66cSDaniel Fojt } 28740cbfa66cSDaniel Fojt 28750cbfa66cSDaniel Fojt if ((out = fopen(out_file, "a")) == NULL) { 28760cbfa66cSDaniel Fojt fatal("Couldn't open moduli file \"%s\": %s", 28770cbfa66cSDaniel Fojt out_file, strerror(errno)); 28780cbfa66cSDaniel Fojt } 28790cbfa66cSDaniel Fojt setvbuf(out, NULL, _IOLBF, 0); 28800cbfa66cSDaniel Fojt if (prime_test(in, out, prime_tests == 0 ? 100 : prime_tests, 28810cbfa66cSDaniel Fojt generator_wanted, checkpoint, 28820cbfa66cSDaniel Fojt start_lineno, lines_to_process) != 0) 28830cbfa66cSDaniel Fojt fatal("modulus screening failed"); 28840cbfa66cSDaniel Fojt #else /* WITH_OPENSSL */ 28850cbfa66cSDaniel Fojt fatal("Moduli screening is not supported"); 28860cbfa66cSDaniel Fojt #endif /* WITH_OPENSSL */ 28870cbfa66cSDaniel Fojt } 28880cbfa66cSDaniel Fojt 28890cbfa66cSDaniel Fojt static char * 28900cbfa66cSDaniel Fojt private_key_passphrase(void) 28910cbfa66cSDaniel Fojt { 28920cbfa66cSDaniel Fojt char *passphrase1, *passphrase2; 28930cbfa66cSDaniel Fojt 28940cbfa66cSDaniel Fojt /* Ask for a passphrase (twice). */ 28950cbfa66cSDaniel Fojt if (identity_passphrase) 28960cbfa66cSDaniel Fojt passphrase1 = xstrdup(identity_passphrase); 28970cbfa66cSDaniel Fojt else if (identity_new_passphrase) 28980cbfa66cSDaniel Fojt passphrase1 = xstrdup(identity_new_passphrase); 28990cbfa66cSDaniel Fojt else { 29000cbfa66cSDaniel Fojt passphrase_again: 29010cbfa66cSDaniel Fojt passphrase1 = 29020cbfa66cSDaniel Fojt read_passphrase("Enter passphrase (empty for no " 29030cbfa66cSDaniel Fojt "passphrase): ", RP_ALLOW_STDIN); 29040cbfa66cSDaniel Fojt passphrase2 = read_passphrase("Enter same passphrase again: ", 29050cbfa66cSDaniel Fojt RP_ALLOW_STDIN); 29060cbfa66cSDaniel Fojt if (strcmp(passphrase1, passphrase2) != 0) { 29070cbfa66cSDaniel Fojt /* 29080cbfa66cSDaniel Fojt * The passphrases do not match. Clear them and 29090cbfa66cSDaniel Fojt * retry. 29100cbfa66cSDaniel Fojt */ 29110cbfa66cSDaniel Fojt freezero(passphrase1, strlen(passphrase1)); 29120cbfa66cSDaniel Fojt freezero(passphrase2, strlen(passphrase2)); 29130cbfa66cSDaniel Fojt printf("Passphrases do not match. Try again.\n"); 29140cbfa66cSDaniel Fojt goto passphrase_again; 29150cbfa66cSDaniel Fojt } 29160cbfa66cSDaniel Fojt /* Clear the other copy of the passphrase. */ 29170cbfa66cSDaniel Fojt freezero(passphrase2, strlen(passphrase2)); 29180cbfa66cSDaniel Fojt } 29190cbfa66cSDaniel Fojt return passphrase1; 29200cbfa66cSDaniel Fojt } 29210cbfa66cSDaniel Fojt 29220cbfa66cSDaniel Fojt static const char * 29230cbfa66cSDaniel Fojt skip_ssh_url_preamble(const char *s) 29240cbfa66cSDaniel Fojt { 29250cbfa66cSDaniel Fojt if (strncmp(s, "ssh://", 6) == 0) 29260cbfa66cSDaniel Fojt return s + 6; 29270cbfa66cSDaniel Fojt else if (strncmp(s, "ssh:", 4) == 0) 29280cbfa66cSDaniel Fojt return s + 4; 29290cbfa66cSDaniel Fojt return s; 29300cbfa66cSDaniel Fojt } 29310cbfa66cSDaniel Fojt 29320cbfa66cSDaniel Fojt static int 29330cbfa66cSDaniel Fojt do_download_sk(const char *skprovider, const char *device) 29340cbfa66cSDaniel Fojt { 29350cbfa66cSDaniel Fojt struct sshkey **keys; 29360cbfa66cSDaniel Fojt size_t nkeys, i; 29370cbfa66cSDaniel Fojt int r, ok = -1; 29380cbfa66cSDaniel Fojt char *fp, *pin = NULL, *pass = NULL, *path, *pubpath; 29390cbfa66cSDaniel Fojt const char *ext; 29400cbfa66cSDaniel Fojt 29410cbfa66cSDaniel Fojt if (skprovider == NULL) 29420cbfa66cSDaniel Fojt fatal("Cannot download keys without provider"); 29430cbfa66cSDaniel Fojt 29440cbfa66cSDaniel Fojt for (i = 0; i < 2; i++) { 29450cbfa66cSDaniel Fojt if (i == 1) { 29460cbfa66cSDaniel Fojt pin = read_passphrase("Enter PIN for authenticator: ", 29470cbfa66cSDaniel Fojt RP_ALLOW_STDIN); 29480cbfa66cSDaniel Fojt } 29490cbfa66cSDaniel Fojt if ((r = sshsk_load_resident(skprovider, device, pin, 29500cbfa66cSDaniel Fojt &keys, &nkeys)) != 0) { 29510cbfa66cSDaniel Fojt if (i == 0 && r == SSH_ERR_KEY_WRONG_PASSPHRASE) 29520cbfa66cSDaniel Fojt continue; 29530cbfa66cSDaniel Fojt if (pin != NULL) 29540cbfa66cSDaniel Fojt freezero(pin, strlen(pin)); 29550cbfa66cSDaniel Fojt error("Unable to load resident keys: %s", ssh_err(r)); 29560cbfa66cSDaniel Fojt return -1; 29570cbfa66cSDaniel Fojt } 29580cbfa66cSDaniel Fojt } 29590cbfa66cSDaniel Fojt if (nkeys == 0) 29600cbfa66cSDaniel Fojt logit("No keys to download"); 29610cbfa66cSDaniel Fojt if (pin != NULL) 29620cbfa66cSDaniel Fojt freezero(pin, strlen(pin)); 29630cbfa66cSDaniel Fojt 29640cbfa66cSDaniel Fojt for (i = 0; i < nkeys; i++) { 29650cbfa66cSDaniel Fojt if (keys[i]->type != KEY_ECDSA_SK && 29660cbfa66cSDaniel Fojt keys[i]->type != KEY_ED25519_SK) { 29670cbfa66cSDaniel Fojt error("Unsupported key type %s (%d)", 29680cbfa66cSDaniel Fojt sshkey_type(keys[i]), keys[i]->type); 29690cbfa66cSDaniel Fojt continue; 29700cbfa66cSDaniel Fojt } 29710cbfa66cSDaniel Fojt if ((fp = sshkey_fingerprint(keys[i], 29720cbfa66cSDaniel Fojt fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 29730cbfa66cSDaniel Fojt fatal("%s: sshkey_fingerprint failed", __func__); 29740cbfa66cSDaniel Fojt debug("%s: key %zu: %s %s %s (flags 0x%02x)", __func__, i, 29750cbfa66cSDaniel Fojt sshkey_type(keys[i]), fp, keys[i]->sk_application, 29760cbfa66cSDaniel Fojt keys[i]->sk_flags); 29770cbfa66cSDaniel Fojt ext = skip_ssh_url_preamble(keys[i]->sk_application); 29780cbfa66cSDaniel Fojt xasprintf(&path, "id_%s_rk%s%s", 29790cbfa66cSDaniel Fojt keys[i]->type == KEY_ECDSA_SK ? "ecdsa_sk" : "ed25519_sk", 29800cbfa66cSDaniel Fojt *ext == '\0' ? "" : "_", ext); 29810cbfa66cSDaniel Fojt 29820cbfa66cSDaniel Fojt /* If the file already exists, ask the user to confirm. */ 29830cbfa66cSDaniel Fojt if (!confirm_overwrite(path)) { 29840cbfa66cSDaniel Fojt free(path); 29850cbfa66cSDaniel Fojt break; 29860cbfa66cSDaniel Fojt } 29870cbfa66cSDaniel Fojt 29880cbfa66cSDaniel Fojt /* Save the key with the application string as the comment */ 29890cbfa66cSDaniel Fojt if (pass == NULL) 29900cbfa66cSDaniel Fojt pass = private_key_passphrase(); 29910cbfa66cSDaniel Fojt if ((r = sshkey_save_private(keys[i], path, pass, 29920cbfa66cSDaniel Fojt keys[i]->sk_application, private_key_format, 29930cbfa66cSDaniel Fojt openssh_format_cipher, rounds)) != 0) { 29940cbfa66cSDaniel Fojt error("Saving key \"%s\" failed: %s", 29950cbfa66cSDaniel Fojt path, ssh_err(r)); 29960cbfa66cSDaniel Fojt free(path); 29970cbfa66cSDaniel Fojt break; 29980cbfa66cSDaniel Fojt } 29990cbfa66cSDaniel Fojt if (!quiet) { 30000cbfa66cSDaniel Fojt printf("Saved %s key%s%s to %s\n", 30010cbfa66cSDaniel Fojt sshkey_type(keys[i]), 30020cbfa66cSDaniel Fojt *ext != '\0' ? " " : "", 30030cbfa66cSDaniel Fojt *ext != '\0' ? keys[i]->sk_application : "", 30040cbfa66cSDaniel Fojt path); 30050cbfa66cSDaniel Fojt } 30060cbfa66cSDaniel Fojt 30070cbfa66cSDaniel Fojt /* Save public key too */ 30080cbfa66cSDaniel Fojt xasprintf(&pubpath, "%s.pub", path); 30090cbfa66cSDaniel Fojt free(path); 30100cbfa66cSDaniel Fojt if ((r = sshkey_save_public(keys[i], pubpath, 30110cbfa66cSDaniel Fojt keys[i]->sk_application)) != 0) { 30120cbfa66cSDaniel Fojt error("Saving public key \"%s\" failed: %s", 30130cbfa66cSDaniel Fojt pubpath, ssh_err(r)); 30140cbfa66cSDaniel Fojt free(pubpath); 30150cbfa66cSDaniel Fojt break; 30160cbfa66cSDaniel Fojt } 30170cbfa66cSDaniel Fojt free(pubpath); 30180cbfa66cSDaniel Fojt } 30190cbfa66cSDaniel Fojt 30200cbfa66cSDaniel Fojt if (i >= nkeys) 30210cbfa66cSDaniel Fojt ok = 0; /* success */ 30220cbfa66cSDaniel Fojt if (pass != NULL) 30230cbfa66cSDaniel Fojt freezero(pass, strlen(pass)); 30240cbfa66cSDaniel Fojt for (i = 0; i < nkeys; i++) 30250cbfa66cSDaniel Fojt sshkey_free(keys[i]); 30260cbfa66cSDaniel Fojt free(keys); 30270cbfa66cSDaniel Fojt return ok ? 0 : -1; 30280cbfa66cSDaniel Fojt } 30290cbfa66cSDaniel Fojt 303018de8d7fSPeter Avalos static void 303118de8d7fSPeter Avalos usage(void) 303218de8d7fSPeter Avalos { 303336e94dc5SPeter Avalos fprintf(stderr, 30340cbfa66cSDaniel Fojt "usage: ssh-keygen [-q] [-b bits] [-C comment] [-f output_keyfile] [-m format]\n" 30350cbfa66cSDaniel Fojt " [-t dsa | ecdsa | ecdsa-sk | ed25519 | ed25519-sk | rsa]\n" 30360cbfa66cSDaniel Fojt " [-N new_passphrase] [-O option] [-w provider]\n" 30370cbfa66cSDaniel Fojt " ssh-keygen -p [-f keyfile] [-m format] [-N new_passphrase]\n" 30380cbfa66cSDaniel Fojt " [-P old_passphrase]\n" 30390cbfa66cSDaniel Fojt " ssh-keygen -i [-f input_keyfile] [-m key_format]\n" 30400cbfa66cSDaniel Fojt " ssh-keygen -e [-f input_keyfile] [-m key_format]\n" 304136e94dc5SPeter Avalos " ssh-keygen -y [-f input_keyfile]\n" 30420cbfa66cSDaniel Fojt " ssh-keygen -c [-C comment] [-f keyfile] [-P passphrase]\n" 3043e9778795SPeter Avalos " ssh-keygen -l [-v] [-E fingerprint_hash] [-f input_keyfile]\n" 304436e94dc5SPeter Avalos " ssh-keygen -B [-f input_keyfile]\n"); 3045856ea928SPeter Avalos #ifdef ENABLE_PKCS11 304636e94dc5SPeter Avalos fprintf(stderr, 304736e94dc5SPeter Avalos " ssh-keygen -D pkcs11\n"); 3048856ea928SPeter Avalos #endif 304936e94dc5SPeter Avalos fprintf(stderr, 30500cbfa66cSDaniel Fojt " ssh-keygen -F hostname [-lv] [-f known_hosts_file]\n" 305136e94dc5SPeter Avalos " ssh-keygen -H [-f known_hosts_file]\n" 30520cbfa66cSDaniel Fojt " ssh-keygen -K [-w provider]\n" 305336e94dc5SPeter Avalos " ssh-keygen -R hostname [-f known_hosts_file]\n" 30540cbfa66cSDaniel Fojt " ssh-keygen -r hostname [-g] [-f input_keyfile]\n" 3055e9778795SPeter Avalos #ifdef WITH_OPENSSL 30560cbfa66cSDaniel Fojt " ssh-keygen -M generate [-O option] output_file\n" 30570cbfa66cSDaniel Fojt " ssh-keygen -M screen [-f input_file] [-O option] output_file\n" 3058e9778795SPeter Avalos #endif 30590cbfa66cSDaniel Fojt " ssh-keygen -I certificate_identity -s ca_key [-hU] [-D pkcs11_provider]\n" 30600cbfa66cSDaniel Fojt " [-n principals] [-O option] [-V validity_interval]\n" 30610cbfa66cSDaniel Fojt " [-z serial_number] file ...\n" 306236e94dc5SPeter Avalos " ssh-keygen -L [-f input_keyfile]\n" 30630cbfa66cSDaniel Fojt " ssh-keygen -A [-f prefix_path]\n" 306436e94dc5SPeter Avalos " ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n" 306536e94dc5SPeter Avalos " file ...\n" 30660cbfa66cSDaniel Fojt " ssh-keygen -Q [-l] -f krl_file [file ...]\n" 30670cbfa66cSDaniel Fojt " ssh-keygen -Y find-principals -s signature_file -f allowed_signers_file\n" 30680cbfa66cSDaniel Fojt " ssh-keygen -Y check-novalidate -n namespace -s signature_file\n" 30690cbfa66cSDaniel Fojt " ssh-keygen -Y sign -f key_file -n namespace file ...\n" 30700cbfa66cSDaniel Fojt " ssh-keygen -Y verify -f allowed_signers_file -I signer_identity\n" 30710cbfa66cSDaniel Fojt " -n namespace -s signature_file [-r revocation_file]\n"); 307218de8d7fSPeter Avalos exit(1); 307318de8d7fSPeter Avalos } 307418de8d7fSPeter Avalos 307518de8d7fSPeter Avalos /* 307618de8d7fSPeter Avalos * Main program for key management. 307718de8d7fSPeter Avalos */ 307818de8d7fSPeter Avalos int 307918de8d7fSPeter Avalos main(int argc, char **argv) 308018de8d7fSPeter Avalos { 30810cbfa66cSDaniel Fojt char dotsshdir[PATH_MAX], comment[1024], *passphrase; 3082e9778795SPeter Avalos char *rr_hostname = NULL, *ep, *fp, *ra; 3083e9778795SPeter Avalos struct sshkey *private, *public; 308418de8d7fSPeter Avalos struct passwd *pw; 308518de8d7fSPeter Avalos struct stat st; 30860cbfa66cSDaniel Fojt int r, opt, type; 3087664f4763Szrj int change_passphrase = 0, change_comment = 0, show_cert = 0; 3088664f4763Szrj int find_host = 0, delete_host = 0, hash_hosts = 0; 308936e94dc5SPeter Avalos int gen_all_hostkeys = 0, gen_krl = 0, update_krl = 0, check_krl = 0; 3090664f4763Szrj int prefer_agent = 0, convert_to = 0, convert_from = 0; 3091664f4763Szrj int print_public = 0, print_generic = 0, cert_serial_autoinc = 0; 30920cbfa66cSDaniel Fojt int do_gen_candidates = 0, do_screen_candidates = 0, download_sk = 0; 3093664f4763Szrj unsigned long long cert_serial = 0; 30940cbfa66cSDaniel Fojt char *identity_comment = NULL, *ca_key_path = NULL, **opts = NULL; 30950cbfa66cSDaniel Fojt char *sk_application = NULL, *sk_device = NULL, *sk_user = NULL; 30960cbfa66cSDaniel Fojt char *sk_attestaion_path = NULL; 30970cbfa66cSDaniel Fojt struct sshbuf *challenge = NULL, *attest = NULL; 30980cbfa66cSDaniel Fojt size_t i, nopts = 0; 30990cbfa66cSDaniel Fojt u_int32_t bits = 0; 31000cbfa66cSDaniel Fojt uint8_t sk_flags = SSH_SK_USER_PRESENCE_REQD; 310118de8d7fSPeter Avalos const char *errstr; 3102664f4763Szrj int log_level = SYSLOG_LEVEL_INFO; 31030cbfa66cSDaniel Fojt char *sign_op = NULL; 310418de8d7fSPeter Avalos 310518de8d7fSPeter Avalos extern int optind; 310618de8d7fSPeter Avalos extern char *optarg; 310718de8d7fSPeter Avalos 310818de8d7fSPeter Avalos /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 310918de8d7fSPeter Avalos sanitise_stdfd(); 311018de8d7fSPeter Avalos 311118de8d7fSPeter Avalos __progname = ssh_get_progname(argv[0]); 311218de8d7fSPeter Avalos 311318de8d7fSPeter Avalos seed_rng(); 311418de8d7fSPeter Avalos 3115664f4763Szrj log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1); 3116664f4763Szrj 3117ce74bacaSMatthew Dillon msetlocale(); 3118ce74bacaSMatthew Dillon 311918de8d7fSPeter Avalos /* we need this for the home * directory. */ 312018de8d7fSPeter Avalos pw = getpwuid(getuid()); 3121e9778795SPeter Avalos if (!pw) 3122e9778795SPeter Avalos fatal("No user exists for uid %lu", (u_long)getuid()); 31230cbfa66cSDaniel Fojt if (gethostname(hostname, sizeof(hostname)) == -1) 3124e9778795SPeter Avalos fatal("gethostname: %s", strerror(errno)); 312518de8d7fSPeter Avalos 31260cbfa66cSDaniel Fojt sk_provider = getenv("SSH_SK_PROVIDER"); 31270cbfa66cSDaniel Fojt 31280cbfa66cSDaniel Fojt /* Remaining characters: dGjJSTWx */ 31290cbfa66cSDaniel Fojt while ((opt = getopt(argc, argv, "ABHKLQUXceghiklopquvy" 31300cbfa66cSDaniel Fojt "C:D:E:F:I:M:N:O:P:R:V:Y:Z:" 31310cbfa66cSDaniel Fojt "a:b:f:g:m:n:r:s:t:w:z:")) != -1) { 313218de8d7fSPeter Avalos switch (opt) { 31331c188a7fSPeter Avalos case 'A': 31341c188a7fSPeter Avalos gen_all_hostkeys = 1; 31351c188a7fSPeter Avalos break; 313618de8d7fSPeter Avalos case 'b': 31370cbfa66cSDaniel Fojt bits = (u_int32_t)strtonum(optarg, 1, UINT32_MAX, 31380cbfa66cSDaniel Fojt &errstr); 313918de8d7fSPeter Avalos if (errstr) 314018de8d7fSPeter Avalos fatal("Bits has bad value %s (%s)", 314118de8d7fSPeter Avalos optarg, errstr); 314218de8d7fSPeter Avalos break; 3143e9778795SPeter Avalos case 'E': 3144e9778795SPeter Avalos fingerprint_hash = ssh_digest_alg_by_name(optarg); 3145e9778795SPeter Avalos if (fingerprint_hash == -1) 3146e9778795SPeter Avalos fatal("Invalid hash algorithm \"%s\"", optarg); 3147e9778795SPeter Avalos break; 314818de8d7fSPeter Avalos case 'F': 314918de8d7fSPeter Avalos find_host = 1; 315018de8d7fSPeter Avalos rr_hostname = optarg; 315118de8d7fSPeter Avalos break; 315218de8d7fSPeter Avalos case 'H': 315318de8d7fSPeter Avalos hash_hosts = 1; 315418de8d7fSPeter Avalos break; 3155856ea928SPeter Avalos case 'I': 3156856ea928SPeter Avalos cert_key_id = optarg; 3157856ea928SPeter Avalos break; 315818de8d7fSPeter Avalos case 'R': 315918de8d7fSPeter Avalos delete_host = 1; 316018de8d7fSPeter Avalos rr_hostname = optarg; 316118de8d7fSPeter Avalos break; 3162856ea928SPeter Avalos case 'L': 3163856ea928SPeter Avalos show_cert = 1; 3164856ea928SPeter Avalos break; 316518de8d7fSPeter Avalos case 'l': 316618de8d7fSPeter Avalos print_fingerprint = 1; 316718de8d7fSPeter Avalos break; 316818de8d7fSPeter Avalos case 'B': 316918de8d7fSPeter Avalos print_bubblebabble = 1; 317018de8d7fSPeter Avalos break; 3171856ea928SPeter Avalos case 'm': 3172856ea928SPeter Avalos if (strcasecmp(optarg, "RFC4716") == 0 || 3173856ea928SPeter Avalos strcasecmp(optarg, "ssh2") == 0) { 3174856ea928SPeter Avalos convert_format = FMT_RFC4716; 3175856ea928SPeter Avalos break; 3176856ea928SPeter Avalos } 3177856ea928SPeter Avalos if (strcasecmp(optarg, "PKCS8") == 0) { 3178856ea928SPeter Avalos convert_format = FMT_PKCS8; 31790cbfa66cSDaniel Fojt private_key_format = SSHKEY_PRIVATE_PKCS8; 3180856ea928SPeter Avalos break; 3181856ea928SPeter Avalos } 3182856ea928SPeter Avalos if (strcasecmp(optarg, "PEM") == 0) { 3183856ea928SPeter Avalos convert_format = FMT_PEM; 31840cbfa66cSDaniel Fojt private_key_format = SSHKEY_PRIVATE_PEM; 3185856ea928SPeter Avalos break; 3186856ea928SPeter Avalos } 3187856ea928SPeter Avalos fatal("Unsupported conversion format \"%s\"", optarg); 3188856ea928SPeter Avalos case 'n': 3189856ea928SPeter Avalos cert_principals = optarg; 3190856ea928SPeter Avalos break; 319136e94dc5SPeter Avalos case 'o': 3192664f4763Szrj /* no-op; new format is already the default */ 319336e94dc5SPeter Avalos break; 319418de8d7fSPeter Avalos case 'p': 319518de8d7fSPeter Avalos change_passphrase = 1; 319618de8d7fSPeter Avalos break; 319718de8d7fSPeter Avalos case 'c': 319818de8d7fSPeter Avalos change_comment = 1; 319918de8d7fSPeter Avalos break; 320018de8d7fSPeter Avalos case 'f': 3201e9778795SPeter Avalos if (strlcpy(identity_file, optarg, 3202e9778795SPeter Avalos sizeof(identity_file)) >= sizeof(identity_file)) 320318de8d7fSPeter Avalos fatal("Identity filename too long"); 320418de8d7fSPeter Avalos have_identity = 1; 320518de8d7fSPeter Avalos break; 320618de8d7fSPeter Avalos case 'g': 320718de8d7fSPeter Avalos print_generic = 1; 320818de8d7fSPeter Avalos break; 32090cbfa66cSDaniel Fojt case 'K': 32100cbfa66cSDaniel Fojt download_sk = 1; 32110cbfa66cSDaniel Fojt break; 321218de8d7fSPeter Avalos case 'P': 321318de8d7fSPeter Avalos identity_passphrase = optarg; 321418de8d7fSPeter Avalos break; 321518de8d7fSPeter Avalos case 'N': 321618de8d7fSPeter Avalos identity_new_passphrase = optarg; 321718de8d7fSPeter Avalos break; 321836e94dc5SPeter Avalos case 'Q': 321936e94dc5SPeter Avalos check_krl = 1; 322036e94dc5SPeter Avalos break; 3221856ea928SPeter Avalos case 'O': 32220cbfa66cSDaniel Fojt opts = xrecallocarray(opts, nopts, nopts + 1, 32230cbfa66cSDaniel Fojt sizeof(*opts)); 32240cbfa66cSDaniel Fojt opts[nopts++] = xstrdup(optarg); 3225856ea928SPeter Avalos break; 322636e94dc5SPeter Avalos case 'Z': 32270cbfa66cSDaniel Fojt openssh_format_cipher = optarg; 322836e94dc5SPeter Avalos break; 322918de8d7fSPeter Avalos case 'C': 323018de8d7fSPeter Avalos identity_comment = optarg; 323118de8d7fSPeter Avalos break; 323218de8d7fSPeter Avalos case 'q': 323318de8d7fSPeter Avalos quiet = 1; 323418de8d7fSPeter Avalos break; 323518de8d7fSPeter Avalos case 'e': 323618de8d7fSPeter Avalos /* export key */ 3237856ea928SPeter Avalos convert_to = 1; 3238856ea928SPeter Avalos break; 3239856ea928SPeter Avalos case 'h': 3240856ea928SPeter Avalos cert_key_type = SSH2_CERT_TYPE_HOST; 3241856ea928SPeter Avalos certflags_flags = 0; 324218de8d7fSPeter Avalos break; 324336e94dc5SPeter Avalos case 'k': 324436e94dc5SPeter Avalos gen_krl = 1; 324536e94dc5SPeter Avalos break; 324618de8d7fSPeter Avalos case 'i': 324718de8d7fSPeter Avalos case 'X': 324818de8d7fSPeter Avalos /* import key */ 3249856ea928SPeter Avalos convert_from = 1; 325018de8d7fSPeter Avalos break; 325118de8d7fSPeter Avalos case 'y': 325218de8d7fSPeter Avalos print_public = 1; 325318de8d7fSPeter Avalos break; 3254856ea928SPeter Avalos case 's': 3255856ea928SPeter Avalos ca_key_path = optarg; 3256856ea928SPeter Avalos break; 325718de8d7fSPeter Avalos case 't': 325818de8d7fSPeter Avalos key_type_name = optarg; 325918de8d7fSPeter Avalos break; 326018de8d7fSPeter Avalos case 'D': 3261856ea928SPeter Avalos pkcs11provider = optarg; 326218de8d7fSPeter Avalos break; 3263ce74bacaSMatthew Dillon case 'U': 3264ce74bacaSMatthew Dillon prefer_agent = 1; 3265ce74bacaSMatthew Dillon break; 326636e94dc5SPeter Avalos case 'u': 326736e94dc5SPeter Avalos update_krl = 1; 326836e94dc5SPeter Avalos break; 326918de8d7fSPeter Avalos case 'v': 327018de8d7fSPeter Avalos if (log_level == SYSLOG_LEVEL_INFO) 327118de8d7fSPeter Avalos log_level = SYSLOG_LEVEL_DEBUG1; 327218de8d7fSPeter Avalos else { 327318de8d7fSPeter Avalos if (log_level >= SYSLOG_LEVEL_DEBUG1 && 327418de8d7fSPeter Avalos log_level < SYSLOG_LEVEL_DEBUG3) 327518de8d7fSPeter Avalos log_level++; 327618de8d7fSPeter Avalos } 327718de8d7fSPeter Avalos break; 327818de8d7fSPeter Avalos case 'r': 327918de8d7fSPeter Avalos rr_hostname = optarg; 328018de8d7fSPeter Avalos break; 3281e9778795SPeter Avalos case 'a': 3282e9778795SPeter Avalos rounds = (int)strtonum(optarg, 1, INT_MAX, &errstr); 3283e9778795SPeter Avalos if (errstr) 3284e9778795SPeter Avalos fatal("Invalid number: %s (%s)", 3285e9778795SPeter Avalos optarg, errstr); 3286e9778795SPeter Avalos break; 3287e9778795SPeter Avalos case 'V': 3288e9778795SPeter Avalos parse_cert_times(optarg); 3289e9778795SPeter Avalos break; 32900cbfa66cSDaniel Fojt case 'Y': 32910cbfa66cSDaniel Fojt sign_op = optarg; 32920cbfa66cSDaniel Fojt break; 32930cbfa66cSDaniel Fojt case 'w': 32940cbfa66cSDaniel Fojt sk_provider = optarg; 32950cbfa66cSDaniel Fojt break; 3296e9778795SPeter Avalos case 'z': 3297e9778795SPeter Avalos errno = 0; 3298664f4763Szrj if (*optarg == '+') { 3299664f4763Szrj cert_serial_autoinc = 1; 3300664f4763Szrj optarg++; 3301664f4763Szrj } 3302e9778795SPeter Avalos cert_serial = strtoull(optarg, &ep, 10); 3303e9778795SPeter Avalos if (*optarg < '0' || *optarg > '9' || *ep != '\0' || 3304e9778795SPeter Avalos (errno == ERANGE && cert_serial == ULLONG_MAX)) 3305e9778795SPeter Avalos fatal("Invalid serial number \"%s\"", optarg); 3306e9778795SPeter Avalos break; 3307ce74bacaSMatthew Dillon case 'M': 33080cbfa66cSDaniel Fojt if (strcmp(optarg, "generate") == 0) 33090cbfa66cSDaniel Fojt do_gen_candidates = 1; 33100cbfa66cSDaniel Fojt else if (strcmp(optarg, "screen") == 0) 331118de8d7fSPeter Avalos do_screen_candidates = 1; 33120cbfa66cSDaniel Fojt else 33130cbfa66cSDaniel Fojt fatal("Unsupported moduli option %s", optarg); 331418de8d7fSPeter Avalos break; 331518de8d7fSPeter Avalos case '?': 331618de8d7fSPeter Avalos default: 331718de8d7fSPeter Avalos usage(); 331818de8d7fSPeter Avalos } 331918de8d7fSPeter Avalos } 332018de8d7fSPeter Avalos 33210cbfa66cSDaniel Fojt #ifdef ENABLE_SK_INTERNAL 33220cbfa66cSDaniel Fojt if (sk_provider == NULL) 33230cbfa66cSDaniel Fojt sk_provider = "internal"; 33240cbfa66cSDaniel Fojt #endif 33250cbfa66cSDaniel Fojt 332618de8d7fSPeter Avalos /* reinit */ 332718de8d7fSPeter Avalos log_init(argv[0], log_level, SYSLOG_FACILITY_USER, 1); 332818de8d7fSPeter Avalos 3329856ea928SPeter Avalos argv += optind; 3330856ea928SPeter Avalos argc -= optind; 3331856ea928SPeter Avalos 33320cbfa66cSDaniel Fojt if (sign_op != NULL) { 33330cbfa66cSDaniel Fojt if (strncmp(sign_op, "find-principals", 15) == 0) { 33340cbfa66cSDaniel Fojt if (ca_key_path == NULL) { 33350cbfa66cSDaniel Fojt error("Too few arguments for find-principals:" 33360cbfa66cSDaniel Fojt "missing signature file"); 33370cbfa66cSDaniel Fojt exit(1); 33380cbfa66cSDaniel Fojt } 33390cbfa66cSDaniel Fojt if (!have_identity) { 33400cbfa66cSDaniel Fojt error("Too few arguments for find-principals:" 33410cbfa66cSDaniel Fojt "missing allowed keys file"); 33420cbfa66cSDaniel Fojt exit(1); 33430cbfa66cSDaniel Fojt } 33440cbfa66cSDaniel Fojt return sig_find_principals(ca_key_path, identity_file); 33450cbfa66cSDaniel Fojt } else if (strncmp(sign_op, "sign", 4) == 0) { 33460cbfa66cSDaniel Fojt if (cert_principals == NULL || 33470cbfa66cSDaniel Fojt *cert_principals == '\0') { 33480cbfa66cSDaniel Fojt error("Too few arguments for sign: " 33490cbfa66cSDaniel Fojt "missing namespace"); 33500cbfa66cSDaniel Fojt exit(1); 33510cbfa66cSDaniel Fojt } 33520cbfa66cSDaniel Fojt if (!have_identity) { 33530cbfa66cSDaniel Fojt error("Too few arguments for sign: " 33540cbfa66cSDaniel Fojt "missing key"); 33550cbfa66cSDaniel Fojt exit(1); 33560cbfa66cSDaniel Fojt } 33570cbfa66cSDaniel Fojt return sig_sign(identity_file, cert_principals, 33580cbfa66cSDaniel Fojt argc, argv); 33590cbfa66cSDaniel Fojt } else if (strncmp(sign_op, "check-novalidate", 16) == 0) { 33600cbfa66cSDaniel Fojt if (ca_key_path == NULL) { 33610cbfa66cSDaniel Fojt error("Too few arguments for check-novalidate: " 33620cbfa66cSDaniel Fojt "missing signature file"); 33630cbfa66cSDaniel Fojt exit(1); 33640cbfa66cSDaniel Fojt } 33650cbfa66cSDaniel Fojt return sig_verify(ca_key_path, cert_principals, 33660cbfa66cSDaniel Fojt NULL, NULL, NULL); 33670cbfa66cSDaniel Fojt } else if (strncmp(sign_op, "verify", 6) == 0) { 33680cbfa66cSDaniel Fojt if (cert_principals == NULL || 33690cbfa66cSDaniel Fojt *cert_principals == '\0') { 33700cbfa66cSDaniel Fojt error("Too few arguments for verify: " 33710cbfa66cSDaniel Fojt "missing namespace"); 33720cbfa66cSDaniel Fojt exit(1); 33730cbfa66cSDaniel Fojt } 33740cbfa66cSDaniel Fojt if (ca_key_path == NULL) { 33750cbfa66cSDaniel Fojt error("Too few arguments for verify: " 33760cbfa66cSDaniel Fojt "missing signature file"); 33770cbfa66cSDaniel Fojt exit(1); 33780cbfa66cSDaniel Fojt } 33790cbfa66cSDaniel Fojt if (!have_identity) { 33800cbfa66cSDaniel Fojt error("Too few arguments for sign: " 33810cbfa66cSDaniel Fojt "missing allowed keys file"); 33820cbfa66cSDaniel Fojt exit(1); 33830cbfa66cSDaniel Fojt } 33840cbfa66cSDaniel Fojt if (cert_key_id == NULL) { 33850cbfa66cSDaniel Fojt error("Too few arguments for verify: " 33860cbfa66cSDaniel Fojt "missing principal ID"); 33870cbfa66cSDaniel Fojt exit(1); 33880cbfa66cSDaniel Fojt } 33890cbfa66cSDaniel Fojt return sig_verify(ca_key_path, cert_principals, 33900cbfa66cSDaniel Fojt cert_key_id, identity_file, rr_hostname); 33910cbfa66cSDaniel Fojt } 33920cbfa66cSDaniel Fojt error("Unsupported operation for -Y: \"%s\"", sign_op); 33930cbfa66cSDaniel Fojt usage(); 33940cbfa66cSDaniel Fojt /* NOTREACHED */ 33950cbfa66cSDaniel Fojt } 33960cbfa66cSDaniel Fojt 3397856ea928SPeter Avalos if (ca_key_path != NULL) { 339836e94dc5SPeter Avalos if (argc < 1 && !gen_krl) { 3399e9778795SPeter Avalos error("Too few arguments."); 3400856ea928SPeter Avalos usage(); 3401856ea928SPeter Avalos } 34020cbfa66cSDaniel Fojt } else if (argc > 0 && !gen_krl && !check_krl && 34030cbfa66cSDaniel Fojt !do_gen_candidates && !do_screen_candidates) { 3404e9778795SPeter Avalos error("Too many arguments."); 340518de8d7fSPeter Avalos usage(); 340618de8d7fSPeter Avalos } 340718de8d7fSPeter Avalos if (change_passphrase && change_comment) { 3408e9778795SPeter Avalos error("Can only have one of -p and -c."); 340918de8d7fSPeter Avalos usage(); 341018de8d7fSPeter Avalos } 341118de8d7fSPeter Avalos if (print_fingerprint && (delete_host || hash_hosts)) { 3412e9778795SPeter Avalos error("Cannot use -l with -H or -R."); 341318de8d7fSPeter Avalos usage(); 341418de8d7fSPeter Avalos } 341536e94dc5SPeter Avalos if (gen_krl) { 3416664f4763Szrj do_gen_krl(pw, update_krl, ca_key_path, 3417664f4763Szrj cert_serial, identity_comment, argc, argv); 341836e94dc5SPeter Avalos return (0); 341936e94dc5SPeter Avalos } 342036e94dc5SPeter Avalos if (check_krl) { 34210cbfa66cSDaniel Fojt do_check_krl(pw, print_fingerprint, argc, argv); 342236e94dc5SPeter Avalos return (0); 342336e94dc5SPeter Avalos } 3424856ea928SPeter Avalos if (ca_key_path != NULL) { 3425856ea928SPeter Avalos if (cert_key_id == NULL) 3426856ea928SPeter Avalos fatal("Must specify key id (-I) when certifying"); 34270cbfa66cSDaniel Fojt for (i = 0; i < nopts; i++) 34280cbfa66cSDaniel Fojt add_cert_option(opts[i]); 3429664f4763Szrj do_ca_sign(pw, ca_key_path, prefer_agent, 3430664f4763Szrj cert_serial, cert_serial_autoinc, argc, argv); 3431856ea928SPeter Avalos } 3432856ea928SPeter Avalos if (show_cert) 3433856ea928SPeter Avalos do_show_cert(pw); 3434664f4763Szrj if (delete_host || hash_hosts || find_host) { 3435664f4763Szrj do_known_hosts(pw, rr_hostname, find_host, 3436664f4763Szrj delete_host, hash_hosts); 3437664f4763Szrj } 343836e94dc5SPeter Avalos if (pkcs11provider != NULL) 343936e94dc5SPeter Avalos do_download(pw); 34400cbfa66cSDaniel Fojt if (download_sk) { 34410cbfa66cSDaniel Fojt for (i = 0; i < nopts; i++) { 34420cbfa66cSDaniel Fojt if (strncasecmp(opts[i], "device=", 7) == 0) { 34430cbfa66cSDaniel Fojt sk_device = xstrdup(opts[i] + 7); 34440cbfa66cSDaniel Fojt } else { 34450cbfa66cSDaniel Fojt fatal("Option \"%s\" is unsupported for " 34460cbfa66cSDaniel Fojt "FIDO authenticator download", opts[i]); 34470cbfa66cSDaniel Fojt } 34480cbfa66cSDaniel Fojt } 34490cbfa66cSDaniel Fojt return do_download_sk(sk_provider, sk_device); 34500cbfa66cSDaniel Fojt } 345118de8d7fSPeter Avalos if (print_fingerprint || print_bubblebabble) 345218de8d7fSPeter Avalos do_fingerprint(pw); 345318de8d7fSPeter Avalos if (change_passphrase) 345418de8d7fSPeter Avalos do_change_passphrase(pw); 345518de8d7fSPeter Avalos if (change_comment) 3456664f4763Szrj do_change_comment(pw, identity_comment); 345736e94dc5SPeter Avalos #ifdef WITH_OPENSSL 3458856ea928SPeter Avalos if (convert_to) 3459856ea928SPeter Avalos do_convert_to(pw); 3460856ea928SPeter Avalos if (convert_from) 3461856ea928SPeter Avalos do_convert_from(pw); 34620cbfa66cSDaniel Fojt #else /* WITH_OPENSSL */ 34630cbfa66cSDaniel Fojt if (convert_to || convert_from) 34640cbfa66cSDaniel Fojt fatal("key conversion disabled at compile time"); 34650cbfa66cSDaniel Fojt #endif /* WITH_OPENSSL */ 346618de8d7fSPeter Avalos if (print_public) 346718de8d7fSPeter Avalos do_print_public(pw); 346818de8d7fSPeter Avalos if (rr_hostname != NULL) { 346918de8d7fSPeter Avalos unsigned int n = 0; 347018de8d7fSPeter Avalos 347118de8d7fSPeter Avalos if (have_identity) { 3472664f4763Szrj n = do_print_resource_record(pw, identity_file, 3473664f4763Szrj rr_hostname, print_generic); 3474e9778795SPeter Avalos if (n == 0) 3475e9778795SPeter Avalos fatal("%s: %s", identity_file, strerror(errno)); 347618de8d7fSPeter Avalos exit(0); 347718de8d7fSPeter Avalos } else { 347818de8d7fSPeter Avalos 347918de8d7fSPeter Avalos n += do_print_resource_record(pw, 3480664f4763Szrj _PATH_HOST_RSA_KEY_FILE, rr_hostname, 3481664f4763Szrj print_generic); 348218de8d7fSPeter Avalos n += do_print_resource_record(pw, 3483664f4763Szrj _PATH_HOST_DSA_KEY_FILE, rr_hostname, 3484664f4763Szrj print_generic); 348599e85e0dSPeter Avalos n += do_print_resource_record(pw, 3486664f4763Szrj _PATH_HOST_ECDSA_KEY_FILE, rr_hostname, 3487664f4763Szrj print_generic); 348836e94dc5SPeter Avalos n += do_print_resource_record(pw, 3489664f4763Szrj _PATH_HOST_ED25519_KEY_FILE, rr_hostname, 3490664f4763Szrj print_generic); 3491664f4763Szrj n += do_print_resource_record(pw, 3492664f4763Szrj _PATH_HOST_XMSS_KEY_FILE, rr_hostname, 3493664f4763Szrj print_generic); 349418de8d7fSPeter Avalos if (n == 0) 349518de8d7fSPeter Avalos fatal("no keys found."); 349618de8d7fSPeter Avalos exit(0); 349718de8d7fSPeter Avalos } 349818de8d7fSPeter Avalos } 349918de8d7fSPeter Avalos 35000cbfa66cSDaniel Fojt if (do_gen_candidates || do_screen_candidates) { 35010cbfa66cSDaniel Fojt if (argc <= 0) 35020cbfa66cSDaniel Fojt fatal("No output file specified"); 35030cbfa66cSDaniel Fojt else if (argc > 1) 35040cbfa66cSDaniel Fojt fatal("Too many output files specified"); 35050cbfa66cSDaniel Fojt } 350618de8d7fSPeter Avalos if (do_gen_candidates) { 35070cbfa66cSDaniel Fojt do_moduli_gen(argv[0], opts, nopts); 35080cbfa66cSDaniel Fojt return 0; 350918de8d7fSPeter Avalos } 351018de8d7fSPeter Avalos if (do_screen_candidates) { 35110cbfa66cSDaniel Fojt do_moduli_screen(argv[0], opts, nopts); 35120cbfa66cSDaniel Fojt return 0; 351318de8d7fSPeter Avalos } 351418de8d7fSPeter Avalos 35151c188a7fSPeter Avalos if (gen_all_hostkeys) { 35161c188a7fSPeter Avalos do_gen_all_hostkeys(pw); 35171c188a7fSPeter Avalos return (0); 35181c188a7fSPeter Avalos } 35191c188a7fSPeter Avalos 352018de8d7fSPeter Avalos if (key_type_name == NULL) 3521e9778795SPeter Avalos key_type_name = DEFAULT_KEY_TYPE_NAME; 352218de8d7fSPeter Avalos 3523e9778795SPeter Avalos type = sshkey_type_from_name(key_type_name); 3524e9778795SPeter Avalos type_bits_valid(type, key_type_name, &bits); 35251c188a7fSPeter Avalos 352618de8d7fSPeter Avalos if (!quiet) 3527e9778795SPeter Avalos printf("Generating public/private %s key pair.\n", 3528e9778795SPeter Avalos key_type_name); 35290cbfa66cSDaniel Fojt switch (type) { 35300cbfa66cSDaniel Fojt case KEY_ECDSA_SK: 35310cbfa66cSDaniel Fojt case KEY_ED25519_SK: 35320cbfa66cSDaniel Fojt for (i = 0; i < nopts; i++) { 35330cbfa66cSDaniel Fojt if (strcasecmp(opts[i], "no-touch-required") == 0) { 35340cbfa66cSDaniel Fojt sk_flags &= ~SSH_SK_USER_PRESENCE_REQD; 35350cbfa66cSDaniel Fojt } else if (strcasecmp(opts[i], "resident") == 0) { 35360cbfa66cSDaniel Fojt sk_flags |= SSH_SK_RESIDENT_KEY; 35370cbfa66cSDaniel Fojt } else if (strncasecmp(opts[i], "device=", 7) == 0) { 35380cbfa66cSDaniel Fojt sk_device = xstrdup(opts[i] + 7); 35390cbfa66cSDaniel Fojt } else if (strncasecmp(opts[i], "user=", 5) == 0) { 35400cbfa66cSDaniel Fojt sk_user = xstrdup(opts[i] + 5); 35410cbfa66cSDaniel Fojt } else if (strncasecmp(opts[i], "challenge=", 10) == 0) { 35420cbfa66cSDaniel Fojt if ((r = sshbuf_load_file(opts[i] + 10, 35430cbfa66cSDaniel Fojt &challenge)) != 0) { 35440cbfa66cSDaniel Fojt fatal("Unable to load FIDO enrollment " 35450cbfa66cSDaniel Fojt "challenge \"%s\": %s", 35460cbfa66cSDaniel Fojt opts[i] + 10, ssh_err(r)); 35470cbfa66cSDaniel Fojt } 35480cbfa66cSDaniel Fojt } else if (strncasecmp(opts[i], 35490cbfa66cSDaniel Fojt "write-attestation=", 18) == 0) { 35500cbfa66cSDaniel Fojt sk_attestaion_path = opts[i] + 18; 35510cbfa66cSDaniel Fojt } else if (strncasecmp(opts[i], 35520cbfa66cSDaniel Fojt "application=", 12) == 0) { 35530cbfa66cSDaniel Fojt sk_application = xstrdup(opts[i] + 12); 35540cbfa66cSDaniel Fojt if (strncmp(sk_application, "ssh:", 4) != 0) { 35550cbfa66cSDaniel Fojt fatal("FIDO application string must " 35560cbfa66cSDaniel Fojt "begin with \"ssh:\""); 35570cbfa66cSDaniel Fojt } 35580cbfa66cSDaniel Fojt } else { 35590cbfa66cSDaniel Fojt fatal("Option \"%s\" is unsupported for " 35600cbfa66cSDaniel Fojt "FIDO authenticator enrollment", opts[i]); 35610cbfa66cSDaniel Fojt } 35620cbfa66cSDaniel Fojt } 35630cbfa66cSDaniel Fojt if (!quiet) { 35640cbfa66cSDaniel Fojt printf("You may need to touch your authenticator " 35650cbfa66cSDaniel Fojt "to authorize key generation.\n"); 35660cbfa66cSDaniel Fojt } 35670cbfa66cSDaniel Fojt passphrase = NULL; 35680cbfa66cSDaniel Fojt if ((attest = sshbuf_new()) == NULL) 35690cbfa66cSDaniel Fojt fatal("sshbuf_new failed"); 35700cbfa66cSDaniel Fojt for (i = 0 ; ; i++) { 35710cbfa66cSDaniel Fojt fflush(stdout); 35720cbfa66cSDaniel Fojt r = sshsk_enroll(type, sk_provider, sk_device, 35730cbfa66cSDaniel Fojt sk_application == NULL ? "ssh:" : sk_application, 35740cbfa66cSDaniel Fojt sk_user, sk_flags, passphrase, challenge, 35750cbfa66cSDaniel Fojt &private, attest); 35760cbfa66cSDaniel Fojt if (r == 0) 35770cbfa66cSDaniel Fojt break; 35780cbfa66cSDaniel Fojt if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) 35790cbfa66cSDaniel Fojt fatal("Key enrollment failed: %s", ssh_err(r)); 35800cbfa66cSDaniel Fojt else if (i > 0) 35810cbfa66cSDaniel Fojt error("PIN incorrect"); 35820cbfa66cSDaniel Fojt if (passphrase != NULL) { 35830cbfa66cSDaniel Fojt freezero(passphrase, strlen(passphrase)); 35840cbfa66cSDaniel Fojt passphrase = NULL; 35850cbfa66cSDaniel Fojt } 35860cbfa66cSDaniel Fojt if (i >= 3) 35870cbfa66cSDaniel Fojt fatal("Too many incorrect PINs"); 35880cbfa66cSDaniel Fojt passphrase = read_passphrase("Enter PIN for " 35890cbfa66cSDaniel Fojt "authenticator: ", RP_ALLOW_STDIN); 35900cbfa66cSDaniel Fojt } 35910cbfa66cSDaniel Fojt if (passphrase != NULL) { 35920cbfa66cSDaniel Fojt freezero(passphrase, strlen(passphrase)); 35930cbfa66cSDaniel Fojt passphrase = NULL; 35940cbfa66cSDaniel Fojt } 35950cbfa66cSDaniel Fojt break; 35960cbfa66cSDaniel Fojt default: 3597e9778795SPeter Avalos if ((r = sshkey_generate(type, bits, &private)) != 0) 3598ce74bacaSMatthew Dillon fatal("sshkey_generate failed"); 35990cbfa66cSDaniel Fojt break; 36000cbfa66cSDaniel Fojt } 3601e9778795SPeter Avalos if ((r = sshkey_from_private(private, &public)) != 0) 3602ce74bacaSMatthew Dillon fatal("sshkey_from_private failed: %s\n", ssh_err(r)); 360318de8d7fSPeter Avalos 360418de8d7fSPeter Avalos if (!have_identity) 360518de8d7fSPeter Avalos ask_filename(pw, "Enter file in which to save the key"); 360618de8d7fSPeter Avalos 360718de8d7fSPeter Avalos /* Create ~/.ssh directory if it doesn't already exist. */ 3608856ea928SPeter Avalos snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", 3609856ea928SPeter Avalos pw->pw_dir, _PATH_SSH_USER_DIR); 3610856ea928SPeter Avalos if (strstr(identity_file, dotsshdir) != NULL) { 36110cbfa66cSDaniel Fojt if (stat(dotsshdir, &st) == -1) { 3612856ea928SPeter Avalos if (errno != ENOENT) { 3613856ea928SPeter Avalos error("Could not stat %s: %s", dotsshdir, 3614856ea928SPeter Avalos strerror(errno)); 36150cbfa66cSDaniel Fojt } else if (mkdir(dotsshdir, 0700) == -1) { 3616856ea928SPeter Avalos error("Could not create directory '%s': %s", 3617856ea928SPeter Avalos dotsshdir, strerror(errno)); 3618856ea928SPeter Avalos } else if (!quiet) 361918de8d7fSPeter Avalos printf("Created directory '%s'.\n", dotsshdir); 362018de8d7fSPeter Avalos } 3621856ea928SPeter Avalos } 362218de8d7fSPeter Avalos /* If the file already exists, ask the user to confirm. */ 36230cbfa66cSDaniel Fojt if (!confirm_overwrite(identity_file)) 362418de8d7fSPeter Avalos exit(1); 362518de8d7fSPeter Avalos 36260cbfa66cSDaniel Fojt /* Determine the passphrase for the private key */ 36270cbfa66cSDaniel Fojt passphrase = private_key_passphrase(); 362818de8d7fSPeter Avalos if (identity_comment) { 362918de8d7fSPeter Avalos strlcpy(comment, identity_comment, sizeof(comment)); 363018de8d7fSPeter Avalos } else { 3631cb5eb4f1SPeter Avalos /* Create default comment field for the passphrase. */ 363218de8d7fSPeter Avalos snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); 363318de8d7fSPeter Avalos } 363418de8d7fSPeter Avalos 363518de8d7fSPeter Avalos /* Save the key with the given passphrase and comment. */ 36360cbfa66cSDaniel Fojt if ((r = sshkey_save_private(private, identity_file, passphrase, 36370cbfa66cSDaniel Fojt comment, private_key_format, openssh_format_cipher, rounds)) != 0) { 3638e9778795SPeter Avalos error("Saving key \"%s\" failed: %s", 3639e9778795SPeter Avalos identity_file, ssh_err(r)); 36400cbfa66cSDaniel Fojt freezero(passphrase, strlen(passphrase)); 364118de8d7fSPeter Avalos exit(1); 364218de8d7fSPeter Avalos } 36430cbfa66cSDaniel Fojt freezero(passphrase, strlen(passphrase)); 3644e9778795SPeter Avalos sshkey_free(private); 364518de8d7fSPeter Avalos 36460cbfa66cSDaniel Fojt if (!quiet) { 36470cbfa66cSDaniel Fojt printf("Your identification has been saved in %s\n", 36480cbfa66cSDaniel Fojt identity_file); 36490cbfa66cSDaniel Fojt } 365018de8d7fSPeter Avalos 365118de8d7fSPeter Avalos strlcat(identity_file, ".pub", sizeof(identity_file)); 36520cbfa66cSDaniel Fojt if ((r = sshkey_save_public(public, identity_file, comment)) != 0) { 3653e9778795SPeter Avalos fatal("Unable to save public key to %s: %s", 36540cbfa66cSDaniel Fojt identity_file, ssh_err(r)); 36550cbfa66cSDaniel Fojt } 365618de8d7fSPeter Avalos 365718de8d7fSPeter Avalos if (!quiet) { 3658e9778795SPeter Avalos fp = sshkey_fingerprint(public, fingerprint_hash, 3659e9778795SPeter Avalos SSH_FP_DEFAULT); 3660e9778795SPeter Avalos ra = sshkey_fingerprint(public, fingerprint_hash, 366118de8d7fSPeter Avalos SSH_FP_RANDOMART); 3662e9778795SPeter Avalos if (fp == NULL || ra == NULL) 3663e9778795SPeter Avalos fatal("sshkey_fingerprint failed"); 36640cbfa66cSDaniel Fojt printf("Your public key has been saved in %s\n", 366518de8d7fSPeter Avalos identity_file); 366618de8d7fSPeter Avalos printf("The key fingerprint is:\n"); 366718de8d7fSPeter Avalos printf("%s %s\n", fp, comment); 366818de8d7fSPeter Avalos printf("The key's randomart image is:\n"); 366918de8d7fSPeter Avalos printf("%s\n", ra); 367036e94dc5SPeter Avalos free(ra); 367136e94dc5SPeter Avalos free(fp); 367218de8d7fSPeter Avalos } 367318de8d7fSPeter Avalos 36740cbfa66cSDaniel Fojt if (sk_attestaion_path != NULL) { 36750cbfa66cSDaniel Fojt if (attest == NULL || sshbuf_len(attest) == 0) { 36760cbfa66cSDaniel Fojt fatal("Enrollment did not return attestation " 36770cbfa66cSDaniel Fojt "certificate"); 36780cbfa66cSDaniel Fojt } 36790cbfa66cSDaniel Fojt if ((r = sshbuf_write_file(sk_attestaion_path, attest)) != 0) { 36800cbfa66cSDaniel Fojt fatal("Unable to write attestation certificate " 36810cbfa66cSDaniel Fojt "\"%s\": %s", sk_attestaion_path, ssh_err(r)); 36820cbfa66cSDaniel Fojt } 36830cbfa66cSDaniel Fojt if (!quiet) { 36840cbfa66cSDaniel Fojt printf("Your FIDO attestation certificate has been " 36850cbfa66cSDaniel Fojt "saved in %s\n", sk_attestaion_path); 36860cbfa66cSDaniel Fojt } 36870cbfa66cSDaniel Fojt } 36880cbfa66cSDaniel Fojt sshbuf_free(attest); 3689e9778795SPeter Avalos sshkey_free(public); 36900cbfa66cSDaniel Fojt 369118de8d7fSPeter Avalos exit(0); 369218de8d7fSPeter Avalos } 3693