1*ee116499SAntonio Huete Jimenez /* $OpenBSD: ssh-keygen.c,v 1.459 2022/08/11 01:56:51 djm 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 */
6850a69bb5SSascha Wildner #include "cipher.h"
69e9778795SPeter Avalos
70e9778795SPeter Avalos #ifdef WITH_OPENSSL
71e9778795SPeter Avalos # define DEFAULT_KEY_TYPE_NAME "rsa"
72e9778795SPeter Avalos #else
73e9778795SPeter Avalos # define DEFAULT_KEY_TYPE_NAME "ed25519"
74e9778795SPeter Avalos #endif
7518de8d7fSPeter Avalos
76664f4763Szrj /*
77664f4763Szrj * Default number of bits in the RSA, DSA and ECDSA keys. These value can be
78664f4763Szrj * overridden on the command line.
79664f4763Szrj *
80664f4763Szrj * These values, with the exception of DSA, provide security equivalent to at
81664f4763Szrj * least 128 bits of security according to NIST Special Publication 800-57:
82664f4763Szrj * Recommendation for Key Management Part 1 rev 4 section 5.6.1.
83664f4763Szrj * For DSA it (and FIPS-186-4 section 4.2) specifies that the only size for
84664f4763Szrj * which a 160bit hash is acceptable is 1kbit, and since ssh-dss specifies only
85664f4763Szrj * SHA1 we limit the DSA key size 1k bits.
86664f4763Szrj */
87664f4763Szrj #define DEFAULT_BITS 3072
8818de8d7fSPeter Avalos #define DEFAULT_BITS_DSA 1024
899f304aafSPeter Avalos #define DEFAULT_BITS_ECDSA 256
9018de8d7fSPeter Avalos
91664f4763Szrj static int quiet = 0;
92856ea928SPeter Avalos
9318de8d7fSPeter Avalos /* Flag indicating that we just want to see the key fingerprint */
94664f4763Szrj static int print_fingerprint = 0;
95664f4763Szrj static int print_bubblebabble = 0;
9618de8d7fSPeter Avalos
97e9778795SPeter Avalos /* Hash algorithm to use for fingerprints. */
98664f4763Szrj static int fingerprint_hash = SSH_FP_HASH_DEFAULT;
99e9778795SPeter Avalos
10018de8d7fSPeter Avalos /* The identity file name, given on the command line or entered by the user. */
1010cbfa66cSDaniel Fojt static char identity_file[PATH_MAX];
102664f4763Szrj static int have_identity = 0;
10318de8d7fSPeter Avalos
10418de8d7fSPeter Avalos /* This is set to the passphrase if given on the command line. */
105664f4763Szrj static char *identity_passphrase = NULL;
10618de8d7fSPeter Avalos
10718de8d7fSPeter Avalos /* This is set to the new passphrase if given on the command line. */
108664f4763Szrj static char *identity_new_passphrase = NULL;
109856ea928SPeter Avalos
110856ea928SPeter Avalos /* Key type when certifying */
111664f4763Szrj static u_int cert_key_type = SSH2_CERT_TYPE_USER;
112856ea928SPeter Avalos
113856ea928SPeter Avalos /* "key ID" of signed key */
114664f4763Szrj static char *cert_key_id = NULL;
115856ea928SPeter Avalos
116856ea928SPeter Avalos /* Comma-separated list of principal names for certifying keys */
117664f4763Szrj static char *cert_principals = NULL;
118856ea928SPeter Avalos
119856ea928SPeter Avalos /* Validity period for certificates */
120664f4763Szrj static u_int64_t cert_valid_from = 0;
121664f4763Szrj static u_int64_t cert_valid_to = ~0ULL;
122856ea928SPeter Avalos
123856ea928SPeter Avalos /* Certificate options */
124856ea928SPeter Avalos #define CERTOPT_X_FWD (1)
125856ea928SPeter Avalos #define CERTOPT_AGENT_FWD (1<<1)
126856ea928SPeter Avalos #define CERTOPT_PORT_FWD (1<<2)
127856ea928SPeter Avalos #define CERTOPT_PTY (1<<3)
128856ea928SPeter Avalos #define CERTOPT_USER_RC (1<<4)
1290cbfa66cSDaniel Fojt #define CERTOPT_NO_REQUIRE_USER_PRESENCE (1<<5)
130*ee116499SAntonio Huete Jimenez #define CERTOPT_REQUIRE_VERIFY (1<<6)
131856ea928SPeter Avalos #define CERTOPT_DEFAULT (CERTOPT_X_FWD|CERTOPT_AGENT_FWD| \
132856ea928SPeter Avalos CERTOPT_PORT_FWD|CERTOPT_PTY|CERTOPT_USER_RC)
133664f4763Szrj static u_int32_t certflags_flags = CERTOPT_DEFAULT;
134664f4763Szrj static char *certflags_command = NULL;
135664f4763Szrj static char *certflags_src_addr = NULL;
136856ea928SPeter Avalos
137ce74bacaSMatthew Dillon /* Arbitrary extensions specified by user */
13850a69bb5SSascha Wildner struct cert_ext {
139ce74bacaSMatthew Dillon char *key;
140ce74bacaSMatthew Dillon char *val;
141ce74bacaSMatthew Dillon int crit;
142ce74bacaSMatthew Dillon };
14350a69bb5SSascha Wildner static struct cert_ext *cert_ext;
14450a69bb5SSascha Wildner static size_t ncert_ext;
145ce74bacaSMatthew Dillon
146856ea928SPeter Avalos /* Conversion to/from various formats */
147856ea928SPeter Avalos enum {
148856ea928SPeter Avalos FMT_RFC4716,
149856ea928SPeter Avalos FMT_PKCS8,
150856ea928SPeter Avalos FMT_PEM
151856ea928SPeter Avalos } convert_format = FMT_RFC4716;
15218de8d7fSPeter Avalos
153664f4763Szrj static char *key_type_name = NULL;
15418de8d7fSPeter Avalos
155856ea928SPeter Avalos /* Load key from this PKCS#11 provider */
156664f4763Szrj static char *pkcs11provider = NULL;
157856ea928SPeter Avalos
1580cbfa66cSDaniel Fojt /* FIDO/U2F provider to use */
1590cbfa66cSDaniel Fojt static char *sk_provider = NULL;
1600cbfa66cSDaniel Fojt
1610cbfa66cSDaniel Fojt /* Format for writing private keys */
1620cbfa66cSDaniel Fojt static int private_key_format = SSHKEY_PRIVATE_OPENSSH;
16336e94dc5SPeter Avalos
16436e94dc5SPeter Avalos /* Cipher for new-format private keys */
1650cbfa66cSDaniel Fojt static char *openssh_format_cipher = NULL;
16636e94dc5SPeter Avalos
1670cbfa66cSDaniel Fojt /* Number of KDF rounds to derive new format keys. */
168664f4763Szrj static int rounds = 0;
16936e94dc5SPeter Avalos
17018de8d7fSPeter Avalos /* argv0 */
17118de8d7fSPeter Avalos extern char *__progname;
17218de8d7fSPeter Avalos
173664f4763Szrj static char hostname[NI_MAXHOST];
17418de8d7fSPeter Avalos
175e9778795SPeter Avalos #ifdef WITH_OPENSSL
17618de8d7fSPeter Avalos /* moduli.c */
17718de8d7fSPeter Avalos int gen_candidates(FILE *, u_int32_t, u_int32_t, BIGNUM *);
17899e85e0dSPeter Avalos int prime_test(FILE *, FILE *, u_int32_t, u_int32_t, char *, unsigned long,
17999e85e0dSPeter Avalos unsigned long);
180e9778795SPeter Avalos #endif
18118de8d7fSPeter Avalos
18218de8d7fSPeter Avalos static void
type_bits_valid(int type,const char * name,u_int32_t * bitsp)183e9778795SPeter Avalos type_bits_valid(int type, const char *name, u_int32_t *bitsp)
1841c188a7fSPeter Avalos {
185e9778795SPeter Avalos if (type == KEY_UNSPEC)
186e9778795SPeter Avalos fatal("unknown key type %s", key_type_name);
1871c188a7fSPeter Avalos if (*bitsp == 0) {
188e9778795SPeter Avalos #ifdef WITH_OPENSSL
18950a69bb5SSascha Wildner int nid;
1900cbfa66cSDaniel Fojt
1910cbfa66cSDaniel Fojt switch(type) {
1920cbfa66cSDaniel Fojt case KEY_DSA:
1931c188a7fSPeter Avalos *bitsp = DEFAULT_BITS_DSA;
1940cbfa66cSDaniel Fojt break;
1950cbfa66cSDaniel Fojt case KEY_ECDSA:
196e9778795SPeter Avalos if (name != NULL &&
197e9778795SPeter Avalos (nid = sshkey_ecdsa_nid_from_name(name)) > 0)
198e9778795SPeter Avalos *bitsp = sshkey_curve_nid_to_bits(nid);
199e9778795SPeter Avalos if (*bitsp == 0)
2001c188a7fSPeter Avalos *bitsp = DEFAULT_BITS_ECDSA;
2010cbfa66cSDaniel Fojt break;
2020cbfa66cSDaniel Fojt case KEY_RSA:
2031c188a7fSPeter Avalos *bitsp = DEFAULT_BITS;
2040cbfa66cSDaniel Fojt break;
2050cbfa66cSDaniel Fojt }
2060cbfa66cSDaniel Fojt #endif
2071c188a7fSPeter Avalos }
208e9778795SPeter Avalos #ifdef WITH_OPENSSL
209ce74bacaSMatthew Dillon switch (type) {
210ce74bacaSMatthew Dillon case KEY_DSA:
211ce74bacaSMatthew Dillon if (*bitsp != 1024)
212ce74bacaSMatthew Dillon fatal("Invalid DSA key length: must be 1024 bits");
213ce74bacaSMatthew Dillon break;
214ce74bacaSMatthew Dillon case KEY_RSA:
215ce74bacaSMatthew Dillon if (*bitsp < SSH_RSA_MINIMUM_MODULUS_SIZE)
216ce74bacaSMatthew Dillon fatal("Invalid RSA key length: minimum is %d bits",
217ce74bacaSMatthew Dillon SSH_RSA_MINIMUM_MODULUS_SIZE);
2180cbfa66cSDaniel Fojt else if (*bitsp > OPENSSL_RSA_MAX_MODULUS_BITS)
2190cbfa66cSDaniel Fojt fatal("Invalid RSA key length: maximum is %d bits",
2200cbfa66cSDaniel Fojt OPENSSL_RSA_MAX_MODULUS_BITS);
221ce74bacaSMatthew Dillon break;
222ce74bacaSMatthew Dillon case KEY_ECDSA:
223ce74bacaSMatthew Dillon if (sshkey_ecdsa_bits_to_nid(*bitsp) == -1)
224664f4763Szrj #ifdef OPENSSL_HAS_NISTP521
22550a69bb5SSascha Wildner fatal("Invalid ECDSA key length: valid lengths are "
2261c188a7fSPeter Avalos "256, 384 or 521 bits");
227664f4763Szrj #else
22850a69bb5SSascha Wildner fatal("Invalid ECDSA key length: valid lengths are "
229664f4763Szrj "256 or 384 bits");
230664f4763Szrj #endif
231ce74bacaSMatthew Dillon }
23236e94dc5SPeter Avalos #endif
2331c188a7fSPeter Avalos }
2341c188a7fSPeter Avalos
2350cbfa66cSDaniel Fojt /*
2360cbfa66cSDaniel Fojt * Checks whether a file exists and, if so, asks the user whether they wish
2370cbfa66cSDaniel Fojt * to overwrite it.
2380cbfa66cSDaniel Fojt * Returns nonzero if the file does not already exist or if the user agrees to
2390cbfa66cSDaniel Fojt * overwrite, or zero otherwise.
2400cbfa66cSDaniel Fojt */
2410cbfa66cSDaniel Fojt static int
confirm_overwrite(const char * filename)2420cbfa66cSDaniel Fojt confirm_overwrite(const char *filename)
2430cbfa66cSDaniel Fojt {
2440cbfa66cSDaniel Fojt char yesno[3];
2450cbfa66cSDaniel Fojt struct stat st;
2460cbfa66cSDaniel Fojt
2470cbfa66cSDaniel Fojt if (stat(filename, &st) != 0)
2480cbfa66cSDaniel Fojt return 1;
2490cbfa66cSDaniel Fojt printf("%s already exists.\n", filename);
2500cbfa66cSDaniel Fojt printf("Overwrite (y/n)? ");
2510cbfa66cSDaniel Fojt fflush(stdout);
2520cbfa66cSDaniel Fojt if (fgets(yesno, sizeof(yesno), stdin) == NULL)
2530cbfa66cSDaniel Fojt return 0;
2540cbfa66cSDaniel Fojt if (yesno[0] != 'y' && yesno[0] != 'Y')
2550cbfa66cSDaniel Fojt return 0;
2560cbfa66cSDaniel Fojt return 1;
2570cbfa66cSDaniel Fojt }
2580cbfa66cSDaniel Fojt
2591c188a7fSPeter Avalos static void
ask_filename(struct passwd * pw,const char * prompt)26018de8d7fSPeter Avalos ask_filename(struct passwd *pw, const char *prompt)
26118de8d7fSPeter Avalos {
26218de8d7fSPeter Avalos char buf[1024];
26318de8d7fSPeter Avalos char *name = NULL;
26418de8d7fSPeter Avalos
26518de8d7fSPeter Avalos if (key_type_name == NULL)
26618de8d7fSPeter Avalos name = _PATH_SSH_CLIENT_ID_RSA;
26718de8d7fSPeter Avalos else {
268e9778795SPeter Avalos switch (sshkey_type_from_name(key_type_name)) {
269856ea928SPeter Avalos case KEY_DSA_CERT:
27018de8d7fSPeter Avalos case KEY_DSA:
27118de8d7fSPeter Avalos name = _PATH_SSH_CLIENT_ID_DSA;
27218de8d7fSPeter Avalos break;
2739f304aafSPeter Avalos #ifdef OPENSSL_HAS_ECC
2749f304aafSPeter Avalos case KEY_ECDSA_CERT:
2759f304aafSPeter Avalos case KEY_ECDSA:
2769f304aafSPeter Avalos name = _PATH_SSH_CLIENT_ID_ECDSA;
2779f304aafSPeter Avalos break;
2780cbfa66cSDaniel Fojt case KEY_ECDSA_SK_CERT:
2790cbfa66cSDaniel Fojt case KEY_ECDSA_SK:
2800cbfa66cSDaniel Fojt name = _PATH_SSH_CLIENT_ID_ECDSA_SK;
2810cbfa66cSDaniel Fojt break;
2829f304aafSPeter Avalos #endif
283856ea928SPeter Avalos case KEY_RSA_CERT:
28418de8d7fSPeter Avalos case KEY_RSA:
28518de8d7fSPeter Avalos name = _PATH_SSH_CLIENT_ID_RSA;
28618de8d7fSPeter Avalos break;
28736e94dc5SPeter Avalos case KEY_ED25519:
28836e94dc5SPeter Avalos case KEY_ED25519_CERT:
28936e94dc5SPeter Avalos name = _PATH_SSH_CLIENT_ID_ED25519;
29036e94dc5SPeter Avalos break;
2910cbfa66cSDaniel Fojt case KEY_ED25519_SK:
2920cbfa66cSDaniel Fojt case KEY_ED25519_SK_CERT:
2930cbfa66cSDaniel Fojt name = _PATH_SSH_CLIENT_ID_ED25519_SK;
2940cbfa66cSDaniel Fojt break;
295664f4763Szrj case KEY_XMSS:
296664f4763Szrj case KEY_XMSS_CERT:
297664f4763Szrj name = _PATH_SSH_CLIENT_ID_XMSS;
298664f4763Szrj break;
29918de8d7fSPeter Avalos default:
300e9778795SPeter Avalos fatal("bad key type");
30118de8d7fSPeter Avalos }
30218de8d7fSPeter Avalos }
303e9778795SPeter Avalos snprintf(identity_file, sizeof(identity_file),
304e9778795SPeter Avalos "%s/%s", pw->pw_dir, name);
305e9778795SPeter Avalos printf("%s (%s): ", prompt, identity_file);
306e9778795SPeter Avalos fflush(stdout);
30718de8d7fSPeter Avalos if (fgets(buf, sizeof(buf), stdin) == NULL)
30818de8d7fSPeter Avalos exit(1);
30918de8d7fSPeter Avalos buf[strcspn(buf, "\n")] = '\0';
31018de8d7fSPeter Avalos if (strcmp(buf, "") != 0)
31118de8d7fSPeter Avalos strlcpy(identity_file, buf, sizeof(identity_file));
31218de8d7fSPeter Avalos have_identity = 1;
31318de8d7fSPeter Avalos }
31418de8d7fSPeter Avalos
315e9778795SPeter Avalos static struct sshkey *
load_identity(const char * filename,char ** commentp)3160cbfa66cSDaniel Fojt load_identity(const char *filename, char **commentp)
31718de8d7fSPeter Avalos {
31818de8d7fSPeter Avalos char *pass;
319e9778795SPeter Avalos struct sshkey *prv;
320e9778795SPeter Avalos int r;
32118de8d7fSPeter Avalos
3220cbfa66cSDaniel Fojt if (commentp != NULL)
3230cbfa66cSDaniel Fojt *commentp = NULL;
3240cbfa66cSDaniel Fojt if ((r = sshkey_load_private(filename, "", &prv, commentp)) == 0)
325e9778795SPeter Avalos return prv;
326e9778795SPeter Avalos if (r != SSH_ERR_KEY_WRONG_PASSPHRASE)
32750a69bb5SSascha Wildner fatal_r(r, "Load key \"%s\"", filename);
32818de8d7fSPeter Avalos if (identity_passphrase)
32918de8d7fSPeter Avalos pass = xstrdup(identity_passphrase);
33018de8d7fSPeter Avalos else
331e9778795SPeter Avalos pass = read_passphrase("Enter passphrase: ", RP_ALLOW_STDIN);
3320cbfa66cSDaniel Fojt r = sshkey_load_private(filename, pass, &prv, commentp);
3330cbfa66cSDaniel Fojt freezero(pass, strlen(pass));
334e9778795SPeter Avalos if (r != 0)
33550a69bb5SSascha Wildner fatal_r(r, "Load key \"%s\"", filename);
33618de8d7fSPeter Avalos return prv;
33718de8d7fSPeter Avalos }
33818de8d7fSPeter Avalos
33918de8d7fSPeter Avalos #define SSH_COM_PUBLIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----"
34018de8d7fSPeter Avalos #define SSH_COM_PUBLIC_END "---- END SSH2 PUBLIC KEY ----"
34118de8d7fSPeter Avalos #define SSH_COM_PRIVATE_BEGIN "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----"
34218de8d7fSPeter Avalos #define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb
34318de8d7fSPeter Avalos
34436e94dc5SPeter Avalos #ifdef WITH_OPENSSL
34518de8d7fSPeter Avalos static void
do_convert_to_ssh2(struct passwd * pw,struct sshkey * k)346e9778795SPeter Avalos do_convert_to_ssh2(struct passwd *pw, struct sshkey *k)
34718de8d7fSPeter Avalos {
3480cbfa66cSDaniel Fojt struct sshbuf *b;
3490cbfa66cSDaniel Fojt char comment[61], *b64;
350e9778795SPeter Avalos int r;
351856ea928SPeter Avalos
3520cbfa66cSDaniel Fojt if ((b = sshbuf_new()) == NULL)
35350a69bb5SSascha Wildner fatal_f("sshbuf_new failed");
3540cbfa66cSDaniel Fojt if ((r = sshkey_putb(k, b)) != 0)
35550a69bb5SSascha Wildner fatal_fr(r, "put key");
3560cbfa66cSDaniel Fojt if ((b64 = sshbuf_dtob64_string(b, 1)) == NULL)
35750a69bb5SSascha Wildner fatal_f("sshbuf_dtob64_string failed");
3580cbfa66cSDaniel Fojt
359856ea928SPeter Avalos /* Comment + surrounds must fit into 72 chars (RFC 4716 sec 3.3) */
360856ea928SPeter Avalos snprintf(comment, sizeof(comment),
361856ea928SPeter Avalos "%u-bit %s, converted by %s@%s from OpenSSH",
362e9778795SPeter Avalos sshkey_size(k), sshkey_type(k),
363856ea928SPeter Avalos pw->pw_name, hostname);
364856ea928SPeter Avalos
365e9778795SPeter Avalos sshkey_free(k);
3660cbfa66cSDaniel Fojt sshbuf_free(b);
3670cbfa66cSDaniel Fojt
3680cbfa66cSDaniel Fojt fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN);
3690cbfa66cSDaniel Fojt fprintf(stdout, "Comment: \"%s\"\n%s", comment, b64);
3700cbfa66cSDaniel Fojt fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END);
3710cbfa66cSDaniel Fojt free(b64);
372856ea928SPeter Avalos exit(0);
373856ea928SPeter Avalos }
374856ea928SPeter Avalos
375856ea928SPeter Avalos static void
do_convert_to_pkcs8(struct sshkey * k)376e9778795SPeter Avalos do_convert_to_pkcs8(struct sshkey *k)
377856ea928SPeter Avalos {
378e9778795SPeter Avalos switch (sshkey_type_plain(k->type)) {
379856ea928SPeter Avalos case KEY_RSA:
380856ea928SPeter Avalos if (!PEM_write_RSA_PUBKEY(stdout, k->rsa))
381856ea928SPeter Avalos fatal("PEM_write_RSA_PUBKEY failed");
382856ea928SPeter Avalos break;
383856ea928SPeter Avalos case KEY_DSA:
384856ea928SPeter Avalos if (!PEM_write_DSA_PUBKEY(stdout, k->dsa))
385856ea928SPeter Avalos fatal("PEM_write_DSA_PUBKEY failed");
386856ea928SPeter Avalos break;
3879f304aafSPeter Avalos #ifdef OPENSSL_HAS_ECC
3889f304aafSPeter Avalos case KEY_ECDSA:
3899f304aafSPeter Avalos if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa))
3909f304aafSPeter Avalos fatal("PEM_write_EC_PUBKEY failed");
3919f304aafSPeter Avalos break;
3929f304aafSPeter Avalos #endif
393856ea928SPeter Avalos default:
39450a69bb5SSascha Wildner fatal_f("unsupported key type %s", sshkey_type(k));
395856ea928SPeter Avalos }
396856ea928SPeter Avalos exit(0);
397856ea928SPeter Avalos }
398856ea928SPeter Avalos
399856ea928SPeter Avalos static void
do_convert_to_pem(struct sshkey * k)400e9778795SPeter Avalos do_convert_to_pem(struct sshkey *k)
401856ea928SPeter Avalos {
402e9778795SPeter Avalos switch (sshkey_type_plain(k->type)) {
403856ea928SPeter Avalos case KEY_RSA:
404856ea928SPeter Avalos if (!PEM_write_RSAPublicKey(stdout, k->rsa))
405856ea928SPeter Avalos fatal("PEM_write_RSAPublicKey failed");
406856ea928SPeter Avalos break;
4070cbfa66cSDaniel Fojt case KEY_DSA:
4080cbfa66cSDaniel Fojt if (!PEM_write_DSA_PUBKEY(stdout, k->dsa))
4090cbfa66cSDaniel Fojt fatal("PEM_write_DSA_PUBKEY failed");
4100cbfa66cSDaniel Fojt break;
4110cbfa66cSDaniel Fojt #ifdef OPENSSL_HAS_ECC
4120cbfa66cSDaniel Fojt case KEY_ECDSA:
4130cbfa66cSDaniel Fojt if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa))
4140cbfa66cSDaniel Fojt fatal("PEM_write_EC_PUBKEY failed");
4150cbfa66cSDaniel Fojt break;
4160cbfa66cSDaniel Fojt #endif
417856ea928SPeter Avalos default:
41850a69bb5SSascha Wildner fatal_f("unsupported key type %s", sshkey_type(k));
419856ea928SPeter Avalos }
420856ea928SPeter Avalos exit(0);
421856ea928SPeter Avalos }
422856ea928SPeter Avalos
423856ea928SPeter Avalos static void
do_convert_to(struct passwd * pw)424856ea928SPeter Avalos do_convert_to(struct passwd *pw)
425856ea928SPeter Avalos {
426e9778795SPeter Avalos struct sshkey *k;
42718de8d7fSPeter Avalos struct stat st;
428e9778795SPeter Avalos int r;
42918de8d7fSPeter Avalos
43018de8d7fSPeter Avalos if (!have_identity)
43118de8d7fSPeter Avalos ask_filename(pw, "Enter file in which the key is");
4320cbfa66cSDaniel Fojt if (stat(identity_file, &st) == -1)
433856ea928SPeter Avalos fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
434e9778795SPeter Avalos if ((r = sshkey_load_public(identity_file, &k, NULL)) != 0)
4350cbfa66cSDaniel Fojt k = load_identity(identity_file, NULL);
436856ea928SPeter Avalos switch (convert_format) {
437856ea928SPeter Avalos case FMT_RFC4716:
438856ea928SPeter Avalos do_convert_to_ssh2(pw, k);
439856ea928SPeter Avalos break;
440856ea928SPeter Avalos case FMT_PKCS8:
441856ea928SPeter Avalos do_convert_to_pkcs8(k);
442856ea928SPeter Avalos break;
443856ea928SPeter Avalos case FMT_PEM:
444856ea928SPeter Avalos do_convert_to_pem(k);
445856ea928SPeter Avalos break;
446856ea928SPeter Avalos default:
44750a69bb5SSascha Wildner fatal_f("unknown key format %d", convert_format);
44818de8d7fSPeter Avalos }
44918de8d7fSPeter Avalos exit(0);
45018de8d7fSPeter Avalos }
45118de8d7fSPeter Avalos
452e9778795SPeter Avalos /*
453e9778795SPeter Avalos * This is almost exactly the bignum1 encoding, but with 32 bit for length
454e9778795SPeter Avalos * instead of 16.
455e9778795SPeter Avalos */
45618de8d7fSPeter Avalos static void
buffer_get_bignum_bits(struct sshbuf * b,BIGNUM * value)457e9778795SPeter Avalos buffer_get_bignum_bits(struct sshbuf *b, BIGNUM *value)
45818de8d7fSPeter Avalos {
459e9778795SPeter Avalos u_int bytes, bignum_bits;
460e9778795SPeter Avalos int r;
46118de8d7fSPeter Avalos
462e9778795SPeter Avalos if ((r = sshbuf_get_u32(b, &bignum_bits)) != 0)
46350a69bb5SSascha Wildner fatal_fr(r, "parse");
464e9778795SPeter Avalos bytes = (bignum_bits + 7) / 8;
465e9778795SPeter Avalos if (sshbuf_len(b) < bytes)
46650a69bb5SSascha Wildner fatal_f("input buffer too small: need %d have %zu",
46750a69bb5SSascha Wildner bytes, sshbuf_len(b));
468e9778795SPeter Avalos if (BN_bin2bn(sshbuf_ptr(b), bytes, value) == NULL)
46950a69bb5SSascha Wildner fatal_f("BN_bin2bn failed");
470e9778795SPeter Avalos if ((r = sshbuf_consume(b, bytes)) != 0)
47150a69bb5SSascha Wildner fatal_fr(r, "consume");
47218de8d7fSPeter Avalos }
47318de8d7fSPeter Avalos
474e9778795SPeter Avalos static struct sshkey *
do_convert_private_ssh2(struct sshbuf * b)4750cbfa66cSDaniel Fojt do_convert_private_ssh2(struct sshbuf *b)
47618de8d7fSPeter Avalos {
477e9778795SPeter Avalos struct sshkey *key = NULL;
47818de8d7fSPeter Avalos char *type, *cipher;
479e9778795SPeter Avalos u_char e1, e2, e3, *sig = NULL, data[] = "abcde12345";
480e9778795SPeter Avalos int r, rlen, ktype;
481e9778795SPeter Avalos u_int magic, i1, i2, i3, i4;
482e9778795SPeter Avalos size_t slen;
48318de8d7fSPeter Avalos u_long e;
484664f4763Szrj BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL;
485664f4763Szrj BIGNUM *dsa_pub_key = NULL, *dsa_priv_key = NULL;
486664f4763Szrj BIGNUM *rsa_n = NULL, *rsa_e = NULL, *rsa_d = NULL;
487664f4763Szrj BIGNUM *rsa_p = NULL, *rsa_q = NULL, *rsa_iqmp = NULL;
4880cbfa66cSDaniel Fojt
489e9778795SPeter Avalos if ((r = sshbuf_get_u32(b, &magic)) != 0)
49050a69bb5SSascha Wildner fatal_fr(r, "parse magic");
49118de8d7fSPeter Avalos
49218de8d7fSPeter Avalos if (magic != SSH_COM_PRIVATE_KEY_MAGIC) {
493e9778795SPeter Avalos error("bad magic 0x%x != 0x%x", magic,
494e9778795SPeter Avalos SSH_COM_PRIVATE_KEY_MAGIC);
49518de8d7fSPeter Avalos return NULL;
49618de8d7fSPeter Avalos }
497e9778795SPeter Avalos if ((r = sshbuf_get_u32(b, &i1)) != 0 ||
498e9778795SPeter Avalos (r = sshbuf_get_cstring(b, &type, NULL)) != 0 ||
499e9778795SPeter Avalos (r = sshbuf_get_cstring(b, &cipher, NULL)) != 0 ||
500e9778795SPeter Avalos (r = sshbuf_get_u32(b, &i2)) != 0 ||
501e9778795SPeter Avalos (r = sshbuf_get_u32(b, &i3)) != 0 ||
502e9778795SPeter Avalos (r = sshbuf_get_u32(b, &i4)) != 0)
50350a69bb5SSascha Wildner fatal_fr(r, "parse");
50418de8d7fSPeter Avalos debug("ignore (%d %d %d %d)", i1, i2, i3, i4);
50518de8d7fSPeter Avalos if (strcmp(cipher, "none") != 0) {
50618de8d7fSPeter Avalos error("unsupported cipher %s", cipher);
50736e94dc5SPeter Avalos free(cipher);
50836e94dc5SPeter Avalos free(type);
50918de8d7fSPeter Avalos return NULL;
51018de8d7fSPeter Avalos }
51136e94dc5SPeter Avalos free(cipher);
51218de8d7fSPeter Avalos
51318de8d7fSPeter Avalos if (strstr(type, "dsa")) {
51418de8d7fSPeter Avalos ktype = KEY_DSA;
51518de8d7fSPeter Avalos } else if (strstr(type, "rsa")) {
51618de8d7fSPeter Avalos ktype = KEY_RSA;
51718de8d7fSPeter Avalos } else {
51836e94dc5SPeter Avalos free(type);
51918de8d7fSPeter Avalos return NULL;
52018de8d7fSPeter Avalos }
521664f4763Szrj if ((key = sshkey_new(ktype)) == NULL)
522664f4763Szrj fatal("sshkey_new failed");
52336e94dc5SPeter Avalos free(type);
52418de8d7fSPeter Avalos
52518de8d7fSPeter Avalos switch (key->type) {
52618de8d7fSPeter Avalos case KEY_DSA:
527664f4763Szrj if ((dsa_p = BN_new()) == NULL ||
528664f4763Szrj (dsa_q = BN_new()) == NULL ||
529664f4763Szrj (dsa_g = BN_new()) == NULL ||
530664f4763Szrj (dsa_pub_key = BN_new()) == NULL ||
531664f4763Szrj (dsa_priv_key = BN_new()) == NULL)
53250a69bb5SSascha Wildner fatal_f("BN_new");
533664f4763Szrj buffer_get_bignum_bits(b, dsa_p);
534664f4763Szrj buffer_get_bignum_bits(b, dsa_g);
535664f4763Szrj buffer_get_bignum_bits(b, dsa_q);
536664f4763Szrj buffer_get_bignum_bits(b, dsa_pub_key);
537664f4763Szrj buffer_get_bignum_bits(b, dsa_priv_key);
538664f4763Szrj if (!DSA_set0_pqg(key->dsa, dsa_p, dsa_q, dsa_g))
53950a69bb5SSascha Wildner fatal_f("DSA_set0_pqg failed");
540664f4763Szrj dsa_p = dsa_q = dsa_g = NULL; /* transferred */
541664f4763Szrj if (!DSA_set0_key(key->dsa, dsa_pub_key, dsa_priv_key))
54250a69bb5SSascha Wildner fatal_f("DSA_set0_key failed");
543664f4763Szrj dsa_pub_key = dsa_priv_key = NULL; /* transferred */
54418de8d7fSPeter Avalos break;
54518de8d7fSPeter Avalos case KEY_RSA:
546e9778795SPeter Avalos if ((r = sshbuf_get_u8(b, &e1)) != 0 ||
547e9778795SPeter Avalos (e1 < 30 && (r = sshbuf_get_u8(b, &e2)) != 0) ||
548e9778795SPeter Avalos (e1 < 30 && (r = sshbuf_get_u8(b, &e3)) != 0))
54950a69bb5SSascha Wildner fatal_fr(r, "parse RSA");
550e9778795SPeter Avalos e = e1;
55118de8d7fSPeter Avalos debug("e %lx", e);
55218de8d7fSPeter Avalos if (e < 30) {
55318de8d7fSPeter Avalos e <<= 8;
554e9778795SPeter Avalos e += e2;
55518de8d7fSPeter Avalos debug("e %lx", e);
55618de8d7fSPeter Avalos e <<= 8;
557e9778795SPeter Avalos e += e3;
55818de8d7fSPeter Avalos debug("e %lx", e);
55918de8d7fSPeter Avalos }
560664f4763Szrj if ((rsa_e = BN_new()) == NULL)
56150a69bb5SSascha Wildner fatal_f("BN_new");
562664f4763Szrj if (!BN_set_word(rsa_e, e)) {
563664f4763Szrj BN_clear_free(rsa_e);
564e9778795SPeter Avalos sshkey_free(key);
56518de8d7fSPeter Avalos return NULL;
56618de8d7fSPeter Avalos }
567664f4763Szrj if ((rsa_n = BN_new()) == NULL ||
568664f4763Szrj (rsa_d = BN_new()) == NULL ||
569664f4763Szrj (rsa_p = BN_new()) == NULL ||
570664f4763Szrj (rsa_q = BN_new()) == NULL ||
571664f4763Szrj (rsa_iqmp = BN_new()) == NULL)
57250a69bb5SSascha Wildner fatal_f("BN_new");
573664f4763Szrj buffer_get_bignum_bits(b, rsa_d);
574664f4763Szrj buffer_get_bignum_bits(b, rsa_n);
575664f4763Szrj buffer_get_bignum_bits(b, rsa_iqmp);
576664f4763Szrj buffer_get_bignum_bits(b, rsa_q);
577664f4763Szrj buffer_get_bignum_bits(b, rsa_p);
578664f4763Szrj if (!RSA_set0_key(key->rsa, rsa_n, rsa_e, rsa_d))
57950a69bb5SSascha Wildner fatal_f("RSA_set0_key failed");
580664f4763Szrj rsa_n = rsa_e = rsa_d = NULL; /* transferred */
581664f4763Szrj if (!RSA_set0_factors(key->rsa, rsa_p, rsa_q))
58250a69bb5SSascha Wildner fatal_f("RSA_set0_factors failed");
583664f4763Szrj rsa_p = rsa_q = NULL; /* transferred */
584664f4763Szrj if ((r = ssh_rsa_complete_crt_parameters(key, rsa_iqmp)) != 0)
58550a69bb5SSascha Wildner fatal_fr(r, "generate RSA parameters");
586664f4763Szrj BN_clear_free(rsa_iqmp);
58718de8d7fSPeter Avalos break;
58818de8d7fSPeter Avalos }
589e9778795SPeter Avalos rlen = sshbuf_len(b);
59018de8d7fSPeter Avalos if (rlen != 0)
59150a69bb5SSascha Wildner error_f("remaining bytes in key blob %d", rlen);
59218de8d7fSPeter Avalos
59318de8d7fSPeter Avalos /* try the key */
594*ee116499SAntonio Huete Jimenez if ((r = sshkey_sign(key, &sig, &slen, data, sizeof(data),
595*ee116499SAntonio Huete Jimenez NULL, NULL, NULL, 0)) != 0)
596*ee116499SAntonio Huete Jimenez error_fr(r, "signing with converted key failed");
597*ee116499SAntonio Huete Jimenez else if ((r = sshkey_verify(key, sig, slen, data, sizeof(data),
598*ee116499SAntonio Huete Jimenez NULL, 0, NULL)) != 0)
599*ee116499SAntonio Huete Jimenez error_fr(r, "verification with converted key failed");
600*ee116499SAntonio Huete Jimenez if (r != 0) {
601e9778795SPeter Avalos sshkey_free(key);
602e9778795SPeter Avalos free(sig);
603e9778795SPeter Avalos return NULL;
604e9778795SPeter Avalos }
60536e94dc5SPeter Avalos free(sig);
60618de8d7fSPeter Avalos return key;
60718de8d7fSPeter Avalos }
60818de8d7fSPeter Avalos
60918de8d7fSPeter Avalos static int
get_line(FILE * fp,char * line,size_t len)61018de8d7fSPeter Avalos get_line(FILE *fp, char *line, size_t len)
61118de8d7fSPeter Avalos {
61218de8d7fSPeter Avalos int c;
61318de8d7fSPeter Avalos size_t pos = 0;
61418de8d7fSPeter Avalos
61518de8d7fSPeter Avalos line[0] = '\0';
61618de8d7fSPeter Avalos while ((c = fgetc(fp)) != EOF) {
617e9778795SPeter Avalos if (pos >= len - 1)
618e9778795SPeter Avalos fatal("input line too long.");
61918de8d7fSPeter Avalos switch (c) {
62018de8d7fSPeter Avalos case '\r':
62118de8d7fSPeter Avalos c = fgetc(fp);
622e9778795SPeter Avalos if (c != EOF && c != '\n' && ungetc(c, fp) == EOF)
623e9778795SPeter Avalos fatal("unget: %s", strerror(errno));
62418de8d7fSPeter Avalos return pos;
62518de8d7fSPeter Avalos case '\n':
62618de8d7fSPeter Avalos return pos;
62718de8d7fSPeter Avalos }
62818de8d7fSPeter Avalos line[pos++] = c;
62918de8d7fSPeter Avalos line[pos] = '\0';
63018de8d7fSPeter Avalos }
63118de8d7fSPeter Avalos /* We reached EOF */
63218de8d7fSPeter Avalos return -1;
63318de8d7fSPeter Avalos }
63418de8d7fSPeter Avalos
63518de8d7fSPeter Avalos static void
do_convert_from_ssh2(struct passwd * pw,struct sshkey ** k,int * private)636e9778795SPeter Avalos do_convert_from_ssh2(struct passwd *pw, struct sshkey **k, int *private)
63718de8d7fSPeter Avalos {
638e9778795SPeter Avalos int r, blen, escaped = 0;
63918de8d7fSPeter Avalos u_int len;
64018de8d7fSPeter Avalos char line[1024];
6410cbfa66cSDaniel Fojt struct sshbuf *buf;
64218de8d7fSPeter Avalos char encoded[8096];
64318de8d7fSPeter Avalos FILE *fp;
64418de8d7fSPeter Avalos
6450cbfa66cSDaniel Fojt if ((buf = sshbuf_new()) == NULL)
6460cbfa66cSDaniel Fojt fatal("sshbuf_new failed");
647856ea928SPeter Avalos if ((fp = fopen(identity_file, "r")) == NULL)
648856ea928SPeter Avalos fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
64918de8d7fSPeter Avalos encoded[0] = '\0';
65018de8d7fSPeter Avalos while ((blen = get_line(fp, line, sizeof(line))) != -1) {
65136e94dc5SPeter Avalos if (blen > 0 && line[blen - 1] == '\\')
65218de8d7fSPeter Avalos escaped++;
65318de8d7fSPeter Avalos if (strncmp(line, "----", 4) == 0 ||
65418de8d7fSPeter Avalos strstr(line, ": ") != NULL) {
65518de8d7fSPeter Avalos if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL)
656856ea928SPeter Avalos *private = 1;
65718de8d7fSPeter Avalos if (strstr(line, " END ") != NULL) {
65818de8d7fSPeter Avalos break;
65918de8d7fSPeter Avalos }
66018de8d7fSPeter Avalos /* fprintf(stderr, "ignore: %s", line); */
66118de8d7fSPeter Avalos continue;
66218de8d7fSPeter Avalos }
66318de8d7fSPeter Avalos if (escaped) {
66418de8d7fSPeter Avalos escaped--;
66518de8d7fSPeter Avalos /* fprintf(stderr, "escaped: %s", line); */
66618de8d7fSPeter Avalos continue;
66718de8d7fSPeter Avalos }
66818de8d7fSPeter Avalos strlcat(encoded, line, sizeof(encoded));
66918de8d7fSPeter Avalos }
67018de8d7fSPeter Avalos len = strlen(encoded);
67118de8d7fSPeter Avalos if (((len % 4) == 3) &&
67218de8d7fSPeter Avalos (encoded[len-1] == '=') &&
67318de8d7fSPeter Avalos (encoded[len-2] == '=') &&
67418de8d7fSPeter Avalos (encoded[len-3] == '='))
67518de8d7fSPeter Avalos encoded[len-3] = '\0';
6760cbfa66cSDaniel Fojt if ((r = sshbuf_b64tod(buf, encoded)) != 0)
67750a69bb5SSascha Wildner fatal_fr(r, "base64 decode");
6780cbfa66cSDaniel Fojt if (*private) {
6790cbfa66cSDaniel Fojt if ((*k = do_convert_private_ssh2(buf)) == NULL)
68050a69bb5SSascha Wildner fatal_f("private key conversion failed");
6810cbfa66cSDaniel Fojt } else if ((r = sshkey_fromb(buf, k)) != 0)
68250a69bb5SSascha Wildner fatal_fr(r, "parse key");
6830cbfa66cSDaniel Fojt sshbuf_free(buf);
684856ea928SPeter Avalos fclose(fp);
685856ea928SPeter Avalos }
686856ea928SPeter Avalos
687856ea928SPeter Avalos static void
do_convert_from_pkcs8(struct sshkey ** k,int * private)688e9778795SPeter Avalos do_convert_from_pkcs8(struct sshkey **k, int *private)
689856ea928SPeter Avalos {
690856ea928SPeter Avalos EVP_PKEY *pubkey;
691856ea928SPeter Avalos FILE *fp;
692856ea928SPeter Avalos
693856ea928SPeter Avalos if ((fp = fopen(identity_file, "r")) == NULL)
694856ea928SPeter Avalos fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
695856ea928SPeter Avalos if ((pubkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
69650a69bb5SSascha Wildner fatal_f("%s is not a recognised public key format",
697856ea928SPeter Avalos identity_file);
698856ea928SPeter Avalos }
699856ea928SPeter Avalos fclose(fp);
700664f4763Szrj switch (EVP_PKEY_base_id(pubkey)) {
701856ea928SPeter Avalos case EVP_PKEY_RSA:
702e9778795SPeter Avalos if ((*k = sshkey_new(KEY_UNSPEC)) == NULL)
703e9778795SPeter Avalos fatal("sshkey_new failed");
704856ea928SPeter Avalos (*k)->type = KEY_RSA;
705856ea928SPeter Avalos (*k)->rsa = EVP_PKEY_get1_RSA(pubkey);
706856ea928SPeter Avalos break;
707856ea928SPeter Avalos case EVP_PKEY_DSA:
708e9778795SPeter Avalos if ((*k = sshkey_new(KEY_UNSPEC)) == NULL)
709e9778795SPeter Avalos fatal("sshkey_new failed");
710856ea928SPeter Avalos (*k)->type = KEY_DSA;
711856ea928SPeter Avalos (*k)->dsa = EVP_PKEY_get1_DSA(pubkey);
712856ea928SPeter Avalos break;
7139f304aafSPeter Avalos #ifdef OPENSSL_HAS_ECC
7149f304aafSPeter Avalos case EVP_PKEY_EC:
715e9778795SPeter Avalos if ((*k = sshkey_new(KEY_UNSPEC)) == NULL)
716e9778795SPeter Avalos fatal("sshkey_new failed");
7179f304aafSPeter Avalos (*k)->type = KEY_ECDSA;
7189f304aafSPeter Avalos (*k)->ecdsa = EVP_PKEY_get1_EC_KEY(pubkey);
719e9778795SPeter Avalos (*k)->ecdsa_nid = sshkey_ecdsa_key_to_nid((*k)->ecdsa);
7209f304aafSPeter Avalos break;
7219f304aafSPeter Avalos #endif
722856ea928SPeter Avalos default:
72350a69bb5SSascha Wildner fatal_f("unsupported pubkey type %d",
724664f4763Szrj EVP_PKEY_base_id(pubkey));
725856ea928SPeter Avalos }
726856ea928SPeter Avalos EVP_PKEY_free(pubkey);
727856ea928SPeter Avalos return;
728856ea928SPeter Avalos }
729856ea928SPeter Avalos
730856ea928SPeter Avalos static void
do_convert_from_pem(struct sshkey ** k,int * private)731e9778795SPeter Avalos do_convert_from_pem(struct sshkey **k, int *private)
732856ea928SPeter Avalos {
733856ea928SPeter Avalos FILE *fp;
734856ea928SPeter Avalos RSA *rsa;
735856ea928SPeter Avalos
736856ea928SPeter Avalos if ((fp = fopen(identity_file, "r")) == NULL)
737856ea928SPeter Avalos fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
738856ea928SPeter Avalos if ((rsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL)) != NULL) {
739e9778795SPeter Avalos if ((*k = sshkey_new(KEY_UNSPEC)) == NULL)
740e9778795SPeter Avalos fatal("sshkey_new failed");
741856ea928SPeter Avalos (*k)->type = KEY_RSA;
742856ea928SPeter Avalos (*k)->rsa = rsa;
743856ea928SPeter Avalos fclose(fp);
744856ea928SPeter Avalos return;
745856ea928SPeter Avalos }
74650a69bb5SSascha Wildner fatal_f("unrecognised raw private key format");
747856ea928SPeter Avalos }
748856ea928SPeter Avalos
749856ea928SPeter Avalos static void
do_convert_from(struct passwd * pw)750856ea928SPeter Avalos do_convert_from(struct passwd *pw)
751856ea928SPeter Avalos {
752e9778795SPeter Avalos struct sshkey *k = NULL;
753e9778795SPeter Avalos int r, private = 0, ok = 0;
754856ea928SPeter Avalos struct stat st;
755856ea928SPeter Avalos
756856ea928SPeter Avalos if (!have_identity)
757856ea928SPeter Avalos ask_filename(pw, "Enter file in which the key is");
7580cbfa66cSDaniel Fojt if (stat(identity_file, &st) == -1)
759856ea928SPeter Avalos fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
760856ea928SPeter Avalos
761856ea928SPeter Avalos switch (convert_format) {
762856ea928SPeter Avalos case FMT_RFC4716:
763856ea928SPeter Avalos do_convert_from_ssh2(pw, &k, &private);
764856ea928SPeter Avalos break;
765856ea928SPeter Avalos case FMT_PKCS8:
766856ea928SPeter Avalos do_convert_from_pkcs8(&k, &private);
767856ea928SPeter Avalos break;
768856ea928SPeter Avalos case FMT_PEM:
769856ea928SPeter Avalos do_convert_from_pem(&k, &private);
770856ea928SPeter Avalos break;
771856ea928SPeter Avalos default:
77250a69bb5SSascha Wildner fatal_f("unknown key format %d", convert_format);
773856ea928SPeter Avalos }
774856ea928SPeter Avalos
775e9778795SPeter Avalos if (!private) {
776e9778795SPeter Avalos if ((r = sshkey_write(k, stdout)) == 0)
777e9778795SPeter Avalos ok = 1;
778856ea928SPeter Avalos if (ok)
779856ea928SPeter Avalos fprintf(stdout, "\n");
780e9778795SPeter Avalos } else {
781856ea928SPeter Avalos switch (k->type) {
782856ea928SPeter Avalos case KEY_DSA:
783856ea928SPeter Avalos ok = PEM_write_DSAPrivateKey(stdout, k->dsa, NULL,
784856ea928SPeter Avalos NULL, 0, NULL, NULL);
785856ea928SPeter Avalos break;
7869f304aafSPeter Avalos #ifdef OPENSSL_HAS_ECC
7879f304aafSPeter Avalos case KEY_ECDSA:
7889f304aafSPeter Avalos ok = PEM_write_ECPrivateKey(stdout, k->ecdsa, NULL,
7899f304aafSPeter Avalos NULL, 0, NULL, NULL);
7909f304aafSPeter Avalos break;
7919f304aafSPeter Avalos #endif
792856ea928SPeter Avalos case KEY_RSA:
793856ea928SPeter Avalos ok = PEM_write_RSAPrivateKey(stdout, k->rsa, NULL,
794856ea928SPeter Avalos NULL, 0, NULL, NULL);
795856ea928SPeter Avalos break;
796856ea928SPeter Avalos default:
79750a69bb5SSascha Wildner fatal_f("unsupported key type %s", sshkey_type(k));
798856ea928SPeter Avalos }
799856ea928SPeter Avalos }
800856ea928SPeter Avalos
801e9778795SPeter Avalos if (!ok)
802e9778795SPeter Avalos fatal("key write failed");
803e9778795SPeter Avalos sshkey_free(k);
80418de8d7fSPeter Avalos exit(0);
80518de8d7fSPeter Avalos }
80636e94dc5SPeter Avalos #endif
80718de8d7fSPeter Avalos
80818de8d7fSPeter Avalos static void
do_print_public(struct passwd * pw)80918de8d7fSPeter Avalos do_print_public(struct passwd *pw)
81018de8d7fSPeter Avalos {
811e9778795SPeter Avalos struct sshkey *prv;
81218de8d7fSPeter Avalos struct stat st;
813e9778795SPeter Avalos int r;
8140cbfa66cSDaniel Fojt char *comment = NULL;
81518de8d7fSPeter Avalos
81618de8d7fSPeter Avalos if (!have_identity)
81718de8d7fSPeter Avalos ask_filename(pw, "Enter file in which the key is");
8180cbfa66cSDaniel Fojt if (stat(identity_file, &st) == -1)
819e9778795SPeter Avalos fatal("%s: %s", identity_file, strerror(errno));
8200cbfa66cSDaniel Fojt prv = load_identity(identity_file, &comment);
821e9778795SPeter Avalos if ((r = sshkey_write(prv, stdout)) != 0)
82250a69bb5SSascha Wildner fatal_fr(r, "write key");
8230cbfa66cSDaniel Fojt if (comment != NULL && *comment != '\0')
8240cbfa66cSDaniel Fojt fprintf(stdout, " %s", comment);
82518de8d7fSPeter Avalos fprintf(stdout, "\n");
82650a69bb5SSascha Wildner if (sshkey_is_sk(prv)) {
82750a69bb5SSascha Wildner debug("sk_application: \"%s\", sk_flags 0x%02x",
82850a69bb5SSascha Wildner prv->sk_application, prv->sk_flags);
82950a69bb5SSascha Wildner }
83050a69bb5SSascha Wildner sshkey_free(prv);
8310cbfa66cSDaniel Fojt free(comment);
83218de8d7fSPeter Avalos exit(0);
83318de8d7fSPeter Avalos }
83418de8d7fSPeter Avalos
83518de8d7fSPeter Avalos static void
do_download(struct passwd * pw)836856ea928SPeter Avalos do_download(struct passwd *pw)
83718de8d7fSPeter Avalos {
838856ea928SPeter Avalos #ifdef ENABLE_PKCS11
839e9778795SPeter Avalos struct sshkey **keys = NULL;
840856ea928SPeter Avalos int i, nkeys;
841e9778795SPeter Avalos enum sshkey_fp_rep rep;
842e9778795SPeter Avalos int fptype;
8430cbfa66cSDaniel Fojt char *fp, *ra, **comments = NULL;
84436e94dc5SPeter Avalos
845e9778795SPeter Avalos fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash;
846e9778795SPeter Avalos rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT;
84718de8d7fSPeter Avalos
848664f4763Szrj pkcs11_init(1);
8490cbfa66cSDaniel Fojt nkeys = pkcs11_add_provider(pkcs11provider, NULL, &keys, &comments);
850856ea928SPeter Avalos if (nkeys <= 0)
851856ea928SPeter Avalos fatal("cannot read public key from pkcs11");
852856ea928SPeter Avalos for (i = 0; i < nkeys; i++) {
85336e94dc5SPeter Avalos if (print_fingerprint) {
854e9778795SPeter Avalos fp = sshkey_fingerprint(keys[i], fptype, rep);
855e9778795SPeter Avalos ra = sshkey_fingerprint(keys[i], fingerprint_hash,
85636e94dc5SPeter Avalos SSH_FP_RANDOMART);
857e9778795SPeter Avalos if (fp == NULL || ra == NULL)
85850a69bb5SSascha Wildner fatal_f("sshkey_fingerprint fail");
859e9778795SPeter Avalos printf("%u %s %s (PKCS11 key)\n", sshkey_size(keys[i]),
860e9778795SPeter Avalos fp, sshkey_type(keys[i]));
861664f4763Szrj if (log_level_get() >= SYSLOG_LEVEL_VERBOSE)
86236e94dc5SPeter Avalos printf("%s\n", ra);
86336e94dc5SPeter Avalos free(ra);
86436e94dc5SPeter Avalos free(fp);
86536e94dc5SPeter Avalos } else {
866e9778795SPeter Avalos (void) sshkey_write(keys[i], stdout); /* XXX check */
8670cbfa66cSDaniel Fojt fprintf(stdout, "%s%s\n",
8680cbfa66cSDaniel Fojt *(comments[i]) == '\0' ? "" : " ", comments[i]);
86918de8d7fSPeter Avalos }
8700cbfa66cSDaniel Fojt free(comments[i]);
871e9778795SPeter Avalos sshkey_free(keys[i]);
87236e94dc5SPeter Avalos }
8730cbfa66cSDaniel Fojt free(comments);
87436e94dc5SPeter Avalos free(keys);
875856ea928SPeter Avalos pkcs11_terminate();
87618de8d7fSPeter Avalos exit(0);
877856ea928SPeter Avalos #else
878856ea928SPeter Avalos fatal("no pkcs11 support");
879856ea928SPeter Avalos #endif /* ENABLE_PKCS11 */
88018de8d7fSPeter Avalos }
88118de8d7fSPeter Avalos
882e9778795SPeter Avalos static struct sshkey *
try_read_key(char ** cpp)883e9778795SPeter Avalos try_read_key(char **cpp)
884e9778795SPeter Avalos {
885e9778795SPeter Avalos struct sshkey *ret;
886e9778795SPeter Avalos int r;
887e9778795SPeter Avalos
888e9778795SPeter Avalos if ((ret = sshkey_new(KEY_UNSPEC)) == NULL)
889e9778795SPeter Avalos fatal("sshkey_new failed");
890e9778795SPeter Avalos if ((r = sshkey_read(ret, cpp)) == 0)
891e9778795SPeter Avalos return ret;
892e9778795SPeter Avalos /* Not a key */
893e9778795SPeter Avalos sshkey_free(ret);
894e9778795SPeter Avalos return NULL;
895e9778795SPeter Avalos }
896e9778795SPeter Avalos
897e9778795SPeter Avalos static void
fingerprint_one_key(const struct sshkey * public,const char * comment)898e9778795SPeter Avalos fingerprint_one_key(const struct sshkey *public, const char *comment)
899e9778795SPeter Avalos {
900e9778795SPeter Avalos char *fp = NULL, *ra = NULL;
901e9778795SPeter Avalos enum sshkey_fp_rep rep;
902e9778795SPeter Avalos int fptype;
903e9778795SPeter Avalos
904e9778795SPeter Avalos fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash;
905e9778795SPeter Avalos rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT;
906e9778795SPeter Avalos fp = sshkey_fingerprint(public, fptype, rep);
907e9778795SPeter Avalos ra = sshkey_fingerprint(public, fingerprint_hash, SSH_FP_RANDOMART);
908e9778795SPeter Avalos if (fp == NULL || ra == NULL)
90950a69bb5SSascha Wildner fatal_f("sshkey_fingerprint failed");
910ce74bacaSMatthew Dillon mprintf("%u %s %s (%s)\n", sshkey_size(public), fp,
911e9778795SPeter Avalos comment ? comment : "no comment", sshkey_type(public));
912664f4763Szrj if (log_level_get() >= SYSLOG_LEVEL_VERBOSE)
913e9778795SPeter Avalos printf("%s\n", ra);
914e9778795SPeter Avalos free(ra);
915e9778795SPeter Avalos free(fp);
916e9778795SPeter Avalos }
917e9778795SPeter Avalos
918e9778795SPeter Avalos static void
fingerprint_private(const char * path)919e9778795SPeter Avalos fingerprint_private(const char *path)
920e9778795SPeter Avalos {
921e9778795SPeter Avalos struct stat st;
922e9778795SPeter Avalos char *comment = NULL;
9230cbfa66cSDaniel Fojt struct sshkey *privkey = NULL, *pubkey = NULL;
924e9778795SPeter Avalos int r;
925e9778795SPeter Avalos
9260cbfa66cSDaniel Fojt if (stat(identity_file, &st) == -1)
927e9778795SPeter Avalos fatal("%s: %s", path, strerror(errno));
9280cbfa66cSDaniel Fojt if ((r = sshkey_load_public(path, &pubkey, &comment)) != 0)
92950a69bb5SSascha Wildner debug_r(r, "load public \"%s\"", path);
9300cbfa66cSDaniel Fojt if (pubkey == NULL || comment == NULL || *comment == '\0') {
9310cbfa66cSDaniel Fojt free(comment);
932e9778795SPeter Avalos if ((r = sshkey_load_private(path, NULL,
9330cbfa66cSDaniel Fojt &privkey, &comment)) != 0)
93450a69bb5SSascha Wildner debug_r(r, "load private \"%s\"", path);
9350cbfa66cSDaniel Fojt }
9360cbfa66cSDaniel Fojt if (pubkey == NULL && privkey == NULL)
937e9778795SPeter Avalos fatal("%s is not a key file.", path);
938e9778795SPeter Avalos
9390cbfa66cSDaniel Fojt fingerprint_one_key(pubkey == NULL ? privkey : pubkey, comment);
9400cbfa66cSDaniel Fojt sshkey_free(pubkey);
9410cbfa66cSDaniel Fojt sshkey_free(privkey);
942e9778795SPeter Avalos free(comment);
943e9778795SPeter Avalos }
944e9778795SPeter Avalos
94518de8d7fSPeter Avalos static void
do_fingerprint(struct passwd * pw)94618de8d7fSPeter Avalos do_fingerprint(struct passwd *pw)
94718de8d7fSPeter Avalos {
94818de8d7fSPeter Avalos FILE *f;
949e9778795SPeter Avalos struct sshkey *public = NULL;
950664f4763Szrj char *comment = NULL, *cp, *ep, *line = NULL;
951664f4763Szrj size_t linesize = 0;
952e9778795SPeter Avalos int i, invalid = 1;
953e9778795SPeter Avalos const char *path;
954e9778795SPeter Avalos u_long lnum = 0;
95518de8d7fSPeter Avalos
95618de8d7fSPeter Avalos if (!have_identity)
95718de8d7fSPeter Avalos ask_filename(pw, "Enter file in which the key is");
958e9778795SPeter Avalos path = identity_file;
959e9778795SPeter Avalos
960e9778795SPeter Avalos if (strcmp(identity_file, "-") == 0) {
961e9778795SPeter Avalos f = stdin;
962e9778795SPeter Avalos path = "(stdin)";
963e9778795SPeter Avalos } else if ((f = fopen(path, "r")) == NULL)
964e9778795SPeter Avalos fatal("%s: %s: %s", __progname, path, strerror(errno));
965e9778795SPeter Avalos
966664f4763Szrj while (getline(&line, &linesize, f) != -1) {
967664f4763Szrj lnum++;
968e9778795SPeter Avalos cp = line;
969e9778795SPeter Avalos cp[strcspn(cp, "\n")] = '\0';
970e9778795SPeter Avalos /* Trim leading space and comments */
971e9778795SPeter Avalos cp = line + strspn(line, " \t");
972e9778795SPeter Avalos if (*cp == '#' || *cp == '\0')
973e9778795SPeter Avalos continue;
974e9778795SPeter Avalos
975e9778795SPeter Avalos /*
976e9778795SPeter Avalos * Input may be plain keys, private keys, authorized_keys
977e9778795SPeter Avalos * or known_hosts.
978e9778795SPeter Avalos */
979e9778795SPeter Avalos
980e9778795SPeter Avalos /*
981e9778795SPeter Avalos * Try private keys first. Assume a key is private if
982e9778795SPeter Avalos * "SSH PRIVATE KEY" appears on the first line and we're
983e9778795SPeter Avalos * not reading from stdin (XXX support private keys on stdin).
984e9778795SPeter Avalos */
985e9778795SPeter Avalos if (lnum == 1 && strcmp(identity_file, "-") != 0 &&
986e9778795SPeter Avalos strstr(cp, "PRIVATE KEY") != NULL) {
987664f4763Szrj free(line);
988e9778795SPeter Avalos fclose(f);
989e9778795SPeter Avalos fingerprint_private(path);
99018de8d7fSPeter Avalos exit(0);
99118de8d7fSPeter Avalos }
99218de8d7fSPeter Avalos
993e9778795SPeter Avalos /*
994e9778795SPeter Avalos * If it's not a private key, then this must be prepared to
995e9778795SPeter Avalos * accept a public key prefixed with a hostname or options.
996e9778795SPeter Avalos * Try a bare key first, otherwise skip the leading stuff.
997e9778795SPeter Avalos */
998e9778795SPeter Avalos if ((public = try_read_key(&cp)) == NULL) {
99918de8d7fSPeter Avalos i = strtol(cp, &ep, 10);
1000e9778795SPeter Avalos if (i == 0 || ep == NULL ||
1001e9778795SPeter Avalos (*ep != ' ' && *ep != '\t')) {
100218de8d7fSPeter Avalos int quoted = 0;
1003e9778795SPeter Avalos
100418de8d7fSPeter Avalos comment = cp;
100518de8d7fSPeter Avalos for (; *cp && (quoted || (*cp != ' ' &&
100618de8d7fSPeter Avalos *cp != '\t')); cp++) {
100718de8d7fSPeter Avalos if (*cp == '\\' && cp[1] == '"')
100818de8d7fSPeter Avalos cp++; /* Skip both */
100918de8d7fSPeter Avalos else if (*cp == '"')
101018de8d7fSPeter Avalos quoted = !quoted;
101118de8d7fSPeter Avalos }
101218de8d7fSPeter Avalos if (!*cp)
101318de8d7fSPeter Avalos continue;
101418de8d7fSPeter Avalos *cp++ = '\0';
101518de8d7fSPeter Avalos }
1016e9778795SPeter Avalos }
1017e9778795SPeter Avalos /* Retry after parsing leading hostname/key options */
1018e9778795SPeter Avalos if (public == NULL && (public = try_read_key(&cp)) == NULL) {
1019e9778795SPeter Avalos debug("%s:%lu: not a public key", path, lnum);
102018de8d7fSPeter Avalos continue;
102118de8d7fSPeter Avalos }
1022e9778795SPeter Avalos
1023e9778795SPeter Avalos /* Find trailing comment, if any */
1024e9778795SPeter Avalos for (; *cp == ' ' || *cp == '\t'; cp++)
1025e9778795SPeter Avalos ;
1026e9778795SPeter Avalos if (*cp != '\0' && *cp != '#')
1027e9778795SPeter Avalos comment = cp;
1028e9778795SPeter Avalos
1029e9778795SPeter Avalos fingerprint_one_key(public, comment);
1030e9778795SPeter Avalos sshkey_free(public);
1031e9778795SPeter Avalos invalid = 0; /* One good key in the file is sufficient */
103218de8d7fSPeter Avalos }
103318de8d7fSPeter Avalos fclose(f);
1034664f4763Szrj free(line);
1035856ea928SPeter Avalos
1036e9778795SPeter Avalos if (invalid)
1037e9778795SPeter Avalos fatal("%s is not a public key file.", path);
103818de8d7fSPeter Avalos exit(0);
103918de8d7fSPeter Avalos }
104018de8d7fSPeter Avalos
104118de8d7fSPeter Avalos static void
do_gen_all_hostkeys(struct passwd * pw)10421c188a7fSPeter Avalos do_gen_all_hostkeys(struct passwd *pw)
10431c188a7fSPeter Avalos {
10441c188a7fSPeter Avalos struct {
10451c188a7fSPeter Avalos char *key_type;
10461c188a7fSPeter Avalos char *key_type_display;
10471c188a7fSPeter Avalos char *path;
10481c188a7fSPeter Avalos } key_types[] = {
1049e9778795SPeter Avalos #ifdef WITH_OPENSSL
10501c188a7fSPeter Avalos { "rsa", "RSA" ,_PATH_HOST_RSA_KEY_FILE },
105199e85e0dSPeter Avalos #ifdef OPENSSL_HAS_ECC
10521c188a7fSPeter Avalos { "ecdsa", "ECDSA",_PATH_HOST_ECDSA_KEY_FILE },
1053e9778795SPeter Avalos #endif /* OPENSSL_HAS_ECC */
1054e9778795SPeter Avalos #endif /* WITH_OPENSSL */
105536e94dc5SPeter Avalos { "ed25519", "ED25519",_PATH_HOST_ED25519_KEY_FILE },
1056664f4763Szrj #ifdef WITH_XMSS
1057664f4763Szrj { "xmss", "XMSS",_PATH_HOST_XMSS_KEY_FILE },
1058664f4763Szrj #endif /* WITH_XMSS */
10591c188a7fSPeter Avalos { NULL, NULL, NULL }
10601c188a7fSPeter Avalos };
10611c188a7fSPeter Avalos
10620cbfa66cSDaniel Fojt u_int32_t bits = 0;
10631c188a7fSPeter Avalos int first = 0;
10641c188a7fSPeter Avalos struct stat st;
1065e9778795SPeter Avalos struct sshkey *private, *public;
1066ce74bacaSMatthew Dillon char comment[1024], *prv_tmp, *pub_tmp, *prv_file, *pub_file;
1067e9778795SPeter Avalos int i, type, fd, r;
10681c188a7fSPeter Avalos
10691c188a7fSPeter Avalos for (i = 0; key_types[i].key_type; i++) {
1070ce74bacaSMatthew Dillon public = private = NULL;
1071ce74bacaSMatthew Dillon prv_tmp = pub_tmp = prv_file = pub_file = NULL;
1072ce74bacaSMatthew Dillon
1073ce74bacaSMatthew Dillon xasprintf(&prv_file, "%s%s",
1074ce74bacaSMatthew Dillon identity_file, key_types[i].path);
1075ce74bacaSMatthew Dillon
1076ce74bacaSMatthew Dillon /* Check whether private key exists and is not zero-length */
1077ce74bacaSMatthew Dillon if (stat(prv_file, &st) == 0) {
1078ce74bacaSMatthew Dillon if (st.st_size != 0)
1079ce74bacaSMatthew Dillon goto next;
1080ce74bacaSMatthew Dillon } else if (errno != ENOENT) {
1081e9778795SPeter Avalos error("Could not stat %s: %s", key_types[i].path,
10821c188a7fSPeter Avalos strerror(errno));
1083ce74bacaSMatthew Dillon goto failnext;
10841c188a7fSPeter Avalos }
10851c188a7fSPeter Avalos
1086ce74bacaSMatthew Dillon /*
1087ce74bacaSMatthew Dillon * Private key doesn't exist or is invalid; proceed with
1088ce74bacaSMatthew Dillon * key generation.
1089ce74bacaSMatthew Dillon */
1090ce74bacaSMatthew Dillon xasprintf(&prv_tmp, "%s%s.XXXXXXXXXX",
1091ce74bacaSMatthew Dillon identity_file, key_types[i].path);
1092ce74bacaSMatthew Dillon xasprintf(&pub_tmp, "%s%s.pub.XXXXXXXXXX",
1093ce74bacaSMatthew Dillon identity_file, key_types[i].path);
1094ce74bacaSMatthew Dillon xasprintf(&pub_file, "%s%s.pub",
1095ce74bacaSMatthew Dillon identity_file, key_types[i].path);
1096ce74bacaSMatthew Dillon
10971c188a7fSPeter Avalos if (first == 0) {
10981c188a7fSPeter Avalos first = 1;
10991c188a7fSPeter Avalos printf("%s: generating new host keys: ", __progname);
11001c188a7fSPeter Avalos }
11011c188a7fSPeter Avalos printf("%s ", key_types[i].key_type_display);
11021c188a7fSPeter Avalos fflush(stdout);
1103e9778795SPeter Avalos type = sshkey_type_from_name(key_types[i].key_type);
1104ce74bacaSMatthew Dillon if ((fd = mkstemp(prv_tmp)) == -1) {
11050cbfa66cSDaniel Fojt error("Could not save your private key in %s: %s",
1106ce74bacaSMatthew Dillon prv_tmp, strerror(errno));
1107ce74bacaSMatthew Dillon goto failnext;
1108ce74bacaSMatthew Dillon }
11090cbfa66cSDaniel Fojt (void)close(fd); /* just using mkstemp() to reserve a name */
11101c188a7fSPeter Avalos bits = 0;
1111e9778795SPeter Avalos type_bits_valid(type, NULL, &bits);
1112e9778795SPeter Avalos if ((r = sshkey_generate(type, bits, &private)) != 0) {
111350a69bb5SSascha Wildner error_r(r, "sshkey_generate failed");
1114ce74bacaSMatthew Dillon goto failnext;
11151c188a7fSPeter Avalos }
1116e9778795SPeter Avalos if ((r = sshkey_from_private(private, &public)) != 0)
111750a69bb5SSascha Wildner fatal_fr(r, "sshkey_from_private");
11181c188a7fSPeter Avalos snprintf(comment, sizeof comment, "%s@%s", pw->pw_name,
11191c188a7fSPeter Avalos hostname);
1120ce74bacaSMatthew Dillon if ((r = sshkey_save_private(private, prv_tmp, "",
11210cbfa66cSDaniel Fojt comment, private_key_format, openssh_format_cipher,
11220cbfa66cSDaniel Fojt rounds)) != 0) {
112350a69bb5SSascha Wildner error_r(r, "Saving key \"%s\" failed", prv_tmp);
1124ce74bacaSMatthew Dillon goto failnext;
11251c188a7fSPeter Avalos }
1126ce74bacaSMatthew Dillon if ((fd = mkstemp(pub_tmp)) == -1) {
1127ce74bacaSMatthew Dillon error("Could not save your public key in %s: %s",
1128ce74bacaSMatthew Dillon pub_tmp, strerror(errno));
1129ce74bacaSMatthew Dillon goto failnext;
11301c188a7fSPeter Avalos }
1131ce74bacaSMatthew Dillon (void)fchmod(fd, 0644);
11320cbfa66cSDaniel Fojt (void)close(fd);
11330cbfa66cSDaniel Fojt if ((r = sshkey_save_public(public, pub_tmp, comment)) != 0) {
113450a69bb5SSascha Wildner error_r(r, "Unable to save public key to %s",
113550a69bb5SSascha Wildner identity_file);
1136ce74bacaSMatthew Dillon goto failnext;
1137ce74bacaSMatthew Dillon }
11381c188a7fSPeter Avalos
1139ce74bacaSMatthew Dillon /* Rename temporary files to their permanent locations. */
1140ce74bacaSMatthew Dillon if (rename(pub_tmp, pub_file) != 0) {
1141ce74bacaSMatthew Dillon error("Unable to move %s into position: %s",
1142ce74bacaSMatthew Dillon pub_file, strerror(errno));
1143ce74bacaSMatthew Dillon goto failnext;
1144ce74bacaSMatthew Dillon }
1145ce74bacaSMatthew Dillon if (rename(prv_tmp, prv_file) != 0) {
1146ce74bacaSMatthew Dillon error("Unable to move %s into position: %s",
1147ce74bacaSMatthew Dillon key_types[i].path, strerror(errno));
1148ce74bacaSMatthew Dillon failnext:
1149ce74bacaSMatthew Dillon first = 0;
1150ce74bacaSMatthew Dillon goto next;
1151ce74bacaSMatthew Dillon }
1152ce74bacaSMatthew Dillon next:
1153ce74bacaSMatthew Dillon sshkey_free(private);
1154ce74bacaSMatthew Dillon sshkey_free(public);
1155ce74bacaSMatthew Dillon free(prv_tmp);
1156ce74bacaSMatthew Dillon free(pub_tmp);
1157ce74bacaSMatthew Dillon free(prv_file);
1158ce74bacaSMatthew Dillon free(pub_file);
11591c188a7fSPeter Avalos }
11601c188a7fSPeter Avalos if (first != 0)
11611c188a7fSPeter Avalos printf("\n");
11621c188a7fSPeter Avalos }
11631c188a7fSPeter Avalos
1164e9778795SPeter Avalos struct known_hosts_ctx {
1165e9778795SPeter Avalos const char *host; /* Hostname searched for in find/delete case */
1166e9778795SPeter Avalos FILE *out; /* Output file, stdout for find_hosts case */
1167e9778795SPeter Avalos int has_unhashed; /* When hashing, original had unhashed hosts */
1168e9778795SPeter Avalos int found_key; /* For find/delete, host was found */
1169e9778795SPeter Avalos int invalid; /* File contained invalid items; don't delete */
1170664f4763Szrj int hash_hosts; /* Hash hostnames as we go */
1171664f4763Szrj int find_host; /* Search for specific hostname */
1172664f4763Szrj int delete_host; /* Delete host from known_hosts */
1173e9778795SPeter Avalos };
117418de8d7fSPeter Avalos
1175e9778795SPeter Avalos static int
known_hosts_hash(struct hostkey_foreach_line * l,void * _ctx)1176e9778795SPeter Avalos known_hosts_hash(struct hostkey_foreach_line *l, void *_ctx)
1177e9778795SPeter Avalos {
1178e9778795SPeter Avalos struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx;
1179e9778795SPeter Avalos char *hashed, *cp, *hosts, *ohosts;
1180e9778795SPeter Avalos int has_wild = l->hosts && strcspn(l->hosts, "*?!") != strlen(l->hosts);
1181ce74bacaSMatthew Dillon int was_hashed = l->hosts && l->hosts[0] == HASH_DELIM;
1182e9778795SPeter Avalos
1183e9778795SPeter Avalos switch (l->status) {
1184e9778795SPeter Avalos case HKF_STATUS_OK:
1185e9778795SPeter Avalos case HKF_STATUS_MATCHED:
1186e9778795SPeter Avalos /*
1187e9778795SPeter Avalos * Don't hash hosts already already hashed, with wildcard
1188e9778795SPeter Avalos * characters or a CA/revocation marker.
1189e9778795SPeter Avalos */
1190ce74bacaSMatthew Dillon if (was_hashed || has_wild || l->marker != MRK_NONE) {
1191e9778795SPeter Avalos fprintf(ctx->out, "%s\n", l->line);
1192664f4763Szrj if (has_wild && !ctx->find_host) {
1193ce74bacaSMatthew Dillon logit("%s:%lu: ignoring host name "
1194e9778795SPeter Avalos "with wildcard: %.64s", l->path,
1195e9778795SPeter Avalos l->linenum, l->hosts);
119618de8d7fSPeter Avalos }
1197e9778795SPeter Avalos return 0;
1198e9778795SPeter Avalos }
1199e9778795SPeter Avalos /*
1200e9778795SPeter Avalos * Split any comma-separated hostnames from the host list,
1201e9778795SPeter Avalos * hash and store separately.
1202e9778795SPeter Avalos */
1203e9778795SPeter Avalos ohosts = hosts = xstrdup(l->hosts);
1204e9778795SPeter Avalos while ((cp = strsep(&hosts, ",")) != NULL && *cp != '\0') {
1205ce74bacaSMatthew Dillon lowercase(cp);
1206e9778795SPeter Avalos if ((hashed = host_hash(cp, NULL, 0)) == NULL)
1207e9778795SPeter Avalos fatal("hash_host failed");
1208e9778795SPeter Avalos fprintf(ctx->out, "%s %s\n", hashed, l->rawkey);
1209*ee116499SAntonio Huete Jimenez free(hashed);
1210e9778795SPeter Avalos ctx->has_unhashed = 1;
1211e9778795SPeter Avalos }
1212e9778795SPeter Avalos free(ohosts);
1213e9778795SPeter Avalos return 0;
1214e9778795SPeter Avalos case HKF_STATUS_INVALID:
1215e9778795SPeter Avalos /* Retain invalid lines, but mark file as invalid. */
1216e9778795SPeter Avalos ctx->invalid = 1;
1217ce74bacaSMatthew Dillon logit("%s:%lu: invalid line", l->path, l->linenum);
1218e9778795SPeter Avalos /* FALLTHROUGH */
1219e9778795SPeter Avalos default:
1220e9778795SPeter Avalos fprintf(ctx->out, "%s\n", l->line);
1221e9778795SPeter Avalos return 0;
1222e9778795SPeter Avalos }
1223e9778795SPeter Avalos /* NOTREACHED */
1224e9778795SPeter Avalos return -1;
1225e9778795SPeter Avalos }
1226e9778795SPeter Avalos
1227e9778795SPeter Avalos static int
known_hosts_find_delete(struct hostkey_foreach_line * l,void * _ctx)1228e9778795SPeter Avalos known_hosts_find_delete(struct hostkey_foreach_line *l, void *_ctx)
1229e9778795SPeter Avalos {
1230e9778795SPeter Avalos struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx;
1231e9778795SPeter Avalos enum sshkey_fp_rep rep;
1232e9778795SPeter Avalos int fptype;
12330cbfa66cSDaniel Fojt char *fp = NULL, *ra = NULL;
1234e9778795SPeter Avalos
1235e9778795SPeter Avalos fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash;
1236e9778795SPeter Avalos rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT;
1237e9778795SPeter Avalos
1238e9778795SPeter Avalos if (l->status == HKF_STATUS_MATCHED) {
1239664f4763Szrj if (ctx->delete_host) {
1240e9778795SPeter Avalos if (l->marker != MRK_NONE) {
1241e9778795SPeter Avalos /* Don't remove CA and revocation lines */
1242e9778795SPeter Avalos fprintf(ctx->out, "%s\n", l->line);
1243e9778795SPeter Avalos } else {
1244e9778795SPeter Avalos /*
1245e9778795SPeter Avalos * Hostname matches and has no CA/revoke
1246e9778795SPeter Avalos * marker, delete it by *not* writing the
1247e9778795SPeter Avalos * line to ctx->out.
1248e9778795SPeter Avalos */
1249e9778795SPeter Avalos ctx->found_key = 1;
1250e9778795SPeter Avalos if (!quiet)
1251ce74bacaSMatthew Dillon printf("# Host %s found: line %lu\n",
1252e9778795SPeter Avalos ctx->host, l->linenum);
1253e9778795SPeter Avalos }
1254e9778795SPeter Avalos return 0;
1255664f4763Szrj } else if (ctx->find_host) {
1256e9778795SPeter Avalos ctx->found_key = 1;
1257e9778795SPeter Avalos if (!quiet) {
1258ce74bacaSMatthew Dillon printf("# Host %s found: line %lu %s\n",
1259e9778795SPeter Avalos ctx->host,
1260e9778795SPeter Avalos l->linenum, l->marker == MRK_CA ? "CA" :
1261e9778795SPeter Avalos (l->marker == MRK_REVOKE ? "REVOKED" : ""));
1262e9778795SPeter Avalos }
1263664f4763Szrj if (ctx->hash_hosts)
1264e9778795SPeter Avalos known_hosts_hash(l, ctx);
1265e9778795SPeter Avalos else if (print_fingerprint) {
1266e9778795SPeter Avalos fp = sshkey_fingerprint(l->key, fptype, rep);
12670cbfa66cSDaniel Fojt ra = sshkey_fingerprint(l->key,
12680cbfa66cSDaniel Fojt fingerprint_hash, SSH_FP_RANDOMART);
12690cbfa66cSDaniel Fojt if (fp == NULL || ra == NULL)
127050a69bb5SSascha Wildner fatal_f("sshkey_fingerprint failed");
12710cbfa66cSDaniel Fojt mprintf("%s %s %s%s%s\n", ctx->host,
12720cbfa66cSDaniel Fojt sshkey_type(l->key), fp,
12730cbfa66cSDaniel Fojt l->comment[0] ? " " : "",
12740cbfa66cSDaniel Fojt l->comment);
12750cbfa66cSDaniel Fojt if (log_level_get() >= SYSLOG_LEVEL_VERBOSE)
12760cbfa66cSDaniel Fojt printf("%s\n", ra);
12770cbfa66cSDaniel Fojt free(ra);
1278e9778795SPeter Avalos free(fp);
1279e9778795SPeter Avalos } else
1280e9778795SPeter Avalos fprintf(ctx->out, "%s\n", l->line);
1281e9778795SPeter Avalos return 0;
1282e9778795SPeter Avalos }
1283664f4763Szrj } else if (ctx->delete_host) {
1284e9778795SPeter Avalos /* Retain non-matching hosts when deleting */
1285e9778795SPeter Avalos if (l->status == HKF_STATUS_INVALID) {
1286e9778795SPeter Avalos ctx->invalid = 1;
1287ce74bacaSMatthew Dillon logit("%s:%lu: invalid line", l->path, l->linenum);
1288e9778795SPeter Avalos }
1289e9778795SPeter Avalos fprintf(ctx->out, "%s\n", l->line);
1290e9778795SPeter Avalos }
1291e9778795SPeter Avalos return 0;
129218de8d7fSPeter Avalos }
129318de8d7fSPeter Avalos
129418de8d7fSPeter Avalos static void
do_known_hosts(struct passwd * pw,const char * name,int find_host,int delete_host,int hash_hosts)1295664f4763Szrj do_known_hosts(struct passwd *pw, const char *name, int find_host,
1296664f4763Szrj int delete_host, int hash_hosts)
129718de8d7fSPeter Avalos {
1298e9778795SPeter Avalos char *cp, tmp[PATH_MAX], old[PATH_MAX];
1299e9778795SPeter Avalos int r, fd, oerrno, inplace = 0;
1300e9778795SPeter Avalos struct known_hosts_ctx ctx;
1301e9778795SPeter Avalos u_int foreach_options;
130250a69bb5SSascha Wildner struct stat sb;
130318de8d7fSPeter Avalos
130418de8d7fSPeter Avalos if (!have_identity) {
130518de8d7fSPeter Avalos cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid);
130618de8d7fSPeter Avalos if (strlcpy(identity_file, cp, sizeof(identity_file)) >=
130718de8d7fSPeter Avalos sizeof(identity_file))
130818de8d7fSPeter Avalos fatal("Specified known hosts path too long");
130936e94dc5SPeter Avalos free(cp);
131018de8d7fSPeter Avalos have_identity = 1;
131118de8d7fSPeter Avalos }
131250a69bb5SSascha Wildner if (stat(identity_file, &sb) != 0)
131350a69bb5SSascha Wildner fatal("Cannot stat %s: %s", identity_file, strerror(errno));
131418de8d7fSPeter Avalos
1315e9778795SPeter Avalos memset(&ctx, 0, sizeof(ctx));
1316e9778795SPeter Avalos ctx.out = stdout;
1317e9778795SPeter Avalos ctx.host = name;
1318664f4763Szrj ctx.hash_hosts = hash_hosts;
1319664f4763Szrj ctx.find_host = find_host;
1320664f4763Szrj ctx.delete_host = delete_host;
1321e9778795SPeter Avalos
132218de8d7fSPeter Avalos /*
132318de8d7fSPeter Avalos * Find hosts goes to stdout, hash and deletions happen in-place
132418de8d7fSPeter Avalos * A corner case is ssh-keygen -HF foo, which should go to stdout
132518de8d7fSPeter Avalos */
132618de8d7fSPeter Avalos if (!find_host && (hash_hosts || delete_host)) {
132718de8d7fSPeter Avalos if (strlcpy(tmp, identity_file, sizeof(tmp)) >= sizeof(tmp) ||
132818de8d7fSPeter Avalos strlcat(tmp, ".XXXXXXXXXX", sizeof(tmp)) >= sizeof(tmp) ||
132918de8d7fSPeter Avalos strlcpy(old, identity_file, sizeof(old)) >= sizeof(old) ||
133018de8d7fSPeter Avalos strlcat(old, ".old", sizeof(old)) >= sizeof(old))
133118de8d7fSPeter Avalos fatal("known_hosts path too long");
133218de8d7fSPeter Avalos umask(077);
1333e9778795SPeter Avalos if ((fd = mkstemp(tmp)) == -1)
133418de8d7fSPeter Avalos fatal("mkstemp: %s", strerror(errno));
1335e9778795SPeter Avalos if ((ctx.out = fdopen(fd, "w")) == NULL) {
1336e9778795SPeter Avalos oerrno = errno;
133718de8d7fSPeter Avalos unlink(tmp);
1338e9778795SPeter Avalos fatal("fdopen: %s", strerror(oerrno));
133918de8d7fSPeter Avalos }
134050a69bb5SSascha Wildner fchmod(fd, sb.st_mode & 0644);
134118de8d7fSPeter Avalos inplace = 1;
134218de8d7fSPeter Avalos }
1343e9778795SPeter Avalos /* XXX support identity_file == "-" for stdin */
1344e9778795SPeter Avalos foreach_options = find_host ? HKF_WANT_MATCH : 0;
1345e9778795SPeter Avalos foreach_options |= print_fingerprint ? HKF_WANT_PARSE_KEY : 0;
1346664f4763Szrj if ((r = hostkeys_foreach(identity_file, (find_host || !hash_hosts) ?
1347664f4763Szrj known_hosts_find_delete : known_hosts_hash, &ctx, name, NULL,
134850a69bb5SSascha Wildner foreach_options, 0)) != 0) {
134918de8d7fSPeter Avalos if (inplace)
1350e9778795SPeter Avalos unlink(tmp);
135150a69bb5SSascha Wildner fatal_fr(r, "hostkeys_foreach");
135218de8d7fSPeter Avalos }
135318de8d7fSPeter Avalos
1354e9778795SPeter Avalos if (inplace)
1355e9778795SPeter Avalos fclose(ctx.out);
135618de8d7fSPeter Avalos
1357e9778795SPeter Avalos if (ctx.invalid) {
1358e9778795SPeter Avalos error("%s is not a valid known_hosts file.", identity_file);
135918de8d7fSPeter Avalos if (inplace) {
1360e9778795SPeter Avalos error("Not replacing existing known_hosts "
1361e9778795SPeter Avalos "file because of errors");
136218de8d7fSPeter Avalos unlink(tmp);
136318de8d7fSPeter Avalos }
136418de8d7fSPeter Avalos exit(1);
1365e9778795SPeter Avalos } else if (delete_host && !ctx.found_key) {
1366e9778795SPeter Avalos logit("Host %s not found in %s", name, identity_file);
1367e9778795SPeter Avalos if (inplace)
1368e9778795SPeter Avalos unlink(tmp);
1369e9778795SPeter Avalos } else if (inplace) {
137018de8d7fSPeter Avalos /* Backup existing file */
137118de8d7fSPeter Avalos if (unlink(old) == -1 && errno != ENOENT)
137218de8d7fSPeter Avalos fatal("unlink %.100s: %s", old, strerror(errno));
137318de8d7fSPeter Avalos if (link(identity_file, old) == -1)
137418de8d7fSPeter Avalos fatal("link %.100s to %.100s: %s", identity_file, old,
137518de8d7fSPeter Avalos strerror(errno));
137618de8d7fSPeter Avalos /* Move new one into place */
137718de8d7fSPeter Avalos if (rename(tmp, identity_file) == -1) {
137818de8d7fSPeter Avalos error("rename\"%s\" to \"%s\": %s", tmp, identity_file,
137918de8d7fSPeter Avalos strerror(errno));
138018de8d7fSPeter Avalos unlink(tmp);
138118de8d7fSPeter Avalos unlink(old);
138218de8d7fSPeter Avalos exit(1);
138318de8d7fSPeter Avalos }
138418de8d7fSPeter Avalos
1385e9778795SPeter Avalos printf("%s updated.\n", identity_file);
1386e9778795SPeter Avalos printf("Original contents retained as %s\n", old);
1387e9778795SPeter Avalos if (ctx.has_unhashed) {
1388e9778795SPeter Avalos logit("WARNING: %s contains unhashed entries", old);
1389e9778795SPeter Avalos logit("Delete this file to ensure privacy "
1390e9778795SPeter Avalos "of hostnames");
139118de8d7fSPeter Avalos }
139218de8d7fSPeter Avalos }
139318de8d7fSPeter Avalos
1394e9778795SPeter Avalos exit (find_host && !ctx.found_key);
139518de8d7fSPeter Avalos }
139618de8d7fSPeter Avalos
139718de8d7fSPeter Avalos /*
139818de8d7fSPeter Avalos * Perform changing a passphrase. The argument is the passwd structure
139918de8d7fSPeter Avalos * for the current user.
140018de8d7fSPeter Avalos */
140118de8d7fSPeter Avalos static void
do_change_passphrase(struct passwd * pw)140218de8d7fSPeter Avalos do_change_passphrase(struct passwd *pw)
140318de8d7fSPeter Avalos {
140418de8d7fSPeter Avalos char *comment;
140518de8d7fSPeter Avalos char *old_passphrase, *passphrase1, *passphrase2;
140618de8d7fSPeter Avalos struct stat st;
1407e9778795SPeter Avalos struct sshkey *private;
1408e9778795SPeter Avalos int r;
140918de8d7fSPeter Avalos
141018de8d7fSPeter Avalos if (!have_identity)
141118de8d7fSPeter Avalos ask_filename(pw, "Enter file in which the key is");
14120cbfa66cSDaniel Fojt if (stat(identity_file, &st) == -1)
1413e9778795SPeter Avalos fatal("%s: %s", identity_file, strerror(errno));
141418de8d7fSPeter Avalos /* Try to load the file with empty passphrase. */
1415e9778795SPeter Avalos r = sshkey_load_private(identity_file, "", &private, &comment);
1416e9778795SPeter Avalos if (r == SSH_ERR_KEY_WRONG_PASSPHRASE) {
141718de8d7fSPeter Avalos if (identity_passphrase)
141818de8d7fSPeter Avalos old_passphrase = xstrdup(identity_passphrase);
141918de8d7fSPeter Avalos else
142018de8d7fSPeter Avalos old_passphrase =
142118de8d7fSPeter Avalos read_passphrase("Enter old passphrase: ",
142218de8d7fSPeter Avalos RP_ALLOW_STDIN);
1423e9778795SPeter Avalos r = sshkey_load_private(identity_file, old_passphrase,
1424e9778795SPeter Avalos &private, &comment);
14250cbfa66cSDaniel Fojt freezero(old_passphrase, strlen(old_passphrase));
1426e9778795SPeter Avalos if (r != 0)
1427e9778795SPeter Avalos goto badkey;
1428e9778795SPeter Avalos } else if (r != 0) {
1429e9778795SPeter Avalos badkey:
143050a69bb5SSascha Wildner fatal_r(r, "Failed to load key %s", identity_file);
143118de8d7fSPeter Avalos }
1432e9778795SPeter Avalos if (comment)
1433ce74bacaSMatthew Dillon mprintf("Key has comment '%s'\n", comment);
143418de8d7fSPeter Avalos
143518de8d7fSPeter Avalos /* Ask the new passphrase (twice). */
143618de8d7fSPeter Avalos if (identity_new_passphrase) {
143718de8d7fSPeter Avalos passphrase1 = xstrdup(identity_new_passphrase);
143818de8d7fSPeter Avalos passphrase2 = NULL;
143918de8d7fSPeter Avalos } else {
144018de8d7fSPeter Avalos passphrase1 =
144118de8d7fSPeter Avalos read_passphrase("Enter new passphrase (empty for no "
144218de8d7fSPeter Avalos "passphrase): ", RP_ALLOW_STDIN);
144318de8d7fSPeter Avalos passphrase2 = read_passphrase("Enter same passphrase again: ",
144418de8d7fSPeter Avalos RP_ALLOW_STDIN);
144518de8d7fSPeter Avalos
144618de8d7fSPeter Avalos /* Verify that they are the same. */
144718de8d7fSPeter Avalos if (strcmp(passphrase1, passphrase2) != 0) {
144836e94dc5SPeter Avalos explicit_bzero(passphrase1, strlen(passphrase1));
144936e94dc5SPeter Avalos explicit_bzero(passphrase2, strlen(passphrase2));
145036e94dc5SPeter Avalos free(passphrase1);
145136e94dc5SPeter Avalos free(passphrase2);
145218de8d7fSPeter Avalos printf("Pass phrases do not match. Try again.\n");
145318de8d7fSPeter Avalos exit(1);
145418de8d7fSPeter Avalos }
145518de8d7fSPeter Avalos /* Destroy the other copy. */
14560cbfa66cSDaniel Fojt freezero(passphrase2, strlen(passphrase2));
145718de8d7fSPeter Avalos }
145818de8d7fSPeter Avalos
145918de8d7fSPeter Avalos /* Save the file using the new passphrase. */
1460e9778795SPeter Avalos if ((r = sshkey_save_private(private, identity_file, passphrase1,
14610cbfa66cSDaniel Fojt comment, private_key_format, openssh_format_cipher, rounds)) != 0) {
146250a69bb5SSascha Wildner error_r(r, "Saving key \"%s\" failed", identity_file);
14630cbfa66cSDaniel Fojt freezero(passphrase1, strlen(passphrase1));
1464e9778795SPeter Avalos sshkey_free(private);
146536e94dc5SPeter Avalos free(comment);
146618de8d7fSPeter Avalos exit(1);
146718de8d7fSPeter Avalos }
146818de8d7fSPeter Avalos /* Destroy the passphrase and the copy of the key in memory. */
14690cbfa66cSDaniel Fojt freezero(passphrase1, strlen(passphrase1));
1470e9778795SPeter Avalos sshkey_free(private); /* Destroys contents */
147136e94dc5SPeter Avalos free(comment);
147218de8d7fSPeter Avalos
147318de8d7fSPeter Avalos printf("Your identification has been saved with the new passphrase.\n");
147418de8d7fSPeter Avalos exit(0);
147518de8d7fSPeter Avalos }
147618de8d7fSPeter Avalos
147718de8d7fSPeter Avalos /*
147818de8d7fSPeter Avalos * Print the SSHFP RR.
147918de8d7fSPeter Avalos */
148018de8d7fSPeter Avalos static int
do_print_resource_record(struct passwd * pw,char * fname,char * hname,int print_generic)1481664f4763Szrj do_print_resource_record(struct passwd *pw, char *fname, char *hname,
1482664f4763Szrj int print_generic)
148318de8d7fSPeter Avalos {
1484e9778795SPeter Avalos struct sshkey *public;
148518de8d7fSPeter Avalos char *comment = NULL;
148618de8d7fSPeter Avalos struct stat st;
1487e9778795SPeter Avalos int r;
148818de8d7fSPeter Avalos
148918de8d7fSPeter Avalos if (fname == NULL)
149050a69bb5SSascha Wildner fatal_f("no filename");
14910cbfa66cSDaniel Fojt if (stat(fname, &st) == -1) {
149218de8d7fSPeter Avalos if (errno == ENOENT)
149318de8d7fSPeter Avalos return 0;
1494e9778795SPeter Avalos fatal("%s: %s", fname, strerror(errno));
149518de8d7fSPeter Avalos }
1496e9778795SPeter Avalos if ((r = sshkey_load_public(fname, &public, &comment)) != 0)
149750a69bb5SSascha Wildner fatal_r(r, "Failed to read v2 public key from \"%s\"", fname);
149818de8d7fSPeter Avalos export_dns_rr(hname, public, stdout, print_generic);
1499e9778795SPeter Avalos sshkey_free(public);
150036e94dc5SPeter Avalos free(comment);
150118de8d7fSPeter Avalos return 1;
150218de8d7fSPeter Avalos }
150318de8d7fSPeter Avalos
150418de8d7fSPeter Avalos /*
150518de8d7fSPeter Avalos * Change the comment of a private key file.
150618de8d7fSPeter Avalos */
150718de8d7fSPeter Avalos static void
do_change_comment(struct passwd * pw,const char * identity_comment)1508664f4763Szrj do_change_comment(struct passwd *pw, const char *identity_comment)
150918de8d7fSPeter Avalos {
151018de8d7fSPeter Avalos char new_comment[1024], *comment, *passphrase;
1511e9778795SPeter Avalos struct sshkey *private;
1512e9778795SPeter Avalos struct sshkey *public;
151318de8d7fSPeter Avalos struct stat st;
15140cbfa66cSDaniel Fojt int r;
151518de8d7fSPeter Avalos
151618de8d7fSPeter Avalos if (!have_identity)
151718de8d7fSPeter Avalos ask_filename(pw, "Enter file in which the key is");
15180cbfa66cSDaniel Fojt if (stat(identity_file, &st) == -1)
1519e9778795SPeter Avalos fatal("%s: %s", identity_file, strerror(errno));
1520e9778795SPeter Avalos if ((r = sshkey_load_private(identity_file, "",
1521e9778795SPeter Avalos &private, &comment)) == 0)
1522e9778795SPeter Avalos passphrase = xstrdup("");
1523e9778795SPeter Avalos else if (r != SSH_ERR_KEY_WRONG_PASSPHRASE)
152450a69bb5SSascha Wildner fatal_r(r, "Cannot load private key \"%s\"", identity_file);
1525e9778795SPeter Avalos else {
152618de8d7fSPeter Avalos if (identity_passphrase)
152718de8d7fSPeter Avalos passphrase = xstrdup(identity_passphrase);
152818de8d7fSPeter Avalos else if (identity_new_passphrase)
152918de8d7fSPeter Avalos passphrase = xstrdup(identity_new_passphrase);
153018de8d7fSPeter Avalos else
153118de8d7fSPeter Avalos passphrase = read_passphrase("Enter passphrase: ",
153218de8d7fSPeter Avalos RP_ALLOW_STDIN);
153318de8d7fSPeter Avalos /* Try to load using the passphrase. */
1534e9778795SPeter Avalos if ((r = sshkey_load_private(identity_file, passphrase,
1535e9778795SPeter Avalos &private, &comment)) != 0) {
15360cbfa66cSDaniel Fojt freezero(passphrase, strlen(passphrase));
153750a69bb5SSascha Wildner fatal_r(r, "Cannot load private key \"%s\"",
153850a69bb5SSascha Wildner identity_file);
153918de8d7fSPeter Avalos }
154018de8d7fSPeter Avalos }
1541e9778795SPeter Avalos
1542664f4763Szrj if (private->type != KEY_ED25519 && private->type != KEY_XMSS &&
15430cbfa66cSDaniel Fojt private_key_format != SSHKEY_PRIVATE_OPENSSH) {
1544ce74bacaSMatthew Dillon error("Comments are only supported for keys stored in "
1545e9778795SPeter Avalos "the new format (-o).");
1546e9778795SPeter Avalos explicit_bzero(passphrase, strlen(passphrase));
1547e9778795SPeter Avalos sshkey_free(private);
154818de8d7fSPeter Avalos exit(1);
154918de8d7fSPeter Avalos }
1550ce74bacaSMatthew Dillon if (comment)
15510cbfa66cSDaniel Fojt printf("Old comment: %s\n", comment);
1552ce74bacaSMatthew Dillon else
15530cbfa66cSDaniel Fojt printf("No existing comment\n");
155418de8d7fSPeter Avalos
155518de8d7fSPeter Avalos if (identity_comment) {
155618de8d7fSPeter Avalos strlcpy(new_comment, identity_comment, sizeof(new_comment));
155718de8d7fSPeter Avalos } else {
15580cbfa66cSDaniel Fojt printf("New comment: ");
155918de8d7fSPeter Avalos fflush(stdout);
156018de8d7fSPeter Avalos if (!fgets(new_comment, sizeof(new_comment), stdin)) {
156136e94dc5SPeter Avalos explicit_bzero(passphrase, strlen(passphrase));
1562e9778795SPeter Avalos sshkey_free(private);
156318de8d7fSPeter Avalos exit(1);
156418de8d7fSPeter Avalos }
156518de8d7fSPeter Avalos new_comment[strcspn(new_comment, "\n")] = '\0';
156618de8d7fSPeter Avalos }
15670cbfa66cSDaniel Fojt if (comment != NULL && strcmp(comment, new_comment) == 0) {
15680cbfa66cSDaniel Fojt printf("No change to comment\n");
15690cbfa66cSDaniel Fojt free(passphrase);
15700cbfa66cSDaniel Fojt sshkey_free(private);
15710cbfa66cSDaniel Fojt free(comment);
15720cbfa66cSDaniel Fojt exit(0);
15730cbfa66cSDaniel Fojt }
157418de8d7fSPeter Avalos
157518de8d7fSPeter Avalos /* Save the file using the new passphrase. */
1576e9778795SPeter Avalos if ((r = sshkey_save_private(private, identity_file, passphrase,
15770cbfa66cSDaniel Fojt new_comment, private_key_format, openssh_format_cipher,
15780cbfa66cSDaniel Fojt rounds)) != 0) {
157950a69bb5SSascha Wildner error_r(r, "Saving key \"%s\" failed", identity_file);
15800cbfa66cSDaniel Fojt freezero(passphrase, strlen(passphrase));
1581e9778795SPeter Avalos sshkey_free(private);
158236e94dc5SPeter Avalos free(comment);
158318de8d7fSPeter Avalos exit(1);
158418de8d7fSPeter Avalos }
15850cbfa66cSDaniel Fojt freezero(passphrase, strlen(passphrase));
1586e9778795SPeter Avalos if ((r = sshkey_from_private(private, &public)) != 0)
158750a69bb5SSascha Wildner fatal_fr(r, "sshkey_from_private");
1588e9778795SPeter Avalos sshkey_free(private);
158918de8d7fSPeter Avalos
159018de8d7fSPeter Avalos strlcat(identity_file, ".pub", sizeof(identity_file));
159150a69bb5SSascha Wildner if ((r = sshkey_save_public(public, identity_file, new_comment)) != 0)
159250a69bb5SSascha Wildner fatal_r(r, "Unable to save public key to %s", identity_file);
1593e9778795SPeter Avalos sshkey_free(public);
159436e94dc5SPeter Avalos free(comment);
159518de8d7fSPeter Avalos
15960cbfa66cSDaniel Fojt if (strlen(new_comment) > 0)
15970cbfa66cSDaniel Fojt printf("Comment '%s' applied\n", new_comment);
15980cbfa66cSDaniel Fojt else
15990cbfa66cSDaniel Fojt printf("Comment removed\n");
16000cbfa66cSDaniel Fojt
160118de8d7fSPeter Avalos exit(0);
160218de8d7fSPeter Avalos }
160318de8d7fSPeter Avalos
1604856ea928SPeter Avalos static void
cert_ext_add(const char * key,const char * value,int iscrit)160550a69bb5SSascha Wildner cert_ext_add(const char *key, const char *value, int iscrit)
1606856ea928SPeter Avalos {
160750a69bb5SSascha Wildner cert_ext = xreallocarray(cert_ext, ncert_ext + 1, sizeof(*cert_ext));
160850a69bb5SSascha Wildner cert_ext[ncert_ext].key = xstrdup(key);
160950a69bb5SSascha Wildner cert_ext[ncert_ext].val = value == NULL ? NULL : xstrdup(value);
161050a69bb5SSascha Wildner cert_ext[ncert_ext].crit = iscrit;
161150a69bb5SSascha Wildner ncert_ext++;
1612856ea928SPeter Avalos }
1613856ea928SPeter Avalos
161450a69bb5SSascha Wildner /* qsort(3) comparison function for certificate extensions */
161550a69bb5SSascha Wildner static int
cert_ext_cmp(const void * _a,const void * _b)161650a69bb5SSascha Wildner cert_ext_cmp(const void *_a, const void *_b)
1617856ea928SPeter Avalos {
161850a69bb5SSascha Wildner const struct cert_ext *a = (const struct cert_ext *)_a;
161950a69bb5SSascha Wildner const struct cert_ext *b = (const struct cert_ext *)_b;
1620e9778795SPeter Avalos int r;
1621856ea928SPeter Avalos
162250a69bb5SSascha Wildner if (a->crit != b->crit)
162350a69bb5SSascha Wildner return (a->crit < b->crit) ? -1 : 1;
162450a69bb5SSascha Wildner if ((r = strcmp(a->key, b->key)) != 0)
162550a69bb5SSascha Wildner return r;
162650a69bb5SSascha Wildner if ((a->val == NULL) != (b->val == NULL))
162750a69bb5SSascha Wildner return (a->val == NULL) ? -1 : 1;
162850a69bb5SSascha Wildner if (a->val != NULL && (r = strcmp(a->val, b->val)) != 0)
162950a69bb5SSascha Wildner return r;
163050a69bb5SSascha Wildner return 0;
1631856ea928SPeter Avalos }
1632856ea928SPeter Avalos
1633856ea928SPeter Avalos #define OPTIONS_CRITICAL 1
1634856ea928SPeter Avalos #define OPTIONS_EXTENSIONS 2
1635856ea928SPeter Avalos static void
prepare_options_buf(struct sshbuf * c,int which)1636e9778795SPeter Avalos prepare_options_buf(struct sshbuf *c, int which)
1637856ea928SPeter Avalos {
163850a69bb5SSascha Wildner struct sshbuf *b;
1639ce74bacaSMatthew Dillon size_t i;
164050a69bb5SSascha Wildner int r;
164150a69bb5SSascha Wildner const struct cert_ext *ext;
1642ce74bacaSMatthew Dillon
164350a69bb5SSascha Wildner if ((b = sshbuf_new()) == NULL)
164450a69bb5SSascha Wildner fatal_f("sshbuf_new failed");
1645e9778795SPeter Avalos sshbuf_reset(c);
164650a69bb5SSascha Wildner for (i = 0; i < ncert_ext; i++) {
164750a69bb5SSascha Wildner ext = &cert_ext[i];
164850a69bb5SSascha Wildner if ((ext->crit && (which & OPTIONS_EXTENSIONS)) ||
164950a69bb5SSascha Wildner (!ext->crit && (which & OPTIONS_CRITICAL)))
1650ce74bacaSMatthew Dillon continue;
165150a69bb5SSascha Wildner if (ext->val == NULL) {
165250a69bb5SSascha Wildner /* flag option */
165350a69bb5SSascha Wildner debug3_f("%s", ext->key);
165450a69bb5SSascha Wildner if ((r = sshbuf_put_cstring(c, ext->key)) != 0 ||
165550a69bb5SSascha Wildner (r = sshbuf_put_string(c, NULL, 0)) != 0)
165650a69bb5SSascha Wildner fatal_fr(r, "prepare flag");
165750a69bb5SSascha Wildner } else {
165850a69bb5SSascha Wildner /* key/value option */
165950a69bb5SSascha Wildner debug3_f("%s=%s", ext->key, ext->val);
166050a69bb5SSascha Wildner sshbuf_reset(b);
166150a69bb5SSascha Wildner if ((r = sshbuf_put_cstring(c, ext->key)) != 0 ||
166250a69bb5SSascha Wildner (r = sshbuf_put_cstring(b, ext->val)) != 0 ||
166350a69bb5SSascha Wildner (r = sshbuf_put_stringb(c, b)) != 0)
166450a69bb5SSascha Wildner fatal_fr(r, "prepare k/v");
1665ce74bacaSMatthew Dillon }
1666ce74bacaSMatthew Dillon }
166750a69bb5SSascha Wildner sshbuf_free(b);
166850a69bb5SSascha Wildner }
166950a69bb5SSascha Wildner
167050a69bb5SSascha Wildner static void
finalise_cert_exts(void)167150a69bb5SSascha Wildner finalise_cert_exts(void)
167250a69bb5SSascha Wildner {
167350a69bb5SSascha Wildner /* critical options */
167450a69bb5SSascha Wildner if (certflags_command != NULL)
167550a69bb5SSascha Wildner cert_ext_add("force-command", certflags_command, 1);
167650a69bb5SSascha Wildner if (certflags_src_addr != NULL)
167750a69bb5SSascha Wildner cert_ext_add("source-address", certflags_src_addr, 1);
1678*ee116499SAntonio Huete Jimenez if ((certflags_flags & CERTOPT_REQUIRE_VERIFY) != 0)
1679*ee116499SAntonio Huete Jimenez cert_ext_add("verify-required", NULL, 1);
168050a69bb5SSascha Wildner /* extensions */
168150a69bb5SSascha Wildner if ((certflags_flags & CERTOPT_X_FWD) != 0)
168250a69bb5SSascha Wildner cert_ext_add("permit-X11-forwarding", NULL, 0);
168350a69bb5SSascha Wildner if ((certflags_flags & CERTOPT_AGENT_FWD) != 0)
168450a69bb5SSascha Wildner cert_ext_add("permit-agent-forwarding", NULL, 0);
168550a69bb5SSascha Wildner if ((certflags_flags & CERTOPT_PORT_FWD) != 0)
168650a69bb5SSascha Wildner cert_ext_add("permit-port-forwarding", NULL, 0);
168750a69bb5SSascha Wildner if ((certflags_flags & CERTOPT_PTY) != 0)
168850a69bb5SSascha Wildner cert_ext_add("permit-pty", NULL, 0);
168950a69bb5SSascha Wildner if ((certflags_flags & CERTOPT_USER_RC) != 0)
169050a69bb5SSascha Wildner cert_ext_add("permit-user-rc", NULL, 0);
169150a69bb5SSascha Wildner if ((certflags_flags & CERTOPT_NO_REQUIRE_USER_PRESENCE) != 0)
169250a69bb5SSascha Wildner cert_ext_add("no-touch-required", NULL, 0);
169350a69bb5SSascha Wildner /* order lexically by key */
169450a69bb5SSascha Wildner if (ncert_ext > 0)
169550a69bb5SSascha Wildner qsort(cert_ext, ncert_ext, sizeof(*cert_ext), cert_ext_cmp);
1696856ea928SPeter Avalos }
1697856ea928SPeter Avalos
1698e9778795SPeter Avalos static struct sshkey *
load_pkcs11_key(char * path)1699856ea928SPeter Avalos load_pkcs11_key(char *path)
1700856ea928SPeter Avalos {
1701856ea928SPeter Avalos #ifdef ENABLE_PKCS11
1702e9778795SPeter Avalos struct sshkey **keys = NULL, *public, *private = NULL;
1703e9778795SPeter Avalos int r, i, nkeys;
1704856ea928SPeter Avalos
1705e9778795SPeter Avalos if ((r = sshkey_load_public(path, &public, NULL)) != 0)
170650a69bb5SSascha Wildner fatal_r(r, "Couldn't load CA public key \"%s\"", path);
1707856ea928SPeter Avalos
17080cbfa66cSDaniel Fojt nkeys = pkcs11_add_provider(pkcs11provider, identity_passphrase,
17090cbfa66cSDaniel Fojt &keys, NULL);
171050a69bb5SSascha Wildner debug3_f("%d keys", nkeys);
1711856ea928SPeter Avalos if (nkeys <= 0)
1712856ea928SPeter Avalos fatal("cannot read public key from pkcs11");
1713856ea928SPeter Avalos for (i = 0; i < nkeys; i++) {
1714e9778795SPeter Avalos if (sshkey_equal_public(public, keys[i])) {
1715856ea928SPeter Avalos private = keys[i];
1716856ea928SPeter Avalos continue;
1717856ea928SPeter Avalos }
1718e9778795SPeter Avalos sshkey_free(keys[i]);
1719856ea928SPeter Avalos }
172036e94dc5SPeter Avalos free(keys);
1721e9778795SPeter Avalos sshkey_free(public);
1722856ea928SPeter Avalos return private;
1723856ea928SPeter Avalos #else
1724856ea928SPeter Avalos fatal("no pkcs11 support");
1725856ea928SPeter Avalos #endif /* ENABLE_PKCS11 */
1726856ea928SPeter Avalos }
1727856ea928SPeter Avalos
1728ce74bacaSMatthew Dillon /* Signer for sshkey_certify_custom that uses the agent */
1729ce74bacaSMatthew Dillon static int
agent_signer(struct sshkey * key,u_char ** sigp,size_t * lenp,const u_char * data,size_t datalen,const char * alg,const char * provider,const char * pin,u_int compat,void * ctx)17300cbfa66cSDaniel Fojt agent_signer(struct sshkey *key, u_char **sigp, size_t *lenp,
1731ce74bacaSMatthew Dillon const u_char *data, size_t datalen,
173250a69bb5SSascha Wildner const char *alg, const char *provider, const char *pin,
173350a69bb5SSascha Wildner u_int compat, void *ctx)
1734ce74bacaSMatthew Dillon {
1735ce74bacaSMatthew Dillon int *agent_fdp = (int *)ctx;
1736ce74bacaSMatthew Dillon
1737ce74bacaSMatthew Dillon return ssh_agent_sign(*agent_fdp, key, sigp, lenp,
1738ce74bacaSMatthew Dillon data, datalen, alg, compat);
1739ce74bacaSMatthew Dillon }
1740ce74bacaSMatthew Dillon
1741856ea928SPeter Avalos static void
do_ca_sign(struct passwd * pw,const char * ca_key_path,int prefer_agent,unsigned long long cert_serial,int cert_serial_autoinc,int argc,char ** argv)1742664f4763Szrj do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent,
1743664f4763Szrj unsigned long long cert_serial, int cert_serial_autoinc,
1744664f4763Szrj int argc, char **argv)
1745856ea928SPeter Avalos {
17460cbfa66cSDaniel Fojt int r, i, found, agent_fd = -1;
1747856ea928SPeter Avalos u_int n;
1748e9778795SPeter Avalos struct sshkey *ca, *public;
17490cbfa66cSDaniel Fojt char valid[64], *otmp, *tmp, *cp, *out, *comment;
175050a69bb5SSascha Wildner char *ca_fp = NULL, **plist = NULL, *pin = NULL;
1751ce74bacaSMatthew Dillon struct ssh_identitylist *agent_ids;
1752ce74bacaSMatthew Dillon size_t j;
17530cbfa66cSDaniel Fojt struct notifier_ctx *notifier = NULL;
1754856ea928SPeter Avalos
175536e94dc5SPeter Avalos #ifdef ENABLE_PKCS11
1756856ea928SPeter Avalos pkcs11_init(1);
175736e94dc5SPeter Avalos #endif
1758856ea928SPeter Avalos tmp = tilde_expand_filename(ca_key_path, pw->pw_uid);
1759856ea928SPeter Avalos if (pkcs11provider != NULL) {
1760ce74bacaSMatthew Dillon /* If a PKCS#11 token was specified then try to use it */
1761856ea928SPeter Avalos if ((ca = load_pkcs11_key(tmp)) == NULL)
1762856ea928SPeter Avalos fatal("No PKCS#11 key matching %s found", ca_key_path);
1763ce74bacaSMatthew Dillon } else if (prefer_agent) {
1764ce74bacaSMatthew Dillon /*
1765ce74bacaSMatthew Dillon * Agent signature requested. Try to use agent after making
1766ce74bacaSMatthew Dillon * sure the public key specified is actually present in the
1767ce74bacaSMatthew Dillon * agent.
1768ce74bacaSMatthew Dillon */
1769ce74bacaSMatthew Dillon if ((r = sshkey_load_public(tmp, &ca, NULL)) != 0)
177050a69bb5SSascha Wildner fatal_r(r, "Cannot load CA public key %s", tmp);
1771ce74bacaSMatthew Dillon if ((r = ssh_get_authentication_socket(&agent_fd)) != 0)
177250a69bb5SSascha Wildner fatal_r(r, "Cannot use public key for CA signature");
1773ce74bacaSMatthew Dillon if ((r = ssh_fetch_identitylist(agent_fd, &agent_ids)) != 0)
177450a69bb5SSascha Wildner fatal_r(r, "Retrieve agent key list");
1775ce74bacaSMatthew Dillon found = 0;
1776ce74bacaSMatthew Dillon for (j = 0; j < agent_ids->nkeys; j++) {
1777ce74bacaSMatthew Dillon if (sshkey_equal(ca, agent_ids->keys[j])) {
1778ce74bacaSMatthew Dillon found = 1;
1779ce74bacaSMatthew Dillon break;
1780ce74bacaSMatthew Dillon }
1781ce74bacaSMatthew Dillon }
1782ce74bacaSMatthew Dillon if (!found)
1783ce74bacaSMatthew Dillon fatal("CA key %s not found in agent", tmp);
1784ce74bacaSMatthew Dillon ssh_free_identitylist(agent_ids);
1785ce74bacaSMatthew Dillon ca->flags |= SSHKEY_FLAG_EXT;
1786ce74bacaSMatthew Dillon } else {
1787ce74bacaSMatthew Dillon /* CA key is assumed to be a private key on the filesystem */
17880cbfa66cSDaniel Fojt ca = load_identity(tmp, NULL);
178950a69bb5SSascha Wildner if (sshkey_is_sk(ca) &&
179050a69bb5SSascha Wildner (ca->sk_flags & SSH_SK_USER_VERIFICATION_REQD)) {
179150a69bb5SSascha Wildner if ((pin = read_passphrase("Enter PIN for CA key: ",
179250a69bb5SSascha Wildner RP_ALLOW_STDIN)) == NULL)
179350a69bb5SSascha Wildner fatal_f("couldn't read PIN");
179450a69bb5SSascha Wildner }
1795ce74bacaSMatthew Dillon }
179636e94dc5SPeter Avalos free(tmp);
1797856ea928SPeter Avalos
17980cbfa66cSDaniel Fojt if (key_type_name != NULL) {
17990cbfa66cSDaniel Fojt if (sshkey_type_from_name(key_type_name) != ca->type) {
1800e9778795SPeter Avalos fatal("CA key type %s doesn't match specified %s",
1801e9778795SPeter Avalos sshkey_ssh_name(ca), key_type_name);
1802e9778795SPeter Avalos }
18030cbfa66cSDaniel Fojt } else if (ca->type == KEY_RSA) {
18040cbfa66cSDaniel Fojt /* Default to a good signature algorithm */
18050cbfa66cSDaniel Fojt key_type_name = "rsa-sha2-512";
18060cbfa66cSDaniel Fojt }
18070cbfa66cSDaniel Fojt ca_fp = sshkey_fingerprint(ca, fingerprint_hash, SSH_FP_DEFAULT);
1808e9778795SPeter Avalos
180950a69bb5SSascha Wildner finalise_cert_exts();
1810856ea928SPeter Avalos for (i = 0; i < argc; i++) {
1811856ea928SPeter Avalos /* Split list of principals */
1812856ea928SPeter Avalos n = 0;
1813856ea928SPeter Avalos if (cert_principals != NULL) {
1814856ea928SPeter Avalos otmp = tmp = xstrdup(cert_principals);
1815856ea928SPeter Avalos plist = NULL;
1816856ea928SPeter Avalos for (; (cp = strsep(&tmp, ",")) != NULL; n++) {
1817e9778795SPeter Avalos plist = xreallocarray(plist, n + 1, sizeof(*plist));
1818856ea928SPeter Avalos if (*(plist[n] = xstrdup(cp)) == '\0')
1819856ea928SPeter Avalos fatal("Empty principal name");
1820856ea928SPeter Avalos }
182136e94dc5SPeter Avalos free(otmp);
1822856ea928SPeter Avalos }
1823664f4763Szrj if (n > SSHKEY_CERT_MAX_PRINCIPALS)
1824664f4763Szrj fatal("Too many certificate principals specified");
1825856ea928SPeter Avalos
1826856ea928SPeter Avalos tmp = tilde_expand_filename(argv[i], pw->pw_uid);
1827e9778795SPeter Avalos if ((r = sshkey_load_public(tmp, &public, &comment)) != 0)
182850a69bb5SSascha Wildner fatal_r(r, "load pubkey \"%s\"", tmp);
18290cbfa66cSDaniel Fojt if (sshkey_is_cert(public))
183050a69bb5SSascha Wildner fatal_f("key \"%s\" type %s cannot be certified",
183150a69bb5SSascha Wildner tmp, sshkey_type(public));
1832856ea928SPeter Avalos
1833856ea928SPeter Avalos /* Prepare certificate to sign */
1834e9778795SPeter Avalos if ((r = sshkey_to_certified(public)) != 0)
183550a69bb5SSascha Wildner fatal_r(r, "Could not upgrade key %s to certificate", tmp);
1836856ea928SPeter Avalos public->cert->type = cert_key_type;
1837856ea928SPeter Avalos public->cert->serial = (u_int64_t)cert_serial;
1838856ea928SPeter Avalos public->cert->key_id = xstrdup(cert_key_id);
1839856ea928SPeter Avalos public->cert->nprincipals = n;
1840856ea928SPeter Avalos public->cert->principals = plist;
1841856ea928SPeter Avalos public->cert->valid_after = cert_valid_from;
1842856ea928SPeter Avalos public->cert->valid_before = cert_valid_to;
1843e9778795SPeter Avalos prepare_options_buf(public->cert->critical, OPTIONS_CRITICAL);
184436e94dc5SPeter Avalos prepare_options_buf(public->cert->extensions,
1845856ea928SPeter Avalos OPTIONS_EXTENSIONS);
1846e9778795SPeter Avalos if ((r = sshkey_from_private(ca,
1847e9778795SPeter Avalos &public->cert->signature_key)) != 0)
184850a69bb5SSascha Wildner fatal_r(r, "sshkey_from_private (ca key)");
1849856ea928SPeter Avalos
1850ce74bacaSMatthew Dillon if (agent_fd != -1 && (ca->flags & SSHKEY_FLAG_EXT) != 0) {
1851ce74bacaSMatthew Dillon if ((r = sshkey_certify_custom(public, ca,
185250a69bb5SSascha Wildner key_type_name, sk_provider, NULL, agent_signer,
18530cbfa66cSDaniel Fojt &agent_fd)) != 0)
185450a69bb5SSascha Wildner fatal_r(r, "Couldn't certify %s via agent", tmp);
1855ce74bacaSMatthew Dillon } else {
18560cbfa66cSDaniel Fojt if (sshkey_is_sk(ca) &&
18570cbfa66cSDaniel Fojt (ca->sk_flags & SSH_SK_USER_PRESENCE_REQD)) {
18580cbfa66cSDaniel Fojt notifier = notify_start(0,
18590cbfa66cSDaniel Fojt "Confirm user presence for key %s %s",
18600cbfa66cSDaniel Fojt sshkey_type(ca), ca_fp);
18610cbfa66cSDaniel Fojt }
18620cbfa66cSDaniel Fojt r = sshkey_certify(public, ca, key_type_name,
186350a69bb5SSascha Wildner sk_provider, pin);
186450a69bb5SSascha Wildner notify_complete(notifier, "User presence confirmed");
18650cbfa66cSDaniel Fojt if (r != 0)
186650a69bb5SSascha Wildner fatal_r(r, "Couldn't certify key %s", tmp);
1867ce74bacaSMatthew Dillon }
1868856ea928SPeter Avalos
1869856ea928SPeter Avalos if ((cp = strrchr(tmp, '.')) != NULL && strcmp(cp, ".pub") == 0)
1870856ea928SPeter Avalos *cp = '\0';
1871856ea928SPeter Avalos xasprintf(&out, "%s-cert.pub", tmp);
187236e94dc5SPeter Avalos free(tmp);
1873856ea928SPeter Avalos
18740cbfa66cSDaniel Fojt if ((r = sshkey_save_public(public, out, comment)) != 0) {
187550a69bb5SSascha Wildner fatal_r(r, "Unable to save public key to %s",
187650a69bb5SSascha Wildner identity_file);
18770cbfa66cSDaniel Fojt }
1878856ea928SPeter Avalos
1879856ea928SPeter Avalos if (!quiet) {
1880e9778795SPeter Avalos sshkey_format_cert_validity(public->cert,
1881e9778795SPeter Avalos valid, sizeof(valid));
1882856ea928SPeter Avalos logit("Signed %s key %s: id \"%s\" serial %llu%s%s "
1883e9778795SPeter Avalos "valid %s", sshkey_cert_type(public),
18849f304aafSPeter Avalos out, public->cert->key_id,
18859f304aafSPeter Avalos (unsigned long long)public->cert->serial,
1886856ea928SPeter Avalos cert_principals != NULL ? " for " : "",
1887856ea928SPeter Avalos cert_principals != NULL ? cert_principals : "",
1888e9778795SPeter Avalos valid);
1889856ea928SPeter Avalos }
1890856ea928SPeter Avalos
1891e9778795SPeter Avalos sshkey_free(public);
189236e94dc5SPeter Avalos free(out);
1893664f4763Szrj if (cert_serial_autoinc)
1894664f4763Szrj cert_serial++;
1895856ea928SPeter Avalos }
189650a69bb5SSascha Wildner if (pin != NULL)
189750a69bb5SSascha Wildner freezero(pin, strlen(pin));
18980cbfa66cSDaniel Fojt free(ca_fp);
189936e94dc5SPeter Avalos #ifdef ENABLE_PKCS11
1900856ea928SPeter Avalos pkcs11_terminate();
190136e94dc5SPeter Avalos #endif
1902856ea928SPeter Avalos exit(0);
1903856ea928SPeter Avalos }
1904856ea928SPeter Avalos
1905856ea928SPeter Avalos static u_int64_t
parse_relative_time(const char * s,time_t now)1906856ea928SPeter Avalos parse_relative_time(const char *s, time_t now)
1907856ea928SPeter Avalos {
1908856ea928SPeter Avalos int64_t mul, secs;
1909856ea928SPeter Avalos
1910856ea928SPeter Avalos mul = *s == '-' ? -1 : 1;
1911856ea928SPeter Avalos
1912856ea928SPeter Avalos if ((secs = convtime(s + 1)) == -1)
1913856ea928SPeter Avalos fatal("Invalid relative certificate time %s", s);
1914856ea928SPeter Avalos if (mul == -1 && secs > now)
1915856ea928SPeter Avalos fatal("Certificate time %s cannot be represented", s);
1916856ea928SPeter Avalos return now + (u_int64_t)(secs * mul);
1917856ea928SPeter Avalos }
1918856ea928SPeter Avalos
1919856ea928SPeter Avalos static void
parse_hex_u64(const char * s,uint64_t * up)1920*ee116499SAntonio Huete Jimenez parse_hex_u64(const char *s, uint64_t *up)
1921*ee116499SAntonio Huete Jimenez {
1922*ee116499SAntonio Huete Jimenez char *ep;
1923*ee116499SAntonio Huete Jimenez unsigned long long ull;
1924*ee116499SAntonio Huete Jimenez
1925*ee116499SAntonio Huete Jimenez errno = 0;
1926*ee116499SAntonio Huete Jimenez ull = strtoull(s, &ep, 16);
1927*ee116499SAntonio Huete Jimenez if (*s == '\0' || *ep != '\0')
1928*ee116499SAntonio Huete Jimenez fatal("Invalid certificate time: not a number");
1929*ee116499SAntonio Huete Jimenez if (errno == ERANGE && ull == ULONG_MAX)
1930*ee116499SAntonio Huete Jimenez fatal_fr(SSH_ERR_SYSTEM_ERROR, "Invalid certificate time");
1931*ee116499SAntonio Huete Jimenez *up = (uint64_t)ull;
1932*ee116499SAntonio Huete Jimenez }
1933*ee116499SAntonio Huete Jimenez
1934*ee116499SAntonio Huete Jimenez static void
parse_cert_times(char * timespec)1935856ea928SPeter Avalos parse_cert_times(char *timespec)
1936856ea928SPeter Avalos {
1937856ea928SPeter Avalos char *from, *to;
1938856ea928SPeter Avalos time_t now = time(NULL);
1939856ea928SPeter Avalos int64_t secs;
1940856ea928SPeter Avalos
1941856ea928SPeter Avalos /* +timespec relative to now */
1942856ea928SPeter Avalos if (*timespec == '+' && strchr(timespec, ':') == NULL) {
1943856ea928SPeter Avalos if ((secs = convtime(timespec + 1)) == -1)
1944856ea928SPeter Avalos fatal("Invalid relative certificate life %s", timespec);
1945856ea928SPeter Avalos cert_valid_to = now + secs;
1946856ea928SPeter Avalos /*
1947856ea928SPeter Avalos * Backdate certificate one minute to avoid problems on hosts
1948856ea928SPeter Avalos * with poorly-synchronised clocks.
1949856ea928SPeter Avalos */
1950856ea928SPeter Avalos cert_valid_from = ((now - 59)/ 60) * 60;
1951856ea928SPeter Avalos return;
1952856ea928SPeter Avalos }
1953856ea928SPeter Avalos
1954856ea928SPeter Avalos /*
1955856ea928SPeter Avalos * from:to, where
1956*ee116499SAntonio Huete Jimenez * from := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS | 0x... | "always"
1957*ee116499SAntonio Huete Jimenez * to := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS | 0x... | "forever"
1958856ea928SPeter Avalos */
1959856ea928SPeter Avalos from = xstrdup(timespec);
1960856ea928SPeter Avalos to = strchr(from, ':');
1961856ea928SPeter Avalos if (to == NULL || from == to || *(to + 1) == '\0')
1962856ea928SPeter Avalos fatal("Invalid certificate life specification %s", timespec);
1963856ea928SPeter Avalos *to++ = '\0';
1964856ea928SPeter Avalos
1965856ea928SPeter Avalos if (*from == '-' || *from == '+')
1966856ea928SPeter Avalos cert_valid_from = parse_relative_time(from, now);
1967664f4763Szrj else if (strcmp(from, "always") == 0)
1968664f4763Szrj cert_valid_from = 0;
1969*ee116499SAntonio Huete Jimenez else if (strncmp(from, "0x", 2) == 0)
1970*ee116499SAntonio Huete Jimenez parse_hex_u64(from, &cert_valid_from);
1971664f4763Szrj else if (parse_absolute_time(from, &cert_valid_from) != 0)
1972664f4763Szrj fatal("Invalid from time \"%s\"", from);
1973856ea928SPeter Avalos
1974856ea928SPeter Avalos if (*to == '-' || *to == '+')
197536e94dc5SPeter Avalos cert_valid_to = parse_relative_time(to, now);
1976664f4763Szrj else if (strcmp(to, "forever") == 0)
1977664f4763Szrj cert_valid_to = ~(u_int64_t)0;
1978*ee116499SAntonio Huete Jimenez else if (strncmp(from, "0x", 2) == 0)
1979*ee116499SAntonio Huete Jimenez parse_hex_u64(to, &cert_valid_to);
1980664f4763Szrj else if (parse_absolute_time(to, &cert_valid_to) != 0)
1981664f4763Szrj fatal("Invalid to time \"%s\"", to);
1982856ea928SPeter Avalos
1983856ea928SPeter Avalos if (cert_valid_to <= cert_valid_from)
1984856ea928SPeter Avalos fatal("Empty certificate validity interval");
198536e94dc5SPeter Avalos free(from);
1986856ea928SPeter Avalos }
1987856ea928SPeter Avalos
1988856ea928SPeter Avalos static void
add_cert_option(char * opt)1989856ea928SPeter Avalos add_cert_option(char *opt)
1990856ea928SPeter Avalos {
1991ce74bacaSMatthew Dillon char *val, *cp;
1992ce74bacaSMatthew Dillon int iscrit = 0;
1993856ea928SPeter Avalos
19941c188a7fSPeter Avalos if (strcasecmp(opt, "clear") == 0)
1995856ea928SPeter Avalos certflags_flags = 0;
1996856ea928SPeter Avalos else if (strcasecmp(opt, "no-x11-forwarding") == 0)
1997856ea928SPeter Avalos certflags_flags &= ~CERTOPT_X_FWD;
1998856ea928SPeter Avalos else if (strcasecmp(opt, "permit-x11-forwarding") == 0)
1999856ea928SPeter Avalos certflags_flags |= CERTOPT_X_FWD;
2000856ea928SPeter Avalos else if (strcasecmp(opt, "no-agent-forwarding") == 0)
2001856ea928SPeter Avalos certflags_flags &= ~CERTOPT_AGENT_FWD;
2002856ea928SPeter Avalos else if (strcasecmp(opt, "permit-agent-forwarding") == 0)
2003856ea928SPeter Avalos certflags_flags |= CERTOPT_AGENT_FWD;
2004856ea928SPeter Avalos else if (strcasecmp(opt, "no-port-forwarding") == 0)
2005856ea928SPeter Avalos certflags_flags &= ~CERTOPT_PORT_FWD;
2006856ea928SPeter Avalos else if (strcasecmp(opt, "permit-port-forwarding") == 0)
2007856ea928SPeter Avalos certflags_flags |= CERTOPT_PORT_FWD;
2008856ea928SPeter Avalos else if (strcasecmp(opt, "no-pty") == 0)
2009856ea928SPeter Avalos certflags_flags &= ~CERTOPT_PTY;
2010856ea928SPeter Avalos else if (strcasecmp(opt, "permit-pty") == 0)
2011856ea928SPeter Avalos certflags_flags |= CERTOPT_PTY;
2012856ea928SPeter Avalos else if (strcasecmp(opt, "no-user-rc") == 0)
2013856ea928SPeter Avalos certflags_flags &= ~CERTOPT_USER_RC;
2014856ea928SPeter Avalos else if (strcasecmp(opt, "permit-user-rc") == 0)
2015856ea928SPeter Avalos certflags_flags |= CERTOPT_USER_RC;
20160cbfa66cSDaniel Fojt else if (strcasecmp(opt, "touch-required") == 0)
20170cbfa66cSDaniel Fojt certflags_flags &= ~CERTOPT_NO_REQUIRE_USER_PRESENCE;
20180cbfa66cSDaniel Fojt else if (strcasecmp(opt, "no-touch-required") == 0)
20190cbfa66cSDaniel Fojt certflags_flags |= CERTOPT_NO_REQUIRE_USER_PRESENCE;
2020*ee116499SAntonio Huete Jimenez else if (strcasecmp(opt, "no-verify-required") == 0)
2021*ee116499SAntonio Huete Jimenez certflags_flags &= ~CERTOPT_REQUIRE_VERIFY;
2022*ee116499SAntonio Huete Jimenez else if (strcasecmp(opt, "verify-required") == 0)
2023*ee116499SAntonio Huete Jimenez certflags_flags |= CERTOPT_REQUIRE_VERIFY;
2024856ea928SPeter Avalos else if (strncasecmp(opt, "force-command=", 14) == 0) {
2025856ea928SPeter Avalos val = opt + 14;
2026856ea928SPeter Avalos if (*val == '\0')
2027856ea928SPeter Avalos fatal("Empty force-command option");
2028856ea928SPeter Avalos if (certflags_command != NULL)
2029856ea928SPeter Avalos fatal("force-command already specified");
2030856ea928SPeter Avalos certflags_command = xstrdup(val);
2031856ea928SPeter Avalos } else if (strncasecmp(opt, "source-address=", 15) == 0) {
2032856ea928SPeter Avalos val = opt + 15;
2033856ea928SPeter Avalos if (*val == '\0')
2034856ea928SPeter Avalos fatal("Empty source-address option");
2035856ea928SPeter Avalos if (certflags_src_addr != NULL)
2036856ea928SPeter Avalos fatal("source-address already specified");
2037856ea928SPeter Avalos if (addr_match_cidr_list(NULL, val) != 0)
2038856ea928SPeter Avalos fatal("Invalid source-address list");
2039856ea928SPeter Avalos certflags_src_addr = xstrdup(val);
2040ce74bacaSMatthew Dillon } else if (strncasecmp(opt, "extension:", 10) == 0 ||
2041ce74bacaSMatthew Dillon (iscrit = (strncasecmp(opt, "critical:", 9) == 0))) {
2042ce74bacaSMatthew Dillon val = xstrdup(strchr(opt, ':') + 1);
2043ce74bacaSMatthew Dillon if ((cp = strchr(val, '=')) != NULL)
2044ce74bacaSMatthew Dillon *cp++ = '\0';
204550a69bb5SSascha Wildner cert_ext_add(val, cp, iscrit);
204650a69bb5SSascha Wildner free(val);
2047856ea928SPeter Avalos } else
2048856ea928SPeter Avalos fatal("Unsupported certificate option \"%s\"", opt);
2049856ea928SPeter Avalos }
2050856ea928SPeter Avalos
2051856ea928SPeter Avalos static void
show_options(struct sshbuf * optbuf,int in_critical)2052e9778795SPeter Avalos show_options(struct sshbuf *optbuf, int in_critical)
2053856ea928SPeter Avalos {
205450a69bb5SSascha Wildner char *name, *arg, *hex;
2055e9778795SPeter Avalos struct sshbuf *options, *option = NULL;
2056e9778795SPeter Avalos int r;
2057856ea928SPeter Avalos
2058e9778795SPeter Avalos if ((options = sshbuf_fromb(optbuf)) == NULL)
205950a69bb5SSascha Wildner fatal_f("sshbuf_fromb failed");
2060e9778795SPeter Avalos while (sshbuf_len(options) != 0) {
2061e9778795SPeter Avalos sshbuf_free(option);
2062e9778795SPeter Avalos option = NULL;
2063e9778795SPeter Avalos if ((r = sshbuf_get_cstring(options, &name, NULL)) != 0 ||
2064e9778795SPeter Avalos (r = sshbuf_froms(options, &option)) != 0)
206550a69bb5SSascha Wildner fatal_fr(r, "parse option");
2066856ea928SPeter Avalos printf(" %s", name);
2067e9778795SPeter Avalos if (!in_critical &&
2068856ea928SPeter Avalos (strcmp(name, "permit-X11-forwarding") == 0 ||
2069856ea928SPeter Avalos strcmp(name, "permit-agent-forwarding") == 0 ||
2070856ea928SPeter Avalos strcmp(name, "permit-port-forwarding") == 0 ||
2071856ea928SPeter Avalos strcmp(name, "permit-pty") == 0 ||
20720cbfa66cSDaniel Fojt strcmp(name, "permit-user-rc") == 0 ||
20730cbfa66cSDaniel Fojt strcmp(name, "no-touch-required") == 0)) {
2074856ea928SPeter Avalos printf("\n");
20750cbfa66cSDaniel Fojt } else if (in_critical &&
2076856ea928SPeter Avalos (strcmp(name, "force-command") == 0 ||
2077856ea928SPeter Avalos strcmp(name, "source-address") == 0)) {
2078e9778795SPeter Avalos if ((r = sshbuf_get_cstring(option, &arg, NULL)) != 0)
207950a69bb5SSascha Wildner fatal_fr(r, "parse critical");
208036e94dc5SPeter Avalos printf(" %s\n", arg);
208136e94dc5SPeter Avalos free(arg);
2082*ee116499SAntonio Huete Jimenez } else if (in_critical &&
2083*ee116499SAntonio Huete Jimenez strcmp(name, "verify-required") == 0) {
2084*ee116499SAntonio Huete Jimenez printf("\n");
208550a69bb5SSascha Wildner } else if (sshbuf_len(option) > 0) {
208650a69bb5SSascha Wildner hex = sshbuf_dtob16(option);
208750a69bb5SSascha Wildner printf(" UNKNOWN OPTION: %s (len %zu)\n",
208850a69bb5SSascha Wildner hex, sshbuf_len(option));
2089e9778795SPeter Avalos sshbuf_reset(option);
209050a69bb5SSascha Wildner free(hex);
209150a69bb5SSascha Wildner } else
209250a69bb5SSascha Wildner printf(" UNKNOWN FLAG OPTION\n");
209336e94dc5SPeter Avalos free(name);
2094e9778795SPeter Avalos if (sshbuf_len(option) != 0)
2095856ea928SPeter Avalos fatal("Option corrupt: extra data at end");
2096856ea928SPeter Avalos }
2097e9778795SPeter Avalos sshbuf_free(option);
2098e9778795SPeter Avalos sshbuf_free(options);
2099856ea928SPeter Avalos }
2100856ea928SPeter Avalos
2101856ea928SPeter Avalos static void
print_cert(struct sshkey * key)2102e9778795SPeter Avalos print_cert(struct sshkey *key)
2103856ea928SPeter Avalos {
2104e9778795SPeter Avalos char valid[64], *key_fp, *ca_fp;
2105e9778795SPeter Avalos u_int i;
2106856ea928SPeter Avalos
2107e9778795SPeter Avalos key_fp = sshkey_fingerprint(key, fingerprint_hash, SSH_FP_DEFAULT);
2108e9778795SPeter Avalos ca_fp = sshkey_fingerprint(key->cert->signature_key,
2109e9778795SPeter Avalos fingerprint_hash, SSH_FP_DEFAULT);
2110e9778795SPeter Avalos if (key_fp == NULL || ca_fp == NULL)
211150a69bb5SSascha Wildner fatal_f("sshkey_fingerprint fail");
2112e9778795SPeter Avalos sshkey_format_cert_validity(key->cert, valid, sizeof(valid));
2113856ea928SPeter Avalos
2114e9778795SPeter Avalos printf(" Type: %s %s certificate\n", sshkey_ssh_name(key),
2115e9778795SPeter Avalos sshkey_cert_type(key));
2116e9778795SPeter Avalos printf(" Public key: %s %s\n", sshkey_type(key), key_fp);
2117664f4763Szrj printf(" Signing CA: %s %s (using %s)\n",
2118664f4763Szrj sshkey_type(key->cert->signature_key), ca_fp,
2119664f4763Szrj key->cert->signature_type);
2120856ea928SPeter Avalos printf(" Key ID: \"%s\"\n", key->cert->key_id);
2121e9778795SPeter Avalos printf(" Serial: %llu\n", (unsigned long long)key->cert->serial);
2122e9778795SPeter Avalos printf(" Valid: %s\n", valid);
2123856ea928SPeter Avalos printf(" Principals: ");
2124856ea928SPeter Avalos if (key->cert->nprincipals == 0)
2125856ea928SPeter Avalos printf("(none)\n");
2126856ea928SPeter Avalos else {
2127856ea928SPeter Avalos for (i = 0; i < key->cert->nprincipals; i++)
2128856ea928SPeter Avalos printf("\n %s",
2129856ea928SPeter Avalos key->cert->principals[i]);
2130856ea928SPeter Avalos printf("\n");
2131856ea928SPeter Avalos }
2132856ea928SPeter Avalos printf(" Critical Options: ");
2133e9778795SPeter Avalos if (sshbuf_len(key->cert->critical) == 0)
2134856ea928SPeter Avalos printf("(none)\n");
2135856ea928SPeter Avalos else {
2136856ea928SPeter Avalos printf("\n");
2137e9778795SPeter Avalos show_options(key->cert->critical, 1);
2138856ea928SPeter Avalos }
2139856ea928SPeter Avalos printf(" Extensions: ");
2140e9778795SPeter Avalos if (sshbuf_len(key->cert->extensions) == 0)
2141856ea928SPeter Avalos printf("(none)\n");
2142856ea928SPeter Avalos else {
2143856ea928SPeter Avalos printf("\n");
2144e9778795SPeter Avalos show_options(key->cert->extensions, 0);
2145856ea928SPeter Avalos }
2146856ea928SPeter Avalos }
2147856ea928SPeter Avalos
2148e9778795SPeter Avalos static void
do_show_cert(struct passwd * pw)2149e9778795SPeter Avalos do_show_cert(struct passwd *pw)
2150e9778795SPeter Avalos {
2151e9778795SPeter Avalos struct sshkey *key = NULL;
2152e9778795SPeter Avalos struct stat st;
2153e9778795SPeter Avalos int r, is_stdin = 0, ok = 0;
2154e9778795SPeter Avalos FILE *f;
2155664f4763Szrj char *cp, *line = NULL;
2156e9778795SPeter Avalos const char *path;
2157664f4763Szrj size_t linesize = 0;
2158e9778795SPeter Avalos u_long lnum = 0;
2159e9778795SPeter Avalos
2160e9778795SPeter Avalos if (!have_identity)
2161e9778795SPeter Avalos ask_filename(pw, "Enter file in which the key is");
21620cbfa66cSDaniel Fojt if (strcmp(identity_file, "-") != 0 && stat(identity_file, &st) == -1)
2163e9778795SPeter Avalos fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
2164e9778795SPeter Avalos
2165e9778795SPeter Avalos path = identity_file;
2166e9778795SPeter Avalos if (strcmp(path, "-") == 0) {
2167e9778795SPeter Avalos f = stdin;
2168e9778795SPeter Avalos path = "(stdin)";
2169e9778795SPeter Avalos is_stdin = 1;
2170e9778795SPeter Avalos } else if ((f = fopen(identity_file, "r")) == NULL)
2171e9778795SPeter Avalos fatal("fopen %s: %s", identity_file, strerror(errno));
2172e9778795SPeter Avalos
2173664f4763Szrj while (getline(&line, &linesize, f) != -1) {
2174664f4763Szrj lnum++;
2175e9778795SPeter Avalos sshkey_free(key);
2176e9778795SPeter Avalos key = NULL;
2177e9778795SPeter Avalos /* Trim leading space and comments */
2178e9778795SPeter Avalos cp = line + strspn(line, " \t");
2179e9778795SPeter Avalos if (*cp == '#' || *cp == '\0')
2180e9778795SPeter Avalos continue;
2181e9778795SPeter Avalos if ((key = sshkey_new(KEY_UNSPEC)) == NULL)
2182ce74bacaSMatthew Dillon fatal("sshkey_new");
2183e9778795SPeter Avalos if ((r = sshkey_read(key, &cp)) != 0) {
218450a69bb5SSascha Wildner error_r(r, "%s:%lu: invalid key", path, lnum);
2185e9778795SPeter Avalos continue;
2186e9778795SPeter Avalos }
2187e9778795SPeter Avalos if (!sshkey_is_cert(key)) {
2188e9778795SPeter Avalos error("%s:%lu is not a certificate", path, lnum);
2189e9778795SPeter Avalos continue;
2190e9778795SPeter Avalos }
2191e9778795SPeter Avalos ok = 1;
2192e9778795SPeter Avalos if (!is_stdin && lnum == 1)
2193e9778795SPeter Avalos printf("%s:\n", path);
2194e9778795SPeter Avalos else
2195e9778795SPeter Avalos printf("%s:%lu:\n", path, lnum);
2196e9778795SPeter Avalos print_cert(key);
2197e9778795SPeter Avalos }
2198664f4763Szrj free(line);
2199e9778795SPeter Avalos sshkey_free(key);
2200e9778795SPeter Avalos fclose(f);
2201e9778795SPeter Avalos exit(ok ? 0 : 1);
2202e9778795SPeter Avalos }
2203e9778795SPeter Avalos
220436e94dc5SPeter Avalos static void
load_krl(const char * path,struct ssh_krl ** krlp)220536e94dc5SPeter Avalos load_krl(const char *path, struct ssh_krl **krlp)
220636e94dc5SPeter Avalos {
2207e9778795SPeter Avalos struct sshbuf *krlbuf;
22080cbfa66cSDaniel Fojt int r;
220936e94dc5SPeter Avalos
22100cbfa66cSDaniel Fojt if ((r = sshbuf_load_file(path, &krlbuf)) != 0)
221150a69bb5SSascha Wildner fatal_r(r, "Unable to load KRL %s", path);
221236e94dc5SPeter Avalos /* XXX check sigs */
2213e9778795SPeter Avalos if ((r = ssh_krl_from_blob(krlbuf, krlp, NULL, 0)) != 0 ||
221436e94dc5SPeter Avalos *krlp == NULL)
221550a69bb5SSascha Wildner fatal_r(r, "Invalid KRL file %s", path);
2216e9778795SPeter Avalos sshbuf_free(krlbuf);
221736e94dc5SPeter Avalos }
221836e94dc5SPeter Avalos
221936e94dc5SPeter Avalos static void
hash_to_blob(const char * cp,u_char ** blobp,size_t * lenp,const char * file,u_long lnum)2220664f4763Szrj hash_to_blob(const char *cp, u_char **blobp, size_t *lenp,
2221664f4763Szrj const char *file, u_long lnum)
2222664f4763Szrj {
2223664f4763Szrj char *tmp;
2224664f4763Szrj size_t tlen;
2225664f4763Szrj struct sshbuf *b;
2226664f4763Szrj int r;
2227664f4763Szrj
2228664f4763Szrj if (strncmp(cp, "SHA256:", 7) != 0)
2229664f4763Szrj fatal("%s:%lu: unsupported hash algorithm", file, lnum);
2230664f4763Szrj cp += 7;
2231664f4763Szrj
2232664f4763Szrj /*
2233664f4763Szrj * OpenSSH base64 hashes omit trailing '='
2234664f4763Szrj * characters; put them back for decode.
2235664f4763Szrj */
2236664f4763Szrj tlen = strlen(cp);
2237664f4763Szrj tmp = xmalloc(tlen + 4 + 1);
2238664f4763Szrj strlcpy(tmp, cp, tlen + 1);
2239664f4763Szrj while ((tlen % 4) != 0) {
2240664f4763Szrj tmp[tlen++] = '=';
2241664f4763Szrj tmp[tlen] = '\0';
2242664f4763Szrj }
2243664f4763Szrj if ((b = sshbuf_new()) == NULL)
224450a69bb5SSascha Wildner fatal_f("sshbuf_new failed");
2245664f4763Szrj if ((r = sshbuf_b64tod(b, tmp)) != 0)
224650a69bb5SSascha Wildner fatal_r(r, "%s:%lu: decode hash failed", file, lnum);
2247664f4763Szrj free(tmp);
2248664f4763Szrj *lenp = sshbuf_len(b);
2249664f4763Szrj *blobp = xmalloc(*lenp);
2250664f4763Szrj memcpy(*blobp, sshbuf_ptr(b), *lenp);
2251664f4763Szrj sshbuf_free(b);
2252664f4763Szrj }
2253664f4763Szrj
2254664f4763Szrj static void
update_krl_from_file(struct passwd * pw,const char * file,int wild_ca,const struct sshkey * ca,struct ssh_krl * krl)2255e9778795SPeter Avalos update_krl_from_file(struct passwd *pw, const char *file, int wild_ca,
2256e9778795SPeter Avalos const struct sshkey *ca, struct ssh_krl *krl)
225736e94dc5SPeter Avalos {
2258e9778795SPeter Avalos struct sshkey *key = NULL;
225936e94dc5SPeter Avalos u_long lnum = 0;
2260664f4763Szrj char *path, *cp, *ep, *line = NULL;
2261664f4763Szrj u_char *blob = NULL;
2262664f4763Szrj size_t blen = 0, linesize = 0;
226336e94dc5SPeter Avalos unsigned long long serial, serial2;
2264664f4763Szrj int i, was_explicit_key, was_sha1, was_sha256, was_hash, r;
226536e94dc5SPeter Avalos FILE *krl_spec;
226636e94dc5SPeter Avalos
226736e94dc5SPeter Avalos path = tilde_expand_filename(file, pw->pw_uid);
226836e94dc5SPeter Avalos if (strcmp(path, "-") == 0) {
226936e94dc5SPeter Avalos krl_spec = stdin;
227036e94dc5SPeter Avalos free(path);
227136e94dc5SPeter Avalos path = xstrdup("(standard input)");
227236e94dc5SPeter Avalos } else if ((krl_spec = fopen(path, "r")) == NULL)
227336e94dc5SPeter Avalos fatal("fopen %s: %s", path, strerror(errno));
227436e94dc5SPeter Avalos
227536e94dc5SPeter Avalos if (!quiet)
227636e94dc5SPeter Avalos printf("Revoking from %s\n", path);
2277664f4763Szrj while (getline(&line, &linesize, krl_spec) != -1) {
2278664f4763Szrj lnum++;
2279664f4763Szrj was_explicit_key = was_sha1 = was_sha256 = was_hash = 0;
228036e94dc5SPeter Avalos cp = line + strspn(line, " \t");
228136e94dc5SPeter Avalos /* Trim trailing space, comments and strip \n */
228236e94dc5SPeter Avalos for (i = 0, r = -1; cp[i] != '\0'; i++) {
228336e94dc5SPeter Avalos if (cp[i] == '#' || cp[i] == '\n') {
228436e94dc5SPeter Avalos cp[i] = '\0';
228536e94dc5SPeter Avalos break;
228636e94dc5SPeter Avalos }
228736e94dc5SPeter Avalos if (cp[i] == ' ' || cp[i] == '\t') {
228836e94dc5SPeter Avalos /* Remember the start of a span of whitespace */
228936e94dc5SPeter Avalos if (r == -1)
229036e94dc5SPeter Avalos r = i;
229136e94dc5SPeter Avalos } else
229236e94dc5SPeter Avalos r = -1;
229336e94dc5SPeter Avalos }
229436e94dc5SPeter Avalos if (r != -1)
229536e94dc5SPeter Avalos cp[r] = '\0';
229636e94dc5SPeter Avalos if (*cp == '\0')
229736e94dc5SPeter Avalos continue;
229836e94dc5SPeter Avalos if (strncasecmp(cp, "serial:", 7) == 0) {
2299e9778795SPeter Avalos if (ca == NULL && !wild_ca) {
230036e94dc5SPeter Avalos fatal("revoking certificates by serial number "
230136e94dc5SPeter Avalos "requires specification of a CA key");
230236e94dc5SPeter Avalos }
230336e94dc5SPeter Avalos cp += 7;
230436e94dc5SPeter Avalos cp = cp + strspn(cp, " \t");
230536e94dc5SPeter Avalos errno = 0;
230636e94dc5SPeter Avalos serial = strtoull(cp, &ep, 0);
230736e94dc5SPeter Avalos if (*cp == '\0' || (*ep != '\0' && *ep != '-'))
230836e94dc5SPeter Avalos fatal("%s:%lu: invalid serial \"%s\"",
230936e94dc5SPeter Avalos path, lnum, cp);
231036e94dc5SPeter Avalos if (errno == ERANGE && serial == ULLONG_MAX)
231136e94dc5SPeter Avalos fatal("%s:%lu: serial out of range",
231236e94dc5SPeter Avalos path, lnum);
231336e94dc5SPeter Avalos serial2 = serial;
231436e94dc5SPeter Avalos if (*ep == '-') {
231536e94dc5SPeter Avalos cp = ep + 1;
231636e94dc5SPeter Avalos errno = 0;
231736e94dc5SPeter Avalos serial2 = strtoull(cp, &ep, 0);
231836e94dc5SPeter Avalos if (*cp == '\0' || *ep != '\0')
231936e94dc5SPeter Avalos fatal("%s:%lu: invalid serial \"%s\"",
232036e94dc5SPeter Avalos path, lnum, cp);
232136e94dc5SPeter Avalos if (errno == ERANGE && serial2 == ULLONG_MAX)
232236e94dc5SPeter Avalos fatal("%s:%lu: serial out of range",
232336e94dc5SPeter Avalos path, lnum);
232436e94dc5SPeter Avalos if (serial2 <= serial)
232536e94dc5SPeter Avalos fatal("%s:%lu: invalid serial range "
232636e94dc5SPeter Avalos "%llu:%llu", path, lnum,
232736e94dc5SPeter Avalos (unsigned long long)serial,
232836e94dc5SPeter Avalos (unsigned long long)serial2);
232936e94dc5SPeter Avalos }
233036e94dc5SPeter Avalos if (ssh_krl_revoke_cert_by_serial_range(krl,
233136e94dc5SPeter Avalos ca, serial, serial2) != 0) {
233250a69bb5SSascha Wildner fatal_f("revoke serial failed");
233336e94dc5SPeter Avalos }
233436e94dc5SPeter Avalos } else if (strncasecmp(cp, "id:", 3) == 0) {
2335e9778795SPeter Avalos if (ca == NULL && !wild_ca) {
233636e94dc5SPeter Avalos fatal("revoking certificates by key ID "
233736e94dc5SPeter Avalos "requires specification of a CA key");
233836e94dc5SPeter Avalos }
233936e94dc5SPeter Avalos cp += 3;
234036e94dc5SPeter Avalos cp = cp + strspn(cp, " \t");
234136e94dc5SPeter Avalos if (ssh_krl_revoke_cert_by_key_id(krl, ca, cp) != 0)
234250a69bb5SSascha Wildner fatal_f("revoke key ID failed");
2343664f4763Szrj } else if (strncasecmp(cp, "hash:", 5) == 0) {
2344664f4763Szrj cp += 5;
2345664f4763Szrj cp = cp + strspn(cp, " \t");
2346664f4763Szrj hash_to_blob(cp, &blob, &blen, file, lnum);
2347664f4763Szrj r = ssh_krl_revoke_key_sha256(krl, blob, blen);
23480cbfa66cSDaniel Fojt if (r != 0)
234950a69bb5SSascha Wildner fatal_fr(r, "revoke key failed");
235036e94dc5SPeter Avalos } else {
235136e94dc5SPeter Avalos if (strncasecmp(cp, "key:", 4) == 0) {
235236e94dc5SPeter Avalos cp += 4;
235336e94dc5SPeter Avalos cp = cp + strspn(cp, " \t");
235436e94dc5SPeter Avalos was_explicit_key = 1;
235536e94dc5SPeter Avalos } else if (strncasecmp(cp, "sha1:", 5) == 0) {
235636e94dc5SPeter Avalos cp += 5;
235736e94dc5SPeter Avalos cp = cp + strspn(cp, " \t");
235836e94dc5SPeter Avalos was_sha1 = 1;
2359664f4763Szrj } else if (strncasecmp(cp, "sha256:", 7) == 0) {
2360664f4763Szrj cp += 7;
2361664f4763Szrj cp = cp + strspn(cp, " \t");
2362664f4763Szrj was_sha256 = 1;
236336e94dc5SPeter Avalos /*
236436e94dc5SPeter Avalos * Just try to process the line as a key.
236536e94dc5SPeter Avalos * Parsing will fail if it isn't.
236636e94dc5SPeter Avalos */
236736e94dc5SPeter Avalos }
2368e9778795SPeter Avalos if ((key = sshkey_new(KEY_UNSPEC)) == NULL)
2369ce74bacaSMatthew Dillon fatal("sshkey_new");
2370e9778795SPeter Avalos if ((r = sshkey_read(key, &cp)) != 0)
237150a69bb5SSascha Wildner fatal_r(r, "%s:%lu: invalid key", path, lnum);
237236e94dc5SPeter Avalos if (was_explicit_key)
237336e94dc5SPeter Avalos r = ssh_krl_revoke_key_explicit(krl, key);
2374664f4763Szrj else if (was_sha1) {
2375664f4763Szrj if (sshkey_fingerprint_raw(key,
2376664f4763Szrj SSH_DIGEST_SHA1, &blob, &blen) != 0) {
2377664f4763Szrj fatal("%s:%lu: fingerprint failed",
2378664f4763Szrj file, lnum);
2379664f4763Szrj }
2380664f4763Szrj r = ssh_krl_revoke_key_sha1(krl, blob, blen);
2381664f4763Szrj } else if (was_sha256) {
2382664f4763Szrj if (sshkey_fingerprint_raw(key,
2383664f4763Szrj SSH_DIGEST_SHA256, &blob, &blen) != 0) {
2384664f4763Szrj fatal("%s:%lu: fingerprint failed",
2385664f4763Szrj file, lnum);
2386664f4763Szrj }
2387664f4763Szrj r = ssh_krl_revoke_key_sha256(krl, blob, blen);
2388664f4763Szrj } else
238936e94dc5SPeter Avalos r = ssh_krl_revoke_key(krl, key);
239036e94dc5SPeter Avalos if (r != 0)
239150a69bb5SSascha Wildner fatal_fr(r, "revoke key failed");
2392664f4763Szrj freezero(blob, blen);
2393664f4763Szrj blob = NULL;
2394664f4763Szrj blen = 0;
2395e9778795SPeter Avalos sshkey_free(key);
239636e94dc5SPeter Avalos }
239736e94dc5SPeter Avalos }
239836e94dc5SPeter Avalos if (strcmp(path, "-") != 0)
239936e94dc5SPeter Avalos fclose(krl_spec);
2400664f4763Szrj free(line);
240136e94dc5SPeter Avalos free(path);
240236e94dc5SPeter Avalos }
240336e94dc5SPeter Avalos
240436e94dc5SPeter Avalos static void
do_gen_krl(struct passwd * pw,int updating,const char * ca_key_path,unsigned long long krl_version,const char * krl_comment,int argc,char ** argv)2405664f4763Szrj do_gen_krl(struct passwd *pw, int updating, const char *ca_key_path,
2406664f4763Szrj unsigned long long krl_version, const char *krl_comment,
2407664f4763Szrj int argc, char **argv)
240836e94dc5SPeter Avalos {
240936e94dc5SPeter Avalos struct ssh_krl *krl;
241036e94dc5SPeter Avalos struct stat sb;
2411e9778795SPeter Avalos struct sshkey *ca = NULL;
24120cbfa66cSDaniel Fojt int i, r, wild_ca = 0;
241336e94dc5SPeter Avalos char *tmp;
2414e9778795SPeter Avalos struct sshbuf *kbuf;
241536e94dc5SPeter Avalos
241636e94dc5SPeter Avalos if (*identity_file == '\0')
241736e94dc5SPeter Avalos fatal("KRL generation requires an output file");
241836e94dc5SPeter Avalos if (stat(identity_file, &sb) == -1) {
241936e94dc5SPeter Avalos if (errno != ENOENT)
242036e94dc5SPeter Avalos fatal("Cannot access KRL \"%s\": %s",
242136e94dc5SPeter Avalos identity_file, strerror(errno));
242236e94dc5SPeter Avalos if (updating)
242336e94dc5SPeter Avalos fatal("KRL \"%s\" does not exist", identity_file);
242436e94dc5SPeter Avalos }
242536e94dc5SPeter Avalos if (ca_key_path != NULL) {
2426e9778795SPeter Avalos if (strcasecmp(ca_key_path, "none") == 0)
2427e9778795SPeter Avalos wild_ca = 1;
2428e9778795SPeter Avalos else {
242936e94dc5SPeter Avalos tmp = tilde_expand_filename(ca_key_path, pw->pw_uid);
2430e9778795SPeter Avalos if ((r = sshkey_load_public(tmp, &ca, NULL)) != 0)
243150a69bb5SSascha Wildner fatal_r(r, "Cannot load CA public key %s", tmp);
243236e94dc5SPeter Avalos free(tmp);
243336e94dc5SPeter Avalos }
2434e9778795SPeter Avalos }
243536e94dc5SPeter Avalos
243636e94dc5SPeter Avalos if (updating)
243736e94dc5SPeter Avalos load_krl(identity_file, &krl);
243836e94dc5SPeter Avalos else if ((krl = ssh_krl_init()) == NULL)
243936e94dc5SPeter Avalos fatal("couldn't create KRL");
244036e94dc5SPeter Avalos
2441664f4763Szrj if (krl_version != 0)
2442664f4763Szrj ssh_krl_set_version(krl, krl_version);
2443664f4763Szrj if (krl_comment != NULL)
2444664f4763Szrj ssh_krl_set_comment(krl, krl_comment);
244536e94dc5SPeter Avalos
244636e94dc5SPeter Avalos for (i = 0; i < argc; i++)
2447e9778795SPeter Avalos update_krl_from_file(pw, argv[i], wild_ca, ca, krl);
244836e94dc5SPeter Avalos
2449e9778795SPeter Avalos if ((kbuf = sshbuf_new()) == NULL)
2450e9778795SPeter Avalos fatal("sshbuf_new failed");
2451e9778795SPeter Avalos if (ssh_krl_to_blob(krl, kbuf, NULL, 0) != 0)
245236e94dc5SPeter Avalos fatal("Couldn't generate KRL");
24530cbfa66cSDaniel Fojt if ((r = sshbuf_write_file(identity_file, kbuf)) != 0)
245436e94dc5SPeter Avalos fatal("write %s: %s", identity_file, strerror(errno));
2455e9778795SPeter Avalos sshbuf_free(kbuf);
245636e94dc5SPeter Avalos ssh_krl_free(krl);
2457e9778795SPeter Avalos sshkey_free(ca);
245836e94dc5SPeter Avalos }
245936e94dc5SPeter Avalos
246036e94dc5SPeter Avalos static void
do_check_krl(struct passwd * pw,int print_krl,int argc,char ** argv)24610cbfa66cSDaniel Fojt do_check_krl(struct passwd *pw, int print_krl, int argc, char **argv)
246236e94dc5SPeter Avalos {
246336e94dc5SPeter Avalos int i, r, ret = 0;
246436e94dc5SPeter Avalos char *comment;
246536e94dc5SPeter Avalos struct ssh_krl *krl;
2466e9778795SPeter Avalos struct sshkey *k;
246736e94dc5SPeter Avalos
246836e94dc5SPeter Avalos if (*identity_file == '\0')
246936e94dc5SPeter Avalos fatal("KRL checking requires an input file");
247036e94dc5SPeter Avalos load_krl(identity_file, &krl);
24710cbfa66cSDaniel Fojt if (print_krl)
24720cbfa66cSDaniel Fojt krl_dump(krl, stdout);
247336e94dc5SPeter Avalos for (i = 0; i < argc; i++) {
2474e9778795SPeter Avalos if ((r = sshkey_load_public(argv[i], &k, &comment)) != 0)
247550a69bb5SSascha Wildner fatal_r(r, "Cannot load public key %s", argv[i]);
247636e94dc5SPeter Avalos r = ssh_krl_check_key(krl, k);
247736e94dc5SPeter Avalos printf("%s%s%s%s: %s\n", argv[i],
247836e94dc5SPeter Avalos *comment ? " (" : "", comment, *comment ? ")" : "",
247936e94dc5SPeter Avalos r == 0 ? "ok" : "REVOKED");
248036e94dc5SPeter Avalos if (r != 0)
248136e94dc5SPeter Avalos ret = 1;
2482e9778795SPeter Avalos sshkey_free(k);
248336e94dc5SPeter Avalos free(comment);
248436e94dc5SPeter Avalos }
248536e94dc5SPeter Avalos ssh_krl_free(krl);
248636e94dc5SPeter Avalos exit(ret);
248736e94dc5SPeter Avalos }
248836e94dc5SPeter Avalos
24890cbfa66cSDaniel Fojt static struct sshkey *
load_sign_key(const char * keypath,const struct sshkey * pubkey)24900cbfa66cSDaniel Fojt load_sign_key(const char *keypath, const struct sshkey *pubkey)
24910cbfa66cSDaniel Fojt {
24920cbfa66cSDaniel Fojt size_t i, slen, plen = strlen(keypath);
24930cbfa66cSDaniel Fojt char *privpath = xstrdup(keypath);
2494*ee116499SAntonio Huete Jimenez static const char * const suffixes[] = { "-cert.pub", ".pub", NULL };
24950cbfa66cSDaniel Fojt struct sshkey *ret = NULL, *privkey = NULL;
2496*ee116499SAntonio Huete Jimenez int r, waspub = 0;
2497*ee116499SAntonio Huete Jimenez struct stat st;
24980cbfa66cSDaniel Fojt
24990cbfa66cSDaniel Fojt /*
25000cbfa66cSDaniel Fojt * If passed a public key filename, then try to locate the corresponding
25010cbfa66cSDaniel Fojt * private key. This lets us specify certificates on the command-line
25020cbfa66cSDaniel Fojt * and have ssh-keygen find the appropriate private key.
25030cbfa66cSDaniel Fojt */
25040cbfa66cSDaniel Fojt for (i = 0; suffixes[i]; i++) {
25050cbfa66cSDaniel Fojt slen = strlen(suffixes[i]);
25060cbfa66cSDaniel Fojt if (plen <= slen ||
25070cbfa66cSDaniel Fojt strcmp(privpath + plen - slen, suffixes[i]) != 0)
25080cbfa66cSDaniel Fojt continue;
25090cbfa66cSDaniel Fojt privpath[plen - slen] = '\0';
251050a69bb5SSascha Wildner debug_f("%s looks like a public key, using private key "
251150a69bb5SSascha Wildner "path %s instead", keypath, privpath);
2512*ee116499SAntonio Huete Jimenez waspub = 1;
25130cbfa66cSDaniel Fojt }
2514*ee116499SAntonio Huete Jimenez if (waspub && stat(privpath, &st) != 0 && errno == ENOENT)
2515*ee116499SAntonio Huete Jimenez fatal("No private key found for public key \"%s\"", keypath);
2516*ee116499SAntonio Huete Jimenez if ((r = sshkey_load_private(privpath, "", &privkey, NULL)) != 0 &&
2517*ee116499SAntonio Huete Jimenez (r != SSH_ERR_KEY_WRONG_PASSPHRASE)) {
2518*ee116499SAntonio Huete Jimenez debug_fr(r, "load private key \"%s\"", privpath);
2519*ee116499SAntonio Huete Jimenez fatal("No private key found for \"%s\"", privpath);
2520*ee116499SAntonio Huete Jimenez } else if (privkey == NULL)
2521*ee116499SAntonio Huete Jimenez privkey = load_identity(privpath, NULL);
2522*ee116499SAntonio Huete Jimenez
25230cbfa66cSDaniel Fojt if (!sshkey_equal_public(pubkey, privkey)) {
25240cbfa66cSDaniel Fojt error("Public key %s doesn't match private %s",
25250cbfa66cSDaniel Fojt keypath, privpath);
25260cbfa66cSDaniel Fojt goto done;
25270cbfa66cSDaniel Fojt }
25280cbfa66cSDaniel Fojt if (sshkey_is_cert(pubkey) && !sshkey_is_cert(privkey)) {
25290cbfa66cSDaniel Fojt /*
25300cbfa66cSDaniel Fojt * Graft the certificate onto the private key to make
25310cbfa66cSDaniel Fojt * it capable of signing.
25320cbfa66cSDaniel Fojt */
25330cbfa66cSDaniel Fojt if ((r = sshkey_to_certified(privkey)) != 0) {
253450a69bb5SSascha Wildner error_fr(r, "sshkey_to_certified");
25350cbfa66cSDaniel Fojt goto done;
25360cbfa66cSDaniel Fojt }
25370cbfa66cSDaniel Fojt if ((r = sshkey_cert_copy(pubkey, privkey)) != 0) {
253850a69bb5SSascha Wildner error_fr(r, "sshkey_cert_copy");
25390cbfa66cSDaniel Fojt goto done;
25400cbfa66cSDaniel Fojt }
25410cbfa66cSDaniel Fojt }
25420cbfa66cSDaniel Fojt /* success */
25430cbfa66cSDaniel Fojt ret = privkey;
25440cbfa66cSDaniel Fojt privkey = NULL;
25450cbfa66cSDaniel Fojt done:
25460cbfa66cSDaniel Fojt sshkey_free(privkey);
25470cbfa66cSDaniel Fojt free(privpath);
25480cbfa66cSDaniel Fojt return ret;
25490cbfa66cSDaniel Fojt }
25500cbfa66cSDaniel Fojt
25510cbfa66cSDaniel Fojt static int
sign_one(struct sshkey * signkey,const char * filename,int fd,const char * sig_namespace,const char * hashalg,sshsig_signer * signer,void * signer_ctx)25520cbfa66cSDaniel Fojt sign_one(struct sshkey *signkey, const char *filename, int fd,
2553*ee116499SAntonio Huete Jimenez const char *sig_namespace, const char *hashalg, sshsig_signer *signer,
2554*ee116499SAntonio Huete Jimenez void *signer_ctx)
25550cbfa66cSDaniel Fojt {
25560cbfa66cSDaniel Fojt struct sshbuf *sigbuf = NULL, *abuf = NULL;
25570cbfa66cSDaniel Fojt int r = SSH_ERR_INTERNAL_ERROR, wfd = -1, oerrno;
25580cbfa66cSDaniel Fojt char *wfile = NULL, *asig = NULL, *fp = NULL;
255950a69bb5SSascha Wildner char *pin = NULL, *prompt = NULL;
25600cbfa66cSDaniel Fojt
25610cbfa66cSDaniel Fojt if (!quiet) {
25620cbfa66cSDaniel Fojt if (fd == STDIN_FILENO)
25630cbfa66cSDaniel Fojt fprintf(stderr, "Signing data on standard input\n");
25640cbfa66cSDaniel Fojt else
25650cbfa66cSDaniel Fojt fprintf(stderr, "Signing file %s\n", filename);
25660cbfa66cSDaniel Fojt }
256750a69bb5SSascha Wildner if (signer == NULL && sshkey_is_sk(signkey)) {
256850a69bb5SSascha Wildner if ((signkey->sk_flags & SSH_SK_USER_VERIFICATION_REQD)) {
256950a69bb5SSascha Wildner xasprintf(&prompt, "Enter PIN for %s key: ",
257050a69bb5SSascha Wildner sshkey_type(signkey));
257150a69bb5SSascha Wildner if ((pin = read_passphrase(prompt,
257250a69bb5SSascha Wildner RP_ALLOW_STDIN)) == NULL)
257350a69bb5SSascha Wildner fatal_f("couldn't read PIN");
257450a69bb5SSascha Wildner }
257550a69bb5SSascha Wildner if ((signkey->sk_flags & SSH_SK_USER_PRESENCE_REQD)) {
25760cbfa66cSDaniel Fojt if ((fp = sshkey_fingerprint(signkey, fingerprint_hash,
25770cbfa66cSDaniel Fojt SSH_FP_DEFAULT)) == NULL)
257850a69bb5SSascha Wildner fatal_f("fingerprint failed");
25790cbfa66cSDaniel Fojt fprintf(stderr, "Confirm user presence for key %s %s\n",
25800cbfa66cSDaniel Fojt sshkey_type(signkey), fp);
25810cbfa66cSDaniel Fojt free(fp);
25820cbfa66cSDaniel Fojt }
258350a69bb5SSascha Wildner }
2584*ee116499SAntonio Huete Jimenez if ((r = sshsig_sign_fd(signkey, hashalg, sk_provider, pin,
258550a69bb5SSascha Wildner fd, sig_namespace, &sigbuf, signer, signer_ctx)) != 0) {
258650a69bb5SSascha Wildner error_r(r, "Signing %s failed", filename);
25870cbfa66cSDaniel Fojt goto out;
25880cbfa66cSDaniel Fojt }
25890cbfa66cSDaniel Fojt if ((r = sshsig_armor(sigbuf, &abuf)) != 0) {
259050a69bb5SSascha Wildner error_fr(r, "sshsig_armor");
25910cbfa66cSDaniel Fojt goto out;
25920cbfa66cSDaniel Fojt }
25930cbfa66cSDaniel Fojt if ((asig = sshbuf_dup_string(abuf)) == NULL) {
259450a69bb5SSascha Wildner error_f("buffer error");
25950cbfa66cSDaniel Fojt r = SSH_ERR_ALLOC_FAIL;
25960cbfa66cSDaniel Fojt goto out;
25970cbfa66cSDaniel Fojt }
25980cbfa66cSDaniel Fojt
25990cbfa66cSDaniel Fojt if (fd == STDIN_FILENO) {
26000cbfa66cSDaniel Fojt fputs(asig, stdout);
26010cbfa66cSDaniel Fojt fflush(stdout);
26020cbfa66cSDaniel Fojt } else {
26030cbfa66cSDaniel Fojt xasprintf(&wfile, "%s.sig", filename);
26040cbfa66cSDaniel Fojt if (confirm_overwrite(wfile)) {
26050cbfa66cSDaniel Fojt if ((wfd = open(wfile, O_WRONLY|O_CREAT|O_TRUNC,
26060cbfa66cSDaniel Fojt 0666)) == -1) {
26070cbfa66cSDaniel Fojt oerrno = errno;
26080cbfa66cSDaniel Fojt error("Cannot open %s: %s",
26090cbfa66cSDaniel Fojt wfile, strerror(errno));
26100cbfa66cSDaniel Fojt errno = oerrno;
26110cbfa66cSDaniel Fojt r = SSH_ERR_SYSTEM_ERROR;
26120cbfa66cSDaniel Fojt goto out;
26130cbfa66cSDaniel Fojt }
26140cbfa66cSDaniel Fojt if (atomicio(vwrite, wfd, asig,
26150cbfa66cSDaniel Fojt strlen(asig)) != strlen(asig)) {
26160cbfa66cSDaniel Fojt oerrno = errno;
26170cbfa66cSDaniel Fojt error("Cannot write to %s: %s",
26180cbfa66cSDaniel Fojt wfile, strerror(errno));
26190cbfa66cSDaniel Fojt errno = oerrno;
26200cbfa66cSDaniel Fojt r = SSH_ERR_SYSTEM_ERROR;
26210cbfa66cSDaniel Fojt goto out;
26220cbfa66cSDaniel Fojt }
26230cbfa66cSDaniel Fojt if (!quiet) {
26240cbfa66cSDaniel Fojt fprintf(stderr, "Write signature to %s\n",
26250cbfa66cSDaniel Fojt wfile);
26260cbfa66cSDaniel Fojt }
26270cbfa66cSDaniel Fojt }
26280cbfa66cSDaniel Fojt }
26290cbfa66cSDaniel Fojt /* success */
26300cbfa66cSDaniel Fojt r = 0;
26310cbfa66cSDaniel Fojt out:
26320cbfa66cSDaniel Fojt free(wfile);
263350a69bb5SSascha Wildner free(prompt);
26340cbfa66cSDaniel Fojt free(asig);
263550a69bb5SSascha Wildner if (pin != NULL)
263650a69bb5SSascha Wildner freezero(pin, strlen(pin));
26370cbfa66cSDaniel Fojt sshbuf_free(abuf);
26380cbfa66cSDaniel Fojt sshbuf_free(sigbuf);
26390cbfa66cSDaniel Fojt if (wfd != -1)
26400cbfa66cSDaniel Fojt close(wfd);
26410cbfa66cSDaniel Fojt return r;
26420cbfa66cSDaniel Fojt }
26430cbfa66cSDaniel Fojt
26440cbfa66cSDaniel Fojt static int
sig_process_opts(char * const * opts,size_t nopts,char ** hashalgp,uint64_t * verify_timep,int * print_pubkey)2645*ee116499SAntonio Huete Jimenez sig_process_opts(char * const *opts, size_t nopts, char **hashalgp,
2646*ee116499SAntonio Huete Jimenez uint64_t *verify_timep, int *print_pubkey)
2647*ee116499SAntonio Huete Jimenez {
2648*ee116499SAntonio Huete Jimenez size_t i;
2649*ee116499SAntonio Huete Jimenez time_t now;
2650*ee116499SAntonio Huete Jimenez
2651*ee116499SAntonio Huete Jimenez if (verify_timep != NULL)
2652*ee116499SAntonio Huete Jimenez *verify_timep = 0;
2653*ee116499SAntonio Huete Jimenez if (print_pubkey != NULL)
2654*ee116499SAntonio Huete Jimenez *print_pubkey = 0;
2655*ee116499SAntonio Huete Jimenez if (hashalgp != NULL)
2656*ee116499SAntonio Huete Jimenez *hashalgp = NULL;
2657*ee116499SAntonio Huete Jimenez for (i = 0; i < nopts; i++) {
2658*ee116499SAntonio Huete Jimenez if (hashalgp != NULL &&
2659*ee116499SAntonio Huete Jimenez strncasecmp(opts[i], "hashalg=", 8) == 0) {
2660*ee116499SAntonio Huete Jimenez *hashalgp = xstrdup(opts[i] + 8);
2661*ee116499SAntonio Huete Jimenez } else if (verify_timep &&
2662*ee116499SAntonio Huete Jimenez strncasecmp(opts[i], "verify-time=", 12) == 0) {
2663*ee116499SAntonio Huete Jimenez if (parse_absolute_time(opts[i] + 12,
2664*ee116499SAntonio Huete Jimenez verify_timep) != 0 || *verify_timep == 0) {
2665*ee116499SAntonio Huete Jimenez error("Invalid \"verify-time\" option");
2666*ee116499SAntonio Huete Jimenez return SSH_ERR_INVALID_ARGUMENT;
2667*ee116499SAntonio Huete Jimenez }
2668*ee116499SAntonio Huete Jimenez } else if (print_pubkey &&
2669*ee116499SAntonio Huete Jimenez strcasecmp(opts[i], "print-pubkey") == 0) {
2670*ee116499SAntonio Huete Jimenez *print_pubkey = 1;
2671*ee116499SAntonio Huete Jimenez } else {
2672*ee116499SAntonio Huete Jimenez error("Invalid option \"%s\"", opts[i]);
2673*ee116499SAntonio Huete Jimenez return SSH_ERR_INVALID_ARGUMENT;
2674*ee116499SAntonio Huete Jimenez }
2675*ee116499SAntonio Huete Jimenez }
2676*ee116499SAntonio Huete Jimenez if (verify_timep && *verify_timep == 0) {
2677*ee116499SAntonio Huete Jimenez if ((now = time(NULL)) < 0) {
2678*ee116499SAntonio Huete Jimenez error("Time is before epoch");
2679*ee116499SAntonio Huete Jimenez return SSH_ERR_INVALID_ARGUMENT;
2680*ee116499SAntonio Huete Jimenez }
2681*ee116499SAntonio Huete Jimenez *verify_timep = (uint64_t)now;
2682*ee116499SAntonio Huete Jimenez }
2683*ee116499SAntonio Huete Jimenez return 0;
2684*ee116499SAntonio Huete Jimenez }
2685*ee116499SAntonio Huete Jimenez
2686*ee116499SAntonio Huete Jimenez
2687*ee116499SAntonio Huete Jimenez static int
sig_sign(const char * keypath,const char * sig_namespace,int require_agent,int argc,char ** argv,char * const * opts,size_t nopts)2688*ee116499SAntonio Huete Jimenez sig_sign(const char *keypath, const char *sig_namespace, int require_agent,
2689*ee116499SAntonio Huete Jimenez int argc, char **argv, char * const *opts, size_t nopts)
26900cbfa66cSDaniel Fojt {
26910cbfa66cSDaniel Fojt int i, fd = -1, r, ret = -1;
26920cbfa66cSDaniel Fojt int agent_fd = -1;
26930cbfa66cSDaniel Fojt struct sshkey *pubkey = NULL, *privkey = NULL, *signkey = NULL;
26940cbfa66cSDaniel Fojt sshsig_signer *signer = NULL;
2695*ee116499SAntonio Huete Jimenez char *hashalg = NULL;
26960cbfa66cSDaniel Fojt
26970cbfa66cSDaniel Fojt /* Check file arguments. */
26980cbfa66cSDaniel Fojt for (i = 0; i < argc; i++) {
26990cbfa66cSDaniel Fojt if (strcmp(argv[i], "-") != 0)
27000cbfa66cSDaniel Fojt continue;
27010cbfa66cSDaniel Fojt if (i > 0 || argc > 1)
27020cbfa66cSDaniel Fojt fatal("Cannot sign mix of paths and standard input");
27030cbfa66cSDaniel Fojt }
27040cbfa66cSDaniel Fojt
2705*ee116499SAntonio Huete Jimenez if (sig_process_opts(opts, nopts, &hashalg, NULL, NULL) != 0)
2706*ee116499SAntonio Huete Jimenez goto done; /* error already logged */
2707*ee116499SAntonio Huete Jimenez
27080cbfa66cSDaniel Fojt if ((r = sshkey_load_public(keypath, &pubkey, NULL)) != 0) {
270950a69bb5SSascha Wildner error_r(r, "Couldn't load public key %s", keypath);
27100cbfa66cSDaniel Fojt goto done;
27110cbfa66cSDaniel Fojt }
27120cbfa66cSDaniel Fojt
2713*ee116499SAntonio Huete Jimenez if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) {
2714*ee116499SAntonio Huete Jimenez if (require_agent)
2715*ee116499SAntonio Huete Jimenez fatal("Couldn't get agent socket");
271650a69bb5SSascha Wildner debug_r(r, "Couldn't get agent socket");
2717*ee116499SAntonio Huete Jimenez } else {
27180cbfa66cSDaniel Fojt if ((r = ssh_agent_has_key(agent_fd, pubkey)) == 0)
27190cbfa66cSDaniel Fojt signer = agent_signer;
2720*ee116499SAntonio Huete Jimenez else {
2721*ee116499SAntonio Huete Jimenez if (require_agent)
2722*ee116499SAntonio Huete Jimenez fatal("Couldn't find key in agent");
272350a69bb5SSascha Wildner debug_r(r, "Couldn't find key in agent");
27240cbfa66cSDaniel Fojt }
2725*ee116499SAntonio Huete Jimenez }
27260cbfa66cSDaniel Fojt
27270cbfa66cSDaniel Fojt if (signer == NULL) {
27280cbfa66cSDaniel Fojt /* Not using agent - try to load private key */
27290cbfa66cSDaniel Fojt if ((privkey = load_sign_key(keypath, pubkey)) == NULL)
27300cbfa66cSDaniel Fojt goto done;
27310cbfa66cSDaniel Fojt signkey = privkey;
27320cbfa66cSDaniel Fojt } else {
27330cbfa66cSDaniel Fojt /* Will use key in agent */
27340cbfa66cSDaniel Fojt signkey = pubkey;
27350cbfa66cSDaniel Fojt }
27360cbfa66cSDaniel Fojt
27370cbfa66cSDaniel Fojt if (argc == 0) {
27380cbfa66cSDaniel Fojt if ((r = sign_one(signkey, "(stdin)", STDIN_FILENO,
2739*ee116499SAntonio Huete Jimenez sig_namespace, hashalg, signer, &agent_fd)) != 0)
27400cbfa66cSDaniel Fojt goto done;
27410cbfa66cSDaniel Fojt } else {
27420cbfa66cSDaniel Fojt for (i = 0; i < argc; i++) {
27430cbfa66cSDaniel Fojt if (strcmp(argv[i], "-") == 0)
27440cbfa66cSDaniel Fojt fd = STDIN_FILENO;
27450cbfa66cSDaniel Fojt else if ((fd = open(argv[i], O_RDONLY)) == -1) {
27460cbfa66cSDaniel Fojt error("Cannot open %s for signing: %s",
27470cbfa66cSDaniel Fojt argv[i], strerror(errno));
27480cbfa66cSDaniel Fojt goto done;
27490cbfa66cSDaniel Fojt }
27500cbfa66cSDaniel Fojt if ((r = sign_one(signkey, argv[i], fd, sig_namespace,
2751*ee116499SAntonio Huete Jimenez hashalg, signer, &agent_fd)) != 0)
27520cbfa66cSDaniel Fojt goto done;
27530cbfa66cSDaniel Fojt if (fd != STDIN_FILENO)
27540cbfa66cSDaniel Fojt close(fd);
27550cbfa66cSDaniel Fojt fd = -1;
27560cbfa66cSDaniel Fojt }
27570cbfa66cSDaniel Fojt }
27580cbfa66cSDaniel Fojt
27590cbfa66cSDaniel Fojt ret = 0;
27600cbfa66cSDaniel Fojt done:
27610cbfa66cSDaniel Fojt if (fd != -1 && fd != STDIN_FILENO)
27620cbfa66cSDaniel Fojt close(fd);
27630cbfa66cSDaniel Fojt sshkey_free(pubkey);
27640cbfa66cSDaniel Fojt sshkey_free(privkey);
2765*ee116499SAntonio Huete Jimenez free(hashalg);
27660cbfa66cSDaniel Fojt return ret;
27670cbfa66cSDaniel Fojt }
27680cbfa66cSDaniel Fojt
27690cbfa66cSDaniel Fojt static int
sig_verify(const char * signature,const char * sig_namespace,const char * principal,const char * allowed_keys,const char * revoked_keys,char * const * opts,size_t nopts)27700cbfa66cSDaniel Fojt sig_verify(const char *signature, const char *sig_namespace,
277150a69bb5SSascha Wildner const char *principal, const char *allowed_keys, const char *revoked_keys,
277250a69bb5SSascha Wildner char * const *opts, size_t nopts)
27730cbfa66cSDaniel Fojt {
27740cbfa66cSDaniel Fojt int r, ret = -1;
277550a69bb5SSascha Wildner int print_pubkey = 0;
27760cbfa66cSDaniel Fojt struct sshbuf *sigbuf = NULL, *abuf = NULL;
27770cbfa66cSDaniel Fojt struct sshkey *sign_key = NULL;
27780cbfa66cSDaniel Fojt char *fp = NULL;
27790cbfa66cSDaniel Fojt struct sshkey_sig_details *sig_details = NULL;
278050a69bb5SSascha Wildner uint64_t verify_time = 0;
278150a69bb5SSascha Wildner
2782*ee116499SAntonio Huete Jimenez if (sig_process_opts(opts, nopts, NULL, &verify_time,
2783*ee116499SAntonio Huete Jimenez &print_pubkey) != 0)
278450a69bb5SSascha Wildner goto done; /* error already logged */
27850cbfa66cSDaniel Fojt
27860cbfa66cSDaniel Fojt memset(&sig_details, 0, sizeof(sig_details));
27870cbfa66cSDaniel Fojt if ((r = sshbuf_load_file(signature, &abuf)) != 0) {
278850a69bb5SSascha Wildner error_r(r, "Couldn't read signature file");
27890cbfa66cSDaniel Fojt goto done;
27900cbfa66cSDaniel Fojt }
27910cbfa66cSDaniel Fojt
27920cbfa66cSDaniel Fojt if ((r = sshsig_dearmor(abuf, &sigbuf)) != 0) {
279350a69bb5SSascha Wildner error_fr(r, "sshsig_armor");
27940cbfa66cSDaniel Fojt goto done;
27950cbfa66cSDaniel Fojt }
27960cbfa66cSDaniel Fojt if ((r = sshsig_verify_fd(sigbuf, STDIN_FILENO, sig_namespace,
27970cbfa66cSDaniel Fojt &sign_key, &sig_details)) != 0)
27980cbfa66cSDaniel Fojt goto done; /* sshsig_verify() prints error */
27990cbfa66cSDaniel Fojt
28000cbfa66cSDaniel Fojt if ((fp = sshkey_fingerprint(sign_key, fingerprint_hash,
28010cbfa66cSDaniel Fojt SSH_FP_DEFAULT)) == NULL)
280250a69bb5SSascha Wildner fatal_f("sshkey_fingerprint failed");
28030cbfa66cSDaniel Fojt debug("Valid (unverified) signature from key %s", fp);
28040cbfa66cSDaniel Fojt if (sig_details != NULL) {
280550a69bb5SSascha Wildner debug2_f("signature details: counter = %u, flags = 0x%02x",
280650a69bb5SSascha Wildner sig_details->sk_counter, sig_details->sk_flags);
28070cbfa66cSDaniel Fojt }
28080cbfa66cSDaniel Fojt free(fp);
28090cbfa66cSDaniel Fojt fp = NULL;
28100cbfa66cSDaniel Fojt
28110cbfa66cSDaniel Fojt if (revoked_keys != NULL) {
28120cbfa66cSDaniel Fojt if ((r = sshkey_check_revoked(sign_key, revoked_keys)) != 0) {
281350a69bb5SSascha Wildner debug3_fr(r, "sshkey_check_revoked");
28140cbfa66cSDaniel Fojt goto done;
28150cbfa66cSDaniel Fojt }
28160cbfa66cSDaniel Fojt }
28170cbfa66cSDaniel Fojt
281850a69bb5SSascha Wildner if (allowed_keys != NULL && (r = sshsig_check_allowed_keys(allowed_keys,
281950a69bb5SSascha Wildner sign_key, principal, sig_namespace, verify_time)) != 0) {
282050a69bb5SSascha Wildner debug3_fr(r, "sshsig_check_allowed_keys");
28210cbfa66cSDaniel Fojt goto done;
28220cbfa66cSDaniel Fojt }
28230cbfa66cSDaniel Fojt /* success */
28240cbfa66cSDaniel Fojt ret = 0;
28250cbfa66cSDaniel Fojt done:
28260cbfa66cSDaniel Fojt if (!quiet) {
28270cbfa66cSDaniel Fojt if (ret == 0) {
28280cbfa66cSDaniel Fojt if ((fp = sshkey_fingerprint(sign_key, fingerprint_hash,
282950a69bb5SSascha Wildner SSH_FP_DEFAULT)) == NULL)
283050a69bb5SSascha Wildner fatal_f("sshkey_fingerprint failed");
28310cbfa66cSDaniel Fojt if (principal == NULL) {
28320cbfa66cSDaniel Fojt printf("Good \"%s\" signature with %s key %s\n",
28330cbfa66cSDaniel Fojt sig_namespace, sshkey_type(sign_key), fp);
28340cbfa66cSDaniel Fojt
28350cbfa66cSDaniel Fojt } else {
28360cbfa66cSDaniel Fojt printf("Good \"%s\" signature for %s with %s key %s\n",
28370cbfa66cSDaniel Fojt sig_namespace, principal,
28380cbfa66cSDaniel Fojt sshkey_type(sign_key), fp);
28390cbfa66cSDaniel Fojt }
28400cbfa66cSDaniel Fojt } else {
28410cbfa66cSDaniel Fojt printf("Could not verify signature.\n");
28420cbfa66cSDaniel Fojt }
28430cbfa66cSDaniel Fojt }
284450a69bb5SSascha Wildner /* Print the signature key if requested */
284550a69bb5SSascha Wildner if (ret == 0 && print_pubkey && sign_key != NULL) {
284650a69bb5SSascha Wildner if ((r = sshkey_write(sign_key, stdout)) == 0)
284750a69bb5SSascha Wildner fputc('\n', stdout);
284850a69bb5SSascha Wildner else {
284950a69bb5SSascha Wildner error_r(r, "Could not print public key.\n");
285050a69bb5SSascha Wildner ret = -1;
285150a69bb5SSascha Wildner }
285250a69bb5SSascha Wildner }
28530cbfa66cSDaniel Fojt sshbuf_free(sigbuf);
28540cbfa66cSDaniel Fojt sshbuf_free(abuf);
28550cbfa66cSDaniel Fojt sshkey_free(sign_key);
28560cbfa66cSDaniel Fojt sshkey_sig_details_free(sig_details);
28570cbfa66cSDaniel Fojt free(fp);
28580cbfa66cSDaniel Fojt return ret;
28590cbfa66cSDaniel Fojt }
28600cbfa66cSDaniel Fojt
28610cbfa66cSDaniel Fojt static int
sig_find_principals(const char * signature,const char * allowed_keys,char * const * opts,size_t nopts)286250a69bb5SSascha Wildner sig_find_principals(const char *signature, const char *allowed_keys,
286350a69bb5SSascha Wildner char * const *opts, size_t nopts)
286450a69bb5SSascha Wildner {
28650cbfa66cSDaniel Fojt int r, ret = -1;
28660cbfa66cSDaniel Fojt struct sshbuf *sigbuf = NULL, *abuf = NULL;
28670cbfa66cSDaniel Fojt struct sshkey *sign_key = NULL;
28680cbfa66cSDaniel Fojt char *principals = NULL, *cp, *tmp;
286950a69bb5SSascha Wildner uint64_t verify_time = 0;
287050a69bb5SSascha Wildner
2871*ee116499SAntonio Huete Jimenez if (sig_process_opts(opts, nopts, NULL, &verify_time, NULL) != 0)
287250a69bb5SSascha Wildner goto done; /* error already logged */
28730cbfa66cSDaniel Fojt
28740cbfa66cSDaniel Fojt if ((r = sshbuf_load_file(signature, &abuf)) != 0) {
287550a69bb5SSascha Wildner error_r(r, "Couldn't read signature file");
28760cbfa66cSDaniel Fojt goto done;
28770cbfa66cSDaniel Fojt }
28780cbfa66cSDaniel Fojt if ((r = sshsig_dearmor(abuf, &sigbuf)) != 0) {
287950a69bb5SSascha Wildner error_fr(r, "sshsig_armor");
28800cbfa66cSDaniel Fojt goto done;
28810cbfa66cSDaniel Fojt }
28820cbfa66cSDaniel Fojt if ((r = sshsig_get_pubkey(sigbuf, &sign_key)) != 0) {
288350a69bb5SSascha Wildner error_fr(r, "sshsig_get_pubkey");
28840cbfa66cSDaniel Fojt goto done;
28850cbfa66cSDaniel Fojt }
28860cbfa66cSDaniel Fojt if ((r = sshsig_find_principals(allowed_keys, sign_key,
288750a69bb5SSascha Wildner verify_time, &principals)) != 0) {
288850a69bb5SSascha Wildner if (r != SSH_ERR_KEY_NOT_FOUND)
288950a69bb5SSascha Wildner error_fr(r, "sshsig_find_principal");
28900cbfa66cSDaniel Fojt goto done;
28910cbfa66cSDaniel Fojt }
28920cbfa66cSDaniel Fojt ret = 0;
28930cbfa66cSDaniel Fojt done:
28940cbfa66cSDaniel Fojt if (ret == 0 ) {
28950cbfa66cSDaniel Fojt /* Emit matching principals one per line */
28960cbfa66cSDaniel Fojt tmp = principals;
28970cbfa66cSDaniel Fojt while ((cp = strsep(&tmp, ",")) != NULL && *cp != '\0')
28980cbfa66cSDaniel Fojt puts(cp);
28990cbfa66cSDaniel Fojt } else {
29000cbfa66cSDaniel Fojt fprintf(stderr, "No principal matched.\n");
29010cbfa66cSDaniel Fojt }
29020cbfa66cSDaniel Fojt sshbuf_free(sigbuf);
29030cbfa66cSDaniel Fojt sshbuf_free(abuf);
29040cbfa66cSDaniel Fojt sshkey_free(sign_key);
29050cbfa66cSDaniel Fojt free(principals);
29060cbfa66cSDaniel Fojt return ret;
29070cbfa66cSDaniel Fojt }
29080cbfa66cSDaniel Fojt
2909*ee116499SAntonio Huete Jimenez static int
sig_match_principals(const char * allowed_keys,char * principal,char * const * opts,size_t nopts)2910*ee116499SAntonio Huete Jimenez sig_match_principals(const char *allowed_keys, char *principal,
2911*ee116499SAntonio Huete Jimenez char * const *opts, size_t nopts)
2912*ee116499SAntonio Huete Jimenez {
2913*ee116499SAntonio Huete Jimenez int r;
2914*ee116499SAntonio Huete Jimenez char **principals = NULL;
2915*ee116499SAntonio Huete Jimenez size_t i, nprincipals = 0;
2916*ee116499SAntonio Huete Jimenez
2917*ee116499SAntonio Huete Jimenez if ((r = sig_process_opts(opts, nopts, NULL, NULL, NULL)) != 0)
2918*ee116499SAntonio Huete Jimenez return r; /* error already logged */
2919*ee116499SAntonio Huete Jimenez
2920*ee116499SAntonio Huete Jimenez if ((r = sshsig_match_principals(allowed_keys, principal,
2921*ee116499SAntonio Huete Jimenez &principals, &nprincipals)) != 0) {
2922*ee116499SAntonio Huete Jimenez debug_f("match: %s", ssh_err(r));
2923*ee116499SAntonio Huete Jimenez fprintf(stderr, "No principal matched.\n");
2924*ee116499SAntonio Huete Jimenez return r;
2925*ee116499SAntonio Huete Jimenez }
2926*ee116499SAntonio Huete Jimenez for (i = 0; i < nprincipals; i++) {
2927*ee116499SAntonio Huete Jimenez printf("%s\n", principals[i]);
2928*ee116499SAntonio Huete Jimenez free(principals[i]);
2929*ee116499SAntonio Huete Jimenez }
2930*ee116499SAntonio Huete Jimenez free(principals);
2931*ee116499SAntonio Huete Jimenez
2932*ee116499SAntonio Huete Jimenez return 0;
2933*ee116499SAntonio Huete Jimenez }
2934*ee116499SAntonio Huete Jimenez
29350cbfa66cSDaniel Fojt static void
do_moduli_gen(const char * out_file,char ** opts,size_t nopts)29360cbfa66cSDaniel Fojt do_moduli_gen(const char *out_file, char **opts, size_t nopts)
29370cbfa66cSDaniel Fojt {
29380cbfa66cSDaniel Fojt #ifdef WITH_OPENSSL
29390cbfa66cSDaniel Fojt /* Moduli generation/screening */
29400cbfa66cSDaniel Fojt u_int32_t memory = 0;
29410cbfa66cSDaniel Fojt BIGNUM *start = NULL;
29420cbfa66cSDaniel Fojt int moduli_bits = 0;
29430cbfa66cSDaniel Fojt FILE *out;
29440cbfa66cSDaniel Fojt size_t i;
29450cbfa66cSDaniel Fojt const char *errstr;
29460cbfa66cSDaniel Fojt
29470cbfa66cSDaniel Fojt /* Parse options */
29480cbfa66cSDaniel Fojt for (i = 0; i < nopts; i++) {
29490cbfa66cSDaniel Fojt if (strncmp(opts[i], "memory=", 7) == 0) {
29500cbfa66cSDaniel Fojt memory = (u_int32_t)strtonum(opts[i]+7, 1,
29510cbfa66cSDaniel Fojt UINT_MAX, &errstr);
29520cbfa66cSDaniel Fojt if (errstr) {
29530cbfa66cSDaniel Fojt fatal("Memory limit is %s: %s",
29540cbfa66cSDaniel Fojt errstr, opts[i]+7);
29550cbfa66cSDaniel Fojt }
29560cbfa66cSDaniel Fojt } else if (strncmp(opts[i], "start=", 6) == 0) {
29570cbfa66cSDaniel Fojt /* XXX - also compare length against bits */
29580cbfa66cSDaniel Fojt if (BN_hex2bn(&start, opts[i]+6) == 0)
29590cbfa66cSDaniel Fojt fatal("Invalid start point.");
29600cbfa66cSDaniel Fojt } else if (strncmp(opts[i], "bits=", 5) == 0) {
29610cbfa66cSDaniel Fojt moduli_bits = (int)strtonum(opts[i]+5, 1,
29620cbfa66cSDaniel Fojt INT_MAX, &errstr);
29630cbfa66cSDaniel Fojt if (errstr) {
29640cbfa66cSDaniel Fojt fatal("Invalid number: %s (%s)",
29650cbfa66cSDaniel Fojt opts[i]+12, errstr);
29660cbfa66cSDaniel Fojt }
29670cbfa66cSDaniel Fojt } else {
29680cbfa66cSDaniel Fojt fatal("Option \"%s\" is unsupported for moduli "
29690cbfa66cSDaniel Fojt "generation", opts[i]);
29700cbfa66cSDaniel Fojt }
29710cbfa66cSDaniel Fojt }
29720cbfa66cSDaniel Fojt
29730cbfa66cSDaniel Fojt if ((out = fopen(out_file, "w")) == NULL) {
29740cbfa66cSDaniel Fojt fatal("Couldn't open modulus candidate file \"%s\": %s",
29750cbfa66cSDaniel Fojt out_file, strerror(errno));
29760cbfa66cSDaniel Fojt }
29770cbfa66cSDaniel Fojt setvbuf(out, NULL, _IOLBF, 0);
29780cbfa66cSDaniel Fojt
29790cbfa66cSDaniel Fojt if (moduli_bits == 0)
29800cbfa66cSDaniel Fojt moduli_bits = DEFAULT_BITS;
29810cbfa66cSDaniel Fojt if (gen_candidates(out, memory, moduli_bits, start) != 0)
29820cbfa66cSDaniel Fojt fatal("modulus candidate generation failed");
29830cbfa66cSDaniel Fojt #else /* WITH_OPENSSL */
29840cbfa66cSDaniel Fojt fatal("Moduli generation is not supported");
29850cbfa66cSDaniel Fojt #endif /* WITH_OPENSSL */
29860cbfa66cSDaniel Fojt }
29870cbfa66cSDaniel Fojt
29880cbfa66cSDaniel Fojt static void
do_moduli_screen(const char * out_file,char ** opts,size_t nopts)29890cbfa66cSDaniel Fojt do_moduli_screen(const char *out_file, char **opts, size_t nopts)
29900cbfa66cSDaniel Fojt {
29910cbfa66cSDaniel Fojt #ifdef WITH_OPENSSL
29920cbfa66cSDaniel Fojt /* Moduli generation/screening */
29930cbfa66cSDaniel Fojt char *checkpoint = NULL;
29940cbfa66cSDaniel Fojt u_int32_t generator_wanted = 0;
29950cbfa66cSDaniel Fojt unsigned long start_lineno = 0, lines_to_process = 0;
29960cbfa66cSDaniel Fojt int prime_tests = 0;
29970cbfa66cSDaniel Fojt FILE *out, *in = stdin;
29980cbfa66cSDaniel Fojt size_t i;
29990cbfa66cSDaniel Fojt const char *errstr;
30000cbfa66cSDaniel Fojt
30010cbfa66cSDaniel Fojt /* Parse options */
30020cbfa66cSDaniel Fojt for (i = 0; i < nopts; i++) {
30030cbfa66cSDaniel Fojt if (strncmp(opts[i], "lines=", 6) == 0) {
30040cbfa66cSDaniel Fojt lines_to_process = strtoul(opts[i]+6, NULL, 10);
30050cbfa66cSDaniel Fojt } else if (strncmp(opts[i], "start-line=", 11) == 0) {
30060cbfa66cSDaniel Fojt start_lineno = strtoul(opts[i]+11, NULL, 10);
30070cbfa66cSDaniel Fojt } else if (strncmp(opts[i], "checkpoint=", 11) == 0) {
30080cbfa66cSDaniel Fojt checkpoint = xstrdup(opts[i]+11);
30090cbfa66cSDaniel Fojt } else if (strncmp(opts[i], "generator=", 10) == 0) {
30100cbfa66cSDaniel Fojt generator_wanted = (u_int32_t)strtonum(
30110cbfa66cSDaniel Fojt opts[i]+10, 1, UINT_MAX, &errstr);
30120cbfa66cSDaniel Fojt if (errstr != NULL) {
30130cbfa66cSDaniel Fojt fatal("Generator invalid: %s (%s)",
30140cbfa66cSDaniel Fojt opts[i]+10, errstr);
30150cbfa66cSDaniel Fojt }
30160cbfa66cSDaniel Fojt } else if (strncmp(opts[i], "prime-tests=", 12) == 0) {
30170cbfa66cSDaniel Fojt prime_tests = (int)strtonum(opts[i]+12, 1,
30180cbfa66cSDaniel Fojt INT_MAX, &errstr);
30190cbfa66cSDaniel Fojt if (errstr) {
30200cbfa66cSDaniel Fojt fatal("Invalid number: %s (%s)",
30210cbfa66cSDaniel Fojt opts[i]+12, errstr);
30220cbfa66cSDaniel Fojt }
30230cbfa66cSDaniel Fojt } else {
30240cbfa66cSDaniel Fojt fatal("Option \"%s\" is unsupported for moduli "
30250cbfa66cSDaniel Fojt "screening", opts[i]);
30260cbfa66cSDaniel Fojt }
30270cbfa66cSDaniel Fojt }
30280cbfa66cSDaniel Fojt
30290cbfa66cSDaniel Fojt if (have_identity && strcmp(identity_file, "-") != 0) {
30300cbfa66cSDaniel Fojt if ((in = fopen(identity_file, "r")) == NULL) {
30310cbfa66cSDaniel Fojt fatal("Couldn't open modulus candidate "
30320cbfa66cSDaniel Fojt "file \"%s\": %s", identity_file,
30330cbfa66cSDaniel Fojt strerror(errno));
30340cbfa66cSDaniel Fojt }
30350cbfa66cSDaniel Fojt }
30360cbfa66cSDaniel Fojt
30370cbfa66cSDaniel Fojt if ((out = fopen(out_file, "a")) == NULL) {
30380cbfa66cSDaniel Fojt fatal("Couldn't open moduli file \"%s\": %s",
30390cbfa66cSDaniel Fojt out_file, strerror(errno));
30400cbfa66cSDaniel Fojt }
30410cbfa66cSDaniel Fojt setvbuf(out, NULL, _IOLBF, 0);
30420cbfa66cSDaniel Fojt if (prime_test(in, out, prime_tests == 0 ? 100 : prime_tests,
30430cbfa66cSDaniel Fojt generator_wanted, checkpoint,
30440cbfa66cSDaniel Fojt start_lineno, lines_to_process) != 0)
30450cbfa66cSDaniel Fojt fatal("modulus screening failed");
30460cbfa66cSDaniel Fojt #else /* WITH_OPENSSL */
30470cbfa66cSDaniel Fojt fatal("Moduli screening is not supported");
30480cbfa66cSDaniel Fojt #endif /* WITH_OPENSSL */
30490cbfa66cSDaniel Fojt }
30500cbfa66cSDaniel Fojt
3051*ee116499SAntonio Huete Jimenez /* Read and confirm a passphrase */
30520cbfa66cSDaniel Fojt static char *
read_check_passphrase(const char * prompt1,const char * prompt2,const char * retry_prompt)3053*ee116499SAntonio Huete Jimenez read_check_passphrase(const char *prompt1, const char *prompt2,
3054*ee116499SAntonio Huete Jimenez const char *retry_prompt)
30550cbfa66cSDaniel Fojt {
30560cbfa66cSDaniel Fojt char *passphrase1, *passphrase2;
30570cbfa66cSDaniel Fojt
3058*ee116499SAntonio Huete Jimenez for (;;) {
3059*ee116499SAntonio Huete Jimenez passphrase1 = read_passphrase(prompt1, RP_ALLOW_STDIN);
3060*ee116499SAntonio Huete Jimenez passphrase2 = read_passphrase(prompt2, RP_ALLOW_STDIN);
3061*ee116499SAntonio Huete Jimenez if (strcmp(passphrase1, passphrase2) == 0) {
30620cbfa66cSDaniel Fojt freezero(passphrase2, strlen(passphrase2));
30630cbfa66cSDaniel Fojt return passphrase1;
30640cbfa66cSDaniel Fojt }
3065*ee116499SAntonio Huete Jimenez /* The passphrases do not match. Clear them and retry. */
3066*ee116499SAntonio Huete Jimenez freezero(passphrase1, strlen(passphrase1));
3067*ee116499SAntonio Huete Jimenez freezero(passphrase2, strlen(passphrase2));
3068*ee116499SAntonio Huete Jimenez fputs(retry_prompt, stdout);
3069*ee116499SAntonio Huete Jimenez fputc('\n', stdout);
3070*ee116499SAntonio Huete Jimenez fflush(stdout);
3071*ee116499SAntonio Huete Jimenez }
3072*ee116499SAntonio Huete Jimenez /* NOTREACHED */
3073*ee116499SAntonio Huete Jimenez return NULL;
3074*ee116499SAntonio Huete Jimenez }
30750cbfa66cSDaniel Fojt
3076*ee116499SAntonio Huete Jimenez static char *
private_key_passphrase(void)3077*ee116499SAntonio Huete Jimenez private_key_passphrase(void)
30780cbfa66cSDaniel Fojt {
3079*ee116499SAntonio Huete Jimenez if (identity_passphrase)
3080*ee116499SAntonio Huete Jimenez return xstrdup(identity_passphrase);
3081*ee116499SAntonio Huete Jimenez if (identity_new_passphrase)
3082*ee116499SAntonio Huete Jimenez return xstrdup(identity_new_passphrase);
3083*ee116499SAntonio Huete Jimenez
3084*ee116499SAntonio Huete Jimenez return read_check_passphrase(
3085*ee116499SAntonio Huete Jimenez "Enter passphrase (empty for no passphrase): ",
3086*ee116499SAntonio Huete Jimenez "Enter same passphrase again: ",
3087*ee116499SAntonio Huete Jimenez "Passphrases do not match. Try again.");
3088*ee116499SAntonio Huete Jimenez }
3089*ee116499SAntonio Huete Jimenez
3090*ee116499SAntonio Huete Jimenez static char *
sk_suffix(const char * application,const uint8_t * user,size_t userlen)3091*ee116499SAntonio Huete Jimenez sk_suffix(const char *application, const uint8_t *user, size_t userlen)
3092*ee116499SAntonio Huete Jimenez {
3093*ee116499SAntonio Huete Jimenez char *ret, *cp;
3094*ee116499SAntonio Huete Jimenez size_t slen, i;
3095*ee116499SAntonio Huete Jimenez
3096*ee116499SAntonio Huete Jimenez /* Trim off URL-like preamble */
3097*ee116499SAntonio Huete Jimenez if (strncmp(application, "ssh://", 6) == 0)
3098*ee116499SAntonio Huete Jimenez ret = xstrdup(application + 6);
3099*ee116499SAntonio Huete Jimenez else if (strncmp(application, "ssh:", 4) == 0)
3100*ee116499SAntonio Huete Jimenez ret = xstrdup(application + 4);
3101*ee116499SAntonio Huete Jimenez else
3102*ee116499SAntonio Huete Jimenez ret = xstrdup(application);
3103*ee116499SAntonio Huete Jimenez
3104*ee116499SAntonio Huete Jimenez /* Count trailing zeros in user */
3105*ee116499SAntonio Huete Jimenez for (i = 0; i < userlen; i++) {
3106*ee116499SAntonio Huete Jimenez if (user[userlen - i - 1] != 0)
3107*ee116499SAntonio Huete Jimenez break;
3108*ee116499SAntonio Huete Jimenez }
3109*ee116499SAntonio Huete Jimenez if (i >= userlen)
3110*ee116499SAntonio Huete Jimenez return ret; /* user-id was default all-zeros */
3111*ee116499SAntonio Huete Jimenez
3112*ee116499SAntonio Huete Jimenez /* Append user-id, escaping non-UTF-8 characters */
3113*ee116499SAntonio Huete Jimenez slen = userlen - i;
3114*ee116499SAntonio Huete Jimenez if (asmprintf(&cp, INT_MAX, NULL, "%.*s", (int)slen, user) == -1)
3115*ee116499SAntonio Huete Jimenez fatal_f("asmprintf failed");
3116*ee116499SAntonio Huete Jimenez /* Don't emit a user-id that contains path or control characters */
3117*ee116499SAntonio Huete Jimenez if (strchr(cp, '/') != NULL || strstr(cp, "..") != NULL ||
3118*ee116499SAntonio Huete Jimenez strchr(cp, '\\') != NULL) {
3119*ee116499SAntonio Huete Jimenez free(cp);
3120*ee116499SAntonio Huete Jimenez cp = tohex(user, slen);
3121*ee116499SAntonio Huete Jimenez }
3122*ee116499SAntonio Huete Jimenez xextendf(&ret, "_", "%s", cp);
3123*ee116499SAntonio Huete Jimenez free(cp);
3124*ee116499SAntonio Huete Jimenez return ret;
31250cbfa66cSDaniel Fojt }
31260cbfa66cSDaniel Fojt
31270cbfa66cSDaniel Fojt static int
do_download_sk(const char * skprovider,const char * device)31280cbfa66cSDaniel Fojt do_download_sk(const char *skprovider, const char *device)
31290cbfa66cSDaniel Fojt {
3130*ee116499SAntonio Huete Jimenez struct sshsk_resident_key **srks;
3131*ee116499SAntonio Huete Jimenez size_t nsrks, i;
313250a69bb5SSascha Wildner int r, ret = -1;
31330cbfa66cSDaniel Fojt char *fp, *pin = NULL, *pass = NULL, *path, *pubpath;
31340cbfa66cSDaniel Fojt const char *ext;
3135*ee116499SAntonio Huete Jimenez struct sshkey *key;
31360cbfa66cSDaniel Fojt
31370cbfa66cSDaniel Fojt if (skprovider == NULL)
31380cbfa66cSDaniel Fojt fatal("Cannot download keys without provider");
31390cbfa66cSDaniel Fojt
314050a69bb5SSascha Wildner pin = read_passphrase("Enter PIN for authenticator: ", RP_ALLOW_STDIN);
314150a69bb5SSascha Wildner if (!quiet) {
314250a69bb5SSascha Wildner printf("You may need to touch your authenticator "
314350a69bb5SSascha Wildner "to authorize key download.\n");
31440cbfa66cSDaniel Fojt }
3145*ee116499SAntonio Huete Jimenez if ((r = sshsk_load_resident(skprovider, device, pin, 0,
3146*ee116499SAntonio Huete Jimenez &srks, &nsrks)) != 0) {
31470cbfa66cSDaniel Fojt if (pin != NULL)
31480cbfa66cSDaniel Fojt freezero(pin, strlen(pin));
314950a69bb5SSascha Wildner error_r(r, "Unable to load resident keys");
31500cbfa66cSDaniel Fojt return -1;
31510cbfa66cSDaniel Fojt }
3152*ee116499SAntonio Huete Jimenez if (nsrks == 0)
31530cbfa66cSDaniel Fojt logit("No keys to download");
31540cbfa66cSDaniel Fojt if (pin != NULL)
31550cbfa66cSDaniel Fojt freezero(pin, strlen(pin));
31560cbfa66cSDaniel Fojt
3157*ee116499SAntonio Huete Jimenez for (i = 0; i < nsrks; i++) {
3158*ee116499SAntonio Huete Jimenez key = srks[i]->key;
3159*ee116499SAntonio Huete Jimenez if (key->type != KEY_ECDSA_SK && key->type != KEY_ED25519_SK) {
31600cbfa66cSDaniel Fojt error("Unsupported key type %s (%d)",
3161*ee116499SAntonio Huete Jimenez sshkey_type(key), key->type);
31620cbfa66cSDaniel Fojt continue;
31630cbfa66cSDaniel Fojt }
3164*ee116499SAntonio Huete Jimenez if ((fp = sshkey_fingerprint(key, fingerprint_hash,
3165*ee116499SAntonio Huete Jimenez SSH_FP_DEFAULT)) == NULL)
316650a69bb5SSascha Wildner fatal_f("sshkey_fingerprint failed");
316750a69bb5SSascha Wildner debug_f("key %zu: %s %s %s (flags 0x%02x)", i,
3168*ee116499SAntonio Huete Jimenez sshkey_type(key), fp, key->sk_application, key->sk_flags);
3169*ee116499SAntonio Huete Jimenez ext = sk_suffix(key->sk_application,
3170*ee116499SAntonio Huete Jimenez srks[i]->user_id, srks[i]->user_id_len);
31710cbfa66cSDaniel Fojt xasprintf(&path, "id_%s_rk%s%s",
3172*ee116499SAntonio Huete Jimenez key->type == KEY_ECDSA_SK ? "ecdsa_sk" : "ed25519_sk",
31730cbfa66cSDaniel Fojt *ext == '\0' ? "" : "_", ext);
31740cbfa66cSDaniel Fojt
31750cbfa66cSDaniel Fojt /* If the file already exists, ask the user to confirm. */
31760cbfa66cSDaniel Fojt if (!confirm_overwrite(path)) {
31770cbfa66cSDaniel Fojt free(path);
31780cbfa66cSDaniel Fojt break;
31790cbfa66cSDaniel Fojt }
31800cbfa66cSDaniel Fojt
31810cbfa66cSDaniel Fojt /* Save the key with the application string as the comment */
31820cbfa66cSDaniel Fojt if (pass == NULL)
31830cbfa66cSDaniel Fojt pass = private_key_passphrase();
3184*ee116499SAntonio Huete Jimenez if ((r = sshkey_save_private(key, path, pass,
3185*ee116499SAntonio Huete Jimenez key->sk_application, private_key_format,
31860cbfa66cSDaniel Fojt openssh_format_cipher, rounds)) != 0) {
318750a69bb5SSascha Wildner error_r(r, "Saving key \"%s\" failed", path);
31880cbfa66cSDaniel Fojt free(path);
31890cbfa66cSDaniel Fojt break;
31900cbfa66cSDaniel Fojt }
31910cbfa66cSDaniel Fojt if (!quiet) {
3192*ee116499SAntonio Huete Jimenez printf("Saved %s key%s%s to %s\n", sshkey_type(key),
31930cbfa66cSDaniel Fojt *ext != '\0' ? " " : "",
3194*ee116499SAntonio Huete Jimenez *ext != '\0' ? key->sk_application : "",
31950cbfa66cSDaniel Fojt path);
31960cbfa66cSDaniel Fojt }
31970cbfa66cSDaniel Fojt
31980cbfa66cSDaniel Fojt /* Save public key too */
31990cbfa66cSDaniel Fojt xasprintf(&pubpath, "%s.pub", path);
32000cbfa66cSDaniel Fojt free(path);
3201*ee116499SAntonio Huete Jimenez if ((r = sshkey_save_public(key, pubpath,
3202*ee116499SAntonio Huete Jimenez key->sk_application)) != 0) {
320350a69bb5SSascha Wildner error_r(r, "Saving public key \"%s\" failed", pubpath);
32040cbfa66cSDaniel Fojt free(pubpath);
32050cbfa66cSDaniel Fojt break;
32060cbfa66cSDaniel Fojt }
32070cbfa66cSDaniel Fojt free(pubpath);
32080cbfa66cSDaniel Fojt }
32090cbfa66cSDaniel Fojt
3210*ee116499SAntonio Huete Jimenez if (i >= nsrks)
321150a69bb5SSascha Wildner ret = 0; /* success */
32120cbfa66cSDaniel Fojt if (pass != NULL)
32130cbfa66cSDaniel Fojt freezero(pass, strlen(pass));
3214*ee116499SAntonio Huete Jimenez sshsk_free_resident_keys(srks, nsrks);
321550a69bb5SSascha Wildner return ret;
321650a69bb5SSascha Wildner }
321750a69bb5SSascha Wildner
321850a69bb5SSascha Wildner static void
save_attestation(struct sshbuf * attest,const char * path)321950a69bb5SSascha Wildner save_attestation(struct sshbuf *attest, const char *path)
322050a69bb5SSascha Wildner {
322150a69bb5SSascha Wildner mode_t omask;
322250a69bb5SSascha Wildner int r;
322350a69bb5SSascha Wildner
322450a69bb5SSascha Wildner if (path == NULL)
322550a69bb5SSascha Wildner return; /* nothing to do */
322650a69bb5SSascha Wildner if (attest == NULL || sshbuf_len(attest) == 0)
322750a69bb5SSascha Wildner fatal("Enrollment did not return attestation data");
322850a69bb5SSascha Wildner omask = umask(077);
322950a69bb5SSascha Wildner r = sshbuf_write_file(path, attest);
323050a69bb5SSascha Wildner umask(omask);
323150a69bb5SSascha Wildner if (r != 0)
323250a69bb5SSascha Wildner fatal_r(r, "Unable to write attestation data \"%s\"", path);
323350a69bb5SSascha Wildner if (!quiet)
323450a69bb5SSascha Wildner printf("Your FIDO attestation certificate has been saved in "
323550a69bb5SSascha Wildner "%s\n", path);
32360cbfa66cSDaniel Fojt }
32370cbfa66cSDaniel Fojt
3238*ee116499SAntonio Huete Jimenez static int
confirm_sk_overwrite(const char * application,const char * user)3239*ee116499SAntonio Huete Jimenez confirm_sk_overwrite(const char *application, const char *user)
3240*ee116499SAntonio Huete Jimenez {
3241*ee116499SAntonio Huete Jimenez char yesno[3];
3242*ee116499SAntonio Huete Jimenez
3243*ee116499SAntonio Huete Jimenez printf("A resident key scoped to '%s' with user id '%s' already "
3244*ee116499SAntonio Huete Jimenez "exists.\n", application == NULL ? "ssh:" : application,
3245*ee116499SAntonio Huete Jimenez user == NULL ? "null" : user);
3246*ee116499SAntonio Huete Jimenez printf("Overwrite key in token (y/n)? ");
3247*ee116499SAntonio Huete Jimenez fflush(stdout);
3248*ee116499SAntonio Huete Jimenez if (fgets(yesno, sizeof(yesno), stdin) == NULL)
3249*ee116499SAntonio Huete Jimenez return 0;
3250*ee116499SAntonio Huete Jimenez if (yesno[0] != 'y' && yesno[0] != 'Y')
3251*ee116499SAntonio Huete Jimenez return 0;
3252*ee116499SAntonio Huete Jimenez return 1;
3253*ee116499SAntonio Huete Jimenez }
3254*ee116499SAntonio Huete Jimenez
325518de8d7fSPeter Avalos static void
usage(void)325618de8d7fSPeter Avalos usage(void)
325718de8d7fSPeter Avalos {
325836e94dc5SPeter Avalos fprintf(stderr,
325950a69bb5SSascha Wildner "usage: ssh-keygen [-q] [-a rounds] [-b bits] [-C comment] [-f output_keyfile]\n"
326050a69bb5SSascha Wildner " [-m format] [-N new_passphrase] [-O option]\n"
32610cbfa66cSDaniel Fojt " [-t dsa | ecdsa | ecdsa-sk | ed25519 | ed25519-sk | rsa]\n"
326250a69bb5SSascha Wildner " [-w provider] [-Z cipher]\n"
326350a69bb5SSascha Wildner " ssh-keygen -p [-a rounds] [-f keyfile] [-m format] [-N new_passphrase]\n"
326450a69bb5SSascha Wildner " [-P old_passphrase] [-Z cipher]\n"
326550a69bb5SSascha Wildner #ifdef WITH_OPENSSL
32660cbfa66cSDaniel Fojt " ssh-keygen -i [-f input_keyfile] [-m key_format]\n"
32670cbfa66cSDaniel Fojt " ssh-keygen -e [-f input_keyfile] [-m key_format]\n"
326850a69bb5SSascha Wildner #endif
326936e94dc5SPeter Avalos " ssh-keygen -y [-f input_keyfile]\n"
327050a69bb5SSascha Wildner " ssh-keygen -c [-a rounds] [-C comment] [-f keyfile] [-P passphrase]\n"
3271e9778795SPeter Avalos " ssh-keygen -l [-v] [-E fingerprint_hash] [-f input_keyfile]\n"
327236e94dc5SPeter Avalos " ssh-keygen -B [-f input_keyfile]\n");
3273856ea928SPeter Avalos #ifdef ENABLE_PKCS11
327436e94dc5SPeter Avalos fprintf(stderr,
327536e94dc5SPeter Avalos " ssh-keygen -D pkcs11\n");
3276856ea928SPeter Avalos #endif
327736e94dc5SPeter Avalos fprintf(stderr,
32780cbfa66cSDaniel Fojt " ssh-keygen -F hostname [-lv] [-f known_hosts_file]\n"
327936e94dc5SPeter Avalos " ssh-keygen -H [-f known_hosts_file]\n"
328050a69bb5SSascha Wildner " ssh-keygen -K [-a rounds] [-w provider]\n"
328136e94dc5SPeter Avalos " ssh-keygen -R hostname [-f known_hosts_file]\n"
32820cbfa66cSDaniel Fojt " ssh-keygen -r hostname [-g] [-f input_keyfile]\n"
3283e9778795SPeter Avalos #ifdef WITH_OPENSSL
32840cbfa66cSDaniel Fojt " ssh-keygen -M generate [-O option] output_file\n"
32850cbfa66cSDaniel Fojt " ssh-keygen -M screen [-f input_file] [-O option] output_file\n"
3286e9778795SPeter Avalos #endif
32870cbfa66cSDaniel Fojt " ssh-keygen -I certificate_identity -s ca_key [-hU] [-D pkcs11_provider]\n"
32880cbfa66cSDaniel Fojt " [-n principals] [-O option] [-V validity_interval]\n"
32890cbfa66cSDaniel Fojt " [-z serial_number] file ...\n"
329036e94dc5SPeter Avalos " ssh-keygen -L [-f input_keyfile]\n"
329150a69bb5SSascha Wildner " ssh-keygen -A [-a rounds] [-f prefix_path]\n"
329236e94dc5SPeter Avalos " ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n"
329336e94dc5SPeter Avalos " file ...\n"
32940cbfa66cSDaniel Fojt " ssh-keygen -Q [-l] -f krl_file [file ...]\n"
32950cbfa66cSDaniel Fojt " ssh-keygen -Y find-principals -s signature_file -f allowed_signers_file\n"
3296*ee116499SAntonio Huete Jimenez " ssh-keygen -Y match-principals -I signer_identity -f allowed_signers_file\n"
32970cbfa66cSDaniel Fojt " ssh-keygen -Y check-novalidate -n namespace -s signature_file\n"
3298*ee116499SAntonio Huete Jimenez " ssh-keygen -Y sign -f key_file -n namespace file [-O option] ...\n"
32990cbfa66cSDaniel Fojt " ssh-keygen -Y verify -f allowed_signers_file -I signer_identity\n"
3300*ee116499SAntonio Huete Jimenez " -n namespace -s signature_file [-r krl_file] [-O option]\n");
330118de8d7fSPeter Avalos exit(1);
330218de8d7fSPeter Avalos }
330318de8d7fSPeter Avalos
330418de8d7fSPeter Avalos /*
330518de8d7fSPeter Avalos * Main program for key management.
330618de8d7fSPeter Avalos */
330718de8d7fSPeter Avalos int
main(int argc,char ** argv)330818de8d7fSPeter Avalos main(int argc, char **argv)
330918de8d7fSPeter Avalos {
3310*ee116499SAntonio Huete Jimenez char comment[1024], *passphrase = NULL;
3311e9778795SPeter Avalos char *rr_hostname = NULL, *ep, *fp, *ra;
3312e9778795SPeter Avalos struct sshkey *private, *public;
331318de8d7fSPeter Avalos struct passwd *pw;
33140cbfa66cSDaniel Fojt int r, opt, type;
3315664f4763Szrj int change_passphrase = 0, change_comment = 0, show_cert = 0;
3316664f4763Szrj int find_host = 0, delete_host = 0, hash_hosts = 0;
331736e94dc5SPeter Avalos int gen_all_hostkeys = 0, gen_krl = 0, update_krl = 0, check_krl = 0;
3318664f4763Szrj int prefer_agent = 0, convert_to = 0, convert_from = 0;
3319664f4763Szrj int print_public = 0, print_generic = 0, cert_serial_autoinc = 0;
33200cbfa66cSDaniel Fojt int do_gen_candidates = 0, do_screen_candidates = 0, download_sk = 0;
3321664f4763Szrj unsigned long long cert_serial = 0;
33220cbfa66cSDaniel Fojt char *identity_comment = NULL, *ca_key_path = NULL, **opts = NULL;
33230cbfa66cSDaniel Fojt char *sk_application = NULL, *sk_device = NULL, *sk_user = NULL;
332450a69bb5SSascha Wildner char *sk_attestation_path = NULL;
33250cbfa66cSDaniel Fojt struct sshbuf *challenge = NULL, *attest = NULL;
33260cbfa66cSDaniel Fojt size_t i, nopts = 0;
33270cbfa66cSDaniel Fojt u_int32_t bits = 0;
33280cbfa66cSDaniel Fojt uint8_t sk_flags = SSH_SK_USER_PRESENCE_REQD;
332918de8d7fSPeter Avalos const char *errstr;
3330664f4763Szrj int log_level = SYSLOG_LEVEL_INFO;
33310cbfa66cSDaniel Fojt char *sign_op = NULL;
333218de8d7fSPeter Avalos
333318de8d7fSPeter Avalos extern int optind;
333418de8d7fSPeter Avalos extern char *optarg;
333518de8d7fSPeter Avalos
333618de8d7fSPeter Avalos /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
333718de8d7fSPeter Avalos sanitise_stdfd();
333818de8d7fSPeter Avalos
333918de8d7fSPeter Avalos __progname = ssh_get_progname(argv[0]);
334018de8d7fSPeter Avalos
334118de8d7fSPeter Avalos seed_rng();
334218de8d7fSPeter Avalos
3343664f4763Szrj log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1);
3344664f4763Szrj
3345ce74bacaSMatthew Dillon msetlocale();
3346ce74bacaSMatthew Dillon
334718de8d7fSPeter Avalos /* we need this for the home * directory. */
334818de8d7fSPeter Avalos pw = getpwuid(getuid());
3349e9778795SPeter Avalos if (!pw)
3350e9778795SPeter Avalos fatal("No user exists for uid %lu", (u_long)getuid());
335150a69bb5SSascha Wildner pw = pwcopy(pw);
33520cbfa66cSDaniel Fojt if (gethostname(hostname, sizeof(hostname)) == -1)
3353e9778795SPeter Avalos fatal("gethostname: %s", strerror(errno));
335418de8d7fSPeter Avalos
33550cbfa66cSDaniel Fojt sk_provider = getenv("SSH_SK_PROVIDER");
33560cbfa66cSDaniel Fojt
33570cbfa66cSDaniel Fojt /* Remaining characters: dGjJSTWx */
33580cbfa66cSDaniel Fojt while ((opt = getopt(argc, argv, "ABHKLQUXceghiklopquvy"
33590cbfa66cSDaniel Fojt "C:D:E:F:I:M:N:O:P:R:V:Y:Z:"
33600cbfa66cSDaniel Fojt "a:b:f:g:m:n:r:s:t:w:z:")) != -1) {
336118de8d7fSPeter Avalos switch (opt) {
33621c188a7fSPeter Avalos case 'A':
33631c188a7fSPeter Avalos gen_all_hostkeys = 1;
33641c188a7fSPeter Avalos break;
336518de8d7fSPeter Avalos case 'b':
33660cbfa66cSDaniel Fojt bits = (u_int32_t)strtonum(optarg, 1, UINT32_MAX,
33670cbfa66cSDaniel Fojt &errstr);
336818de8d7fSPeter Avalos if (errstr)
336918de8d7fSPeter Avalos fatal("Bits has bad value %s (%s)",
337018de8d7fSPeter Avalos optarg, errstr);
337118de8d7fSPeter Avalos break;
3372e9778795SPeter Avalos case 'E':
3373e9778795SPeter Avalos fingerprint_hash = ssh_digest_alg_by_name(optarg);
3374e9778795SPeter Avalos if (fingerprint_hash == -1)
3375e9778795SPeter Avalos fatal("Invalid hash algorithm \"%s\"", optarg);
3376e9778795SPeter Avalos break;
337718de8d7fSPeter Avalos case 'F':
337818de8d7fSPeter Avalos find_host = 1;
337918de8d7fSPeter Avalos rr_hostname = optarg;
338018de8d7fSPeter Avalos break;
338118de8d7fSPeter Avalos case 'H':
338218de8d7fSPeter Avalos hash_hosts = 1;
338318de8d7fSPeter Avalos break;
3384856ea928SPeter Avalos case 'I':
3385856ea928SPeter Avalos cert_key_id = optarg;
3386856ea928SPeter Avalos break;
338718de8d7fSPeter Avalos case 'R':
338818de8d7fSPeter Avalos delete_host = 1;
338918de8d7fSPeter Avalos rr_hostname = optarg;
339018de8d7fSPeter Avalos break;
3391856ea928SPeter Avalos case 'L':
3392856ea928SPeter Avalos show_cert = 1;
3393856ea928SPeter Avalos break;
339418de8d7fSPeter Avalos case 'l':
339518de8d7fSPeter Avalos print_fingerprint = 1;
339618de8d7fSPeter Avalos break;
339718de8d7fSPeter Avalos case 'B':
339818de8d7fSPeter Avalos print_bubblebabble = 1;
339918de8d7fSPeter Avalos break;
3400856ea928SPeter Avalos case 'm':
3401856ea928SPeter Avalos if (strcasecmp(optarg, "RFC4716") == 0 ||
3402856ea928SPeter Avalos strcasecmp(optarg, "ssh2") == 0) {
3403856ea928SPeter Avalos convert_format = FMT_RFC4716;
3404856ea928SPeter Avalos break;
3405856ea928SPeter Avalos }
3406856ea928SPeter Avalos if (strcasecmp(optarg, "PKCS8") == 0) {
3407856ea928SPeter Avalos convert_format = FMT_PKCS8;
34080cbfa66cSDaniel Fojt private_key_format = SSHKEY_PRIVATE_PKCS8;
3409856ea928SPeter Avalos break;
3410856ea928SPeter Avalos }
3411856ea928SPeter Avalos if (strcasecmp(optarg, "PEM") == 0) {
3412856ea928SPeter Avalos convert_format = FMT_PEM;
34130cbfa66cSDaniel Fojt private_key_format = SSHKEY_PRIVATE_PEM;
3414856ea928SPeter Avalos break;
3415856ea928SPeter Avalos }
3416856ea928SPeter Avalos fatal("Unsupported conversion format \"%s\"", optarg);
3417856ea928SPeter Avalos case 'n':
3418856ea928SPeter Avalos cert_principals = optarg;
3419856ea928SPeter Avalos break;
342036e94dc5SPeter Avalos case 'o':
3421664f4763Szrj /* no-op; new format is already the default */
342236e94dc5SPeter Avalos break;
342318de8d7fSPeter Avalos case 'p':
342418de8d7fSPeter Avalos change_passphrase = 1;
342518de8d7fSPeter Avalos break;
342618de8d7fSPeter Avalos case 'c':
342718de8d7fSPeter Avalos change_comment = 1;
342818de8d7fSPeter Avalos break;
342918de8d7fSPeter Avalos case 'f':
3430e9778795SPeter Avalos if (strlcpy(identity_file, optarg,
3431e9778795SPeter Avalos sizeof(identity_file)) >= sizeof(identity_file))
343218de8d7fSPeter Avalos fatal("Identity filename too long");
343318de8d7fSPeter Avalos have_identity = 1;
343418de8d7fSPeter Avalos break;
343518de8d7fSPeter Avalos case 'g':
343618de8d7fSPeter Avalos print_generic = 1;
343718de8d7fSPeter Avalos break;
34380cbfa66cSDaniel Fojt case 'K':
34390cbfa66cSDaniel Fojt download_sk = 1;
34400cbfa66cSDaniel Fojt break;
344118de8d7fSPeter Avalos case 'P':
344218de8d7fSPeter Avalos identity_passphrase = optarg;
344318de8d7fSPeter Avalos break;
344418de8d7fSPeter Avalos case 'N':
344518de8d7fSPeter Avalos identity_new_passphrase = optarg;
344618de8d7fSPeter Avalos break;
344736e94dc5SPeter Avalos case 'Q':
344836e94dc5SPeter Avalos check_krl = 1;
344936e94dc5SPeter Avalos break;
3450856ea928SPeter Avalos case 'O':
34510cbfa66cSDaniel Fojt opts = xrecallocarray(opts, nopts, nopts + 1,
34520cbfa66cSDaniel Fojt sizeof(*opts));
34530cbfa66cSDaniel Fojt opts[nopts++] = xstrdup(optarg);
3454856ea928SPeter Avalos break;
345536e94dc5SPeter Avalos case 'Z':
34560cbfa66cSDaniel Fojt openssh_format_cipher = optarg;
345750a69bb5SSascha Wildner if (cipher_by_name(openssh_format_cipher) == NULL)
345850a69bb5SSascha Wildner fatal("Invalid OpenSSH-format cipher '%s'",
345950a69bb5SSascha Wildner openssh_format_cipher);
346036e94dc5SPeter Avalos break;
346118de8d7fSPeter Avalos case 'C':
346218de8d7fSPeter Avalos identity_comment = optarg;
346318de8d7fSPeter Avalos break;
346418de8d7fSPeter Avalos case 'q':
346518de8d7fSPeter Avalos quiet = 1;
346618de8d7fSPeter Avalos break;
346718de8d7fSPeter Avalos case 'e':
346818de8d7fSPeter Avalos /* export key */
3469856ea928SPeter Avalos convert_to = 1;
3470856ea928SPeter Avalos break;
3471856ea928SPeter Avalos case 'h':
3472856ea928SPeter Avalos cert_key_type = SSH2_CERT_TYPE_HOST;
3473856ea928SPeter Avalos certflags_flags = 0;
347418de8d7fSPeter Avalos break;
347536e94dc5SPeter Avalos case 'k':
347636e94dc5SPeter Avalos gen_krl = 1;
347736e94dc5SPeter Avalos break;
347818de8d7fSPeter Avalos case 'i':
347918de8d7fSPeter Avalos case 'X':
348018de8d7fSPeter Avalos /* import key */
3481856ea928SPeter Avalos convert_from = 1;
348218de8d7fSPeter Avalos break;
348318de8d7fSPeter Avalos case 'y':
348418de8d7fSPeter Avalos print_public = 1;
348518de8d7fSPeter Avalos break;
3486856ea928SPeter Avalos case 's':
3487856ea928SPeter Avalos ca_key_path = optarg;
3488856ea928SPeter Avalos break;
348918de8d7fSPeter Avalos case 't':
349018de8d7fSPeter Avalos key_type_name = optarg;
349118de8d7fSPeter Avalos break;
349218de8d7fSPeter Avalos case 'D':
3493856ea928SPeter Avalos pkcs11provider = optarg;
349418de8d7fSPeter Avalos break;
3495ce74bacaSMatthew Dillon case 'U':
3496ce74bacaSMatthew Dillon prefer_agent = 1;
3497ce74bacaSMatthew Dillon break;
349836e94dc5SPeter Avalos case 'u':
349936e94dc5SPeter Avalos update_krl = 1;
350036e94dc5SPeter Avalos break;
350118de8d7fSPeter Avalos case 'v':
350218de8d7fSPeter Avalos if (log_level == SYSLOG_LEVEL_INFO)
350318de8d7fSPeter Avalos log_level = SYSLOG_LEVEL_DEBUG1;
350418de8d7fSPeter Avalos else {
350518de8d7fSPeter Avalos if (log_level >= SYSLOG_LEVEL_DEBUG1 &&
350618de8d7fSPeter Avalos log_level < SYSLOG_LEVEL_DEBUG3)
350718de8d7fSPeter Avalos log_level++;
350818de8d7fSPeter Avalos }
350918de8d7fSPeter Avalos break;
351018de8d7fSPeter Avalos case 'r':
351118de8d7fSPeter Avalos rr_hostname = optarg;
351218de8d7fSPeter Avalos break;
3513e9778795SPeter Avalos case 'a':
3514e9778795SPeter Avalos rounds = (int)strtonum(optarg, 1, INT_MAX, &errstr);
3515e9778795SPeter Avalos if (errstr)
3516e9778795SPeter Avalos fatal("Invalid number: %s (%s)",
3517e9778795SPeter Avalos optarg, errstr);
3518e9778795SPeter Avalos break;
3519e9778795SPeter Avalos case 'V':
3520e9778795SPeter Avalos parse_cert_times(optarg);
3521e9778795SPeter Avalos break;
35220cbfa66cSDaniel Fojt case 'Y':
35230cbfa66cSDaniel Fojt sign_op = optarg;
35240cbfa66cSDaniel Fojt break;
35250cbfa66cSDaniel Fojt case 'w':
35260cbfa66cSDaniel Fojt sk_provider = optarg;
35270cbfa66cSDaniel Fojt break;
3528e9778795SPeter Avalos case 'z':
3529e9778795SPeter Avalos errno = 0;
3530664f4763Szrj if (*optarg == '+') {
3531664f4763Szrj cert_serial_autoinc = 1;
3532664f4763Szrj optarg++;
3533664f4763Szrj }
3534e9778795SPeter Avalos cert_serial = strtoull(optarg, &ep, 10);
3535e9778795SPeter Avalos if (*optarg < '0' || *optarg > '9' || *ep != '\0' ||
3536e9778795SPeter Avalos (errno == ERANGE && cert_serial == ULLONG_MAX))
3537e9778795SPeter Avalos fatal("Invalid serial number \"%s\"", optarg);
3538e9778795SPeter Avalos break;
3539ce74bacaSMatthew Dillon case 'M':
35400cbfa66cSDaniel Fojt if (strcmp(optarg, "generate") == 0)
35410cbfa66cSDaniel Fojt do_gen_candidates = 1;
35420cbfa66cSDaniel Fojt else if (strcmp(optarg, "screen") == 0)
354318de8d7fSPeter Avalos do_screen_candidates = 1;
35440cbfa66cSDaniel Fojt else
35450cbfa66cSDaniel Fojt fatal("Unsupported moduli option %s", optarg);
354618de8d7fSPeter Avalos break;
354718de8d7fSPeter Avalos case '?':
354818de8d7fSPeter Avalos default:
354918de8d7fSPeter Avalos usage();
355018de8d7fSPeter Avalos }
355118de8d7fSPeter Avalos }
355218de8d7fSPeter Avalos
35530cbfa66cSDaniel Fojt #ifdef ENABLE_SK_INTERNAL
35540cbfa66cSDaniel Fojt if (sk_provider == NULL)
35550cbfa66cSDaniel Fojt sk_provider = "internal";
35560cbfa66cSDaniel Fojt #endif
35570cbfa66cSDaniel Fojt
355818de8d7fSPeter Avalos /* reinit */
355918de8d7fSPeter Avalos log_init(argv[0], log_level, SYSLOG_FACILITY_USER, 1);
356018de8d7fSPeter Avalos
3561856ea928SPeter Avalos argv += optind;
3562856ea928SPeter Avalos argc -= optind;
3563856ea928SPeter Avalos
35640cbfa66cSDaniel Fojt if (sign_op != NULL) {
35650cbfa66cSDaniel Fojt if (strncmp(sign_op, "find-principals", 15) == 0) {
35660cbfa66cSDaniel Fojt if (ca_key_path == NULL) {
35670cbfa66cSDaniel Fojt error("Too few arguments for find-principals:"
35680cbfa66cSDaniel Fojt "missing signature file");
35690cbfa66cSDaniel Fojt exit(1);
35700cbfa66cSDaniel Fojt }
35710cbfa66cSDaniel Fojt if (!have_identity) {
35720cbfa66cSDaniel Fojt error("Too few arguments for find-principals:"
35730cbfa66cSDaniel Fojt "missing allowed keys file");
35740cbfa66cSDaniel Fojt exit(1);
35750cbfa66cSDaniel Fojt }
357650a69bb5SSascha Wildner return sig_find_principals(ca_key_path, identity_file,
357750a69bb5SSascha Wildner opts, nopts);
3578*ee116499SAntonio Huete Jimenez } else if (strncmp(sign_op, "match-principals", 16) == 0) {
3579*ee116499SAntonio Huete Jimenez if (!have_identity) {
3580*ee116499SAntonio Huete Jimenez error("Too few arguments for match-principals:"
3581*ee116499SAntonio Huete Jimenez "missing allowed keys file");
3582*ee116499SAntonio Huete Jimenez exit(1);
3583*ee116499SAntonio Huete Jimenez }
3584*ee116499SAntonio Huete Jimenez if (cert_key_id == NULL) {
3585*ee116499SAntonio Huete Jimenez error("Too few arguments for match-principals: "
3586*ee116499SAntonio Huete Jimenez "missing principal ID");
3587*ee116499SAntonio Huete Jimenez exit(1);
3588*ee116499SAntonio Huete Jimenez }
3589*ee116499SAntonio Huete Jimenez return sig_match_principals(identity_file, cert_key_id,
3590*ee116499SAntonio Huete Jimenez opts, nopts);
35910cbfa66cSDaniel Fojt } else if (strncmp(sign_op, "sign", 4) == 0) {
3592*ee116499SAntonio Huete Jimenez /* NB. cert_principals is actually namespace, via -n */
35930cbfa66cSDaniel Fojt if (cert_principals == NULL ||
35940cbfa66cSDaniel Fojt *cert_principals == '\0') {
35950cbfa66cSDaniel Fojt error("Too few arguments for sign: "
35960cbfa66cSDaniel Fojt "missing namespace");
35970cbfa66cSDaniel Fojt exit(1);
35980cbfa66cSDaniel Fojt }
35990cbfa66cSDaniel Fojt if (!have_identity) {
36000cbfa66cSDaniel Fojt error("Too few arguments for sign: "
36010cbfa66cSDaniel Fojt "missing key");
36020cbfa66cSDaniel Fojt exit(1);
36030cbfa66cSDaniel Fojt }
36040cbfa66cSDaniel Fojt return sig_sign(identity_file, cert_principals,
3605*ee116499SAntonio Huete Jimenez prefer_agent, argc, argv, opts, nopts);
36060cbfa66cSDaniel Fojt } else if (strncmp(sign_op, "check-novalidate", 16) == 0) {
3607*ee116499SAntonio Huete Jimenez /* NB. cert_principals is actually namespace, via -n */
3608*ee116499SAntonio Huete Jimenez if (cert_principals == NULL ||
3609*ee116499SAntonio Huete Jimenez *cert_principals == '\0') {
3610*ee116499SAntonio Huete Jimenez error("Too few arguments for check-novalidate: "
3611*ee116499SAntonio Huete Jimenez "missing namespace");
3612*ee116499SAntonio Huete Jimenez exit(1);
3613*ee116499SAntonio Huete Jimenez }
36140cbfa66cSDaniel Fojt if (ca_key_path == NULL) {
36150cbfa66cSDaniel Fojt error("Too few arguments for check-novalidate: "
36160cbfa66cSDaniel Fojt "missing signature file");
36170cbfa66cSDaniel Fojt exit(1);
36180cbfa66cSDaniel Fojt }
36190cbfa66cSDaniel Fojt return sig_verify(ca_key_path, cert_principals,
362050a69bb5SSascha Wildner NULL, NULL, NULL, opts, nopts);
36210cbfa66cSDaniel Fojt } else if (strncmp(sign_op, "verify", 6) == 0) {
3622*ee116499SAntonio Huete Jimenez /* NB. cert_principals is actually namespace, via -n */
36230cbfa66cSDaniel Fojt if (cert_principals == NULL ||
36240cbfa66cSDaniel Fojt *cert_principals == '\0') {
36250cbfa66cSDaniel Fojt error("Too few arguments for verify: "
36260cbfa66cSDaniel Fojt "missing namespace");
36270cbfa66cSDaniel Fojt exit(1);
36280cbfa66cSDaniel Fojt }
36290cbfa66cSDaniel Fojt if (ca_key_path == NULL) {
36300cbfa66cSDaniel Fojt error("Too few arguments for verify: "
36310cbfa66cSDaniel Fojt "missing signature file");
36320cbfa66cSDaniel Fojt exit(1);
36330cbfa66cSDaniel Fojt }
36340cbfa66cSDaniel Fojt if (!have_identity) {
36350cbfa66cSDaniel Fojt error("Too few arguments for sign: "
36360cbfa66cSDaniel Fojt "missing allowed keys file");
36370cbfa66cSDaniel Fojt exit(1);
36380cbfa66cSDaniel Fojt }
36390cbfa66cSDaniel Fojt if (cert_key_id == NULL) {
36400cbfa66cSDaniel Fojt error("Too few arguments for verify: "
3641*ee116499SAntonio Huete Jimenez "missing principal identity");
36420cbfa66cSDaniel Fojt exit(1);
36430cbfa66cSDaniel Fojt }
36440cbfa66cSDaniel Fojt return sig_verify(ca_key_path, cert_principals,
364550a69bb5SSascha Wildner cert_key_id, identity_file, rr_hostname,
364650a69bb5SSascha Wildner opts, nopts);
36470cbfa66cSDaniel Fojt }
36480cbfa66cSDaniel Fojt error("Unsupported operation for -Y: \"%s\"", sign_op);
36490cbfa66cSDaniel Fojt usage();
36500cbfa66cSDaniel Fojt /* NOTREACHED */
36510cbfa66cSDaniel Fojt }
36520cbfa66cSDaniel Fojt
3653856ea928SPeter Avalos if (ca_key_path != NULL) {
365436e94dc5SPeter Avalos if (argc < 1 && !gen_krl) {
3655e9778795SPeter Avalos error("Too few arguments.");
3656856ea928SPeter Avalos usage();
3657856ea928SPeter Avalos }
36580cbfa66cSDaniel Fojt } else if (argc > 0 && !gen_krl && !check_krl &&
36590cbfa66cSDaniel Fojt !do_gen_candidates && !do_screen_candidates) {
3660e9778795SPeter Avalos error("Too many arguments.");
366118de8d7fSPeter Avalos usage();
366218de8d7fSPeter Avalos }
366318de8d7fSPeter Avalos if (change_passphrase && change_comment) {
3664e9778795SPeter Avalos error("Can only have one of -p and -c.");
366518de8d7fSPeter Avalos usage();
366618de8d7fSPeter Avalos }
366718de8d7fSPeter Avalos if (print_fingerprint && (delete_host || hash_hosts)) {
3668e9778795SPeter Avalos error("Cannot use -l with -H or -R.");
366918de8d7fSPeter Avalos usage();
367018de8d7fSPeter Avalos }
367136e94dc5SPeter Avalos if (gen_krl) {
3672664f4763Szrj do_gen_krl(pw, update_krl, ca_key_path,
3673664f4763Szrj cert_serial, identity_comment, argc, argv);
367436e94dc5SPeter Avalos return (0);
367536e94dc5SPeter Avalos }
367636e94dc5SPeter Avalos if (check_krl) {
36770cbfa66cSDaniel Fojt do_check_krl(pw, print_fingerprint, argc, argv);
367836e94dc5SPeter Avalos return (0);
367936e94dc5SPeter Avalos }
3680856ea928SPeter Avalos if (ca_key_path != NULL) {
3681856ea928SPeter Avalos if (cert_key_id == NULL)
3682856ea928SPeter Avalos fatal("Must specify key id (-I) when certifying");
36830cbfa66cSDaniel Fojt for (i = 0; i < nopts; i++)
36840cbfa66cSDaniel Fojt add_cert_option(opts[i]);
3685664f4763Szrj do_ca_sign(pw, ca_key_path, prefer_agent,
3686664f4763Szrj cert_serial, cert_serial_autoinc, argc, argv);
3687856ea928SPeter Avalos }
3688856ea928SPeter Avalos if (show_cert)
3689856ea928SPeter Avalos do_show_cert(pw);
3690664f4763Szrj if (delete_host || hash_hosts || find_host) {
3691664f4763Szrj do_known_hosts(pw, rr_hostname, find_host,
3692664f4763Szrj delete_host, hash_hosts);
3693664f4763Szrj }
369436e94dc5SPeter Avalos if (pkcs11provider != NULL)
369536e94dc5SPeter Avalos do_download(pw);
36960cbfa66cSDaniel Fojt if (download_sk) {
36970cbfa66cSDaniel Fojt for (i = 0; i < nopts; i++) {
36980cbfa66cSDaniel Fojt if (strncasecmp(opts[i], "device=", 7) == 0) {
36990cbfa66cSDaniel Fojt sk_device = xstrdup(opts[i] + 7);
37000cbfa66cSDaniel Fojt } else {
37010cbfa66cSDaniel Fojt fatal("Option \"%s\" is unsupported for "
37020cbfa66cSDaniel Fojt "FIDO authenticator download", opts[i]);
37030cbfa66cSDaniel Fojt }
37040cbfa66cSDaniel Fojt }
37050cbfa66cSDaniel Fojt return do_download_sk(sk_provider, sk_device);
37060cbfa66cSDaniel Fojt }
370718de8d7fSPeter Avalos if (print_fingerprint || print_bubblebabble)
370818de8d7fSPeter Avalos do_fingerprint(pw);
370918de8d7fSPeter Avalos if (change_passphrase)
371018de8d7fSPeter Avalos do_change_passphrase(pw);
371118de8d7fSPeter Avalos if (change_comment)
3712664f4763Szrj do_change_comment(pw, identity_comment);
371336e94dc5SPeter Avalos #ifdef WITH_OPENSSL
3714856ea928SPeter Avalos if (convert_to)
3715856ea928SPeter Avalos do_convert_to(pw);
3716856ea928SPeter Avalos if (convert_from)
3717856ea928SPeter Avalos do_convert_from(pw);
37180cbfa66cSDaniel Fojt #else /* WITH_OPENSSL */
37190cbfa66cSDaniel Fojt if (convert_to || convert_from)
37200cbfa66cSDaniel Fojt fatal("key conversion disabled at compile time");
37210cbfa66cSDaniel Fojt #endif /* WITH_OPENSSL */
372218de8d7fSPeter Avalos if (print_public)
372318de8d7fSPeter Avalos do_print_public(pw);
372418de8d7fSPeter Avalos if (rr_hostname != NULL) {
372518de8d7fSPeter Avalos unsigned int n = 0;
372618de8d7fSPeter Avalos
372718de8d7fSPeter Avalos if (have_identity) {
3728664f4763Szrj n = do_print_resource_record(pw, identity_file,
3729664f4763Szrj rr_hostname, print_generic);
3730e9778795SPeter Avalos if (n == 0)
3731e9778795SPeter Avalos fatal("%s: %s", identity_file, strerror(errno));
373218de8d7fSPeter Avalos exit(0);
373318de8d7fSPeter Avalos } else {
373418de8d7fSPeter Avalos
373518de8d7fSPeter Avalos n += do_print_resource_record(pw,
3736664f4763Szrj _PATH_HOST_RSA_KEY_FILE, rr_hostname,
3737664f4763Szrj print_generic);
373818de8d7fSPeter Avalos n += do_print_resource_record(pw,
3739664f4763Szrj _PATH_HOST_DSA_KEY_FILE, rr_hostname,
3740664f4763Szrj print_generic);
374199e85e0dSPeter Avalos n += do_print_resource_record(pw,
3742664f4763Szrj _PATH_HOST_ECDSA_KEY_FILE, rr_hostname,
3743664f4763Szrj print_generic);
374436e94dc5SPeter Avalos n += do_print_resource_record(pw,
3745664f4763Szrj _PATH_HOST_ED25519_KEY_FILE, rr_hostname,
3746664f4763Szrj print_generic);
3747664f4763Szrj n += do_print_resource_record(pw,
3748664f4763Szrj _PATH_HOST_XMSS_KEY_FILE, rr_hostname,
3749664f4763Szrj print_generic);
375018de8d7fSPeter Avalos if (n == 0)
375118de8d7fSPeter Avalos fatal("no keys found.");
375218de8d7fSPeter Avalos exit(0);
375318de8d7fSPeter Avalos }
375418de8d7fSPeter Avalos }
375518de8d7fSPeter Avalos
37560cbfa66cSDaniel Fojt if (do_gen_candidates || do_screen_candidates) {
37570cbfa66cSDaniel Fojt if (argc <= 0)
37580cbfa66cSDaniel Fojt fatal("No output file specified");
37590cbfa66cSDaniel Fojt else if (argc > 1)
37600cbfa66cSDaniel Fojt fatal("Too many output files specified");
37610cbfa66cSDaniel Fojt }
376218de8d7fSPeter Avalos if (do_gen_candidates) {
37630cbfa66cSDaniel Fojt do_moduli_gen(argv[0], opts, nopts);
37640cbfa66cSDaniel Fojt return 0;
376518de8d7fSPeter Avalos }
376618de8d7fSPeter Avalos if (do_screen_candidates) {
37670cbfa66cSDaniel Fojt do_moduli_screen(argv[0], opts, nopts);
37680cbfa66cSDaniel Fojt return 0;
376918de8d7fSPeter Avalos }
377018de8d7fSPeter Avalos
37711c188a7fSPeter Avalos if (gen_all_hostkeys) {
37721c188a7fSPeter Avalos do_gen_all_hostkeys(pw);
37731c188a7fSPeter Avalos return (0);
37741c188a7fSPeter Avalos }
37751c188a7fSPeter Avalos
377618de8d7fSPeter Avalos if (key_type_name == NULL)
3777e9778795SPeter Avalos key_type_name = DEFAULT_KEY_TYPE_NAME;
377818de8d7fSPeter Avalos
3779e9778795SPeter Avalos type = sshkey_type_from_name(key_type_name);
3780e9778795SPeter Avalos type_bits_valid(type, key_type_name, &bits);
37811c188a7fSPeter Avalos
378218de8d7fSPeter Avalos if (!quiet)
3783e9778795SPeter Avalos printf("Generating public/private %s key pair.\n",
3784e9778795SPeter Avalos key_type_name);
37850cbfa66cSDaniel Fojt switch (type) {
37860cbfa66cSDaniel Fojt case KEY_ECDSA_SK:
37870cbfa66cSDaniel Fojt case KEY_ED25519_SK:
37880cbfa66cSDaniel Fojt for (i = 0; i < nopts; i++) {
37890cbfa66cSDaniel Fojt if (strcasecmp(opts[i], "no-touch-required") == 0) {
37900cbfa66cSDaniel Fojt sk_flags &= ~SSH_SK_USER_PRESENCE_REQD;
379150a69bb5SSascha Wildner } else if (strcasecmp(opts[i], "verify-required") == 0) {
379250a69bb5SSascha Wildner sk_flags |= SSH_SK_USER_VERIFICATION_REQD;
37930cbfa66cSDaniel Fojt } else if (strcasecmp(opts[i], "resident") == 0) {
37940cbfa66cSDaniel Fojt sk_flags |= SSH_SK_RESIDENT_KEY;
37950cbfa66cSDaniel Fojt } else if (strncasecmp(opts[i], "device=", 7) == 0) {
37960cbfa66cSDaniel Fojt sk_device = xstrdup(opts[i] + 7);
37970cbfa66cSDaniel Fojt } else if (strncasecmp(opts[i], "user=", 5) == 0) {
37980cbfa66cSDaniel Fojt sk_user = xstrdup(opts[i] + 5);
37990cbfa66cSDaniel Fojt } else if (strncasecmp(opts[i], "challenge=", 10) == 0) {
38000cbfa66cSDaniel Fojt if ((r = sshbuf_load_file(opts[i] + 10,
38010cbfa66cSDaniel Fojt &challenge)) != 0) {
380250a69bb5SSascha Wildner fatal_r(r, "Unable to load FIDO "
380350a69bb5SSascha Wildner "enrollment challenge \"%s\"",
380450a69bb5SSascha Wildner opts[i] + 10);
38050cbfa66cSDaniel Fojt }
38060cbfa66cSDaniel Fojt } else if (strncasecmp(opts[i],
38070cbfa66cSDaniel Fojt "write-attestation=", 18) == 0) {
380850a69bb5SSascha Wildner sk_attestation_path = opts[i] + 18;
38090cbfa66cSDaniel Fojt } else if (strncasecmp(opts[i],
38100cbfa66cSDaniel Fojt "application=", 12) == 0) {
38110cbfa66cSDaniel Fojt sk_application = xstrdup(opts[i] + 12);
38120cbfa66cSDaniel Fojt if (strncmp(sk_application, "ssh:", 4) != 0) {
38130cbfa66cSDaniel Fojt fatal("FIDO application string must "
38140cbfa66cSDaniel Fojt "begin with \"ssh:\"");
38150cbfa66cSDaniel Fojt }
38160cbfa66cSDaniel Fojt } else {
38170cbfa66cSDaniel Fojt fatal("Option \"%s\" is unsupported for "
38180cbfa66cSDaniel Fojt "FIDO authenticator enrollment", opts[i]);
38190cbfa66cSDaniel Fojt }
38200cbfa66cSDaniel Fojt }
38210cbfa66cSDaniel Fojt if ((attest = sshbuf_new()) == NULL)
38220cbfa66cSDaniel Fojt fatal("sshbuf_new failed");
3823*ee116499SAntonio Huete Jimenez r = 0;
3824*ee116499SAntonio Huete Jimenez for (i = 0 ;;) {
3825*ee116499SAntonio Huete Jimenez if (!quiet) {
3826*ee116499SAntonio Huete Jimenez printf("You may need to touch your "
3827*ee116499SAntonio Huete Jimenez "authenticator%s to authorize key "
3828*ee116499SAntonio Huete Jimenez "generation.\n",
3829*ee116499SAntonio Huete Jimenez r == 0 ? "" : " again");
383050a69bb5SSascha Wildner }
38310cbfa66cSDaniel Fojt fflush(stdout);
38320cbfa66cSDaniel Fojt r = sshsk_enroll(type, sk_provider, sk_device,
38330cbfa66cSDaniel Fojt sk_application == NULL ? "ssh:" : sk_application,
38340cbfa66cSDaniel Fojt sk_user, sk_flags, passphrase, challenge,
38350cbfa66cSDaniel Fojt &private, attest);
38360cbfa66cSDaniel Fojt if (r == 0)
38370cbfa66cSDaniel Fojt break;
3838*ee116499SAntonio Huete Jimenez if (r == SSH_ERR_KEY_BAD_PERMISSIONS &&
3839*ee116499SAntonio Huete Jimenez (sk_flags & SSH_SK_RESIDENT_KEY) != 0 &&
3840*ee116499SAntonio Huete Jimenez (sk_flags & SSH_SK_FORCE_OPERATION) == 0 &&
3841*ee116499SAntonio Huete Jimenez confirm_sk_overwrite(sk_application, sk_user)) {
3842*ee116499SAntonio Huete Jimenez sk_flags |= SSH_SK_FORCE_OPERATION;
3843*ee116499SAntonio Huete Jimenez continue;
3844*ee116499SAntonio Huete Jimenez }
38450cbfa66cSDaniel Fojt if (r != SSH_ERR_KEY_WRONG_PASSPHRASE)
384650a69bb5SSascha Wildner fatal_r(r, "Key enrollment failed");
384750a69bb5SSascha Wildner else if (passphrase != NULL) {
38480cbfa66cSDaniel Fojt error("PIN incorrect");
38490cbfa66cSDaniel Fojt freezero(passphrase, strlen(passphrase));
38500cbfa66cSDaniel Fojt passphrase = NULL;
38510cbfa66cSDaniel Fojt }
3852*ee116499SAntonio Huete Jimenez if (++i >= 3)
38530cbfa66cSDaniel Fojt fatal("Too many incorrect PINs");
38540cbfa66cSDaniel Fojt passphrase = read_passphrase("Enter PIN for "
38550cbfa66cSDaniel Fojt "authenticator: ", RP_ALLOW_STDIN);
38560cbfa66cSDaniel Fojt }
38570cbfa66cSDaniel Fojt if (passphrase != NULL) {
38580cbfa66cSDaniel Fojt freezero(passphrase, strlen(passphrase));
38590cbfa66cSDaniel Fojt passphrase = NULL;
38600cbfa66cSDaniel Fojt }
38610cbfa66cSDaniel Fojt break;
38620cbfa66cSDaniel Fojt default:
3863e9778795SPeter Avalos if ((r = sshkey_generate(type, bits, &private)) != 0)
3864ce74bacaSMatthew Dillon fatal("sshkey_generate failed");
38650cbfa66cSDaniel Fojt break;
38660cbfa66cSDaniel Fojt }
3867e9778795SPeter Avalos if ((r = sshkey_from_private(private, &public)) != 0)
386850a69bb5SSascha Wildner fatal_r(r, "sshkey_from_private");
386918de8d7fSPeter Avalos
387018de8d7fSPeter Avalos if (!have_identity)
387118de8d7fSPeter Avalos ask_filename(pw, "Enter file in which to save the key");
387218de8d7fSPeter Avalos
387318de8d7fSPeter Avalos /* Create ~/.ssh directory if it doesn't already exist. */
387450a69bb5SSascha Wildner hostfile_create_user_ssh_dir(identity_file, !quiet);
387550a69bb5SSascha Wildner
387618de8d7fSPeter Avalos /* If the file already exists, ask the user to confirm. */
38770cbfa66cSDaniel Fojt if (!confirm_overwrite(identity_file))
387818de8d7fSPeter Avalos exit(1);
387918de8d7fSPeter Avalos
38800cbfa66cSDaniel Fojt /* Determine the passphrase for the private key */
38810cbfa66cSDaniel Fojt passphrase = private_key_passphrase();
388218de8d7fSPeter Avalos if (identity_comment) {
388318de8d7fSPeter Avalos strlcpy(comment, identity_comment, sizeof(comment));
388418de8d7fSPeter Avalos } else {
3885cb5eb4f1SPeter Avalos /* Create default comment field for the passphrase. */
388618de8d7fSPeter Avalos snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname);
388718de8d7fSPeter Avalos }
388818de8d7fSPeter Avalos
388918de8d7fSPeter Avalos /* Save the key with the given passphrase and comment. */
38900cbfa66cSDaniel Fojt if ((r = sshkey_save_private(private, identity_file, passphrase,
38910cbfa66cSDaniel Fojt comment, private_key_format, openssh_format_cipher, rounds)) != 0) {
389250a69bb5SSascha Wildner error_r(r, "Saving key \"%s\" failed", identity_file);
38930cbfa66cSDaniel Fojt freezero(passphrase, strlen(passphrase));
389418de8d7fSPeter Avalos exit(1);
389518de8d7fSPeter Avalos }
38960cbfa66cSDaniel Fojt freezero(passphrase, strlen(passphrase));
3897e9778795SPeter Avalos sshkey_free(private);
389818de8d7fSPeter Avalos
38990cbfa66cSDaniel Fojt if (!quiet) {
39000cbfa66cSDaniel Fojt printf("Your identification has been saved in %s\n",
39010cbfa66cSDaniel Fojt identity_file);
39020cbfa66cSDaniel Fojt }
390318de8d7fSPeter Avalos
390418de8d7fSPeter Avalos strlcat(identity_file, ".pub", sizeof(identity_file));
390550a69bb5SSascha Wildner if ((r = sshkey_save_public(public, identity_file, comment)) != 0)
390650a69bb5SSascha Wildner fatal_r(r, "Unable to save public key to %s", identity_file);
390718de8d7fSPeter Avalos
390818de8d7fSPeter Avalos if (!quiet) {
3909e9778795SPeter Avalos fp = sshkey_fingerprint(public, fingerprint_hash,
3910e9778795SPeter Avalos SSH_FP_DEFAULT);
3911e9778795SPeter Avalos ra = sshkey_fingerprint(public, fingerprint_hash,
391218de8d7fSPeter Avalos SSH_FP_RANDOMART);
3913e9778795SPeter Avalos if (fp == NULL || ra == NULL)
3914e9778795SPeter Avalos fatal("sshkey_fingerprint failed");
39150cbfa66cSDaniel Fojt printf("Your public key has been saved in %s\n",
391618de8d7fSPeter Avalos identity_file);
391718de8d7fSPeter Avalos printf("The key fingerprint is:\n");
391818de8d7fSPeter Avalos printf("%s %s\n", fp, comment);
391918de8d7fSPeter Avalos printf("The key's randomart image is:\n");
392018de8d7fSPeter Avalos printf("%s\n", ra);
392136e94dc5SPeter Avalos free(ra);
392236e94dc5SPeter Avalos free(fp);
392318de8d7fSPeter Avalos }
392418de8d7fSPeter Avalos
392550a69bb5SSascha Wildner if (sk_attestation_path != NULL)
392650a69bb5SSascha Wildner save_attestation(attest, sk_attestation_path);
392750a69bb5SSascha Wildner
39280cbfa66cSDaniel Fojt sshbuf_free(attest);
3929e9778795SPeter Avalos sshkey_free(public);
39300cbfa66cSDaniel Fojt
393118de8d7fSPeter Avalos exit(0);
393218de8d7fSPeter Avalos }
3933