xref: /dragonfly/crypto/openssh/ssh-keygen.c (revision ee116499)
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