xref: /freebsd/crypto/openssh/ssh-keygen.c (revision 3d9fd9fcb432750f3716b28f6ccb0104cd9d351a)
1*3d9fd9fcSEd Maste /* $OpenBSD: ssh-keygen.c,v 1.475 2024/09/15 00:47:01 djm Exp $ */
2511b41d2SMark Murray /*
3511b41d2SMark Murray  * Author: Tatu Ylonen <ylo@cs.hut.fi>
4511b41d2SMark Murray  * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
5511b41d2SMark Murray  *                    All rights reserved
6511b41d2SMark Murray  * Identity and host key generation and maintenance.
7b66f2d16SKris Kennaway  *
8b66f2d16SKris Kennaway  * As far as I am concerned, the code I have written for this software
9b66f2d16SKris Kennaway  * can be used freely for any purpose.  Any derived versions of this
10b66f2d16SKris Kennaway  * software must be clearly marked as such, and if the derived work is
11b66f2d16SKris Kennaway  * incompatible with the protocol description in the RFC file, it must be
12b66f2d16SKris Kennaway  * called by a name other than "ssh" or "Secure Shell".
13511b41d2SMark Murray  */
14511b41d2SMark Murray 
15511b41d2SMark Murray #include "includes.h"
16761efaa7SDag-Erling Smørgrav 
17761efaa7SDag-Erling Smørgrav #include <sys/types.h>
18761efaa7SDag-Erling Smørgrav #include <sys/socket.h>
19761efaa7SDag-Erling Smørgrav #include <sys/stat.h>
20511b41d2SMark Murray 
21bc5531deSDag-Erling Smørgrav #ifdef WITH_OPENSSL
22a04a10f8SKris Kennaway #include <openssl/evp.h>
23a04a10f8SKris Kennaway #include <openssl/pem.h>
24d4af9e69SDag-Erling Smørgrav #include "openbsd-compat/openssl-compat.h"
25bc5531deSDag-Erling Smørgrav #endif
26a04a10f8SKris Kennaway 
2719261079SEd Maste #ifdef HAVE_STDINT_H
2819261079SEd Maste # include <stdint.h>
2919261079SEd Maste #endif
30761efaa7SDag-Erling Smørgrav #include <errno.h>
31761efaa7SDag-Erling Smørgrav #include <fcntl.h>
32761efaa7SDag-Erling Smørgrav #include <netdb.h>
33761efaa7SDag-Erling Smørgrav #ifdef HAVE_PATHS_H
34761efaa7SDag-Erling Smørgrav # include <paths.h>
35761efaa7SDag-Erling Smørgrav #endif
36761efaa7SDag-Erling Smørgrav #include <pwd.h>
37761efaa7SDag-Erling Smørgrav #include <stdarg.h>
38761efaa7SDag-Erling Smørgrav #include <stdio.h>
39761efaa7SDag-Erling Smørgrav #include <stdlib.h>
40761efaa7SDag-Erling Smørgrav #include <string.h>
41761efaa7SDag-Erling Smørgrav #include <unistd.h>
42bc5531deSDag-Erling Smørgrav #include <limits.h>
43d93a896eSDag-Erling Smørgrav #include <locale.h>
44190cef3dSDag-Erling Smørgrav #include <time.h>
45761efaa7SDag-Erling Smørgrav 
46511b41d2SMark Murray #include "xmalloc.h"
47bc5531deSDag-Erling Smørgrav #include "sshkey.h"
48a04a10f8SKris Kennaway #include "authfile.h"
49bc5531deSDag-Erling Smørgrav #include "sshbuf.h"
501e8db6e2SBrian Feldman #include "pathnames.h"
511e8db6e2SBrian Feldman #include "log.h"
52d74d50a8SDag-Erling Smørgrav #include "misc.h"
535e8dbd04SDag-Erling Smørgrav #include "match.h"
545e8dbd04SDag-Erling Smørgrav #include "hostfile.h"
55761efaa7SDag-Erling Smørgrav #include "dns.h"
566888a9beSDag-Erling Smørgrav #include "ssh.h"
57b15c8340SDag-Erling Smørgrav #include "ssh2.h"
58bc5531deSDag-Erling Smørgrav #include "ssherr.h"
59b15c8340SDag-Erling Smørgrav #include "ssh-pkcs11.h"
606888a9beSDag-Erling Smørgrav #include "atomicio.h"
616888a9beSDag-Erling Smørgrav #include "krl.h"
62bc5531deSDag-Erling Smørgrav #include "digest.h"
63d93a896eSDag-Erling Smørgrav #include "utf8.h"
644f52dfbbSDag-Erling Smørgrav #include "authfd.h"
6519261079SEd Maste #include "sshsig.h"
6619261079SEd Maste #include "ssh-sk.h"
6719261079SEd Maste #include "sk-api.h" /* XXX for SSH_SK_USER_PRESENCE_REQD; remove */
6819261079SEd Maste #include "cipher.h"
69ae1f160dSDag-Erling Smørgrav 
70557f75e5SDag-Erling Smørgrav #define DEFAULT_KEY_TYPE_NAME "ed25519"
71557f75e5SDag-Erling Smørgrav 
7219261079SEd Maste /*
7319261079SEd Maste  * Default number of bits in the RSA, DSA and ECDSA keys.  These value can be
7419261079SEd Maste  * overridden on the command line.
7519261079SEd Maste  *
7619261079SEd Maste  * These values, with the exception of DSA, provide security equivalent to at
7719261079SEd Maste  * least 128 bits of security according to NIST Special Publication 800-57:
7819261079SEd Maste  * Recommendation for Key Management Part 1 rev 4 section 5.6.1.
7919261079SEd Maste  * For DSA it (and FIPS-186-4 section 4.2) specifies that the only size for
8019261079SEd Maste  * which a 160bit hash is acceptable is 1kbit, and since ssh-dss specifies only
8119261079SEd Maste  * SHA1 we limit the DSA key size 1k bits.
8219261079SEd Maste  */
8319261079SEd Maste #define DEFAULT_BITS		3072
84021d409fSDag-Erling Smørgrav #define DEFAULT_BITS_DSA	1024
854a421b63SDag-Erling Smørgrav #define DEFAULT_BITS_ECDSA	256
86511b41d2SMark Murray 
8719261079SEd Maste static int quiet = 0;
88b15c8340SDag-Erling Smørgrav 
89511b41d2SMark Murray /* Flag indicating that we just want to see the key fingerprint */
9019261079SEd Maste static int print_fingerprint = 0;
9119261079SEd Maste static int print_bubblebabble = 0;
92511b41d2SMark Murray 
93bc5531deSDag-Erling Smørgrav /* Hash algorithm to use for fingerprints. */
9419261079SEd Maste static int fingerprint_hash = SSH_FP_HASH_DEFAULT;
95bc5531deSDag-Erling Smørgrav 
96511b41d2SMark Murray /* The identity file name, given on the command line or entered by the user. */
9719261079SEd Maste static char identity_file[PATH_MAX];
9819261079SEd Maste static int have_identity = 0;
99511b41d2SMark Murray 
100511b41d2SMark Murray /* This is set to the passphrase if given on the command line. */
10119261079SEd Maste static char *identity_passphrase = NULL;
102511b41d2SMark Murray 
103511b41d2SMark Murray /* This is set to the new passphrase if given on the command line. */
10419261079SEd Maste static char *identity_new_passphrase = NULL;
105e2f6069cSDag-Erling Smørgrav 
106b15c8340SDag-Erling Smørgrav /* Key type when certifying */
10719261079SEd Maste static u_int cert_key_type = SSH2_CERT_TYPE_USER;
108b15c8340SDag-Erling Smørgrav 
109b15c8340SDag-Erling Smørgrav /* "key ID" of signed key */
11019261079SEd Maste static char *cert_key_id = NULL;
111b15c8340SDag-Erling Smørgrav 
112b15c8340SDag-Erling Smørgrav /* Comma-separated list of principal names for certifying keys */
11319261079SEd Maste static char *cert_principals = NULL;
114b15c8340SDag-Erling Smørgrav 
115b15c8340SDag-Erling Smørgrav /* Validity period for certificates */
11619261079SEd Maste static u_int64_t cert_valid_from = 0;
11719261079SEd Maste static u_int64_t cert_valid_to = ~0ULL;
118b15c8340SDag-Erling Smørgrav 
119e2f6069cSDag-Erling Smørgrav /* Certificate options */
120e2f6069cSDag-Erling Smørgrav #define CERTOPT_X_FWD				(1)
121e2f6069cSDag-Erling Smørgrav #define CERTOPT_AGENT_FWD			(1<<1)
122e2f6069cSDag-Erling Smørgrav #define CERTOPT_PORT_FWD			(1<<2)
123e2f6069cSDag-Erling Smørgrav #define CERTOPT_PTY				(1<<3)
124e2f6069cSDag-Erling Smørgrav #define CERTOPT_USER_RC				(1<<4)
12519261079SEd Maste #define CERTOPT_NO_REQUIRE_USER_PRESENCE	(1<<5)
12638a52bd3SEd Maste #define CERTOPT_REQUIRE_VERIFY			(1<<6)
127e2f6069cSDag-Erling Smørgrav #define CERTOPT_DEFAULT	(CERTOPT_X_FWD|CERTOPT_AGENT_FWD| \
128e2f6069cSDag-Erling Smørgrav 			 CERTOPT_PORT_FWD|CERTOPT_PTY|CERTOPT_USER_RC)
12919261079SEd Maste static u_int32_t certflags_flags = CERTOPT_DEFAULT;
13019261079SEd Maste static char *certflags_command = NULL;
13119261079SEd Maste static char *certflags_src_addr = NULL;
132b15c8340SDag-Erling Smørgrav 
1334f52dfbbSDag-Erling Smørgrav /* Arbitrary extensions specified by user */
13419261079SEd Maste struct cert_ext {
1354f52dfbbSDag-Erling Smørgrav 	char *key;
1364f52dfbbSDag-Erling Smørgrav 	char *val;
1374f52dfbbSDag-Erling Smørgrav 	int crit;
1384f52dfbbSDag-Erling Smørgrav };
13919261079SEd Maste static struct cert_ext *cert_ext;
14019261079SEd Maste static size_t ncert_ext;
1414f52dfbbSDag-Erling Smørgrav 
142e2f6069cSDag-Erling Smørgrav /* Conversion to/from various formats */
143e2f6069cSDag-Erling Smørgrav enum {
144e2f6069cSDag-Erling Smørgrav 	FMT_RFC4716,
145e2f6069cSDag-Erling Smørgrav 	FMT_PKCS8,
146e2f6069cSDag-Erling Smørgrav 	FMT_PEM
147e2f6069cSDag-Erling Smørgrav } convert_format = FMT_RFC4716;
1481e8db6e2SBrian Feldman 
14919261079SEd Maste static char *key_type_name = NULL;
150a04a10f8SKris Kennaway 
151e2f6069cSDag-Erling Smørgrav /* Load key from this PKCS#11 provider */
15219261079SEd Maste static char *pkcs11provider = NULL;
153e2f6069cSDag-Erling Smørgrav 
15419261079SEd Maste /* FIDO/U2F provider to use */
15519261079SEd Maste static char *sk_provider = NULL;
15619261079SEd Maste 
15719261079SEd Maste /* Format for writing private keys */
15819261079SEd Maste static int private_key_format = SSHKEY_PRIVATE_OPENSSH;
159f7167e0eSDag-Erling Smørgrav 
160f7167e0eSDag-Erling Smørgrav /* Cipher for new-format private keys */
16119261079SEd Maste static char *openssh_format_cipher = NULL;
162f7167e0eSDag-Erling Smørgrav 
16319261079SEd Maste /* Number of KDF rounds to derive new format keys. */
16419261079SEd Maste static int rounds = 0;
165f7167e0eSDag-Erling Smørgrav 
166511b41d2SMark Murray /* argv0 */
167511b41d2SMark Murray extern char *__progname;
168511b41d2SMark Murray 
16919261079SEd Maste static char hostname[NI_MAXHOST];
170a04a10f8SKris Kennaway 
171557f75e5SDag-Erling Smørgrav #ifdef WITH_OPENSSL
172d74d50a8SDag-Erling Smørgrav /* moduli.c */
173043840dfSDag-Erling Smørgrav int gen_candidates(FILE *, u_int32_t, u_int32_t, BIGNUM *);
174462c32cbSDag-Erling Smørgrav int prime_test(FILE *, FILE *, u_int32_t, u_int32_t, char *, unsigned long,
175462c32cbSDag-Erling Smørgrav     unsigned long);
176557f75e5SDag-Erling Smørgrav #endif
177d74d50a8SDag-Erling Smørgrav 
178ae1f160dSDag-Erling Smørgrav static void
type_bits_valid(int type,const char * name,u_int32_t * bitsp)179bc5531deSDag-Erling Smørgrav type_bits_valid(int type, const char *name, u_int32_t *bitsp)
180e146993eSDag-Erling Smørgrav {
181557f75e5SDag-Erling Smørgrav 	if (type == KEY_UNSPEC)
182557f75e5SDag-Erling Smørgrav 		fatal("unknown key type %s", key_type_name);
183e146993eSDag-Erling Smørgrav 	if (*bitsp == 0) {
184bc5531deSDag-Erling Smørgrav #ifdef WITH_OPENSSL
18519261079SEd Maste 		int nid;
18619261079SEd Maste 
18719261079SEd Maste 		switch(type) {
18819261079SEd Maste 		case KEY_DSA:
189e146993eSDag-Erling Smørgrav 			*bitsp = DEFAULT_BITS_DSA;
19019261079SEd Maste 			break;
19119261079SEd Maste 		case KEY_ECDSA:
192bc5531deSDag-Erling Smørgrav 			if (name != NULL &&
193bc5531deSDag-Erling Smørgrav 			    (nid = sshkey_ecdsa_nid_from_name(name)) > 0)
194bc5531deSDag-Erling Smørgrav 				*bitsp = sshkey_curve_nid_to_bits(nid);
195bc5531deSDag-Erling Smørgrav 			if (*bitsp == 0)
196e146993eSDag-Erling Smørgrav 				*bitsp = DEFAULT_BITS_ECDSA;
19719261079SEd Maste 			break;
19819261079SEd Maste 		case KEY_RSA:
199e146993eSDag-Erling Smørgrav 			*bitsp = DEFAULT_BITS;
20019261079SEd Maste 			break;
20119261079SEd Maste 		}
20219261079SEd Maste #endif
203e146993eSDag-Erling Smørgrav 	}
204bc5531deSDag-Erling Smørgrav #ifdef WITH_OPENSSL
2054f52dfbbSDag-Erling Smørgrav 	switch (type) {
2064f52dfbbSDag-Erling Smørgrav 	case KEY_DSA:
2074f52dfbbSDag-Erling Smørgrav 		if (*bitsp != 1024)
2084f52dfbbSDag-Erling Smørgrav 			fatal("Invalid DSA key length: must be 1024 bits");
2094f52dfbbSDag-Erling Smørgrav 		break;
2104f52dfbbSDag-Erling Smørgrav 	case KEY_RSA:
2114f52dfbbSDag-Erling Smørgrav 		if (*bitsp < SSH_RSA_MINIMUM_MODULUS_SIZE)
2124f52dfbbSDag-Erling Smørgrav 			fatal("Invalid RSA key length: minimum is %d bits",
2134f52dfbbSDag-Erling Smørgrav 			    SSH_RSA_MINIMUM_MODULUS_SIZE);
21419261079SEd Maste 		else if (*bitsp > OPENSSL_RSA_MAX_MODULUS_BITS)
21519261079SEd Maste 			fatal("Invalid RSA key length: maximum is %d bits",
21619261079SEd Maste 			    OPENSSL_RSA_MAX_MODULUS_BITS);
2174f52dfbbSDag-Erling Smørgrav 		break;
2184f52dfbbSDag-Erling Smørgrav 	case KEY_ECDSA:
2194f52dfbbSDag-Erling Smørgrav 		if (sshkey_ecdsa_bits_to_nid(*bitsp) == -1)
22019261079SEd Maste #ifdef OPENSSL_HAS_NISTP521
2214f52dfbbSDag-Erling Smørgrav 			fatal("Invalid ECDSA key length: valid lengths are "
222e146993eSDag-Erling Smørgrav 			    "256, 384 or 521 bits");
22319261079SEd Maste #else
22419261079SEd Maste 			fatal("Invalid ECDSA key length: valid lengths are "
22519261079SEd Maste 			    "256 or 384 bits");
22619261079SEd Maste #endif
2274f52dfbbSDag-Erling Smørgrav 	}
228a0ee8cc6SDag-Erling Smørgrav #endif
229e146993eSDag-Erling Smørgrav }
230e146993eSDag-Erling Smørgrav 
23119261079SEd Maste /*
23219261079SEd Maste  * Checks whether a file exists and, if so, asks the user whether they wish
23319261079SEd Maste  * to overwrite it.
23419261079SEd Maste  * Returns nonzero if the file does not already exist or if the user agrees to
23519261079SEd Maste  * overwrite, or zero otherwise.
23619261079SEd Maste  */
23719261079SEd Maste static int
confirm_overwrite(const char * filename)23819261079SEd Maste confirm_overwrite(const char *filename)
23919261079SEd Maste {
24019261079SEd Maste 	char yesno[3];
24119261079SEd Maste 	struct stat st;
24219261079SEd Maste 
24319261079SEd Maste 	if (stat(filename, &st) != 0)
24419261079SEd Maste 		return 1;
24519261079SEd Maste 	printf("%s already exists.\n", filename);
24619261079SEd Maste 	printf("Overwrite (y/n)? ");
24719261079SEd Maste 	fflush(stdout);
24819261079SEd Maste 	if (fgets(yesno, sizeof(yesno), stdin) == NULL)
24919261079SEd Maste 		return 0;
25019261079SEd Maste 	if (yesno[0] != 'y' && yesno[0] != 'Y')
25119261079SEd Maste 		return 0;
25219261079SEd Maste 	return 1;
25319261079SEd Maste }
25419261079SEd Maste 
255e146993eSDag-Erling Smørgrav static void
ask_filename(struct passwd * pw,const char * prompt)256511b41d2SMark Murray ask_filename(struct passwd *pw, const char *prompt)
257511b41d2SMark Murray {
258511b41d2SMark Murray 	char buf[1024];
2591e8db6e2SBrian Feldman 	char *name = NULL;
2601e8db6e2SBrian Feldman 
261ae1f160dSDag-Erling Smørgrav 	if (key_type_name == NULL)
262c9315099SEd Maste 		name = _PATH_SSH_CLIENT_ID_ED25519;
263761efaa7SDag-Erling Smørgrav 	else {
264*3d9fd9fcSEd Maste 		switch (sshkey_type_from_shortname(key_type_name)) {
265a91a2465SEd Maste #ifdef WITH_DSA
266e2f6069cSDag-Erling Smørgrav 		case KEY_DSA_CERT:
2671e8db6e2SBrian Feldman 		case KEY_DSA:
2681e8db6e2SBrian Feldman 			name = _PATH_SSH_CLIENT_ID_DSA;
2691e8db6e2SBrian Feldman 			break;
270a91a2465SEd Maste #endif
2714a421b63SDag-Erling Smørgrav #ifdef OPENSSL_HAS_ECC
2724a421b63SDag-Erling Smørgrav 		case KEY_ECDSA_CERT:
2734a421b63SDag-Erling Smørgrav 		case KEY_ECDSA:
2744a421b63SDag-Erling Smørgrav 			name = _PATH_SSH_CLIENT_ID_ECDSA;
2754a421b63SDag-Erling Smørgrav 			break;
27619261079SEd Maste 		case KEY_ECDSA_SK_CERT:
27719261079SEd Maste 		case KEY_ECDSA_SK:
27819261079SEd Maste 			name = _PATH_SSH_CLIENT_ID_ECDSA_SK;
27919261079SEd Maste 			break;
2804a421b63SDag-Erling Smørgrav #endif
281e2f6069cSDag-Erling Smørgrav 		case KEY_RSA_CERT:
2821e8db6e2SBrian Feldman 		case KEY_RSA:
2831e8db6e2SBrian Feldman 			name = _PATH_SSH_CLIENT_ID_RSA;
2841e8db6e2SBrian Feldman 			break;
285f7167e0eSDag-Erling Smørgrav 		case KEY_ED25519:
286f7167e0eSDag-Erling Smørgrav 		case KEY_ED25519_CERT:
287f7167e0eSDag-Erling Smørgrav 			name = _PATH_SSH_CLIENT_ID_ED25519;
288f7167e0eSDag-Erling Smørgrav 			break;
28919261079SEd Maste 		case KEY_ED25519_SK:
29019261079SEd Maste 		case KEY_ED25519_SK_CERT:
29119261079SEd Maste 			name = _PATH_SSH_CLIENT_ID_ED25519_SK;
29219261079SEd Maste 			break;
29347dd1d1bSDag-Erling Smørgrav 		case KEY_XMSS:
29447dd1d1bSDag-Erling Smørgrav 		case KEY_XMSS_CERT:
29547dd1d1bSDag-Erling Smørgrav 			name = _PATH_SSH_CLIENT_ID_XMSS;
29647dd1d1bSDag-Erling Smørgrav 			break;
2971e8db6e2SBrian Feldman 		default:
298557f75e5SDag-Erling Smørgrav 			fatal("bad key type");
2991e8db6e2SBrian Feldman 		}
300761efaa7SDag-Erling Smørgrav 	}
301557f75e5SDag-Erling Smørgrav 	snprintf(identity_file, sizeof(identity_file),
302557f75e5SDag-Erling Smørgrav 	    "%s/%s", pw->pw_dir, name);
303557f75e5SDag-Erling Smørgrav 	printf("%s (%s): ", prompt, identity_file);
304557f75e5SDag-Erling Smørgrav 	fflush(stdout);
305511b41d2SMark Murray 	if (fgets(buf, sizeof(buf), stdin) == NULL)
306511b41d2SMark Murray 		exit(1);
307d4af9e69SDag-Erling Smørgrav 	buf[strcspn(buf, "\n")] = '\0';
308511b41d2SMark Murray 	if (strcmp(buf, "") != 0)
309511b41d2SMark Murray 		strlcpy(identity_file, buf, sizeof(identity_file));
310511b41d2SMark Murray 	have_identity = 1;
311511b41d2SMark Murray }
312511b41d2SMark Murray 
313bc5531deSDag-Erling Smørgrav static struct sshkey *
load_identity(const char * filename,char ** commentp)31419261079SEd Maste load_identity(const char *filename, char **commentp)
315a04a10f8SKris Kennaway {
316*3d9fd9fcSEd Maste 	char *prompt, *pass;
317bc5531deSDag-Erling Smørgrav 	struct sshkey *prv;
318bc5531deSDag-Erling Smørgrav 	int r;
3191e8db6e2SBrian Feldman 
32019261079SEd Maste 	if (commentp != NULL)
32119261079SEd Maste 		*commentp = NULL;
32219261079SEd Maste 	if ((r = sshkey_load_private(filename, "", &prv, commentp)) == 0)
323bc5531deSDag-Erling Smørgrav 		return prv;
324bc5531deSDag-Erling Smørgrav 	if (r != SSH_ERR_KEY_WRONG_PASSPHRASE)
32519261079SEd Maste 		fatal_r(r, "Load key \"%s\"", filename);
326ae1f160dSDag-Erling Smørgrav 	if (identity_passphrase)
327ae1f160dSDag-Erling Smørgrav 		pass = xstrdup(identity_passphrase);
328*3d9fd9fcSEd Maste 	else {
329*3d9fd9fcSEd Maste 		xasprintf(&prompt, "Enter passphrase for \"%s\": ", filename);
330*3d9fd9fcSEd Maste 		pass = read_passphrase(prompt, RP_ALLOW_STDIN);
331*3d9fd9fcSEd Maste 		free(prompt);
332*3d9fd9fcSEd Maste 	}
33319261079SEd Maste 	r = sshkey_load_private(filename, pass, &prv, commentp);
33419261079SEd Maste 	freezero(pass, strlen(pass));
335bc5531deSDag-Erling Smørgrav 	if (r != 0)
33619261079SEd Maste 		fatal_r(r, "Load key \"%s\"", filename);
3371e8db6e2SBrian Feldman 	return prv;
338a04a10f8SKris Kennaway }
339a04a10f8SKris Kennaway 
3405b9b2fafSBrian Feldman #define SSH_COM_PUBLIC_BEGIN		"---- BEGIN SSH2 PUBLIC KEY ----"
3415b9b2fafSBrian Feldman #define SSH_COM_PUBLIC_END		"---- END SSH2 PUBLIC KEY ----"
3425b9b2fafSBrian Feldman #define SSH_COM_PRIVATE_BEGIN		"---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----"
3435b9b2fafSBrian Feldman #define	SSH_COM_PRIVATE_KEY_MAGIC	0x3f6ff9eb
344a04a10f8SKris Kennaway 
345a0ee8cc6SDag-Erling Smørgrav #ifdef WITH_OPENSSL
346ae1f160dSDag-Erling Smørgrav static void
do_convert_to_ssh2(struct passwd * pw,struct sshkey * k)347bc5531deSDag-Erling Smørgrav do_convert_to_ssh2(struct passwd *pw, struct sshkey *k)
348a04a10f8SKris Kennaway {
34919261079SEd Maste 	struct sshbuf *b;
35019261079SEd Maste 	char comment[61], *b64;
351bc5531deSDag-Erling Smørgrav 	int r;
352a04a10f8SKris Kennaway 
35319261079SEd Maste 	if ((b = sshbuf_new()) == NULL)
35419261079SEd Maste 		fatal_f("sshbuf_new failed");
35519261079SEd Maste 	if ((r = sshkey_putb(k, b)) != 0)
35619261079SEd Maste 		fatal_fr(r, "put key");
35719261079SEd Maste 	if ((b64 = sshbuf_dtob64_string(b, 1)) == NULL)
35819261079SEd Maste 		fatal_f("sshbuf_dtob64_string failed");
35919261079SEd Maste 
360b15c8340SDag-Erling Smørgrav 	/* Comment + surrounds must fit into 72 chars (RFC 4716 sec 3.3) */
361b15c8340SDag-Erling Smørgrav 	snprintf(comment, sizeof(comment),
362b15c8340SDag-Erling Smørgrav 	    "%u-bit %s, converted by %s@%s from OpenSSH",
363bc5531deSDag-Erling Smørgrav 	    sshkey_size(k), sshkey_type(k),
364a04a10f8SKris Kennaway 	    pw->pw_name, hostname);
365b15c8340SDag-Erling Smørgrav 
366bc5531deSDag-Erling Smørgrav 	sshkey_free(k);
36719261079SEd Maste 	sshbuf_free(b);
36819261079SEd Maste 
36919261079SEd Maste 	fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN);
37019261079SEd Maste 	fprintf(stdout, "Comment: \"%s\"\n%s", comment, b64);
37119261079SEd Maste 	fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END);
37219261079SEd Maste 	free(b64);
373a04a10f8SKris Kennaway 	exit(0);
374a04a10f8SKris Kennaway }
375a04a10f8SKris Kennaway 
376ae1f160dSDag-Erling Smørgrav static void
do_convert_to_pkcs8(struct sshkey * k)377bc5531deSDag-Erling Smørgrav do_convert_to_pkcs8(struct sshkey *k)
378e2f6069cSDag-Erling Smørgrav {
379bc5531deSDag-Erling Smørgrav 	switch (sshkey_type_plain(k->type)) {
380e2f6069cSDag-Erling Smørgrav 	case KEY_RSA:
381*3d9fd9fcSEd Maste 		if (!PEM_write_RSA_PUBKEY(stdout,
382*3d9fd9fcSEd Maste 		    EVP_PKEY_get0_RSA(k->pkey)))
383e2f6069cSDag-Erling Smørgrav 			fatal("PEM_write_RSA_PUBKEY failed");
384e2f6069cSDag-Erling Smørgrav 		break;
385a91a2465SEd Maste #ifdef WITH_DSA
386e2f6069cSDag-Erling Smørgrav 	case KEY_DSA:
387e2f6069cSDag-Erling Smørgrav 		if (!PEM_write_DSA_PUBKEY(stdout, k->dsa))
388e2f6069cSDag-Erling Smørgrav 			fatal("PEM_write_DSA_PUBKEY failed");
389e2f6069cSDag-Erling Smørgrav 		break;
390a91a2465SEd Maste #endif
3914a421b63SDag-Erling Smørgrav #ifdef OPENSSL_HAS_ECC
3924a421b63SDag-Erling Smørgrav 	case KEY_ECDSA:
393*3d9fd9fcSEd Maste 		if (!PEM_write_EC_PUBKEY(stdout,
394*3d9fd9fcSEd Maste 		    EVP_PKEY_get0_EC_KEY(k->pkey)))
3954a421b63SDag-Erling Smørgrav 			fatal("PEM_write_EC_PUBKEY failed");
3964a421b63SDag-Erling Smørgrav 		break;
3974a421b63SDag-Erling Smørgrav #endif
398e2f6069cSDag-Erling Smørgrav 	default:
39919261079SEd Maste 		fatal_f("unsupported key type %s", sshkey_type(k));
400e2f6069cSDag-Erling Smørgrav 	}
401e2f6069cSDag-Erling Smørgrav 	exit(0);
402e2f6069cSDag-Erling Smørgrav }
403e2f6069cSDag-Erling Smørgrav 
404e2f6069cSDag-Erling Smørgrav static void
do_convert_to_pem(struct sshkey * k)405bc5531deSDag-Erling Smørgrav do_convert_to_pem(struct sshkey *k)
406e2f6069cSDag-Erling Smørgrav {
407bc5531deSDag-Erling Smørgrav 	switch (sshkey_type_plain(k->type)) {
408e2f6069cSDag-Erling Smørgrav 	case KEY_RSA:
409*3d9fd9fcSEd Maste 		if (!PEM_write_RSAPublicKey(stdout,
410*3d9fd9fcSEd Maste 		    EVP_PKEY_get0_RSA(k->pkey)))
411e2f6069cSDag-Erling Smørgrav 			fatal("PEM_write_RSAPublicKey failed");
412e2f6069cSDag-Erling Smørgrav 		break;
413a91a2465SEd Maste #ifdef WITH_DSA
41419261079SEd Maste 	case KEY_DSA:
41519261079SEd Maste 		if (!PEM_write_DSA_PUBKEY(stdout, k->dsa))
41619261079SEd Maste 			fatal("PEM_write_DSA_PUBKEY failed");
41719261079SEd Maste 		break;
418a91a2465SEd Maste #endif
41919261079SEd Maste #ifdef OPENSSL_HAS_ECC
42019261079SEd Maste 	case KEY_ECDSA:
421*3d9fd9fcSEd Maste 		if (!PEM_write_EC_PUBKEY(stdout,
422*3d9fd9fcSEd Maste 		    EVP_PKEY_get0_EC_KEY(k->pkey)))
42319261079SEd Maste 			fatal("PEM_write_EC_PUBKEY failed");
42419261079SEd Maste 		break;
42519261079SEd Maste #endif
426e2f6069cSDag-Erling Smørgrav 	default:
42719261079SEd Maste 		fatal_f("unsupported key type %s", sshkey_type(k));
428e2f6069cSDag-Erling Smørgrav 	}
429e2f6069cSDag-Erling Smørgrav 	exit(0);
430e2f6069cSDag-Erling Smørgrav }
431e2f6069cSDag-Erling Smørgrav 
432e2f6069cSDag-Erling Smørgrav static void
do_convert_to(struct passwd * pw)433e2f6069cSDag-Erling Smørgrav do_convert_to(struct passwd *pw)
434e2f6069cSDag-Erling Smørgrav {
435bc5531deSDag-Erling Smørgrav 	struct sshkey *k;
436e2f6069cSDag-Erling Smørgrav 	struct stat st;
437bc5531deSDag-Erling Smørgrav 	int r;
438e2f6069cSDag-Erling Smørgrav 
439e2f6069cSDag-Erling Smørgrav 	if (!have_identity)
440e2f6069cSDag-Erling Smørgrav 		ask_filename(pw, "Enter file in which the key is");
44119261079SEd Maste 	if (stat(identity_file, &st) == -1)
442e2f6069cSDag-Erling Smørgrav 		fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
443bc5531deSDag-Erling Smørgrav 	if ((r = sshkey_load_public(identity_file, &k, NULL)) != 0)
44419261079SEd Maste 		k = load_identity(identity_file, NULL);
445e2f6069cSDag-Erling Smørgrav 	switch (convert_format) {
446e2f6069cSDag-Erling Smørgrav 	case FMT_RFC4716:
447e2f6069cSDag-Erling Smørgrav 		do_convert_to_ssh2(pw, k);
448e2f6069cSDag-Erling Smørgrav 		break;
449e2f6069cSDag-Erling Smørgrav 	case FMT_PKCS8:
450e2f6069cSDag-Erling Smørgrav 		do_convert_to_pkcs8(k);
451e2f6069cSDag-Erling Smørgrav 		break;
452e2f6069cSDag-Erling Smørgrav 	case FMT_PEM:
453e2f6069cSDag-Erling Smørgrav 		do_convert_to_pem(k);
454e2f6069cSDag-Erling Smørgrav 		break;
455e2f6069cSDag-Erling Smørgrav 	default:
45619261079SEd Maste 		fatal_f("unknown key format %d", convert_format);
457e2f6069cSDag-Erling Smørgrav 	}
458e2f6069cSDag-Erling Smørgrav 	exit(0);
459e2f6069cSDag-Erling Smørgrav }
460e2f6069cSDag-Erling Smørgrav 
461bc5531deSDag-Erling Smørgrav /*
462bc5531deSDag-Erling Smørgrav  * This is almost exactly the bignum1 encoding, but with 32 bit for length
463bc5531deSDag-Erling Smørgrav  * instead of 16.
464bc5531deSDag-Erling Smørgrav  */
465e2f6069cSDag-Erling Smørgrav static void
buffer_get_bignum_bits(struct sshbuf * b,BIGNUM * value)466bc5531deSDag-Erling Smørgrav buffer_get_bignum_bits(struct sshbuf *b, BIGNUM *value)
4675b9b2fafSBrian Feldman {
468bc5531deSDag-Erling Smørgrav 	u_int bytes, bignum_bits;
469bc5531deSDag-Erling Smørgrav 	int r;
4701e8db6e2SBrian Feldman 
471bc5531deSDag-Erling Smørgrav 	if ((r = sshbuf_get_u32(b, &bignum_bits)) != 0)
47219261079SEd Maste 		fatal_fr(r, "parse");
473bc5531deSDag-Erling Smørgrav 	bytes = (bignum_bits + 7) / 8;
474bc5531deSDag-Erling Smørgrav 	if (sshbuf_len(b) < bytes)
47519261079SEd Maste 		fatal_f("input buffer too small: need %d have %zu",
47619261079SEd Maste 		    bytes, sshbuf_len(b));
477bc5531deSDag-Erling Smørgrav 	if (BN_bin2bn(sshbuf_ptr(b), bytes, value) == NULL)
47819261079SEd Maste 		fatal_f("BN_bin2bn failed");
479bc5531deSDag-Erling Smørgrav 	if ((r = sshbuf_consume(b, bytes)) != 0)
48019261079SEd Maste 		fatal_fr(r, "consume");
4815b9b2fafSBrian Feldman }
4825b9b2fafSBrian Feldman 
483bc5531deSDag-Erling Smørgrav static struct sshkey *
do_convert_private_ssh2(struct sshbuf * b)48419261079SEd Maste do_convert_private_ssh2(struct sshbuf *b)
4855b9b2fafSBrian Feldman {
486bc5531deSDag-Erling Smørgrav 	struct sshkey *key = NULL;
4875b9b2fafSBrian Feldman 	char *type, *cipher;
4884d3fc8b0SEd Maste 	const char *alg = NULL;
489bc5531deSDag-Erling Smørgrav 	u_char e1, e2, e3, *sig = NULL, data[] = "abcde12345";
490bc5531deSDag-Erling Smørgrav 	int r, rlen, ktype;
491bc5531deSDag-Erling Smørgrav 	u_int magic, i1, i2, i3, i4;
492bc5531deSDag-Erling Smørgrav 	size_t slen;
493ae1f160dSDag-Erling Smørgrav 	u_long e;
494a91a2465SEd Maste #ifdef WITH_DSA
4952a01feabSEd Maste 	BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL;
4962a01feabSEd Maste 	BIGNUM *dsa_pub_key = NULL, *dsa_priv_key = NULL;
497a91a2465SEd Maste #endif
4982a01feabSEd Maste 	BIGNUM *rsa_n = NULL, *rsa_e = NULL, *rsa_d = NULL;
4992a01feabSEd Maste 	BIGNUM *rsa_p = NULL, *rsa_q = NULL, *rsa_iqmp = NULL;
500*3d9fd9fcSEd Maste 	BIGNUM *rsa_dmp1 = NULL, *rsa_dmq1 = NULL;
501*3d9fd9fcSEd Maste 	RSA *rsa = NULL;
50219261079SEd Maste 
503bc5531deSDag-Erling Smørgrav 	if ((r = sshbuf_get_u32(b, &magic)) != 0)
50419261079SEd Maste 		fatal_fr(r, "parse magic");
5055b9b2fafSBrian Feldman 
5065b9b2fafSBrian Feldman 	if (magic != SSH_COM_PRIVATE_KEY_MAGIC) {
507bc5531deSDag-Erling Smørgrav 		error("bad magic 0x%x != 0x%x", magic,
508bc5531deSDag-Erling Smørgrav 		    SSH_COM_PRIVATE_KEY_MAGIC);
5095b9b2fafSBrian Feldman 		return NULL;
5105b9b2fafSBrian Feldman 	}
511bc5531deSDag-Erling Smørgrav 	if ((r = sshbuf_get_u32(b, &i1)) != 0 ||
512bc5531deSDag-Erling Smørgrav 	    (r = sshbuf_get_cstring(b, &type, NULL)) != 0 ||
513bc5531deSDag-Erling Smørgrav 	    (r = sshbuf_get_cstring(b, &cipher, NULL)) != 0 ||
514bc5531deSDag-Erling Smørgrav 	    (r = sshbuf_get_u32(b, &i2)) != 0 ||
515bc5531deSDag-Erling Smørgrav 	    (r = sshbuf_get_u32(b, &i3)) != 0 ||
516bc5531deSDag-Erling Smørgrav 	    (r = sshbuf_get_u32(b, &i4)) != 0)
51719261079SEd Maste 		fatal_fr(r, "parse");
518ae1f160dSDag-Erling Smørgrav 	debug("ignore (%d %d %d %d)", i1, i2, i3, i4);
5195b9b2fafSBrian Feldman 	if (strcmp(cipher, "none") != 0) {
5205b9b2fafSBrian Feldman 		error("unsupported cipher %s", cipher);
521e4a9863fSDag-Erling Smørgrav 		free(cipher);
522e4a9863fSDag-Erling Smørgrav 		free(type);
5235b9b2fafSBrian Feldman 		return NULL;
5245b9b2fafSBrian Feldman 	}
525e4a9863fSDag-Erling Smørgrav 	free(cipher);
5265b9b2fafSBrian Feldman 
527a91a2465SEd Maste 	if (strstr(type, "rsa")) {
5281e8db6e2SBrian Feldman 		ktype = KEY_RSA;
529a91a2465SEd Maste #ifdef WITH_DSA
530a91a2465SEd Maste 	} else if (strstr(type, "dsa")) {
531a91a2465SEd Maste 		ktype = KEY_DSA;
532a91a2465SEd Maste #endif
5331e8db6e2SBrian Feldman 	} else {
534e4a9863fSDag-Erling Smørgrav 		free(type);
5351e8db6e2SBrian Feldman 		return NULL;
5361e8db6e2SBrian Feldman 	}
5372f513db7SEd Maste 	if ((key = sshkey_new(ktype)) == NULL)
5382f513db7SEd Maste 		fatal("sshkey_new failed");
539e4a9863fSDag-Erling Smørgrav 	free(type);
5401e8db6e2SBrian Feldman 
5411e8db6e2SBrian Feldman 	switch (key->type) {
542a91a2465SEd Maste #ifdef WITH_DSA
5431e8db6e2SBrian Feldman 	case KEY_DSA:
5442a01feabSEd Maste 		if ((dsa_p = BN_new()) == NULL ||
5452a01feabSEd Maste 		    (dsa_q = BN_new()) == NULL ||
5462a01feabSEd Maste 		    (dsa_g = BN_new()) == NULL ||
5472a01feabSEd Maste 		    (dsa_pub_key = BN_new()) == NULL ||
5482a01feabSEd Maste 		    (dsa_priv_key = BN_new()) == NULL)
54919261079SEd Maste 			fatal_f("BN_new");
5502a01feabSEd Maste 		buffer_get_bignum_bits(b, dsa_p);
5512a01feabSEd Maste 		buffer_get_bignum_bits(b, dsa_g);
5522a01feabSEd Maste 		buffer_get_bignum_bits(b, dsa_q);
5532a01feabSEd Maste 		buffer_get_bignum_bits(b, dsa_pub_key);
5542a01feabSEd Maste 		buffer_get_bignum_bits(b, dsa_priv_key);
5552a01feabSEd Maste 		if (!DSA_set0_pqg(key->dsa, dsa_p, dsa_q, dsa_g))
55619261079SEd Maste 			fatal_f("DSA_set0_pqg failed");
5572a01feabSEd Maste 		dsa_p = dsa_q = dsa_g = NULL; /* transferred */
5582a01feabSEd Maste 		if (!DSA_set0_key(key->dsa, dsa_pub_key, dsa_priv_key))
55919261079SEd Maste 			fatal_f("DSA_set0_key failed");
5602a01feabSEd Maste 		dsa_pub_key = dsa_priv_key = NULL; /* transferred */
5611e8db6e2SBrian Feldman 		break;
562a91a2465SEd Maste #endif
5631e8db6e2SBrian Feldman 	case KEY_RSA:
564bc5531deSDag-Erling Smørgrav 		if ((r = sshbuf_get_u8(b, &e1)) != 0 ||
565bc5531deSDag-Erling Smørgrav 		    (e1 < 30 && (r = sshbuf_get_u8(b, &e2)) != 0) ||
566bc5531deSDag-Erling Smørgrav 		    (e1 < 30 && (r = sshbuf_get_u8(b, &e3)) != 0))
56719261079SEd Maste 			fatal_fr(r, "parse RSA");
568bc5531deSDag-Erling Smørgrav 		e = e1;
569ae1f160dSDag-Erling Smørgrav 		debug("e %lx", e);
570ae1f160dSDag-Erling Smørgrav 		if (e < 30) {
571ae1f160dSDag-Erling Smørgrav 			e <<= 8;
572bc5531deSDag-Erling Smørgrav 			e += e2;
573ae1f160dSDag-Erling Smørgrav 			debug("e %lx", e);
574ae1f160dSDag-Erling Smørgrav 			e <<= 8;
575bc5531deSDag-Erling Smørgrav 			e += e3;
576ae1f160dSDag-Erling Smørgrav 			debug("e %lx", e);
577ae1f160dSDag-Erling Smørgrav 		}
5782a01feabSEd Maste 		if ((rsa_e = BN_new()) == NULL)
57919261079SEd Maste 			fatal_f("BN_new");
5802a01feabSEd Maste 		if (!BN_set_word(rsa_e, e)) {
5812a01feabSEd Maste 			BN_clear_free(rsa_e);
582bc5531deSDag-Erling Smørgrav 			sshkey_free(key);
5835b9b2fafSBrian Feldman 			return NULL;
5845b9b2fafSBrian Feldman 		}
5852a01feabSEd Maste 		if ((rsa_n = BN_new()) == NULL ||
5862a01feabSEd Maste 		    (rsa_d = BN_new()) == NULL ||
5872a01feabSEd Maste 		    (rsa_p = BN_new()) == NULL ||
5882a01feabSEd Maste 		    (rsa_q = BN_new()) == NULL ||
5892a01feabSEd Maste 		    (rsa_iqmp = BN_new()) == NULL)
59019261079SEd Maste 			fatal_f("BN_new");
5912a01feabSEd Maste 		buffer_get_bignum_bits(b, rsa_d);
5922a01feabSEd Maste 		buffer_get_bignum_bits(b, rsa_n);
5932a01feabSEd Maste 		buffer_get_bignum_bits(b, rsa_iqmp);
5942a01feabSEd Maste 		buffer_get_bignum_bits(b, rsa_q);
5952a01feabSEd Maste 		buffer_get_bignum_bits(b, rsa_p);
596*3d9fd9fcSEd Maste 		if ((r = ssh_rsa_complete_crt_parameters(rsa_d, rsa_p, rsa_q,
597*3d9fd9fcSEd Maste 		    rsa_iqmp, &rsa_dmp1, &rsa_dmq1)) != 0)
598*3d9fd9fcSEd Maste 			fatal_fr(r, "generate RSA CRT parameters");
599*3d9fd9fcSEd Maste 		if ((key->pkey = EVP_PKEY_new()) == NULL)
600*3d9fd9fcSEd Maste 			fatal_f("EVP_PKEY_new failed");
601*3d9fd9fcSEd Maste 		if ((rsa = RSA_new()) == NULL)
602*3d9fd9fcSEd Maste 			fatal_f("RSA_new failed");
603*3d9fd9fcSEd Maste 		if (!RSA_set0_key(rsa, rsa_n, rsa_e, rsa_d))
60419261079SEd Maste 			fatal_f("RSA_set0_key failed");
6052a01feabSEd Maste 		rsa_n = rsa_e = rsa_d = NULL; /* transferred */
606*3d9fd9fcSEd Maste 		if (!RSA_set0_factors(rsa, rsa_p, rsa_q))
60719261079SEd Maste 			fatal_f("RSA_set0_factors failed");
6082a01feabSEd Maste 		rsa_p = rsa_q = NULL; /* transferred */
609*3d9fd9fcSEd Maste 		if (RSA_set0_crt_params(rsa, rsa_dmp1, rsa_dmq1, rsa_iqmp) != 1)
610*3d9fd9fcSEd Maste 			fatal_f("RSA_set0_crt_params failed");
611*3d9fd9fcSEd Maste 		rsa_dmp1 = rsa_dmq1 = rsa_iqmp = NULL;
612*3d9fd9fcSEd Maste 		if (EVP_PKEY_set1_RSA(key->pkey, rsa) != 1)
613*3d9fd9fcSEd Maste 			fatal_f("EVP_PKEY_set1_RSA failed");
614*3d9fd9fcSEd Maste 		RSA_free(rsa);
6154d3fc8b0SEd Maste 		alg = "rsa-sha2-256";
6161e8db6e2SBrian Feldman 		break;
6171e8db6e2SBrian Feldman 	}
618bc5531deSDag-Erling Smørgrav 	rlen = sshbuf_len(b);
6195b9b2fafSBrian Feldman 	if (rlen != 0)
62019261079SEd Maste 		error_f("remaining bytes in key blob %d", rlen);
6211e8db6e2SBrian Feldman 
622ae1f160dSDag-Erling Smørgrav 	/* try the key */
62338a52bd3SEd Maste 	if ((r = sshkey_sign(key, &sig, &slen, data, sizeof(data),
6244d3fc8b0SEd Maste 	    alg, NULL, NULL, 0)) != 0)
62538a52bd3SEd Maste 		error_fr(r, "signing with converted key failed");
62638a52bd3SEd Maste 	else if ((r = sshkey_verify(key, sig, slen, data, sizeof(data),
6274d3fc8b0SEd Maste 	    alg, 0, NULL)) != 0)
62838a52bd3SEd Maste 		error_fr(r, "verification with converted key failed");
62938a52bd3SEd Maste 	if (r != 0) {
630bc5531deSDag-Erling Smørgrav 		sshkey_free(key);
631bc5531deSDag-Erling Smørgrav 		free(sig);
632bc5531deSDag-Erling Smørgrav 		return NULL;
633bc5531deSDag-Erling Smørgrav 	}
634e4a9863fSDag-Erling Smørgrav 	free(sig);
6355b9b2fafSBrian Feldman 	return key;
6365b9b2fafSBrian Feldman }
6375b9b2fafSBrian Feldman 
638761efaa7SDag-Erling Smørgrav static int
get_line(FILE * fp,char * line,size_t len)639761efaa7SDag-Erling Smørgrav get_line(FILE *fp, char *line, size_t len)
640761efaa7SDag-Erling Smørgrav {
641761efaa7SDag-Erling Smørgrav 	int c;
642761efaa7SDag-Erling Smørgrav 	size_t pos = 0;
643761efaa7SDag-Erling Smørgrav 
644761efaa7SDag-Erling Smørgrav 	line[0] = '\0';
645761efaa7SDag-Erling Smørgrav 	while ((c = fgetc(fp)) != EOF) {
646557f75e5SDag-Erling Smørgrav 		if (pos >= len - 1)
647557f75e5SDag-Erling Smørgrav 			fatal("input line too long.");
648761efaa7SDag-Erling Smørgrav 		switch (c) {
649761efaa7SDag-Erling Smørgrav 		case '\r':
650761efaa7SDag-Erling Smørgrav 			c = fgetc(fp);
651557f75e5SDag-Erling Smørgrav 			if (c != EOF && c != '\n' && ungetc(c, fp) == EOF)
652557f75e5SDag-Erling Smørgrav 				fatal("unget: %s", strerror(errno));
653761efaa7SDag-Erling Smørgrav 			return pos;
654761efaa7SDag-Erling Smørgrav 		case '\n':
655761efaa7SDag-Erling Smørgrav 			return pos;
656761efaa7SDag-Erling Smørgrav 		}
657761efaa7SDag-Erling Smørgrav 		line[pos++] = c;
658761efaa7SDag-Erling Smørgrav 		line[pos] = '\0';
659761efaa7SDag-Erling Smørgrav 	}
660d4af9e69SDag-Erling Smørgrav 	/* We reached EOF */
661761efaa7SDag-Erling Smørgrav 	return -1;
662761efaa7SDag-Erling Smørgrav }
663761efaa7SDag-Erling Smørgrav 
664ae1f160dSDag-Erling Smørgrav static void
do_convert_from_ssh2(struct passwd * pw,struct sshkey ** k,int * private)665bc5531deSDag-Erling Smørgrav do_convert_from_ssh2(struct passwd *pw, struct sshkey **k, int *private)
666a04a10f8SKris Kennaway {
667bc5531deSDag-Erling Smørgrav 	int r, blen, escaped = 0;
668545d5ecaSDag-Erling Smørgrav 	u_int len;
669761efaa7SDag-Erling Smørgrav 	char line[1024];
67019261079SEd Maste 	struct sshbuf *buf;
671a04a10f8SKris Kennaway 	char encoded[8096];
672a04a10f8SKris Kennaway 	FILE *fp;
673a04a10f8SKris Kennaway 
67419261079SEd Maste 	if ((buf = sshbuf_new()) == NULL)
67519261079SEd Maste 		fatal("sshbuf_new failed");
676e2f6069cSDag-Erling Smørgrav 	if ((fp = fopen(identity_file, "r")) == NULL)
677e2f6069cSDag-Erling Smørgrav 		fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
678a04a10f8SKris Kennaway 	encoded[0] = '\0';
679761efaa7SDag-Erling Smørgrav 	while ((blen = get_line(fp, line, sizeof(line))) != -1) {
680e4a9863fSDag-Erling Smørgrav 		if (blen > 0 && line[blen - 1] == '\\')
681a04a10f8SKris Kennaway 			escaped++;
682a04a10f8SKris Kennaway 		if (strncmp(line, "----", 4) == 0 ||
683a04a10f8SKris Kennaway 		    strstr(line, ": ") != NULL) {
6845b9b2fafSBrian Feldman 			if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL)
685e2f6069cSDag-Erling Smørgrav 				*private = 1;
686ae1f160dSDag-Erling Smørgrav 			if (strstr(line, " END ") != NULL) {
687ae1f160dSDag-Erling Smørgrav 				break;
688ae1f160dSDag-Erling Smørgrav 			}
6891e8db6e2SBrian Feldman 			/* fprintf(stderr, "ignore: %s", line); */
690a04a10f8SKris Kennaway 			continue;
691a04a10f8SKris Kennaway 		}
692a04a10f8SKris Kennaway 		if (escaped) {
693a04a10f8SKris Kennaway 			escaped--;
6941e8db6e2SBrian Feldman 			/* fprintf(stderr, "escaped: %s", line); */
695a04a10f8SKris Kennaway 			continue;
696a04a10f8SKris Kennaway 		}
697a04a10f8SKris Kennaway 		strlcat(encoded, line, sizeof(encoded));
698a04a10f8SKris Kennaway 	}
699545d5ecaSDag-Erling Smørgrav 	len = strlen(encoded);
700545d5ecaSDag-Erling Smørgrav 	if (((len % 4) == 3) &&
701545d5ecaSDag-Erling Smørgrav 	    (encoded[len-1] == '=') &&
702545d5ecaSDag-Erling Smørgrav 	    (encoded[len-2] == '=') &&
703545d5ecaSDag-Erling Smørgrav 	    (encoded[len-3] == '='))
704545d5ecaSDag-Erling Smørgrav 		encoded[len-3] = '\0';
70519261079SEd Maste 	if ((r = sshbuf_b64tod(buf, encoded)) != 0)
70619261079SEd Maste 		fatal_fr(r, "base64 decode");
70719261079SEd Maste 	if (*private) {
70819261079SEd Maste 		if ((*k = do_convert_private_ssh2(buf)) == NULL)
70919261079SEd Maste 			fatal_f("private key conversion failed");
71019261079SEd Maste 	} else if ((r = sshkey_fromb(buf, k)) != 0)
71119261079SEd Maste 		fatal_fr(r, "parse key");
71219261079SEd Maste 	sshbuf_free(buf);
713e2f6069cSDag-Erling Smørgrav 	fclose(fp);
714e2f6069cSDag-Erling Smørgrav }
715e2f6069cSDag-Erling Smørgrav 
716e2f6069cSDag-Erling Smørgrav static void
do_convert_from_pkcs8(struct sshkey ** k,int * private)717bc5531deSDag-Erling Smørgrav do_convert_from_pkcs8(struct sshkey **k, int *private)
718e2f6069cSDag-Erling Smørgrav {
719e2f6069cSDag-Erling Smørgrav 	EVP_PKEY *pubkey;
720e2f6069cSDag-Erling Smørgrav 	FILE *fp;
721e2f6069cSDag-Erling Smørgrav 
722e2f6069cSDag-Erling Smørgrav 	if ((fp = fopen(identity_file, "r")) == NULL)
723e2f6069cSDag-Erling Smørgrav 		fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
724e2f6069cSDag-Erling Smørgrav 	if ((pubkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
72519261079SEd Maste 		fatal_f("%s is not a recognised public key format",
726e2f6069cSDag-Erling Smørgrav 		    identity_file);
727e2f6069cSDag-Erling Smørgrav 	}
728e2f6069cSDag-Erling Smørgrav 	fclose(fp);
7292a01feabSEd Maste 	switch (EVP_PKEY_base_id(pubkey)) {
730e2f6069cSDag-Erling Smørgrav 	case EVP_PKEY_RSA:
731bc5531deSDag-Erling Smørgrav 		if ((*k = sshkey_new(KEY_UNSPEC)) == NULL)
732bc5531deSDag-Erling Smørgrav 			fatal("sshkey_new failed");
733e2f6069cSDag-Erling Smørgrav 		(*k)->type = KEY_RSA;
734*3d9fd9fcSEd Maste 		(*k)->pkey = pubkey;
735*3d9fd9fcSEd Maste 		pubkey = NULL;
736e2f6069cSDag-Erling Smørgrav 		break;
737a91a2465SEd Maste #ifdef WITH_DSA
738e2f6069cSDag-Erling Smørgrav 	case EVP_PKEY_DSA:
739bc5531deSDag-Erling Smørgrav 		if ((*k = sshkey_new(KEY_UNSPEC)) == NULL)
740bc5531deSDag-Erling Smørgrav 			fatal("sshkey_new failed");
741e2f6069cSDag-Erling Smørgrav 		(*k)->type = KEY_DSA;
742e2f6069cSDag-Erling Smørgrav 		(*k)->dsa = EVP_PKEY_get1_DSA(pubkey);
743e2f6069cSDag-Erling Smørgrav 		break;
744a91a2465SEd Maste #endif
7454a421b63SDag-Erling Smørgrav #ifdef OPENSSL_HAS_ECC
7464a421b63SDag-Erling Smørgrav 	case EVP_PKEY_EC:
747bc5531deSDag-Erling Smørgrav 		if ((*k = sshkey_new(KEY_UNSPEC)) == NULL)
748bc5531deSDag-Erling Smørgrav 			fatal("sshkey_new failed");
749*3d9fd9fcSEd Maste 		if (((*k)->ecdsa_nid = sshkey_ecdsa_fixup_group(pubkey)) == -1)
750*3d9fd9fcSEd Maste 			fatal("sshkey_ecdsa_fixup_group failed");
7514a421b63SDag-Erling Smørgrav 		(*k)->type = KEY_ECDSA;
752*3d9fd9fcSEd Maste 		(*k)->pkey = pubkey;
753*3d9fd9fcSEd Maste 		pubkey = NULL;
7544a421b63SDag-Erling Smørgrav 		break;
7554a421b63SDag-Erling Smørgrav #endif
756e2f6069cSDag-Erling Smørgrav 	default:
75719261079SEd Maste 		fatal_f("unsupported pubkey type %d",
7582a01feabSEd Maste 		    EVP_PKEY_base_id(pubkey));
759e2f6069cSDag-Erling Smørgrav 	}
760e2f6069cSDag-Erling Smørgrav 	EVP_PKEY_free(pubkey);
761e2f6069cSDag-Erling Smørgrav 	return;
762e2f6069cSDag-Erling Smørgrav }
763e2f6069cSDag-Erling Smørgrav 
764e2f6069cSDag-Erling Smørgrav static void
do_convert_from_pem(struct sshkey ** k,int * private)765bc5531deSDag-Erling Smørgrav do_convert_from_pem(struct sshkey **k, int *private)
766e2f6069cSDag-Erling Smørgrav {
767e2f6069cSDag-Erling Smørgrav 	FILE *fp;
768e2f6069cSDag-Erling Smørgrav 	RSA *rsa;
769e2f6069cSDag-Erling Smørgrav 
770e2f6069cSDag-Erling Smørgrav 	if ((fp = fopen(identity_file, "r")) == NULL)
771e2f6069cSDag-Erling Smørgrav 		fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
772e2f6069cSDag-Erling Smørgrav 	if ((rsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL)) != NULL) {
773bc5531deSDag-Erling Smørgrav 		if ((*k = sshkey_new(KEY_UNSPEC)) == NULL)
774bc5531deSDag-Erling Smørgrav 			fatal("sshkey_new failed");
775*3d9fd9fcSEd Maste 		if (((*k)->pkey = EVP_PKEY_new()) == NULL)
776*3d9fd9fcSEd Maste 			fatal("EVP_PKEY_new failed");
777e2f6069cSDag-Erling Smørgrav 		(*k)->type = KEY_RSA;
778*3d9fd9fcSEd Maste 		if (EVP_PKEY_set1_RSA((*k)->pkey, rsa) != 1)
779*3d9fd9fcSEd Maste 			fatal("EVP_PKEY_set1_RSA failed");
780*3d9fd9fcSEd Maste 		RSA_free(rsa);
781e2f6069cSDag-Erling Smørgrav 		fclose(fp);
782e2f6069cSDag-Erling Smørgrav 		return;
783e2f6069cSDag-Erling Smørgrav 	}
78419261079SEd Maste 	fatal_f("unrecognised raw private key format");
785e2f6069cSDag-Erling Smørgrav }
786e2f6069cSDag-Erling Smørgrav 
787e2f6069cSDag-Erling Smørgrav static void
do_convert_from(struct passwd * pw)788e2f6069cSDag-Erling Smørgrav do_convert_from(struct passwd *pw)
789e2f6069cSDag-Erling Smørgrav {
790bc5531deSDag-Erling Smørgrav 	struct sshkey *k = NULL;
791bc5531deSDag-Erling Smørgrav 	int r, private = 0, ok = 0;
792e2f6069cSDag-Erling Smørgrav 	struct stat st;
793e2f6069cSDag-Erling Smørgrav 
794e2f6069cSDag-Erling Smørgrav 	if (!have_identity)
795e2f6069cSDag-Erling Smørgrav 		ask_filename(pw, "Enter file in which the key is");
79619261079SEd Maste 	if (stat(identity_file, &st) == -1)
797e2f6069cSDag-Erling Smørgrav 		fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
798e2f6069cSDag-Erling Smørgrav 
799e2f6069cSDag-Erling Smørgrav 	switch (convert_format) {
800e2f6069cSDag-Erling Smørgrav 	case FMT_RFC4716:
801e2f6069cSDag-Erling Smørgrav 		do_convert_from_ssh2(pw, &k, &private);
802e2f6069cSDag-Erling Smørgrav 		break;
803e2f6069cSDag-Erling Smørgrav 	case FMT_PKCS8:
804e2f6069cSDag-Erling Smørgrav 		do_convert_from_pkcs8(&k, &private);
805e2f6069cSDag-Erling Smørgrav 		break;
806e2f6069cSDag-Erling Smørgrav 	case FMT_PEM:
807e2f6069cSDag-Erling Smørgrav 		do_convert_from_pem(&k, &private);
808e2f6069cSDag-Erling Smørgrav 		break;
809e2f6069cSDag-Erling Smørgrav 	default:
81019261079SEd Maste 		fatal_f("unknown key format %d", convert_format);
811e2f6069cSDag-Erling Smørgrav 	}
812e2f6069cSDag-Erling Smørgrav 
813bc5531deSDag-Erling Smørgrav 	if (!private) {
814bc5531deSDag-Erling Smørgrav 		if ((r = sshkey_write(k, stdout)) == 0)
815bc5531deSDag-Erling Smørgrav 			ok = 1;
816e2f6069cSDag-Erling Smørgrav 		if (ok)
817e2f6069cSDag-Erling Smørgrav 			fprintf(stdout, "\n");
818bc5531deSDag-Erling Smørgrav 	} else {
819e2f6069cSDag-Erling Smørgrav 		switch (k->type) {
820a91a2465SEd Maste #ifdef WITH_DSA
821e2f6069cSDag-Erling Smørgrav 		case KEY_DSA:
822e2f6069cSDag-Erling Smørgrav 			ok = PEM_write_DSAPrivateKey(stdout, k->dsa, NULL,
823e2f6069cSDag-Erling Smørgrav 			    NULL, 0, NULL, NULL);
824e2f6069cSDag-Erling Smørgrav 			break;
825a91a2465SEd Maste #endif
8264a421b63SDag-Erling Smørgrav #ifdef OPENSSL_HAS_ECC
8274a421b63SDag-Erling Smørgrav 		case KEY_ECDSA:
828*3d9fd9fcSEd Maste 			ok = PEM_write_ECPrivateKey(stdout,
829*3d9fd9fcSEd Maste 			    EVP_PKEY_get0_EC_KEY(k->pkey), NULL, NULL, 0,
830*3d9fd9fcSEd Maste 			    NULL, NULL);
8314a421b63SDag-Erling Smørgrav 			break;
8324a421b63SDag-Erling Smørgrav #endif
833e2f6069cSDag-Erling Smørgrav 		case KEY_RSA:
834*3d9fd9fcSEd Maste 			ok = PEM_write_RSAPrivateKey(stdout,
835*3d9fd9fcSEd Maste 			    EVP_PKEY_get0_RSA(k->pkey), NULL, NULL, 0,
836*3d9fd9fcSEd Maste 			    NULL, NULL);
837e2f6069cSDag-Erling Smørgrav 			break;
838e2f6069cSDag-Erling Smørgrav 		default:
83919261079SEd Maste 			fatal_f("unsupported key type %s", sshkey_type(k));
840e2f6069cSDag-Erling Smørgrav 		}
841e2f6069cSDag-Erling Smørgrav 	}
842e2f6069cSDag-Erling Smørgrav 
843557f75e5SDag-Erling Smørgrav 	if (!ok)
844557f75e5SDag-Erling Smørgrav 		fatal("key write failed");
845bc5531deSDag-Erling Smørgrav 	sshkey_free(k);
846a04a10f8SKris Kennaway 	exit(0);
847a04a10f8SKris Kennaway }
848a0ee8cc6SDag-Erling Smørgrav #endif
849a04a10f8SKris Kennaway 
850ae1f160dSDag-Erling Smørgrav static void
do_print_public(struct passwd * pw)851a04a10f8SKris Kennaway do_print_public(struct passwd *pw)
852a04a10f8SKris Kennaway {
853bc5531deSDag-Erling Smørgrav 	struct sshkey *prv;
854a04a10f8SKris Kennaway 	struct stat st;
855bc5531deSDag-Erling Smørgrav 	int r;
85619261079SEd Maste 	char *comment = NULL;
857a04a10f8SKris Kennaway 
858a04a10f8SKris Kennaway 	if (!have_identity)
859a04a10f8SKris Kennaway 		ask_filename(pw, "Enter file in which the key is");
86019261079SEd Maste 	if (stat(identity_file, &st) == -1)
861557f75e5SDag-Erling Smørgrav 		fatal("%s: %s", identity_file, strerror(errno));
86219261079SEd Maste 	prv = load_identity(identity_file, &comment);
863bc5531deSDag-Erling Smørgrav 	if ((r = sshkey_write(prv, stdout)) != 0)
86419261079SEd Maste 		fatal_fr(r, "write key");
86519261079SEd Maste 	if (comment != NULL && *comment != '\0')
86619261079SEd Maste 		fprintf(stdout, " %s", comment);
867a04a10f8SKris Kennaway 	fprintf(stdout, "\n");
86819261079SEd Maste 	if (sshkey_is_sk(prv)) {
86919261079SEd Maste 		debug("sk_application: \"%s\", sk_flags 0x%02x",
87019261079SEd Maste 			prv->sk_application, prv->sk_flags);
87119261079SEd Maste 	}
87219261079SEd Maste 	sshkey_free(prv);
87319261079SEd Maste 	free(comment);
874a04a10f8SKris Kennaway 	exit(0);
875a04a10f8SKris Kennaway }
876a04a10f8SKris Kennaway 
877ae1f160dSDag-Erling Smørgrav static void
do_download(struct passwd * pw)878e2f6069cSDag-Erling Smørgrav do_download(struct passwd *pw)
879ae1f160dSDag-Erling Smørgrav {
880b15c8340SDag-Erling Smørgrav #ifdef ENABLE_PKCS11
881bc5531deSDag-Erling Smørgrav 	struct sshkey **keys = NULL;
882b15c8340SDag-Erling Smørgrav 	int i, nkeys;
883bc5531deSDag-Erling Smørgrav 	enum sshkey_fp_rep rep;
884bc5531deSDag-Erling Smørgrav 	int fptype;
88519261079SEd Maste 	char *fp, *ra, **comments = NULL;
8866888a9beSDag-Erling Smørgrav 
887bc5531deSDag-Erling Smørgrav 	fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash;
888bc5531deSDag-Erling Smørgrav 	rep =    print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT;
889ae1f160dSDag-Erling Smørgrav 
89019261079SEd Maste 	pkcs11_init(1);
89119261079SEd Maste 	nkeys = pkcs11_add_provider(pkcs11provider, NULL, &keys, &comments);
892b15c8340SDag-Erling Smørgrav 	if (nkeys <= 0)
893b15c8340SDag-Erling Smørgrav 		fatal("cannot read public key from pkcs11");
894b15c8340SDag-Erling Smørgrav 	for (i = 0; i < nkeys; i++) {
8956888a9beSDag-Erling Smørgrav 		if (print_fingerprint) {
896bc5531deSDag-Erling Smørgrav 			fp = sshkey_fingerprint(keys[i], fptype, rep);
897bc5531deSDag-Erling Smørgrav 			ra = sshkey_fingerprint(keys[i], fingerprint_hash,
8986888a9beSDag-Erling Smørgrav 			    SSH_FP_RANDOMART);
899bc5531deSDag-Erling Smørgrav 			if (fp == NULL || ra == NULL)
90019261079SEd Maste 				fatal_f("sshkey_fingerprint fail");
901bc5531deSDag-Erling Smørgrav 			printf("%u %s %s (PKCS11 key)\n", sshkey_size(keys[i]),
902bc5531deSDag-Erling Smørgrav 			    fp, sshkey_type(keys[i]));
90319261079SEd Maste 			if (log_level_get() >= SYSLOG_LEVEL_VERBOSE)
9046888a9beSDag-Erling Smørgrav 				printf("%s\n", ra);
905e4a9863fSDag-Erling Smørgrav 			free(ra);
906e4a9863fSDag-Erling Smørgrav 			free(fp);
9076888a9beSDag-Erling Smørgrav 		} else {
908bc5531deSDag-Erling Smørgrav 			(void) sshkey_write(keys[i], stdout); /* XXX check */
90919261079SEd Maste 			fprintf(stdout, "%s%s\n",
91019261079SEd Maste 			    *(comments[i]) == '\0' ? "" : " ", comments[i]);
911545d5ecaSDag-Erling Smørgrav 		}
91219261079SEd Maste 		free(comments[i]);
913bc5531deSDag-Erling Smørgrav 		sshkey_free(keys[i]);
9146888a9beSDag-Erling Smørgrav 	}
91519261079SEd Maste 	free(comments);
916e4a9863fSDag-Erling Smørgrav 	free(keys);
917b15c8340SDag-Erling Smørgrav 	pkcs11_terminate();
918ae1f160dSDag-Erling Smørgrav 	exit(0);
919b15c8340SDag-Erling Smørgrav #else
920b15c8340SDag-Erling Smørgrav 	fatal("no pkcs11 support");
921b15c8340SDag-Erling Smørgrav #endif /* ENABLE_PKCS11 */
922ae1f160dSDag-Erling Smørgrav }
923ae1f160dSDag-Erling Smørgrav 
924acc1a9efSDag-Erling Smørgrav static struct sshkey *
try_read_key(char ** cpp)925acc1a9efSDag-Erling Smørgrav try_read_key(char **cpp)
926acc1a9efSDag-Erling Smørgrav {
927acc1a9efSDag-Erling Smørgrav 	struct sshkey *ret;
928acc1a9efSDag-Erling Smørgrav 	int r;
929acc1a9efSDag-Erling Smørgrav 
930acc1a9efSDag-Erling Smørgrav 	if ((ret = sshkey_new(KEY_UNSPEC)) == NULL)
931acc1a9efSDag-Erling Smørgrav 		fatal("sshkey_new failed");
932acc1a9efSDag-Erling Smørgrav 	if ((r = sshkey_read(ret, cpp)) == 0)
933acc1a9efSDag-Erling Smørgrav 		return ret;
934acc1a9efSDag-Erling Smørgrav 	/* Not a key */
935acc1a9efSDag-Erling Smørgrav 	sshkey_free(ret);
936acc1a9efSDag-Erling Smørgrav 	return NULL;
937acc1a9efSDag-Erling Smørgrav }
938acc1a9efSDag-Erling Smørgrav 
939acc1a9efSDag-Erling Smørgrav static void
fingerprint_one_key(const struct sshkey * public,const char * comment)940acc1a9efSDag-Erling Smørgrav fingerprint_one_key(const struct sshkey *public, const char *comment)
941acc1a9efSDag-Erling Smørgrav {
942acc1a9efSDag-Erling Smørgrav 	char *fp = NULL, *ra = NULL;
943acc1a9efSDag-Erling Smørgrav 	enum sshkey_fp_rep rep;
944acc1a9efSDag-Erling Smørgrav 	int fptype;
945acc1a9efSDag-Erling Smørgrav 
946acc1a9efSDag-Erling Smørgrav 	fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash;
947acc1a9efSDag-Erling Smørgrav 	rep =    print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT;
948acc1a9efSDag-Erling Smørgrav 	fp = sshkey_fingerprint(public, fptype, rep);
949acc1a9efSDag-Erling Smørgrav 	ra = sshkey_fingerprint(public, fingerprint_hash, SSH_FP_RANDOMART);
950acc1a9efSDag-Erling Smørgrav 	if (fp == NULL || ra == NULL)
95119261079SEd Maste 		fatal_f("sshkey_fingerprint failed");
952d93a896eSDag-Erling Smørgrav 	mprintf("%u %s %s (%s)\n", sshkey_size(public), fp,
953acc1a9efSDag-Erling Smørgrav 	    comment ? comment : "no comment", sshkey_type(public));
95419261079SEd Maste 	if (log_level_get() >= SYSLOG_LEVEL_VERBOSE)
955acc1a9efSDag-Erling Smørgrav 		printf("%s\n", ra);
956acc1a9efSDag-Erling Smørgrav 	free(ra);
957acc1a9efSDag-Erling Smørgrav 	free(fp);
958acc1a9efSDag-Erling Smørgrav }
959acc1a9efSDag-Erling Smørgrav 
960acc1a9efSDag-Erling Smørgrav static void
fingerprint_private(const char * path)961acc1a9efSDag-Erling Smørgrav fingerprint_private(const char *path)
962acc1a9efSDag-Erling Smørgrav {
963acc1a9efSDag-Erling Smørgrav 	struct stat st;
964acc1a9efSDag-Erling Smørgrav 	char *comment = NULL;
96519261079SEd Maste 	struct sshkey *privkey = NULL, *pubkey = NULL;
966acc1a9efSDag-Erling Smørgrav 	int r;
967acc1a9efSDag-Erling Smørgrav 
96819261079SEd Maste 	if (stat(identity_file, &st) == -1)
969acc1a9efSDag-Erling Smørgrav 		fatal("%s: %s", path, strerror(errno));
97019261079SEd Maste 	if ((r = sshkey_load_public(path, &pubkey, &comment)) != 0)
97119261079SEd Maste 		debug_r(r, "load public \"%s\"", path);
97219261079SEd Maste 	if (pubkey == NULL || comment == NULL || *comment == '\0') {
97319261079SEd Maste 		free(comment);
974acc1a9efSDag-Erling Smørgrav 		if ((r = sshkey_load_private(path, NULL,
97519261079SEd Maste 		    &privkey, &comment)) != 0)
97619261079SEd Maste 			debug_r(r, "load private \"%s\"", path);
97719261079SEd Maste 	}
97819261079SEd Maste 	if (pubkey == NULL && privkey == NULL)
979acc1a9efSDag-Erling Smørgrav 		fatal("%s is not a key file.", path);
980acc1a9efSDag-Erling Smørgrav 
98119261079SEd Maste 	fingerprint_one_key(pubkey == NULL ? privkey : pubkey, comment);
98219261079SEd Maste 	sshkey_free(pubkey);
98319261079SEd Maste 	sshkey_free(privkey);
984acc1a9efSDag-Erling Smørgrav 	free(comment);
985acc1a9efSDag-Erling Smørgrav }
986acc1a9efSDag-Erling Smørgrav 
987ae1f160dSDag-Erling Smørgrav static void
do_fingerprint(struct passwd * pw)988511b41d2SMark Murray do_fingerprint(struct passwd *pw)
989511b41d2SMark Murray {
990511b41d2SMark Murray 	FILE *f;
991acc1a9efSDag-Erling Smørgrav 	struct sshkey *public = NULL;
992190cef3dSDag-Erling Smørgrav 	char *comment = NULL, *cp, *ep, *line = NULL;
993190cef3dSDag-Erling Smørgrav 	size_t linesize = 0;
994acc1a9efSDag-Erling Smørgrav 	int i, invalid = 1;
995acc1a9efSDag-Erling Smørgrav 	const char *path;
996076ad2f8SDag-Erling Smørgrav 	u_long lnum = 0;
997511b41d2SMark Murray 
998511b41d2SMark Murray 	if (!have_identity)
999511b41d2SMark Murray 		ask_filename(pw, "Enter file in which the key is");
1000acc1a9efSDag-Erling Smørgrav 	path = identity_file;
1001acc1a9efSDag-Erling Smørgrav 
1002acc1a9efSDag-Erling Smørgrav 	if (strcmp(identity_file, "-") == 0) {
1003acc1a9efSDag-Erling Smørgrav 		f = stdin;
1004acc1a9efSDag-Erling Smørgrav 		path = "(stdin)";
1005acc1a9efSDag-Erling Smørgrav 	} else if ((f = fopen(path, "r")) == NULL)
1006acc1a9efSDag-Erling Smørgrav 		fatal("%s: %s: %s", __progname, path, strerror(errno));
1007acc1a9efSDag-Erling Smørgrav 
1008190cef3dSDag-Erling Smørgrav 	while (getline(&line, &linesize, f) != -1) {
1009190cef3dSDag-Erling Smørgrav 		lnum++;
1010acc1a9efSDag-Erling Smørgrav 		cp = line;
1011acc1a9efSDag-Erling Smørgrav 		cp[strcspn(cp, "\n")] = '\0';
1012acc1a9efSDag-Erling Smørgrav 		/* Trim leading space and comments */
1013acc1a9efSDag-Erling Smørgrav 		cp = line + strspn(line, " \t");
1014acc1a9efSDag-Erling Smørgrav 		if (*cp == '#' || *cp == '\0')
1015acc1a9efSDag-Erling Smørgrav 			continue;
1016acc1a9efSDag-Erling Smørgrav 
1017acc1a9efSDag-Erling Smørgrav 		/*
1018acc1a9efSDag-Erling Smørgrav 		 * Input may be plain keys, private keys, authorized_keys
1019acc1a9efSDag-Erling Smørgrav 		 * or known_hosts.
1020acc1a9efSDag-Erling Smørgrav 		 */
1021acc1a9efSDag-Erling Smørgrav 
1022acc1a9efSDag-Erling Smørgrav 		/*
1023acc1a9efSDag-Erling Smørgrav 		 * Try private keys first. Assume a key is private if
1024acc1a9efSDag-Erling Smørgrav 		 * "SSH PRIVATE KEY" appears on the first line and we're
1025acc1a9efSDag-Erling Smørgrav 		 * not reading from stdin (XXX support private keys on stdin).
1026acc1a9efSDag-Erling Smørgrav 		 */
1027acc1a9efSDag-Erling Smørgrav 		if (lnum == 1 && strcmp(identity_file, "-") != 0 &&
1028acc1a9efSDag-Erling Smørgrav 		    strstr(cp, "PRIVATE KEY") != NULL) {
1029190cef3dSDag-Erling Smørgrav 			free(line);
1030acc1a9efSDag-Erling Smørgrav 			fclose(f);
1031acc1a9efSDag-Erling Smørgrav 			fingerprint_private(path);
1032511b41d2SMark Murray 			exit(0);
1033511b41d2SMark Murray 		}
1034511b41d2SMark Murray 
1035acc1a9efSDag-Erling Smørgrav 		/*
1036acc1a9efSDag-Erling Smørgrav 		 * If it's not a private key, then this must be prepared to
1037acc1a9efSDag-Erling Smørgrav 		 * accept a public key prefixed with a hostname or options.
1038acc1a9efSDag-Erling Smørgrav 		 * Try a bare key first, otherwise skip the leading stuff.
1039acc1a9efSDag-Erling Smørgrav 		 */
1040535af610SEd Maste 		comment = NULL;
1041acc1a9efSDag-Erling Smørgrav 		if ((public = try_read_key(&cp)) == NULL) {
1042511b41d2SMark Murray 			i = strtol(cp, &ep, 10);
1043acc1a9efSDag-Erling Smørgrav 			if (i == 0 || ep == NULL ||
1044acc1a9efSDag-Erling Smørgrav 			    (*ep != ' ' && *ep != '\t')) {
1045511b41d2SMark Murray 				int quoted = 0;
1046acc1a9efSDag-Erling Smørgrav 
1047511b41d2SMark Murray 				comment = cp;
1048ee21a45fSDag-Erling Smørgrav 				for (; *cp && (quoted || (*cp != ' ' &&
1049ee21a45fSDag-Erling Smørgrav 				    *cp != '\t')); cp++) {
1050511b41d2SMark Murray 					if (*cp == '\\' && cp[1] == '"')
1051511b41d2SMark Murray 						cp++;	/* Skip both */
1052511b41d2SMark Murray 					else if (*cp == '"')
1053511b41d2SMark Murray 						quoted = !quoted;
1054511b41d2SMark Murray 				}
1055511b41d2SMark Murray 				if (!*cp)
1056511b41d2SMark Murray 					continue;
1057511b41d2SMark Murray 				*cp++ = '\0';
1058511b41d2SMark Murray 			}
1059acc1a9efSDag-Erling Smørgrav 		}
1060acc1a9efSDag-Erling Smørgrav 		/* Retry after parsing leading hostname/key options */
1061acc1a9efSDag-Erling Smørgrav 		if (public == NULL && (public = try_read_key(&cp)) == NULL) {
1062076ad2f8SDag-Erling Smørgrav 			debug("%s:%lu: not a public key", path, lnum);
10631e8db6e2SBrian Feldman 			continue;
1064511b41d2SMark Murray 		}
1065acc1a9efSDag-Erling Smørgrav 
1066acc1a9efSDag-Erling Smørgrav 		/* Find trailing comment, if any */
1067acc1a9efSDag-Erling Smørgrav 		for (; *cp == ' ' || *cp == '\t'; cp++)
1068acc1a9efSDag-Erling Smørgrav 			;
1069acc1a9efSDag-Erling Smørgrav 		if (*cp != '\0' && *cp != '#')
1070acc1a9efSDag-Erling Smørgrav 			comment = cp;
1071acc1a9efSDag-Erling Smørgrav 
1072acc1a9efSDag-Erling Smørgrav 		fingerprint_one_key(public, comment);
1073bc5531deSDag-Erling Smørgrav 		sshkey_free(public);
1074acc1a9efSDag-Erling Smørgrav 		invalid = 0; /* One good key in the file is sufficient */
10751e8db6e2SBrian Feldman 	}
1076511b41d2SMark Murray 	fclose(f);
1077190cef3dSDag-Erling Smørgrav 	free(line);
1078e2f6069cSDag-Erling Smørgrav 
1079557f75e5SDag-Erling Smørgrav 	if (invalid)
1080acc1a9efSDag-Erling Smørgrav 		fatal("%s is not a public key file.", path);
1081511b41d2SMark Murray 	exit(0);
1082511b41d2SMark Murray }
1083511b41d2SMark Murray 
10845e8dbd04SDag-Erling Smørgrav static void
do_gen_all_hostkeys(struct passwd * pw)1085e146993eSDag-Erling Smørgrav do_gen_all_hostkeys(struct passwd *pw)
1086e146993eSDag-Erling Smørgrav {
1087e146993eSDag-Erling Smørgrav 	struct {
1088e146993eSDag-Erling Smørgrav 		char *key_type;
1089e146993eSDag-Erling Smørgrav 		char *key_type_display;
1090e146993eSDag-Erling Smørgrav 		char *path;
1091e146993eSDag-Erling Smørgrav 	} key_types[] = {
1092557f75e5SDag-Erling Smørgrav #ifdef WITH_OPENSSL
1093e146993eSDag-Erling Smørgrav 		{ "rsa", "RSA" ,_PATH_HOST_RSA_KEY_FILE },
1094462c32cbSDag-Erling Smørgrav #ifdef OPENSSL_HAS_ECC
1095e146993eSDag-Erling Smørgrav 		{ "ecdsa", "ECDSA",_PATH_HOST_ECDSA_KEY_FILE },
1096557f75e5SDag-Erling Smørgrav #endif /* OPENSSL_HAS_ECC */
1097557f75e5SDag-Erling Smørgrav #endif /* WITH_OPENSSL */
1098f7167e0eSDag-Erling Smørgrav 		{ "ed25519", "ED25519",_PATH_HOST_ED25519_KEY_FILE },
109947dd1d1bSDag-Erling Smørgrav #ifdef WITH_XMSS
110047dd1d1bSDag-Erling Smørgrav 		{ "xmss", "XMSS",_PATH_HOST_XMSS_KEY_FILE },
110147dd1d1bSDag-Erling Smørgrav #endif /* WITH_XMSS */
1102e146993eSDag-Erling Smørgrav 		{ NULL, NULL, NULL }
1103e146993eSDag-Erling Smørgrav 	};
1104e146993eSDag-Erling Smørgrav 
110519261079SEd Maste 	u_int32_t bits = 0;
1106e146993eSDag-Erling Smørgrav 	int first = 0;
1107e146993eSDag-Erling Smørgrav 	struct stat st;
1108bc5531deSDag-Erling Smørgrav 	struct sshkey *private, *public;
11094f52dfbbSDag-Erling Smørgrav 	char comment[1024], *prv_tmp, *pub_tmp, *prv_file, *pub_file;
1110bc5531deSDag-Erling Smørgrav 	int i, type, fd, r;
1111e146993eSDag-Erling Smørgrav 
1112e146993eSDag-Erling Smørgrav 	for (i = 0; key_types[i].key_type; i++) {
11134f52dfbbSDag-Erling Smørgrav 		public = private = NULL;
11144f52dfbbSDag-Erling Smørgrav 		prv_tmp = pub_tmp = prv_file = pub_file = NULL;
11154f52dfbbSDag-Erling Smørgrav 
11164f52dfbbSDag-Erling Smørgrav 		xasprintf(&prv_file, "%s%s",
11174f52dfbbSDag-Erling Smørgrav 		    identity_file, key_types[i].path);
11184f52dfbbSDag-Erling Smørgrav 
11194f52dfbbSDag-Erling Smørgrav 		/* Check whether private key exists and is not zero-length */
11204f52dfbbSDag-Erling Smørgrav 		if (stat(prv_file, &st) == 0) {
11214f52dfbbSDag-Erling Smørgrav 			if (st.st_size != 0)
11224f52dfbbSDag-Erling Smørgrav 				goto next;
11234f52dfbbSDag-Erling Smørgrav 		} else if (errno != ENOENT) {
1124557f75e5SDag-Erling Smørgrav 			error("Could not stat %s: %s", key_types[i].path,
1125e146993eSDag-Erling Smørgrav 			    strerror(errno));
11264f52dfbbSDag-Erling Smørgrav 			goto failnext;
1127e146993eSDag-Erling Smørgrav 		}
1128e146993eSDag-Erling Smørgrav 
11294f52dfbbSDag-Erling Smørgrav 		/*
11304f52dfbbSDag-Erling Smørgrav 		 * Private key doesn't exist or is invalid; proceed with
11314f52dfbbSDag-Erling Smørgrav 		 * key generation.
11324f52dfbbSDag-Erling Smørgrav 		 */
11334f52dfbbSDag-Erling Smørgrav 		xasprintf(&prv_tmp, "%s%s.XXXXXXXXXX",
11344f52dfbbSDag-Erling Smørgrav 		    identity_file, key_types[i].path);
11354f52dfbbSDag-Erling Smørgrav 		xasprintf(&pub_tmp, "%s%s.pub.XXXXXXXXXX",
11364f52dfbbSDag-Erling Smørgrav 		    identity_file, key_types[i].path);
11374f52dfbbSDag-Erling Smørgrav 		xasprintf(&pub_file, "%s%s.pub",
11384f52dfbbSDag-Erling Smørgrav 		    identity_file, key_types[i].path);
11394f52dfbbSDag-Erling Smørgrav 
1140e146993eSDag-Erling Smørgrav 		if (first == 0) {
1141e146993eSDag-Erling Smørgrav 			first = 1;
1142e146993eSDag-Erling Smørgrav 			printf("%s: generating new host keys: ", __progname);
1143e146993eSDag-Erling Smørgrav 		}
1144e146993eSDag-Erling Smørgrav 		printf("%s ", key_types[i].key_type_display);
1145e146993eSDag-Erling Smørgrav 		fflush(stdout);
1146*3d9fd9fcSEd Maste 		type = sshkey_type_from_shortname(key_types[i].key_type);
11474f52dfbbSDag-Erling Smørgrav 		if ((fd = mkstemp(prv_tmp)) == -1) {
114819261079SEd Maste 			error("Could not save your private key in %s: %s",
11494f52dfbbSDag-Erling Smørgrav 			    prv_tmp, strerror(errno));
11504f52dfbbSDag-Erling Smørgrav 			goto failnext;
11514f52dfbbSDag-Erling Smørgrav 		}
115219261079SEd Maste 		(void)close(fd); /* just using mkstemp() to reserve a name */
1153e146993eSDag-Erling Smørgrav 		bits = 0;
1154bc5531deSDag-Erling Smørgrav 		type_bits_valid(type, NULL, &bits);
1155bc5531deSDag-Erling Smørgrav 		if ((r = sshkey_generate(type, bits, &private)) != 0) {
115619261079SEd Maste 			error_r(r, "sshkey_generate failed");
11574f52dfbbSDag-Erling Smørgrav 			goto failnext;
1158e146993eSDag-Erling Smørgrav 		}
1159bc5531deSDag-Erling Smørgrav 		if ((r = sshkey_from_private(private, &public)) != 0)
116019261079SEd Maste 			fatal_fr(r, "sshkey_from_private");
1161e146993eSDag-Erling Smørgrav 		snprintf(comment, sizeof comment, "%s@%s", pw->pw_name,
1162e146993eSDag-Erling Smørgrav 		    hostname);
11634f52dfbbSDag-Erling Smørgrav 		if ((r = sshkey_save_private(private, prv_tmp, "",
116419261079SEd Maste 		    comment, private_key_format, openssh_format_cipher,
116519261079SEd Maste 		    rounds)) != 0) {
116619261079SEd Maste 			error_r(r, "Saving key \"%s\" failed", prv_tmp);
11674f52dfbbSDag-Erling Smørgrav 			goto failnext;
1168e146993eSDag-Erling Smørgrav 		}
11694f52dfbbSDag-Erling Smørgrav 		if ((fd = mkstemp(pub_tmp)) == -1) {
11704f52dfbbSDag-Erling Smørgrav 			error("Could not save your public key in %s: %s",
11714f52dfbbSDag-Erling Smørgrav 			    pub_tmp, strerror(errno));
11724f52dfbbSDag-Erling Smørgrav 			goto failnext;
1173e146993eSDag-Erling Smørgrav 		}
11744f52dfbbSDag-Erling Smørgrav 		(void)fchmod(fd, 0644);
117519261079SEd Maste 		(void)close(fd);
117619261079SEd Maste 		if ((r = sshkey_save_public(public, pub_tmp, comment)) != 0) {
117719261079SEd Maste 			error_r(r, "Unable to save public key to %s",
117819261079SEd Maste 			    identity_file);
11794f52dfbbSDag-Erling Smørgrav 			goto failnext;
11804f52dfbbSDag-Erling Smørgrav 		}
1181e146993eSDag-Erling Smørgrav 
11824f52dfbbSDag-Erling Smørgrav 		/* Rename temporary files to their permanent locations. */
11834f52dfbbSDag-Erling Smørgrav 		if (rename(pub_tmp, pub_file) != 0) {
11844f52dfbbSDag-Erling Smørgrav 			error("Unable to move %s into position: %s",
11854f52dfbbSDag-Erling Smørgrav 			    pub_file, strerror(errno));
11864f52dfbbSDag-Erling Smørgrav 			goto failnext;
11874f52dfbbSDag-Erling Smørgrav 		}
11884f52dfbbSDag-Erling Smørgrav 		if (rename(prv_tmp, prv_file) != 0) {
11894f52dfbbSDag-Erling Smørgrav 			error("Unable to move %s into position: %s",
11904f52dfbbSDag-Erling Smørgrav 			    key_types[i].path, strerror(errno));
11914f52dfbbSDag-Erling Smørgrav  failnext:
11924f52dfbbSDag-Erling Smørgrav 			first = 0;
11934f52dfbbSDag-Erling Smørgrav 			goto next;
11944f52dfbbSDag-Erling Smørgrav 		}
11954f52dfbbSDag-Erling Smørgrav  next:
11964f52dfbbSDag-Erling Smørgrav 		sshkey_free(private);
11974f52dfbbSDag-Erling Smørgrav 		sshkey_free(public);
11984f52dfbbSDag-Erling Smørgrav 		free(prv_tmp);
11994f52dfbbSDag-Erling Smørgrav 		free(pub_tmp);
12004f52dfbbSDag-Erling Smørgrav 		free(prv_file);
12014f52dfbbSDag-Erling Smørgrav 		free(pub_file);
1202e146993eSDag-Erling Smørgrav 	}
1203e146993eSDag-Erling Smørgrav 	if (first != 0)
1204e146993eSDag-Erling Smørgrav 		printf("\n");
1205e146993eSDag-Erling Smørgrav }
1206e146993eSDag-Erling Smørgrav 
1207bc5531deSDag-Erling Smørgrav struct known_hosts_ctx {
1208bc5531deSDag-Erling Smørgrav 	const char *host;	/* Hostname searched for in find/delete case */
1209bc5531deSDag-Erling Smørgrav 	FILE *out;		/* Output file, stdout for find_hosts case */
1210bc5531deSDag-Erling Smørgrav 	int has_unhashed;	/* When hashing, original had unhashed hosts */
1211bc5531deSDag-Erling Smørgrav 	int found_key;		/* For find/delete, host was found */
1212bc5531deSDag-Erling Smørgrav 	int invalid;		/* File contained invalid items; don't delete */
121319261079SEd Maste 	int hash_hosts;		/* Hash hostnames as we go */
121419261079SEd Maste 	int find_host;		/* Search for specific hostname */
121519261079SEd Maste 	int delete_host;	/* Delete host from known_hosts */
1216bc5531deSDag-Erling Smørgrav };
1217d4af9e69SDag-Erling Smørgrav 
1218bc5531deSDag-Erling Smørgrav static int
known_hosts_hash(struct hostkey_foreach_line * l,void * _ctx)1219bc5531deSDag-Erling Smørgrav known_hosts_hash(struct hostkey_foreach_line *l, void *_ctx)
1220bc5531deSDag-Erling Smørgrav {
1221bc5531deSDag-Erling Smørgrav 	struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx;
1222bc5531deSDag-Erling Smørgrav 	char *hashed, *cp, *hosts, *ohosts;
1223bc5531deSDag-Erling Smørgrav 	int has_wild = l->hosts && strcspn(l->hosts, "*?!") != strlen(l->hosts);
1224d93a896eSDag-Erling Smørgrav 	int was_hashed = l->hosts && l->hosts[0] == HASH_DELIM;
1225bc5531deSDag-Erling Smørgrav 
1226bc5531deSDag-Erling Smørgrav 	switch (l->status) {
1227bc5531deSDag-Erling Smørgrav 	case HKF_STATUS_OK:
1228bc5531deSDag-Erling Smørgrav 	case HKF_STATUS_MATCHED:
1229bc5531deSDag-Erling Smørgrav 		/*
1230535af610SEd Maste 		 * Don't hash hosts already hashed, with wildcard
1231bc5531deSDag-Erling Smørgrav 		 * characters or a CA/revocation marker.
1232bc5531deSDag-Erling Smørgrav 		 */
1233d93a896eSDag-Erling Smørgrav 		if (was_hashed || has_wild || l->marker != MRK_NONE) {
1234bc5531deSDag-Erling Smørgrav 			fprintf(ctx->out, "%s\n", l->line);
123519261079SEd Maste 			if (has_wild && !ctx->find_host) {
1236d93a896eSDag-Erling Smørgrav 				logit("%s:%lu: ignoring host name "
1237557f75e5SDag-Erling Smørgrav 				    "with wildcard: %.64s", l->path,
1238bc5531deSDag-Erling Smørgrav 				    l->linenum, l->hosts);
12395e8dbd04SDag-Erling Smørgrav 			}
1240bc5531deSDag-Erling Smørgrav 			return 0;
1241bc5531deSDag-Erling Smørgrav 		}
1242bc5531deSDag-Erling Smørgrav 		/*
1243bc5531deSDag-Erling Smørgrav 		 * Split any comma-separated hostnames from the host list,
1244bc5531deSDag-Erling Smørgrav 		 * hash and store separately.
1245bc5531deSDag-Erling Smørgrav 		 */
1246bc5531deSDag-Erling Smørgrav 		ohosts = hosts = xstrdup(l->hosts);
1247bc5531deSDag-Erling Smørgrav 		while ((cp = strsep(&hosts, ",")) != NULL && *cp != '\0') {
1248d93a896eSDag-Erling Smørgrav 			lowercase(cp);
1249bc5531deSDag-Erling Smørgrav 			if ((hashed = host_hash(cp, NULL, 0)) == NULL)
1250bc5531deSDag-Erling Smørgrav 				fatal("hash_host failed");
1251bc5531deSDag-Erling Smørgrav 			fprintf(ctx->out, "%s %s\n", hashed, l->rawkey);
12521323ec57SEd Maste 			free(hashed);
1253bc5531deSDag-Erling Smørgrav 			ctx->has_unhashed = 1;
1254bc5531deSDag-Erling Smørgrav 		}
1255bc5531deSDag-Erling Smørgrav 		free(ohosts);
1256bc5531deSDag-Erling Smørgrav 		return 0;
1257bc5531deSDag-Erling Smørgrav 	case HKF_STATUS_INVALID:
1258bc5531deSDag-Erling Smørgrav 		/* Retain invalid lines, but mark file as invalid. */
1259bc5531deSDag-Erling Smørgrav 		ctx->invalid = 1;
1260d93a896eSDag-Erling Smørgrav 		logit("%s:%lu: invalid line", l->path, l->linenum);
1261bc5531deSDag-Erling Smørgrav 		/* FALLTHROUGH */
1262bc5531deSDag-Erling Smørgrav 	default:
1263bc5531deSDag-Erling Smørgrav 		fprintf(ctx->out, "%s\n", l->line);
1264bc5531deSDag-Erling Smørgrav 		return 0;
1265bc5531deSDag-Erling Smørgrav 	}
1266bc5531deSDag-Erling Smørgrav 	/* NOTREACHED */
1267bc5531deSDag-Erling Smørgrav 	return -1;
1268bc5531deSDag-Erling Smørgrav }
1269bc5531deSDag-Erling Smørgrav 
1270bc5531deSDag-Erling Smørgrav static int
known_hosts_find_delete(struct hostkey_foreach_line * l,void * _ctx)1271bc5531deSDag-Erling Smørgrav known_hosts_find_delete(struct hostkey_foreach_line *l, void *_ctx)
1272bc5531deSDag-Erling Smørgrav {
1273bc5531deSDag-Erling Smørgrav 	struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx;
1274557f75e5SDag-Erling Smørgrav 	enum sshkey_fp_rep rep;
1275557f75e5SDag-Erling Smørgrav 	int fptype;
127619261079SEd Maste 	char *fp = NULL, *ra = NULL;
1277557f75e5SDag-Erling Smørgrav 
1278557f75e5SDag-Erling Smørgrav 	fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash;
1279557f75e5SDag-Erling Smørgrav 	rep =    print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT;
1280bc5531deSDag-Erling Smørgrav 
1281bc5531deSDag-Erling Smørgrav 	if (l->status == HKF_STATUS_MATCHED) {
128219261079SEd Maste 		if (ctx->delete_host) {
1283bc5531deSDag-Erling Smørgrav 			if (l->marker != MRK_NONE) {
1284bc5531deSDag-Erling Smørgrav 				/* Don't remove CA and revocation lines */
1285bc5531deSDag-Erling Smørgrav 				fprintf(ctx->out, "%s\n", l->line);
1286bc5531deSDag-Erling Smørgrav 			} else {
1287bc5531deSDag-Erling Smørgrav 				/*
1288bc5531deSDag-Erling Smørgrav 				 * Hostname matches and has no CA/revoke
1289bc5531deSDag-Erling Smørgrav 				 * marker, delete it by *not* writing the
1290bc5531deSDag-Erling Smørgrav 				 * line to ctx->out.
1291bc5531deSDag-Erling Smørgrav 				 */
1292bc5531deSDag-Erling Smørgrav 				ctx->found_key = 1;
1293bc5531deSDag-Erling Smørgrav 				if (!quiet)
1294d93a896eSDag-Erling Smørgrav 					printf("# Host %s found: line %lu\n",
1295bc5531deSDag-Erling Smørgrav 					    ctx->host, l->linenum);
1296bc5531deSDag-Erling Smørgrav 			}
1297bc5531deSDag-Erling Smørgrav 			return 0;
129819261079SEd Maste 		} else if (ctx->find_host) {
1299bc5531deSDag-Erling Smørgrav 			ctx->found_key = 1;
1300bc5531deSDag-Erling Smørgrav 			if (!quiet) {
1301d93a896eSDag-Erling Smørgrav 				printf("# Host %s found: line %lu %s\n",
1302bc5531deSDag-Erling Smørgrav 				    ctx->host,
1303bc5531deSDag-Erling Smørgrav 				    l->linenum, l->marker == MRK_CA ? "CA" :
1304bc5531deSDag-Erling Smørgrav 				    (l->marker == MRK_REVOKE ? "REVOKED" : ""));
1305bc5531deSDag-Erling Smørgrav 			}
130619261079SEd Maste 			if (ctx->hash_hosts)
1307bc5531deSDag-Erling Smørgrav 				known_hosts_hash(l, ctx);
1308557f75e5SDag-Erling Smørgrav 			else if (print_fingerprint) {
1309557f75e5SDag-Erling Smørgrav 				fp = sshkey_fingerprint(l->key, fptype, rep);
131019261079SEd Maste 				ra = sshkey_fingerprint(l->key,
131119261079SEd Maste 				    fingerprint_hash, SSH_FP_RANDOMART);
131219261079SEd Maste 				if (fp == NULL || ra == NULL)
131319261079SEd Maste 					fatal_f("sshkey_fingerprint failed");
131419261079SEd Maste 				mprintf("%s %s %s%s%s\n", ctx->host,
131519261079SEd Maste 				    sshkey_type(l->key), fp,
131619261079SEd Maste 				    l->comment[0] ? " " : "",
131719261079SEd Maste 				    l->comment);
131819261079SEd Maste 				if (log_level_get() >= SYSLOG_LEVEL_VERBOSE)
131919261079SEd Maste 					printf("%s\n", ra);
132019261079SEd Maste 				free(ra);
1321557f75e5SDag-Erling Smørgrav 				free(fp);
1322557f75e5SDag-Erling Smørgrav 			} else
1323bc5531deSDag-Erling Smørgrav 				fprintf(ctx->out, "%s\n", l->line);
1324bc5531deSDag-Erling Smørgrav 			return 0;
1325bc5531deSDag-Erling Smørgrav 		}
132619261079SEd Maste 	} else if (ctx->delete_host) {
1327bc5531deSDag-Erling Smørgrav 		/* Retain non-matching hosts when deleting */
1328bc5531deSDag-Erling Smørgrav 		if (l->status == HKF_STATUS_INVALID) {
1329bc5531deSDag-Erling Smørgrav 			ctx->invalid = 1;
1330d93a896eSDag-Erling Smørgrav 			logit("%s:%lu: invalid line", l->path, l->linenum);
1331bc5531deSDag-Erling Smørgrav 		}
1332bc5531deSDag-Erling Smørgrav 		fprintf(ctx->out, "%s\n", l->line);
1333bc5531deSDag-Erling Smørgrav 	}
1334bc5531deSDag-Erling Smørgrav 	return 0;
1335d4af9e69SDag-Erling Smørgrav }
13365e8dbd04SDag-Erling Smørgrav 
13375e8dbd04SDag-Erling Smørgrav static void
do_known_hosts(struct passwd * pw,const char * name,int find_host,int delete_host,int hash_hosts)133819261079SEd Maste do_known_hosts(struct passwd *pw, const char *name, int find_host,
133919261079SEd Maste     int delete_host, int hash_hosts)
13405e8dbd04SDag-Erling Smørgrav {
1341bc5531deSDag-Erling Smørgrav 	char *cp, tmp[PATH_MAX], old[PATH_MAX];
1342bc5531deSDag-Erling Smørgrav 	int r, fd, oerrno, inplace = 0;
1343bc5531deSDag-Erling Smørgrav 	struct known_hosts_ctx ctx;
1344557f75e5SDag-Erling Smørgrav 	u_int foreach_options;
134519261079SEd Maste 	struct stat sb;
13465e8dbd04SDag-Erling Smørgrav 
13475e8dbd04SDag-Erling Smørgrav 	if (!have_identity) {
13485e8dbd04SDag-Erling Smørgrav 		cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid);
13495e8dbd04SDag-Erling Smørgrav 		if (strlcpy(identity_file, cp, sizeof(identity_file)) >=
13505e8dbd04SDag-Erling Smørgrav 		    sizeof(identity_file))
13515e8dbd04SDag-Erling Smørgrav 			fatal("Specified known hosts path too long");
1352e4a9863fSDag-Erling Smørgrav 		free(cp);
13535e8dbd04SDag-Erling Smørgrav 		have_identity = 1;
13545e8dbd04SDag-Erling Smørgrav 	}
135519261079SEd Maste 	if (stat(identity_file, &sb) != 0)
135619261079SEd Maste 		fatal("Cannot stat %s: %s", identity_file, strerror(errno));
13575e8dbd04SDag-Erling Smørgrav 
1358bc5531deSDag-Erling Smørgrav 	memset(&ctx, 0, sizeof(ctx));
1359bc5531deSDag-Erling Smørgrav 	ctx.out = stdout;
1360bc5531deSDag-Erling Smørgrav 	ctx.host = name;
136119261079SEd Maste 	ctx.hash_hosts = hash_hosts;
136219261079SEd Maste 	ctx.find_host = find_host;
136319261079SEd Maste 	ctx.delete_host = delete_host;
1364bc5531deSDag-Erling Smørgrav 
13655e8dbd04SDag-Erling Smørgrav 	/*
13665e8dbd04SDag-Erling Smørgrav 	 * Find hosts goes to stdout, hash and deletions happen in-place
13675e8dbd04SDag-Erling Smørgrav 	 * A corner case is ssh-keygen -HF foo, which should go to stdout
13685e8dbd04SDag-Erling Smørgrav 	 */
13695e8dbd04SDag-Erling Smørgrav 	if (!find_host && (hash_hosts || delete_host)) {
13705e8dbd04SDag-Erling Smørgrav 		if (strlcpy(tmp, identity_file, sizeof(tmp)) >= sizeof(tmp) ||
13715e8dbd04SDag-Erling Smørgrav 		    strlcat(tmp, ".XXXXXXXXXX", sizeof(tmp)) >= sizeof(tmp) ||
13725e8dbd04SDag-Erling Smørgrav 		    strlcpy(old, identity_file, sizeof(old)) >= sizeof(old) ||
13735e8dbd04SDag-Erling Smørgrav 		    strlcat(old, ".old", sizeof(old)) >= sizeof(old))
13745e8dbd04SDag-Erling Smørgrav 			fatal("known_hosts path too long");
13755e8dbd04SDag-Erling Smørgrav 		umask(077);
1376bc5531deSDag-Erling Smørgrav 		if ((fd = mkstemp(tmp)) == -1)
13775e8dbd04SDag-Erling Smørgrav 			fatal("mkstemp: %s", strerror(errno));
1378bc5531deSDag-Erling Smørgrav 		if ((ctx.out = fdopen(fd, "w")) == NULL) {
1379bc5531deSDag-Erling Smørgrav 			oerrno = errno;
13805e8dbd04SDag-Erling Smørgrav 			unlink(tmp);
1381bc5531deSDag-Erling Smørgrav 			fatal("fdopen: %s", strerror(oerrno));
13825e8dbd04SDag-Erling Smørgrav 		}
13834d3fc8b0SEd Maste 		(void)fchmod(fd, sb.st_mode & 0644);
13845e8dbd04SDag-Erling Smørgrav 		inplace = 1;
13855e8dbd04SDag-Erling Smørgrav 	}
1386bc5531deSDag-Erling Smørgrav 	/* XXX support identity_file == "-" for stdin */
1387557f75e5SDag-Erling Smørgrav 	foreach_options = find_host ? HKF_WANT_MATCH : 0;
1388557f75e5SDag-Erling Smørgrav 	foreach_options |= print_fingerprint ? HKF_WANT_PARSE_KEY : 0;
1389190cef3dSDag-Erling Smørgrav 	if ((r = hostkeys_foreach(identity_file, (find_host || !hash_hosts) ?
1390190cef3dSDag-Erling Smørgrav 	    known_hosts_find_delete : known_hosts_hash, &ctx, name, NULL,
139119261079SEd Maste 	    foreach_options, 0)) != 0) {
1392acc1a9efSDag-Erling Smørgrav 		if (inplace)
1393acc1a9efSDag-Erling Smørgrav 			unlink(tmp);
139419261079SEd Maste 		fatal_fr(r, "hostkeys_foreach");
1395acc1a9efSDag-Erling Smørgrav 	}
13965e8dbd04SDag-Erling Smørgrav 
13975e8dbd04SDag-Erling Smørgrav 	if (inplace)
1398bc5531deSDag-Erling Smørgrav 		fclose(ctx.out);
1399b15c8340SDag-Erling Smørgrav 
1400bc5531deSDag-Erling Smørgrav 	if (ctx.invalid) {
1401557f75e5SDag-Erling Smørgrav 		error("%s is not a valid known_hosts file.", identity_file);
14025e8dbd04SDag-Erling Smørgrav 		if (inplace) {
1403557f75e5SDag-Erling Smørgrav 			error("Not replacing existing known_hosts "
1404557f75e5SDag-Erling Smørgrav 			    "file because of errors");
14055e8dbd04SDag-Erling Smørgrav 			unlink(tmp);
14065e8dbd04SDag-Erling Smørgrav 		}
14075e8dbd04SDag-Erling Smørgrav 		exit(1);
1408bc5531deSDag-Erling Smørgrav 	} else if (delete_host && !ctx.found_key) {
1409557f75e5SDag-Erling Smørgrav 		logit("Host %s not found in %s", name, identity_file);
1410fc1ba28aSDag-Erling Smørgrav 		if (inplace)
1411bc5531deSDag-Erling Smørgrav 			unlink(tmp);
1412bc5531deSDag-Erling Smørgrav 	} else if (inplace) {
14135e8dbd04SDag-Erling Smørgrav 		/* Backup existing file */
14145e8dbd04SDag-Erling Smørgrav 		if (unlink(old) == -1 && errno != ENOENT)
14155e8dbd04SDag-Erling Smørgrav 			fatal("unlink %.100s: %s", old, strerror(errno));
14165e8dbd04SDag-Erling Smørgrav 		if (link(identity_file, old) == -1)
14175e8dbd04SDag-Erling Smørgrav 			fatal("link %.100s to %.100s: %s", identity_file, old,
14185e8dbd04SDag-Erling Smørgrav 			    strerror(errno));
14195e8dbd04SDag-Erling Smørgrav 		/* Move new one into place */
14205e8dbd04SDag-Erling Smørgrav 		if (rename(tmp, identity_file) == -1) {
14215e8dbd04SDag-Erling Smørgrav 			error("rename\"%s\" to \"%s\": %s", tmp, identity_file,
14225e8dbd04SDag-Erling Smørgrav 			    strerror(errno));
14235e8dbd04SDag-Erling Smørgrav 			unlink(tmp);
14245e8dbd04SDag-Erling Smørgrav 			unlink(old);
14255e8dbd04SDag-Erling Smørgrav 			exit(1);
14265e8dbd04SDag-Erling Smørgrav 		}
14275e8dbd04SDag-Erling Smørgrav 
1428557f75e5SDag-Erling Smørgrav 		printf("%s updated.\n", identity_file);
1429557f75e5SDag-Erling Smørgrav 		printf("Original contents retained as %s\n", old);
1430bc5531deSDag-Erling Smørgrav 		if (ctx.has_unhashed) {
1431557f75e5SDag-Erling Smørgrav 			logit("WARNING: %s contains unhashed entries", old);
1432557f75e5SDag-Erling Smørgrav 			logit("Delete this file to ensure privacy "
1433557f75e5SDag-Erling Smørgrav 			    "of hostnames");
14345e8dbd04SDag-Erling Smørgrav 		}
14355e8dbd04SDag-Erling Smørgrav 	}
14365e8dbd04SDag-Erling Smørgrav 
1437bc5531deSDag-Erling Smørgrav 	exit (find_host && !ctx.found_key);
14385e8dbd04SDag-Erling Smørgrav }
14395e8dbd04SDag-Erling Smørgrav 
1440511b41d2SMark Murray /*
1441511b41d2SMark Murray  * Perform changing a passphrase.  The argument is the passwd structure
1442511b41d2SMark Murray  * for the current user.
1443511b41d2SMark Murray  */
1444ae1f160dSDag-Erling Smørgrav static void
do_change_passphrase(struct passwd * pw)1445511b41d2SMark Murray do_change_passphrase(struct passwd *pw)
1446511b41d2SMark Murray {
1447511b41d2SMark Murray 	char *comment;
1448511b41d2SMark Murray 	char *old_passphrase, *passphrase1, *passphrase2;
1449511b41d2SMark Murray 	struct stat st;
1450bc5531deSDag-Erling Smørgrav 	struct sshkey *private;
1451bc5531deSDag-Erling Smørgrav 	int r;
1452511b41d2SMark Murray 
1453511b41d2SMark Murray 	if (!have_identity)
1454511b41d2SMark Murray 		ask_filename(pw, "Enter file in which the key is");
145519261079SEd Maste 	if (stat(identity_file, &st) == -1)
1456557f75e5SDag-Erling Smørgrav 		fatal("%s: %s", identity_file, strerror(errno));
1457511b41d2SMark Murray 	/* Try to load the file with empty passphrase. */
1458bc5531deSDag-Erling Smørgrav 	r = sshkey_load_private(identity_file, "", &private, &comment);
1459bc5531deSDag-Erling Smørgrav 	if (r == SSH_ERR_KEY_WRONG_PASSPHRASE) {
1460511b41d2SMark Murray 		if (identity_passphrase)
1461511b41d2SMark Murray 			old_passphrase = xstrdup(identity_passphrase);
1462511b41d2SMark Murray 		else
1463ae1f160dSDag-Erling Smørgrav 			old_passphrase =
1464ae1f160dSDag-Erling Smørgrav 			    read_passphrase("Enter old passphrase: ",
1465ae1f160dSDag-Erling Smørgrav 			    RP_ALLOW_STDIN);
1466bc5531deSDag-Erling Smørgrav 		r = sshkey_load_private(identity_file, old_passphrase,
1467bc5531deSDag-Erling Smørgrav 		    &private, &comment);
146819261079SEd Maste 		freezero(old_passphrase, strlen(old_passphrase));
1469bc5531deSDag-Erling Smørgrav 		if (r != 0)
1470bc5531deSDag-Erling Smørgrav 			goto badkey;
1471bc5531deSDag-Erling Smørgrav 	} else if (r != 0) {
1472bc5531deSDag-Erling Smørgrav  badkey:
147319261079SEd Maste 		fatal_r(r, "Failed to load key %s", identity_file);
1474511b41d2SMark Murray 	}
1475bc5531deSDag-Erling Smørgrav 	if (comment)
1476d93a896eSDag-Erling Smørgrav 		mprintf("Key has comment '%s'\n", comment);
1477511b41d2SMark Murray 
1478511b41d2SMark Murray 	/* Ask the new passphrase (twice). */
1479511b41d2SMark Murray 	if (identity_new_passphrase) {
1480511b41d2SMark Murray 		passphrase1 = xstrdup(identity_new_passphrase);
1481511b41d2SMark Murray 		passphrase2 = NULL;
1482511b41d2SMark Murray 	} else {
1483511b41d2SMark Murray 		passphrase1 =
1484ae1f160dSDag-Erling Smørgrav 			read_passphrase("Enter new passphrase (empty for no "
1485ae1f160dSDag-Erling Smørgrav 			    "passphrase): ", RP_ALLOW_STDIN);
1486ae1f160dSDag-Erling Smørgrav 		passphrase2 = read_passphrase("Enter same passphrase again: ",
1487ae1f160dSDag-Erling Smørgrav 		    RP_ALLOW_STDIN);
1488511b41d2SMark Murray 
1489511b41d2SMark Murray 		/* Verify that they are the same. */
1490511b41d2SMark Murray 		if (strcmp(passphrase1, passphrase2) != 0) {
1491b83788ffSDag-Erling Smørgrav 			explicit_bzero(passphrase1, strlen(passphrase1));
1492b83788ffSDag-Erling Smørgrav 			explicit_bzero(passphrase2, strlen(passphrase2));
1493e4a9863fSDag-Erling Smørgrav 			free(passphrase1);
1494e4a9863fSDag-Erling Smørgrav 			free(passphrase2);
1495511b41d2SMark Murray 			printf("Pass phrases do not match.  Try again.\n");
1496511b41d2SMark Murray 			exit(1);
1497511b41d2SMark Murray 		}
1498511b41d2SMark Murray 		/* Destroy the other copy. */
149919261079SEd Maste 		freezero(passphrase2, strlen(passphrase2));
1500511b41d2SMark Murray 	}
1501511b41d2SMark Murray 
1502511b41d2SMark Murray 	/* Save the file using the new passphrase. */
1503bc5531deSDag-Erling Smørgrav 	if ((r = sshkey_save_private(private, identity_file, passphrase1,
150419261079SEd Maste 	    comment, private_key_format, openssh_format_cipher, rounds)) != 0) {
150519261079SEd Maste 		error_r(r, "Saving key \"%s\" failed", identity_file);
150619261079SEd Maste 		freezero(passphrase1, strlen(passphrase1));
1507bc5531deSDag-Erling Smørgrav 		sshkey_free(private);
1508e4a9863fSDag-Erling Smørgrav 		free(comment);
1509511b41d2SMark Murray 		exit(1);
1510511b41d2SMark Murray 	}
1511511b41d2SMark Murray 	/* Destroy the passphrase and the copy of the key in memory. */
151219261079SEd Maste 	freezero(passphrase1, strlen(passphrase1));
1513bc5531deSDag-Erling Smørgrav 	sshkey_free(private);		 /* Destroys contents */
1514e4a9863fSDag-Erling Smørgrav 	free(comment);
1515511b41d2SMark Murray 
1516511b41d2SMark Murray 	printf("Your identification has been saved with the new passphrase.\n");
1517511b41d2SMark Murray 	exit(0);
1518511b41d2SMark Murray }
1519511b41d2SMark Murray 
1520d95e11bfSDag-Erling Smørgrav /*
1521d95e11bfSDag-Erling Smørgrav  * Print the SSHFP RR.
1522d95e11bfSDag-Erling Smørgrav  */
1523761efaa7SDag-Erling Smørgrav static int
do_print_resource_record(struct passwd * pw,char * fname,char * hname,int print_generic,char * const * opts,size_t nopts)152419261079SEd Maste do_print_resource_record(struct passwd *pw, char *fname, char *hname,
15254d3fc8b0SEd Maste     int print_generic, char * const *opts, size_t nopts)
1526d95e11bfSDag-Erling Smørgrav {
1527bc5531deSDag-Erling Smørgrav 	struct sshkey *public;
1528d95e11bfSDag-Erling Smørgrav 	char *comment = NULL;
1529d95e11bfSDag-Erling Smørgrav 	struct stat st;
15304d3fc8b0SEd Maste 	int r, hash = -1;
15314d3fc8b0SEd Maste 	size_t i;
1532d95e11bfSDag-Erling Smørgrav 
15334d3fc8b0SEd Maste 	for (i = 0; i < nopts; i++) {
15344d3fc8b0SEd Maste 		if (strncasecmp(opts[i], "hashalg=", 8) == 0) {
15354d3fc8b0SEd Maste 			if ((hash = ssh_digest_alg_by_name(opts[i] + 8)) == -1)
15364d3fc8b0SEd Maste 				fatal("Unsupported hash algorithm");
15374d3fc8b0SEd Maste 		} else {
15384d3fc8b0SEd Maste 			error("Invalid option \"%s\"", opts[i]);
15394d3fc8b0SEd Maste 			return SSH_ERR_INVALID_ARGUMENT;
15404d3fc8b0SEd Maste 		}
15414d3fc8b0SEd Maste 	}
1542761efaa7SDag-Erling Smørgrav 	if (fname == NULL)
154319261079SEd Maste 		fatal_f("no filename");
154419261079SEd Maste 	if (stat(fname, &st) == -1) {
1545761efaa7SDag-Erling Smørgrav 		if (errno == ENOENT)
1546761efaa7SDag-Erling Smørgrav 			return 0;
1547557f75e5SDag-Erling Smørgrav 		fatal("%s: %s", fname, strerror(errno));
1548d95e11bfSDag-Erling Smørgrav 	}
1549557f75e5SDag-Erling Smørgrav 	if ((r = sshkey_load_public(fname, &public, &comment)) != 0)
155019261079SEd Maste 		fatal_r(r, "Failed to read v2 public key from \"%s\"", fname);
15514d3fc8b0SEd Maste 	export_dns_rr(hname, public, stdout, print_generic, hash);
1552bc5531deSDag-Erling Smørgrav 	sshkey_free(public);
1553e4a9863fSDag-Erling Smørgrav 	free(comment);
1554761efaa7SDag-Erling Smørgrav 	return 1;
1555d95e11bfSDag-Erling Smørgrav }
1556d95e11bfSDag-Erling Smørgrav 
1557511b41d2SMark Murray /*
1558511b41d2SMark Murray  * Change the comment of a private key file.
1559511b41d2SMark Murray  */
1560ae1f160dSDag-Erling Smørgrav static void
do_change_comment(struct passwd * pw,const char * identity_comment)156119261079SEd Maste do_change_comment(struct passwd *pw, const char *identity_comment)
1562511b41d2SMark Murray {
15631e8db6e2SBrian Feldman 	char new_comment[1024], *comment, *passphrase;
1564bc5531deSDag-Erling Smørgrav 	struct sshkey *private;
1565bc5531deSDag-Erling Smørgrav 	struct sshkey *public;
1566511b41d2SMark Murray 	struct stat st;
156719261079SEd Maste 	int r;
1568511b41d2SMark Murray 
1569511b41d2SMark Murray 	if (!have_identity)
1570511b41d2SMark Murray 		ask_filename(pw, "Enter file in which the key is");
157119261079SEd Maste 	if (stat(identity_file, &st) == -1)
1572557f75e5SDag-Erling Smørgrav 		fatal("%s: %s", identity_file, strerror(errno));
1573bc5531deSDag-Erling Smørgrav 	if ((r = sshkey_load_private(identity_file, "",
1574bc5531deSDag-Erling Smørgrav 	    &private, &comment)) == 0)
1575bc5531deSDag-Erling Smørgrav 		passphrase = xstrdup("");
1576557f75e5SDag-Erling Smørgrav 	else if (r != SSH_ERR_KEY_WRONG_PASSPHRASE)
157719261079SEd Maste 		fatal_r(r, "Cannot load private key \"%s\"", identity_file);
1578557f75e5SDag-Erling Smørgrav 	else {
1579511b41d2SMark Murray 		if (identity_passphrase)
1580511b41d2SMark Murray 			passphrase = xstrdup(identity_passphrase);
1581511b41d2SMark Murray 		else if (identity_new_passphrase)
1582511b41d2SMark Murray 			passphrase = xstrdup(identity_new_passphrase);
1583511b41d2SMark Murray 		else
1584ae1f160dSDag-Erling Smørgrav 			passphrase = read_passphrase("Enter passphrase: ",
1585ae1f160dSDag-Erling Smørgrav 			    RP_ALLOW_STDIN);
1586511b41d2SMark Murray 		/* Try to load using the passphrase. */
1587bc5531deSDag-Erling Smørgrav 		if ((r = sshkey_load_private(identity_file, passphrase,
1588bc5531deSDag-Erling Smørgrav 		    &private, &comment)) != 0) {
158919261079SEd Maste 			freezero(passphrase, strlen(passphrase));
159019261079SEd Maste 			fatal_r(r, "Cannot load private key \"%s\"",
159119261079SEd Maste 			    identity_file);
1592511b41d2SMark Murray 		}
15931e8db6e2SBrian Feldman 	}
1594acc1a9efSDag-Erling Smørgrav 
159547dd1d1bSDag-Erling Smørgrav 	if (private->type != KEY_ED25519 && private->type != KEY_XMSS &&
159619261079SEd Maste 	    private_key_format != SSHKEY_PRIVATE_OPENSSH) {
15974f52dfbbSDag-Erling Smørgrav 		error("Comments are only supported for keys stored in "
1598acc1a9efSDag-Erling Smørgrav 		    "the new format (-o).");
1599557f75e5SDag-Erling Smørgrav 		explicit_bzero(passphrase, strlen(passphrase));
1600bc5531deSDag-Erling Smørgrav 		sshkey_free(private);
16011e8db6e2SBrian Feldman 		exit(1);
1602511b41d2SMark Murray 	}
1603d93a896eSDag-Erling Smørgrav 	if (comment)
160419261079SEd Maste 		printf("Old comment: %s\n", comment);
1605d93a896eSDag-Erling Smørgrav 	else
160619261079SEd Maste 		printf("No existing comment\n");
1607511b41d2SMark Murray 
1608511b41d2SMark Murray 	if (identity_comment) {
1609511b41d2SMark Murray 		strlcpy(new_comment, identity_comment, sizeof(new_comment));
1610511b41d2SMark Murray 	} else {
161119261079SEd Maste 		printf("New comment: ");
1612511b41d2SMark Murray 		fflush(stdout);
1613511b41d2SMark Murray 		if (!fgets(new_comment, sizeof(new_comment), stdin)) {
1614b83788ffSDag-Erling Smørgrav 			explicit_bzero(passphrase, strlen(passphrase));
1615bc5531deSDag-Erling Smørgrav 			sshkey_free(private);
1616511b41d2SMark Murray 			exit(1);
1617511b41d2SMark Murray 		}
1618d4af9e69SDag-Erling Smørgrav 		new_comment[strcspn(new_comment, "\n")] = '\0';
1619511b41d2SMark Murray 	}
162019261079SEd Maste 	if (comment != NULL && strcmp(comment, new_comment) == 0) {
162119261079SEd Maste 		printf("No change to comment\n");
162219261079SEd Maste 		free(passphrase);
162319261079SEd Maste 		sshkey_free(private);
162419261079SEd Maste 		free(comment);
162519261079SEd Maste 		exit(0);
162619261079SEd Maste 	}
1627511b41d2SMark Murray 
1628511b41d2SMark Murray 	/* Save the file using the new passphrase. */
1629bc5531deSDag-Erling Smørgrav 	if ((r = sshkey_save_private(private, identity_file, passphrase,
163019261079SEd Maste 	    new_comment, private_key_format, openssh_format_cipher,
163119261079SEd Maste 	    rounds)) != 0) {
163219261079SEd Maste 		error_r(r, "Saving key \"%s\" failed", identity_file);
163319261079SEd Maste 		freezero(passphrase, strlen(passphrase));
1634bc5531deSDag-Erling Smørgrav 		sshkey_free(private);
1635e4a9863fSDag-Erling Smørgrav 		free(comment);
1636511b41d2SMark Murray 		exit(1);
1637511b41d2SMark Murray 	}
163819261079SEd Maste 	freezero(passphrase, strlen(passphrase));
1639bc5531deSDag-Erling Smørgrav 	if ((r = sshkey_from_private(private, &public)) != 0)
164019261079SEd Maste 		fatal_fr(r, "sshkey_from_private");
1641bc5531deSDag-Erling Smørgrav 	sshkey_free(private);
1642511b41d2SMark Murray 
1643511b41d2SMark Murray 	strlcat(identity_file, ".pub", sizeof(identity_file));
164419261079SEd Maste 	if ((r = sshkey_save_public(public, identity_file, new_comment)) != 0)
164519261079SEd Maste 		fatal_r(r, "Unable to save public key to %s", identity_file);
1646bc5531deSDag-Erling Smørgrav 	sshkey_free(public);
1647e4a9863fSDag-Erling Smørgrav 	free(comment);
1648511b41d2SMark Murray 
164919261079SEd Maste 	if (strlen(new_comment) > 0)
165019261079SEd Maste 		printf("Comment '%s' applied\n", new_comment);
165119261079SEd Maste 	else
165219261079SEd Maste 		printf("Comment removed\n");
165319261079SEd Maste 
1654511b41d2SMark Murray 	exit(0);
1655511b41d2SMark Murray }
1656511b41d2SMark Murray 
1657b15c8340SDag-Erling Smørgrav static void
cert_ext_add(const char * key,const char * value,int iscrit)165819261079SEd Maste cert_ext_add(const char *key, const char *value, int iscrit)
1659b15c8340SDag-Erling Smørgrav {
166019261079SEd Maste 	cert_ext = xreallocarray(cert_ext, ncert_ext + 1, sizeof(*cert_ext));
166119261079SEd Maste 	cert_ext[ncert_ext].key = xstrdup(key);
166219261079SEd Maste 	cert_ext[ncert_ext].val = value == NULL ? NULL : xstrdup(value);
166319261079SEd Maste 	cert_ext[ncert_ext].crit = iscrit;
166419261079SEd Maste 	ncert_ext++;
1665b15c8340SDag-Erling Smørgrav }
1666b15c8340SDag-Erling Smørgrav 
166719261079SEd Maste /* qsort(3) comparison function for certificate extensions */
166819261079SEd Maste static int
cert_ext_cmp(const void * _a,const void * _b)166919261079SEd Maste cert_ext_cmp(const void *_a, const void *_b)
1670b15c8340SDag-Erling Smørgrav {
167119261079SEd Maste 	const struct cert_ext *a = (const struct cert_ext *)_a;
167219261079SEd Maste 	const struct cert_ext *b = (const struct cert_ext *)_b;
1673bc5531deSDag-Erling Smørgrav 	int r;
1674b15c8340SDag-Erling Smørgrav 
167519261079SEd Maste 	if (a->crit != b->crit)
167619261079SEd Maste 		return (a->crit < b->crit) ? -1 : 1;
167719261079SEd Maste 	if ((r = strcmp(a->key, b->key)) != 0)
167819261079SEd Maste 		return r;
167919261079SEd Maste 	if ((a->val == NULL) != (b->val == NULL))
168019261079SEd Maste 		return (a->val == NULL) ? -1 : 1;
168119261079SEd Maste 	if (a->val != NULL && (r = strcmp(a->val, b->val)) != 0)
168219261079SEd Maste 		return r;
168319261079SEd Maste 	return 0;
1684b15c8340SDag-Erling Smørgrav }
1685b15c8340SDag-Erling Smørgrav 
1686e2f6069cSDag-Erling Smørgrav #define OPTIONS_CRITICAL	1
1687e2f6069cSDag-Erling Smørgrav #define OPTIONS_EXTENSIONS	2
1688b15c8340SDag-Erling Smørgrav static void
prepare_options_buf(struct sshbuf * c,int which)1689bc5531deSDag-Erling Smørgrav prepare_options_buf(struct sshbuf *c, int which)
1690b15c8340SDag-Erling Smørgrav {
169119261079SEd Maste 	struct sshbuf *b;
16924f52dfbbSDag-Erling Smørgrav 	size_t i;
169319261079SEd Maste 	int r;
169419261079SEd Maste 	const struct cert_ext *ext;
16954f52dfbbSDag-Erling Smørgrav 
169619261079SEd Maste 	if ((b = sshbuf_new()) == NULL)
169719261079SEd Maste 		fatal_f("sshbuf_new failed");
1698bc5531deSDag-Erling Smørgrav 	sshbuf_reset(c);
169919261079SEd Maste 	for (i = 0; i < ncert_ext; i++) {
170019261079SEd Maste 		ext = &cert_ext[i];
170119261079SEd Maste 		if ((ext->crit && (which & OPTIONS_EXTENSIONS)) ||
170219261079SEd Maste 		    (!ext->crit && (which & OPTIONS_CRITICAL)))
17034f52dfbbSDag-Erling Smørgrav 			continue;
170419261079SEd Maste 		if (ext->val == NULL) {
170519261079SEd Maste 			/* flag option */
170619261079SEd Maste 			debug3_f("%s", ext->key);
170719261079SEd Maste 			if ((r = sshbuf_put_cstring(c, ext->key)) != 0 ||
170819261079SEd Maste 			    (r = sshbuf_put_string(c, NULL, 0)) != 0)
170919261079SEd Maste 				fatal_fr(r, "prepare flag");
171019261079SEd Maste 		} else {
171119261079SEd Maste 			/* key/value option */
171219261079SEd Maste 			debug3_f("%s=%s", ext->key, ext->val);
171319261079SEd Maste 			sshbuf_reset(b);
171419261079SEd Maste 			if ((r = sshbuf_put_cstring(c, ext->key)) != 0 ||
171519261079SEd Maste 			    (r = sshbuf_put_cstring(b, ext->val)) != 0 ||
171619261079SEd Maste 			    (r = sshbuf_put_stringb(c, b)) != 0)
171719261079SEd Maste 				fatal_fr(r, "prepare k/v");
17184f52dfbbSDag-Erling Smørgrav 		}
17194f52dfbbSDag-Erling Smørgrav 	}
172019261079SEd Maste 	sshbuf_free(b);
172119261079SEd Maste }
172219261079SEd Maste 
172319261079SEd Maste static void
finalise_cert_exts(void)172419261079SEd Maste finalise_cert_exts(void)
172519261079SEd Maste {
172619261079SEd Maste 	/* critical options */
172719261079SEd Maste 	if (certflags_command != NULL)
172819261079SEd Maste 		cert_ext_add("force-command", certflags_command, 1);
172919261079SEd Maste 	if (certflags_src_addr != NULL)
173019261079SEd Maste 		cert_ext_add("source-address", certflags_src_addr, 1);
173138a52bd3SEd Maste 	if ((certflags_flags & CERTOPT_REQUIRE_VERIFY) != 0)
173238a52bd3SEd Maste 		cert_ext_add("verify-required", NULL, 1);
173319261079SEd Maste 	/* extensions */
173419261079SEd Maste 	if ((certflags_flags & CERTOPT_X_FWD) != 0)
173519261079SEd Maste 		cert_ext_add("permit-X11-forwarding", NULL, 0);
173619261079SEd Maste 	if ((certflags_flags & CERTOPT_AGENT_FWD) != 0)
173719261079SEd Maste 		cert_ext_add("permit-agent-forwarding", NULL, 0);
173819261079SEd Maste 	if ((certflags_flags & CERTOPT_PORT_FWD) != 0)
173919261079SEd Maste 		cert_ext_add("permit-port-forwarding", NULL, 0);
174019261079SEd Maste 	if ((certflags_flags & CERTOPT_PTY) != 0)
174119261079SEd Maste 		cert_ext_add("permit-pty", NULL, 0);
174219261079SEd Maste 	if ((certflags_flags & CERTOPT_USER_RC) != 0)
174319261079SEd Maste 		cert_ext_add("permit-user-rc", NULL, 0);
174419261079SEd Maste 	if ((certflags_flags & CERTOPT_NO_REQUIRE_USER_PRESENCE) != 0)
174519261079SEd Maste 		cert_ext_add("no-touch-required", NULL, 0);
174619261079SEd Maste 	/* order lexically by key */
174719261079SEd Maste 	if (ncert_ext > 0)
174819261079SEd Maste 		qsort(cert_ext, ncert_ext, sizeof(*cert_ext), cert_ext_cmp);
1749e2f6069cSDag-Erling Smørgrav }
1750e2f6069cSDag-Erling Smørgrav 
1751bc5531deSDag-Erling Smørgrav static struct sshkey *
load_pkcs11_key(char * path)1752e2f6069cSDag-Erling Smørgrav load_pkcs11_key(char *path)
1753e2f6069cSDag-Erling Smørgrav {
1754e2f6069cSDag-Erling Smørgrav #ifdef ENABLE_PKCS11
1755bc5531deSDag-Erling Smørgrav 	struct sshkey **keys = NULL, *public, *private = NULL;
1756bc5531deSDag-Erling Smørgrav 	int r, i, nkeys;
1757e2f6069cSDag-Erling Smørgrav 
1758bc5531deSDag-Erling Smørgrav 	if ((r = sshkey_load_public(path, &public, NULL)) != 0)
175919261079SEd Maste 		fatal_r(r, "Couldn't load CA public key \"%s\"", path);
1760e2f6069cSDag-Erling Smørgrav 
176119261079SEd Maste 	nkeys = pkcs11_add_provider(pkcs11provider, identity_passphrase,
176219261079SEd Maste 	    &keys, NULL);
176319261079SEd Maste 	debug3_f("%d keys", nkeys);
1764e2f6069cSDag-Erling Smørgrav 	if (nkeys <= 0)
1765e2f6069cSDag-Erling Smørgrav 		fatal("cannot read public key from pkcs11");
1766e2f6069cSDag-Erling Smørgrav 	for (i = 0; i < nkeys; i++) {
1767bc5531deSDag-Erling Smørgrav 		if (sshkey_equal_public(public, keys[i])) {
1768e2f6069cSDag-Erling Smørgrav 			private = keys[i];
1769e2f6069cSDag-Erling Smørgrav 			continue;
1770e2f6069cSDag-Erling Smørgrav 		}
1771bc5531deSDag-Erling Smørgrav 		sshkey_free(keys[i]);
1772e2f6069cSDag-Erling Smørgrav 	}
1773e4a9863fSDag-Erling Smørgrav 	free(keys);
1774bc5531deSDag-Erling Smørgrav 	sshkey_free(public);
1775e2f6069cSDag-Erling Smørgrav 	return private;
1776e2f6069cSDag-Erling Smørgrav #else
1777e2f6069cSDag-Erling Smørgrav 	fatal("no pkcs11 support");
1778e2f6069cSDag-Erling Smørgrav #endif /* ENABLE_PKCS11 */
1779b15c8340SDag-Erling Smørgrav }
1780b15c8340SDag-Erling Smørgrav 
17814f52dfbbSDag-Erling Smørgrav /* Signer for sshkey_certify_custom that uses the agent */
17824f52dfbbSDag-Erling Smørgrav 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)178319261079SEd Maste agent_signer(struct sshkey *key, u_char **sigp, size_t *lenp,
17844f52dfbbSDag-Erling Smørgrav     const u_char *data, size_t datalen,
178519261079SEd Maste     const char *alg, const char *provider, const char *pin,
178619261079SEd Maste     u_int compat, void *ctx)
17874f52dfbbSDag-Erling Smørgrav {
17884f52dfbbSDag-Erling Smørgrav 	int *agent_fdp = (int *)ctx;
17894f52dfbbSDag-Erling Smørgrav 
17904f52dfbbSDag-Erling Smørgrav 	return ssh_agent_sign(*agent_fdp, key, sigp, lenp,
17914f52dfbbSDag-Erling Smørgrav 	    data, datalen, alg, compat);
17924f52dfbbSDag-Erling Smørgrav }
17934f52dfbbSDag-Erling Smørgrav 
1794b15c8340SDag-Erling Smørgrav 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)179519261079SEd Maste do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent,
179619261079SEd Maste     unsigned long long cert_serial, int cert_serial_autoinc,
179719261079SEd Maste     int argc, char **argv)
1798b15c8340SDag-Erling Smørgrav {
179919261079SEd Maste 	int r, i, found, agent_fd = -1;
1800b15c8340SDag-Erling Smørgrav 	u_int n;
1801bc5531deSDag-Erling Smørgrav 	struct sshkey *ca, *public;
180219261079SEd Maste 	char valid[64], *otmp, *tmp, *cp, *out, *comment;
180319261079SEd Maste 	char *ca_fp = NULL, **plist = NULL, *pin = NULL;
18044f52dfbbSDag-Erling Smørgrav 	struct ssh_identitylist *agent_ids;
18054f52dfbbSDag-Erling Smørgrav 	size_t j;
180619261079SEd Maste 	struct notifier_ctx *notifier = NULL;
1807e2f6069cSDag-Erling Smørgrav 
1808a0ee8cc6SDag-Erling Smørgrav #ifdef ENABLE_PKCS11
1809e2f6069cSDag-Erling Smørgrav 	pkcs11_init(1);
1810a0ee8cc6SDag-Erling Smørgrav #endif
1811b15c8340SDag-Erling Smørgrav 	tmp = tilde_expand_filename(ca_key_path, pw->pw_uid);
1812e2f6069cSDag-Erling Smørgrav 	if (pkcs11provider != NULL) {
18134f52dfbbSDag-Erling Smørgrav 		/* If a PKCS#11 token was specified then try to use it */
1814e2f6069cSDag-Erling Smørgrav 		if ((ca = load_pkcs11_key(tmp)) == NULL)
1815e2f6069cSDag-Erling Smørgrav 			fatal("No PKCS#11 key matching %s found", ca_key_path);
18164f52dfbbSDag-Erling Smørgrav 	} else if (prefer_agent) {
18174f52dfbbSDag-Erling Smørgrav 		/*
18184f52dfbbSDag-Erling Smørgrav 		 * Agent signature requested. Try to use agent after making
18194f52dfbbSDag-Erling Smørgrav 		 * sure the public key specified is actually present in the
18204f52dfbbSDag-Erling Smørgrav 		 * agent.
18214f52dfbbSDag-Erling Smørgrav 		 */
18224f52dfbbSDag-Erling Smørgrav 		if ((r = sshkey_load_public(tmp, &ca, NULL)) != 0)
182319261079SEd Maste 			fatal_r(r, "Cannot load CA public key %s", tmp);
18244f52dfbbSDag-Erling Smørgrav 		if ((r = ssh_get_authentication_socket(&agent_fd)) != 0)
182519261079SEd Maste 			fatal_r(r, "Cannot use public key for CA signature");
18264f52dfbbSDag-Erling Smørgrav 		if ((r = ssh_fetch_identitylist(agent_fd, &agent_ids)) != 0)
182719261079SEd Maste 			fatal_r(r, "Retrieve agent key list");
18284f52dfbbSDag-Erling Smørgrav 		found = 0;
18294f52dfbbSDag-Erling Smørgrav 		for (j = 0; j < agent_ids->nkeys; j++) {
18304f52dfbbSDag-Erling Smørgrav 			if (sshkey_equal(ca, agent_ids->keys[j])) {
18314f52dfbbSDag-Erling Smørgrav 				found = 1;
18324f52dfbbSDag-Erling Smørgrav 				break;
18334f52dfbbSDag-Erling Smørgrav 			}
18344f52dfbbSDag-Erling Smørgrav 		}
18354f52dfbbSDag-Erling Smørgrav 		if (!found)
18364f52dfbbSDag-Erling Smørgrav 			fatal("CA key %s not found in agent", tmp);
18374f52dfbbSDag-Erling Smørgrav 		ssh_free_identitylist(agent_ids);
18384f52dfbbSDag-Erling Smørgrav 		ca->flags |= SSHKEY_FLAG_EXT;
18394f52dfbbSDag-Erling Smørgrav 	} else {
18404f52dfbbSDag-Erling Smørgrav 		/* CA key is assumed to be a private key on the filesystem */
184119261079SEd Maste 		ca = load_identity(tmp, NULL);
184219261079SEd Maste 		if (sshkey_is_sk(ca) &&
184319261079SEd Maste 		    (ca->sk_flags & SSH_SK_USER_VERIFICATION_REQD)) {
184419261079SEd Maste 			if ((pin = read_passphrase("Enter PIN for CA key: ",
184519261079SEd Maste 			    RP_ALLOW_STDIN)) == NULL)
184619261079SEd Maste 				fatal_f("couldn't read PIN");
184719261079SEd Maste 		}
18484f52dfbbSDag-Erling Smørgrav 	}
1849e4a9863fSDag-Erling Smørgrav 	free(tmp);
1850b15c8340SDag-Erling Smørgrav 
185119261079SEd Maste 	if (key_type_name != NULL) {
1852*3d9fd9fcSEd Maste 		if (sshkey_type_from_shortname(key_type_name) != ca->type) {
1853076ad2f8SDag-Erling Smørgrav 			fatal("CA key type %s doesn't match specified %s",
1854076ad2f8SDag-Erling Smørgrav 			    sshkey_ssh_name(ca), key_type_name);
1855076ad2f8SDag-Erling Smørgrav 		}
185619261079SEd Maste 	} else if (ca->type == KEY_RSA) {
185719261079SEd Maste 		/* Default to a good signature algorithm */
185819261079SEd Maste 		key_type_name = "rsa-sha2-512";
185919261079SEd Maste 	}
186019261079SEd Maste 	ca_fp = sshkey_fingerprint(ca, fingerprint_hash, SSH_FP_DEFAULT);
1861076ad2f8SDag-Erling Smørgrav 
186219261079SEd Maste 	finalise_cert_exts();
1863b15c8340SDag-Erling Smørgrav 	for (i = 0; i < argc; i++) {
1864b15c8340SDag-Erling Smørgrav 		/* Split list of principals */
1865b15c8340SDag-Erling Smørgrav 		n = 0;
1866b15c8340SDag-Erling Smørgrav 		if (cert_principals != NULL) {
1867b15c8340SDag-Erling Smørgrav 			otmp = tmp = xstrdup(cert_principals);
1868b15c8340SDag-Erling Smørgrav 			plist = NULL;
1869b15c8340SDag-Erling Smørgrav 			for (; (cp = strsep(&tmp, ",")) != NULL; n++) {
1870557f75e5SDag-Erling Smørgrav 				plist = xreallocarray(plist, n + 1, sizeof(*plist));
1871b15c8340SDag-Erling Smørgrav 				if (*(plist[n] = xstrdup(cp)) == '\0')
1872b15c8340SDag-Erling Smørgrav 					fatal("Empty principal name");
1873b15c8340SDag-Erling Smørgrav 			}
1874e4a9863fSDag-Erling Smørgrav 			free(otmp);
1875b15c8340SDag-Erling Smørgrav 		}
187647dd1d1bSDag-Erling Smørgrav 		if (n > SSHKEY_CERT_MAX_PRINCIPALS)
187747dd1d1bSDag-Erling Smørgrav 			fatal("Too many certificate principals specified");
1878b15c8340SDag-Erling Smørgrav 
1879b15c8340SDag-Erling Smørgrav 		tmp = tilde_expand_filename(argv[i], pw->pw_uid);
1880bc5531deSDag-Erling Smørgrav 		if ((r = sshkey_load_public(tmp, &public, &comment)) != 0)
188119261079SEd Maste 			fatal_r(r, "load pubkey \"%s\"", tmp);
188219261079SEd Maste 		if (sshkey_is_cert(public))
188319261079SEd Maste 			fatal_f("key \"%s\" type %s cannot be certified",
188419261079SEd Maste 			    tmp, sshkey_type(public));
1885b15c8340SDag-Erling Smørgrav 
1886b15c8340SDag-Erling Smørgrav 		/* Prepare certificate to sign */
1887eccfee6eSDag-Erling Smørgrav 		if ((r = sshkey_to_certified(public)) != 0)
188819261079SEd Maste 			fatal_r(r, "Could not upgrade key %s to certificate", tmp);
1889b15c8340SDag-Erling Smørgrav 		public->cert->type = cert_key_type;
1890e2f6069cSDag-Erling Smørgrav 		public->cert->serial = (u_int64_t)cert_serial;
1891b15c8340SDag-Erling Smørgrav 		public->cert->key_id = xstrdup(cert_key_id);
1892b15c8340SDag-Erling Smørgrav 		public->cert->nprincipals = n;
1893b15c8340SDag-Erling Smørgrav 		public->cert->principals = plist;
1894b15c8340SDag-Erling Smørgrav 		public->cert->valid_after = cert_valid_from;
1895b15c8340SDag-Erling Smørgrav 		public->cert->valid_before = cert_valid_to;
1896eccfee6eSDag-Erling Smørgrav 		prepare_options_buf(public->cert->critical, OPTIONS_CRITICAL);
1897a0ee8cc6SDag-Erling Smørgrav 		prepare_options_buf(public->cert->extensions,
1898e2f6069cSDag-Erling Smørgrav 		    OPTIONS_EXTENSIONS);
1899bc5531deSDag-Erling Smørgrav 		if ((r = sshkey_from_private(ca,
1900bc5531deSDag-Erling Smørgrav 		    &public->cert->signature_key)) != 0)
190119261079SEd Maste 			fatal_r(r, "sshkey_from_private (ca key)");
1902b15c8340SDag-Erling Smørgrav 
19034f52dfbbSDag-Erling Smørgrav 		if (agent_fd != -1 && (ca->flags & SSHKEY_FLAG_EXT) != 0) {
19044f52dfbbSDag-Erling Smørgrav 			if ((r = sshkey_certify_custom(public, ca,
190519261079SEd Maste 			    key_type_name, sk_provider, NULL, agent_signer,
190619261079SEd Maste 			    &agent_fd)) != 0)
190719261079SEd Maste 				fatal_r(r, "Couldn't certify %s via agent", tmp);
19084f52dfbbSDag-Erling Smørgrav 		} else {
190919261079SEd Maste 			if (sshkey_is_sk(ca) &&
191019261079SEd Maste 			    (ca->sk_flags & SSH_SK_USER_PRESENCE_REQD)) {
191119261079SEd Maste 				notifier = notify_start(0,
191219261079SEd Maste 				    "Confirm user presence for key %s %s",
191319261079SEd Maste 				    sshkey_type(ca), ca_fp);
191419261079SEd Maste 			}
191519261079SEd Maste 			r = sshkey_certify(public, ca, key_type_name,
191619261079SEd Maste 			    sk_provider, pin);
191719261079SEd Maste 			notify_complete(notifier, "User presence confirmed");
191819261079SEd Maste 			if (r != 0)
191919261079SEd Maste 				fatal_r(r, "Couldn't certify key %s", tmp);
19204f52dfbbSDag-Erling Smørgrav 		}
1921b15c8340SDag-Erling Smørgrav 
1922b15c8340SDag-Erling Smørgrav 		if ((cp = strrchr(tmp, '.')) != NULL && strcmp(cp, ".pub") == 0)
1923b15c8340SDag-Erling Smørgrav 			*cp = '\0';
1924b15c8340SDag-Erling Smørgrav 		xasprintf(&out, "%s-cert.pub", tmp);
1925e4a9863fSDag-Erling Smørgrav 		free(tmp);
1926b15c8340SDag-Erling Smørgrav 
192719261079SEd Maste 		if ((r = sshkey_save_public(public, out, comment)) != 0) {
192819261079SEd Maste 			fatal_r(r, "Unable to save public key to %s",
192919261079SEd Maste 			    identity_file);
193019261079SEd Maste 		}
1931b15c8340SDag-Erling Smørgrav 
1932e2f6069cSDag-Erling Smørgrav 		if (!quiet) {
1933acc1a9efSDag-Erling Smørgrav 			sshkey_format_cert_validity(public->cert,
1934acc1a9efSDag-Erling Smørgrav 			    valid, sizeof(valid));
1935e2f6069cSDag-Erling Smørgrav 			logit("Signed %s key %s: id \"%s\" serial %llu%s%s "
1936bc5531deSDag-Erling Smørgrav 			    "valid %s", sshkey_cert_type(public),
19374a421b63SDag-Erling Smørgrav 			    out, public->cert->key_id,
19384a421b63SDag-Erling Smørgrav 			    (unsigned long long)public->cert->serial,
1939b15c8340SDag-Erling Smørgrav 			    cert_principals != NULL ? " for " : "",
1940b15c8340SDag-Erling Smørgrav 			    cert_principals != NULL ? cert_principals : "",
1941acc1a9efSDag-Erling Smørgrav 			    valid);
1942e2f6069cSDag-Erling Smørgrav 		}
1943b15c8340SDag-Erling Smørgrav 
1944bc5531deSDag-Erling Smørgrav 		sshkey_free(public);
1945e4a9863fSDag-Erling Smørgrav 		free(out);
194619261079SEd Maste 		if (cert_serial_autoinc)
194719261079SEd Maste 			cert_serial++;
1948b15c8340SDag-Erling Smørgrav 	}
194919261079SEd Maste 	if (pin != NULL)
195019261079SEd Maste 		freezero(pin, strlen(pin));
195119261079SEd Maste 	free(ca_fp);
1952a0ee8cc6SDag-Erling Smørgrav #ifdef ENABLE_PKCS11
1953e2f6069cSDag-Erling Smørgrav 	pkcs11_terminate();
1954a0ee8cc6SDag-Erling Smørgrav #endif
1955b15c8340SDag-Erling Smørgrav 	exit(0);
1956b15c8340SDag-Erling Smørgrav }
1957b15c8340SDag-Erling Smørgrav 
1958b15c8340SDag-Erling Smørgrav static u_int64_t
parse_relative_time(const char * s,time_t now)1959b15c8340SDag-Erling Smørgrav parse_relative_time(const char *s, time_t now)
1960b15c8340SDag-Erling Smørgrav {
1961b15c8340SDag-Erling Smørgrav 	int64_t mul, secs;
1962b15c8340SDag-Erling Smørgrav 
1963b15c8340SDag-Erling Smørgrav 	mul = *s == '-' ? -1 : 1;
1964b15c8340SDag-Erling Smørgrav 
1965b15c8340SDag-Erling Smørgrav 	if ((secs = convtime(s + 1)) == -1)
1966b15c8340SDag-Erling Smørgrav 		fatal("Invalid relative certificate time %s", s);
1967b15c8340SDag-Erling Smørgrav 	if (mul == -1 && secs > now)
1968b15c8340SDag-Erling Smørgrav 		fatal("Certificate time %s cannot be represented", s);
1969b15c8340SDag-Erling Smørgrav 	return now + (u_int64_t)(secs * mul);
1970b15c8340SDag-Erling Smørgrav }
1971b15c8340SDag-Erling Smørgrav 
1972b15c8340SDag-Erling Smørgrav static void
parse_hex_u64(const char * s,uint64_t * up)197338a52bd3SEd Maste parse_hex_u64(const char *s, uint64_t *up)
197438a52bd3SEd Maste {
197538a52bd3SEd Maste 	char *ep;
197638a52bd3SEd Maste 	unsigned long long ull;
197738a52bd3SEd Maste 
197838a52bd3SEd Maste 	errno = 0;
197938a52bd3SEd Maste 	ull = strtoull(s, &ep, 16);
198038a52bd3SEd Maste 	if (*s == '\0' || *ep != '\0')
198138a52bd3SEd Maste 		fatal("Invalid certificate time: not a number");
198238a52bd3SEd Maste 	if (errno == ERANGE && ull == ULONG_MAX)
198338a52bd3SEd Maste 		fatal_fr(SSH_ERR_SYSTEM_ERROR, "Invalid certificate time");
198438a52bd3SEd Maste 	*up = (uint64_t)ull;
198538a52bd3SEd Maste }
198638a52bd3SEd Maste 
198738a52bd3SEd Maste static void
parse_cert_times(char * timespec)1988b15c8340SDag-Erling Smørgrav parse_cert_times(char *timespec)
1989b15c8340SDag-Erling Smørgrav {
1990b15c8340SDag-Erling Smørgrav 	char *from, *to;
1991b15c8340SDag-Erling Smørgrav 	time_t now = time(NULL);
1992b15c8340SDag-Erling Smørgrav 	int64_t secs;
1993b15c8340SDag-Erling Smørgrav 
1994b15c8340SDag-Erling Smørgrav 	/* +timespec relative to now */
1995b15c8340SDag-Erling Smørgrav 	if (*timespec == '+' && strchr(timespec, ':') == NULL) {
1996b15c8340SDag-Erling Smørgrav 		if ((secs = convtime(timespec + 1)) == -1)
1997b15c8340SDag-Erling Smørgrav 			fatal("Invalid relative certificate life %s", timespec);
1998b15c8340SDag-Erling Smørgrav 		cert_valid_to = now + secs;
1999b15c8340SDag-Erling Smørgrav 		/*
2000b15c8340SDag-Erling Smørgrav 		 * Backdate certificate one minute to avoid problems on hosts
2001b15c8340SDag-Erling Smørgrav 		 * with poorly-synchronised clocks.
2002b15c8340SDag-Erling Smørgrav 		 */
2003b15c8340SDag-Erling Smørgrav 		cert_valid_from = ((now - 59)/ 60) * 60;
2004b15c8340SDag-Erling Smørgrav 		return;
2005b15c8340SDag-Erling Smørgrav 	}
2006b15c8340SDag-Erling Smørgrav 
2007b15c8340SDag-Erling Smørgrav 	/*
2008b15c8340SDag-Erling Smørgrav 	 * from:to, where
200938a52bd3SEd Maste 	 * from := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS | 0x... | "always"
201038a52bd3SEd Maste 	 *   to := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS | 0x... | "forever"
2011b15c8340SDag-Erling Smørgrav 	 */
2012b15c8340SDag-Erling Smørgrav 	from = xstrdup(timespec);
2013b15c8340SDag-Erling Smørgrav 	to = strchr(from, ':');
2014b15c8340SDag-Erling Smørgrav 	if (to == NULL || from == to || *(to + 1) == '\0')
2015b15c8340SDag-Erling Smørgrav 		fatal("Invalid certificate life specification %s", timespec);
2016b15c8340SDag-Erling Smørgrav 	*to++ = '\0';
2017b15c8340SDag-Erling Smørgrav 
2018b15c8340SDag-Erling Smørgrav 	if (*from == '-' || *from == '+')
2019b15c8340SDag-Erling Smørgrav 		cert_valid_from = parse_relative_time(from, now);
202047dd1d1bSDag-Erling Smørgrav 	else if (strcmp(from, "always") == 0)
202147dd1d1bSDag-Erling Smørgrav 		cert_valid_from = 0;
202238a52bd3SEd Maste 	else if (strncmp(from, "0x", 2) == 0)
202338a52bd3SEd Maste 		parse_hex_u64(from, &cert_valid_from);
202447dd1d1bSDag-Erling Smørgrav 	else if (parse_absolute_time(from, &cert_valid_from) != 0)
202547dd1d1bSDag-Erling Smørgrav 		fatal("Invalid from time \"%s\"", from);
2026b15c8340SDag-Erling Smørgrav 
2027b15c8340SDag-Erling Smørgrav 	if (*to == '-' || *to == '+')
2028f7167e0eSDag-Erling Smørgrav 		cert_valid_to = parse_relative_time(to, now);
202947dd1d1bSDag-Erling Smørgrav 	else if (strcmp(to, "forever") == 0)
203047dd1d1bSDag-Erling Smørgrav 		cert_valid_to = ~(u_int64_t)0;
20310657b232SEd Maste 	else if (strncmp(to, "0x", 2) == 0)
203238a52bd3SEd Maste 		parse_hex_u64(to, &cert_valid_to);
203347dd1d1bSDag-Erling Smørgrav 	else if (parse_absolute_time(to, &cert_valid_to) != 0)
203447dd1d1bSDag-Erling Smørgrav 		fatal("Invalid to time \"%s\"", to);
2035b15c8340SDag-Erling Smørgrav 
2036b15c8340SDag-Erling Smørgrav 	if (cert_valid_to <= cert_valid_from)
2037b15c8340SDag-Erling Smørgrav 		fatal("Empty certificate validity interval");
2038e4a9863fSDag-Erling Smørgrav 	free(from);
2039b15c8340SDag-Erling Smørgrav }
2040b15c8340SDag-Erling Smørgrav 
2041b15c8340SDag-Erling Smørgrav static void
add_cert_option(char * opt)2042e2f6069cSDag-Erling Smørgrav add_cert_option(char *opt)
2043b15c8340SDag-Erling Smørgrav {
20444f52dfbbSDag-Erling Smørgrav 	char *val, *cp;
20454f52dfbbSDag-Erling Smørgrav 	int iscrit = 0;
2046b15c8340SDag-Erling Smørgrav 
2047e146993eSDag-Erling Smørgrav 	if (strcasecmp(opt, "clear") == 0)
2048e2f6069cSDag-Erling Smørgrav 		certflags_flags = 0;
2049b15c8340SDag-Erling Smørgrav 	else if (strcasecmp(opt, "no-x11-forwarding") == 0)
2050e2f6069cSDag-Erling Smørgrav 		certflags_flags &= ~CERTOPT_X_FWD;
2051b15c8340SDag-Erling Smørgrav 	else if (strcasecmp(opt, "permit-x11-forwarding") == 0)
2052e2f6069cSDag-Erling Smørgrav 		certflags_flags |= CERTOPT_X_FWD;
2053b15c8340SDag-Erling Smørgrav 	else if (strcasecmp(opt, "no-agent-forwarding") == 0)
2054e2f6069cSDag-Erling Smørgrav 		certflags_flags &= ~CERTOPT_AGENT_FWD;
2055b15c8340SDag-Erling Smørgrav 	else if (strcasecmp(opt, "permit-agent-forwarding") == 0)
2056e2f6069cSDag-Erling Smørgrav 		certflags_flags |= CERTOPT_AGENT_FWD;
2057b15c8340SDag-Erling Smørgrav 	else if (strcasecmp(opt, "no-port-forwarding") == 0)
2058e2f6069cSDag-Erling Smørgrav 		certflags_flags &= ~CERTOPT_PORT_FWD;
2059b15c8340SDag-Erling Smørgrav 	else if (strcasecmp(opt, "permit-port-forwarding") == 0)
2060e2f6069cSDag-Erling Smørgrav 		certflags_flags |= CERTOPT_PORT_FWD;
2061b15c8340SDag-Erling Smørgrav 	else if (strcasecmp(opt, "no-pty") == 0)
2062e2f6069cSDag-Erling Smørgrav 		certflags_flags &= ~CERTOPT_PTY;
2063b15c8340SDag-Erling Smørgrav 	else if (strcasecmp(opt, "permit-pty") == 0)
2064e2f6069cSDag-Erling Smørgrav 		certflags_flags |= CERTOPT_PTY;
2065b15c8340SDag-Erling Smørgrav 	else if (strcasecmp(opt, "no-user-rc") == 0)
2066e2f6069cSDag-Erling Smørgrav 		certflags_flags &= ~CERTOPT_USER_RC;
2067b15c8340SDag-Erling Smørgrav 	else if (strcasecmp(opt, "permit-user-rc") == 0)
2068e2f6069cSDag-Erling Smørgrav 		certflags_flags |= CERTOPT_USER_RC;
206919261079SEd Maste 	else if (strcasecmp(opt, "touch-required") == 0)
207019261079SEd Maste 		certflags_flags &= ~CERTOPT_NO_REQUIRE_USER_PRESENCE;
207119261079SEd Maste 	else if (strcasecmp(opt, "no-touch-required") == 0)
207219261079SEd Maste 		certflags_flags |= CERTOPT_NO_REQUIRE_USER_PRESENCE;
207338a52bd3SEd Maste 	else if (strcasecmp(opt, "no-verify-required") == 0)
207438a52bd3SEd Maste 		certflags_flags &= ~CERTOPT_REQUIRE_VERIFY;
207538a52bd3SEd Maste 	else if (strcasecmp(opt, "verify-required") == 0)
207638a52bd3SEd Maste 		certflags_flags |= CERTOPT_REQUIRE_VERIFY;
2077b15c8340SDag-Erling Smørgrav 	else if (strncasecmp(opt, "force-command=", 14) == 0) {
2078b15c8340SDag-Erling Smørgrav 		val = opt + 14;
2079b15c8340SDag-Erling Smørgrav 		if (*val == '\0')
2080e2f6069cSDag-Erling Smørgrav 			fatal("Empty force-command option");
2081e2f6069cSDag-Erling Smørgrav 		if (certflags_command != NULL)
2082b15c8340SDag-Erling Smørgrav 			fatal("force-command already specified");
2083e2f6069cSDag-Erling Smørgrav 		certflags_command = xstrdup(val);
2084b15c8340SDag-Erling Smørgrav 	} else if (strncasecmp(opt, "source-address=", 15) == 0) {
2085b15c8340SDag-Erling Smørgrav 		val = opt + 15;
2086b15c8340SDag-Erling Smørgrav 		if (*val == '\0')
2087e2f6069cSDag-Erling Smørgrav 			fatal("Empty source-address option");
2088e2f6069cSDag-Erling Smørgrav 		if (certflags_src_addr != NULL)
2089b15c8340SDag-Erling Smørgrav 			fatal("source-address already specified");
2090b15c8340SDag-Erling Smørgrav 		if (addr_match_cidr_list(NULL, val) != 0)
2091b15c8340SDag-Erling Smørgrav 			fatal("Invalid source-address list");
2092e2f6069cSDag-Erling Smørgrav 		certflags_src_addr = xstrdup(val);
20934f52dfbbSDag-Erling Smørgrav 	} else if (strncasecmp(opt, "extension:", 10) == 0 ||
20944f52dfbbSDag-Erling Smørgrav 		    (iscrit = (strncasecmp(opt, "critical:", 9) == 0))) {
20954f52dfbbSDag-Erling Smørgrav 		val = xstrdup(strchr(opt, ':') + 1);
20964f52dfbbSDag-Erling Smørgrav 		if ((cp = strchr(val, '=')) != NULL)
20974f52dfbbSDag-Erling Smørgrav 			*cp++ = '\0';
209819261079SEd Maste 		cert_ext_add(val, cp, iscrit);
209919261079SEd Maste 		free(val);
2100b15c8340SDag-Erling Smørgrav 	} else
2101e2f6069cSDag-Erling Smørgrav 		fatal("Unsupported certificate option \"%s\"", opt);
2102e2f6069cSDag-Erling Smørgrav }
2103e2f6069cSDag-Erling Smørgrav 
2104e2f6069cSDag-Erling Smørgrav static void
show_options(struct sshbuf * optbuf,int in_critical)2105eccfee6eSDag-Erling Smørgrav show_options(struct sshbuf *optbuf, int in_critical)
2106e2f6069cSDag-Erling Smørgrav {
210719261079SEd Maste 	char *name, *arg, *hex;
2108bc5531deSDag-Erling Smørgrav 	struct sshbuf *options, *option = NULL;
2109bc5531deSDag-Erling Smørgrav 	int r;
2110e2f6069cSDag-Erling Smørgrav 
2111bc5531deSDag-Erling Smørgrav 	if ((options = sshbuf_fromb(optbuf)) == NULL)
211219261079SEd Maste 		fatal_f("sshbuf_fromb failed");
2113bc5531deSDag-Erling Smørgrav 	while (sshbuf_len(options) != 0) {
2114bc5531deSDag-Erling Smørgrav 		sshbuf_free(option);
2115bc5531deSDag-Erling Smørgrav 		option = NULL;
2116bc5531deSDag-Erling Smørgrav 		if ((r = sshbuf_get_cstring(options, &name, NULL)) != 0 ||
2117bc5531deSDag-Erling Smørgrav 		    (r = sshbuf_froms(options, &option)) != 0)
211819261079SEd Maste 			fatal_fr(r, "parse option");
2119e2f6069cSDag-Erling Smørgrav 		printf("                %s", name);
2120eccfee6eSDag-Erling Smørgrav 		if (!in_critical &&
2121e2f6069cSDag-Erling Smørgrav 		    (strcmp(name, "permit-X11-forwarding") == 0 ||
2122e2f6069cSDag-Erling Smørgrav 		    strcmp(name, "permit-agent-forwarding") == 0 ||
2123e2f6069cSDag-Erling Smørgrav 		    strcmp(name, "permit-port-forwarding") == 0 ||
2124e2f6069cSDag-Erling Smørgrav 		    strcmp(name, "permit-pty") == 0 ||
212519261079SEd Maste 		    strcmp(name, "permit-user-rc") == 0 ||
212619261079SEd Maste 		    strcmp(name, "no-touch-required") == 0)) {
2127e2f6069cSDag-Erling Smørgrav 			printf("\n");
212819261079SEd Maste 		} else if (in_critical &&
2129e2f6069cSDag-Erling Smørgrav 		    (strcmp(name, "force-command") == 0 ||
2130e2f6069cSDag-Erling Smørgrav 		    strcmp(name, "source-address") == 0)) {
2131bc5531deSDag-Erling Smørgrav 			if ((r = sshbuf_get_cstring(option, &arg, NULL)) != 0)
213219261079SEd Maste 				fatal_fr(r, "parse critical");
2133a0ee8cc6SDag-Erling Smørgrav 			printf(" %s\n", arg);
2134a0ee8cc6SDag-Erling Smørgrav 			free(arg);
213538a52bd3SEd Maste 		} else if (in_critical &&
213638a52bd3SEd Maste 		    strcmp(name, "verify-required") == 0) {
213738a52bd3SEd Maste 			printf("\n");
213819261079SEd Maste 		} else if (sshbuf_len(option) > 0) {
213919261079SEd Maste 			hex = sshbuf_dtob16(option);
214019261079SEd Maste 			printf(" UNKNOWN OPTION: %s (len %zu)\n",
214119261079SEd Maste 			    hex, sshbuf_len(option));
2142bc5531deSDag-Erling Smørgrav 			sshbuf_reset(option);
214319261079SEd Maste 			free(hex);
214419261079SEd Maste 		} else
214519261079SEd Maste 			printf(" UNKNOWN FLAG OPTION\n");
2146e4a9863fSDag-Erling Smørgrav 		free(name);
2147bc5531deSDag-Erling Smørgrav 		if (sshbuf_len(option) != 0)
2148e2f6069cSDag-Erling Smørgrav 			fatal("Option corrupt: extra data at end");
2149e2f6069cSDag-Erling Smørgrav 	}
2150bc5531deSDag-Erling Smørgrav 	sshbuf_free(option);
2151bc5531deSDag-Erling Smørgrav 	sshbuf_free(options);
2152b15c8340SDag-Erling Smørgrav }
2153b15c8340SDag-Erling Smørgrav 
2154b15c8340SDag-Erling Smørgrav static void
print_cert(struct sshkey * key)2155acc1a9efSDag-Erling Smørgrav print_cert(struct sshkey *key)
2156b15c8340SDag-Erling Smørgrav {
2157acc1a9efSDag-Erling Smørgrav 	char valid[64], *key_fp, *ca_fp;
2158eccfee6eSDag-Erling Smørgrav 	u_int i;
2159b15c8340SDag-Erling Smørgrav 
2160bc5531deSDag-Erling Smørgrav 	key_fp = sshkey_fingerprint(key, fingerprint_hash, SSH_FP_DEFAULT);
2161bc5531deSDag-Erling Smørgrav 	ca_fp = sshkey_fingerprint(key->cert->signature_key,
2162bc5531deSDag-Erling Smørgrav 	    fingerprint_hash, SSH_FP_DEFAULT);
2163bc5531deSDag-Erling Smørgrav 	if (key_fp == NULL || ca_fp == NULL)
216419261079SEd Maste 		fatal_f("sshkey_fingerprint fail");
2165acc1a9efSDag-Erling Smørgrav 	sshkey_format_cert_validity(key->cert, valid, sizeof(valid));
2166b15c8340SDag-Erling Smørgrav 
2167bc5531deSDag-Erling Smørgrav 	printf("        Type: %s %s certificate\n", sshkey_ssh_name(key),
2168bc5531deSDag-Erling Smørgrav 	    sshkey_cert_type(key));
2169bc5531deSDag-Erling Smørgrav 	printf("        Public key: %s %s\n", sshkey_type(key), key_fp);
217019261079SEd Maste 	printf("        Signing CA: %s %s (using %s)\n",
217119261079SEd Maste 	    sshkey_type(key->cert->signature_key), ca_fp,
217219261079SEd Maste 	    key->cert->signature_type);
2173e2f6069cSDag-Erling Smørgrav 	printf("        Key ID: \"%s\"\n", key->cert->key_id);
2174eccfee6eSDag-Erling Smørgrav 	printf("        Serial: %llu\n", (unsigned long long)key->cert->serial);
2175acc1a9efSDag-Erling Smørgrav 	printf("        Valid: %s\n", valid);
2176b15c8340SDag-Erling Smørgrav 	printf("        Principals: ");
2177b15c8340SDag-Erling Smørgrav 	if (key->cert->nprincipals == 0)
2178b15c8340SDag-Erling Smørgrav 		printf("(none)\n");
2179b15c8340SDag-Erling Smørgrav 	else {
2180b15c8340SDag-Erling Smørgrav 		for (i = 0; i < key->cert->nprincipals; i++)
2181b15c8340SDag-Erling Smørgrav 			printf("\n                %s",
2182b15c8340SDag-Erling Smørgrav 			    key->cert->principals[i]);
2183b15c8340SDag-Erling Smørgrav 		printf("\n");
2184b15c8340SDag-Erling Smørgrav 	}
2185e2f6069cSDag-Erling Smørgrav 	printf("        Critical Options: ");
2186bc5531deSDag-Erling Smørgrav 	if (sshbuf_len(key->cert->critical) == 0)
2187b15c8340SDag-Erling Smørgrav 		printf("(none)\n");
2188b15c8340SDag-Erling Smørgrav 	else {
2189b15c8340SDag-Erling Smørgrav 		printf("\n");
2190eccfee6eSDag-Erling Smørgrav 		show_options(key->cert->critical, 1);
2191e2f6069cSDag-Erling Smørgrav 	}
2192e2f6069cSDag-Erling Smørgrav 	printf("        Extensions: ");
2193bc5531deSDag-Erling Smørgrav 	if (sshbuf_len(key->cert->extensions) == 0)
2194e2f6069cSDag-Erling Smørgrav 		printf("(none)\n");
2195e2f6069cSDag-Erling Smørgrav 	else {
2196b15c8340SDag-Erling Smørgrav 		printf("\n");
2197eccfee6eSDag-Erling Smørgrav 		show_options(key->cert->extensions, 0);
2198b15c8340SDag-Erling Smørgrav 	}
2199acc1a9efSDag-Erling Smørgrav }
2200acc1a9efSDag-Erling Smørgrav 
2201acc1a9efSDag-Erling Smørgrav static void
do_show_cert(struct passwd * pw)2202acc1a9efSDag-Erling Smørgrav do_show_cert(struct passwd *pw)
2203acc1a9efSDag-Erling Smørgrav {
2204acc1a9efSDag-Erling Smørgrav 	struct sshkey *key = NULL;
2205acc1a9efSDag-Erling Smørgrav 	struct stat st;
2206acc1a9efSDag-Erling Smørgrav 	int r, is_stdin = 0, ok = 0;
2207acc1a9efSDag-Erling Smørgrav 	FILE *f;
2208190cef3dSDag-Erling Smørgrav 	char *cp, *line = NULL;
2209acc1a9efSDag-Erling Smørgrav 	const char *path;
2210190cef3dSDag-Erling Smørgrav 	size_t linesize = 0;
2211076ad2f8SDag-Erling Smørgrav 	u_long lnum = 0;
2212acc1a9efSDag-Erling Smørgrav 
2213acc1a9efSDag-Erling Smørgrav 	if (!have_identity)
2214acc1a9efSDag-Erling Smørgrav 		ask_filename(pw, "Enter file in which the key is");
221519261079SEd Maste 	if (strcmp(identity_file, "-") != 0 && stat(identity_file, &st) == -1)
2216acc1a9efSDag-Erling Smørgrav 		fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
2217acc1a9efSDag-Erling Smørgrav 
2218acc1a9efSDag-Erling Smørgrav 	path = identity_file;
2219acc1a9efSDag-Erling Smørgrav 	if (strcmp(path, "-") == 0) {
2220acc1a9efSDag-Erling Smørgrav 		f = stdin;
2221acc1a9efSDag-Erling Smørgrav 		path = "(stdin)";
2222acc1a9efSDag-Erling Smørgrav 		is_stdin = 1;
2223acc1a9efSDag-Erling Smørgrav 	} else if ((f = fopen(identity_file, "r")) == NULL)
2224acc1a9efSDag-Erling Smørgrav 		fatal("fopen %s: %s", identity_file, strerror(errno));
2225acc1a9efSDag-Erling Smørgrav 
2226190cef3dSDag-Erling Smørgrav 	while (getline(&line, &linesize, f) != -1) {
2227190cef3dSDag-Erling Smørgrav 		lnum++;
2228acc1a9efSDag-Erling Smørgrav 		sshkey_free(key);
2229acc1a9efSDag-Erling Smørgrav 		key = NULL;
2230acc1a9efSDag-Erling Smørgrav 		/* Trim leading space and comments */
2231acc1a9efSDag-Erling Smørgrav 		cp = line + strspn(line, " \t");
2232acc1a9efSDag-Erling Smørgrav 		if (*cp == '#' || *cp == '\0')
2233acc1a9efSDag-Erling Smørgrav 			continue;
2234acc1a9efSDag-Erling Smørgrav 		if ((key = sshkey_new(KEY_UNSPEC)) == NULL)
22354f52dfbbSDag-Erling Smørgrav 			fatal("sshkey_new");
2236acc1a9efSDag-Erling Smørgrav 		if ((r = sshkey_read(key, &cp)) != 0) {
223719261079SEd Maste 			error_r(r, "%s:%lu: invalid key", path, lnum);
2238acc1a9efSDag-Erling Smørgrav 			continue;
2239acc1a9efSDag-Erling Smørgrav 		}
2240acc1a9efSDag-Erling Smørgrav 		if (!sshkey_is_cert(key)) {
2241acc1a9efSDag-Erling Smørgrav 			error("%s:%lu is not a certificate", path, lnum);
2242acc1a9efSDag-Erling Smørgrav 			continue;
2243acc1a9efSDag-Erling Smørgrav 		}
2244acc1a9efSDag-Erling Smørgrav 		ok = 1;
2245acc1a9efSDag-Erling Smørgrav 		if (!is_stdin && lnum == 1)
2246acc1a9efSDag-Erling Smørgrav 			printf("%s:\n", path);
2247acc1a9efSDag-Erling Smørgrav 		else
2248acc1a9efSDag-Erling Smørgrav 			printf("%s:%lu:\n", path, lnum);
2249acc1a9efSDag-Erling Smørgrav 		print_cert(key);
2250acc1a9efSDag-Erling Smørgrav 	}
2251190cef3dSDag-Erling Smørgrav 	free(line);
2252acc1a9efSDag-Erling Smørgrav 	sshkey_free(key);
2253acc1a9efSDag-Erling Smørgrav 	fclose(f);
2254acc1a9efSDag-Erling Smørgrav 	exit(ok ? 0 : 1);
2255b15c8340SDag-Erling Smørgrav }
2256b15c8340SDag-Erling Smørgrav 
2257ae1f160dSDag-Erling Smørgrav static void
load_krl(const char * path,struct ssh_krl ** krlp)22586888a9beSDag-Erling Smørgrav load_krl(const char *path, struct ssh_krl **krlp)
22596888a9beSDag-Erling Smørgrav {
2260bc5531deSDag-Erling Smørgrav 	struct sshbuf *krlbuf;
226119261079SEd Maste 	int r;
22626888a9beSDag-Erling Smørgrav 
226319261079SEd Maste 	if ((r = sshbuf_load_file(path, &krlbuf)) != 0)
226419261079SEd Maste 		fatal_r(r, "Unable to load KRL %s", path);
22656888a9beSDag-Erling Smørgrav 	/* XXX check sigs */
2266535af610SEd Maste 	if ((r = ssh_krl_from_blob(krlbuf, krlp)) != 0 ||
22676888a9beSDag-Erling Smørgrav 	    *krlp == NULL)
226819261079SEd Maste 		fatal_r(r, "Invalid KRL file %s", path);
2269bc5531deSDag-Erling Smørgrav 	sshbuf_free(krlbuf);
22706888a9beSDag-Erling Smørgrav }
22716888a9beSDag-Erling Smørgrav 
22726888a9beSDag-Erling Smørgrav static void
hash_to_blob(const char * cp,u_char ** blobp,size_t * lenp,const char * file,u_long lnum)22732f513db7SEd Maste hash_to_blob(const char *cp, u_char **blobp, size_t *lenp,
22742f513db7SEd Maste     const char *file, u_long lnum)
22752f513db7SEd Maste {
22762f513db7SEd Maste 	char *tmp;
22772f513db7SEd Maste 	size_t tlen;
22782f513db7SEd Maste 	struct sshbuf *b;
22792f513db7SEd Maste 	int r;
22802f513db7SEd Maste 
22812f513db7SEd Maste 	if (strncmp(cp, "SHA256:", 7) != 0)
22822f513db7SEd Maste 		fatal("%s:%lu: unsupported hash algorithm", file, lnum);
22832f513db7SEd Maste 	cp += 7;
22842f513db7SEd Maste 
22852f513db7SEd Maste 	/*
22862f513db7SEd Maste 	 * OpenSSH base64 hashes omit trailing '='
22872f513db7SEd Maste 	 * characters; put them back for decode.
22882f513db7SEd Maste 	 */
2289535af610SEd Maste 	if ((tlen = strlen(cp)) >= SIZE_MAX - 5)
2290535af610SEd Maste 		fatal_f("hash too long: %zu bytes", tlen);
22912f513db7SEd Maste 	tmp = xmalloc(tlen + 4 + 1);
22922f513db7SEd Maste 	strlcpy(tmp, cp, tlen + 1);
22932f513db7SEd Maste 	while ((tlen % 4) != 0) {
22942f513db7SEd Maste 		tmp[tlen++] = '=';
22952f513db7SEd Maste 		tmp[tlen] = '\0';
22962f513db7SEd Maste 	}
22972f513db7SEd Maste 	if ((b = sshbuf_new()) == NULL)
229819261079SEd Maste 		fatal_f("sshbuf_new failed");
22992f513db7SEd Maste 	if ((r = sshbuf_b64tod(b, tmp)) != 0)
230019261079SEd Maste 		fatal_r(r, "%s:%lu: decode hash failed", file, lnum);
23012f513db7SEd Maste 	free(tmp);
23022f513db7SEd Maste 	*lenp = sshbuf_len(b);
23032f513db7SEd Maste 	*blobp = xmalloc(*lenp);
23042f513db7SEd Maste 	memcpy(*blobp, sshbuf_ptr(b), *lenp);
23052f513db7SEd Maste 	sshbuf_free(b);
23062f513db7SEd Maste }
23072f513db7SEd Maste 
23082f513db7SEd Maste static void
update_krl_from_file(struct passwd * pw,const char * file,int wild_ca,const struct sshkey * ca,struct ssh_krl * krl)2309bc5531deSDag-Erling Smørgrav update_krl_from_file(struct passwd *pw, const char *file, int wild_ca,
2310bc5531deSDag-Erling Smørgrav     const struct sshkey *ca, struct ssh_krl *krl)
23116888a9beSDag-Erling Smørgrav {
2312bc5531deSDag-Erling Smørgrav 	struct sshkey *key = NULL;
23136888a9beSDag-Erling Smørgrav 	u_long lnum = 0;
2314190cef3dSDag-Erling Smørgrav 	char *path, *cp, *ep, *line = NULL;
23152f513db7SEd Maste 	u_char *blob = NULL;
23162f513db7SEd Maste 	size_t blen = 0, linesize = 0;
23176888a9beSDag-Erling Smørgrav 	unsigned long long serial, serial2;
23182f513db7SEd Maste 	int i, was_explicit_key, was_sha1, was_sha256, was_hash, r;
23196888a9beSDag-Erling Smørgrav 	FILE *krl_spec;
23206888a9beSDag-Erling Smørgrav 
23216888a9beSDag-Erling Smørgrav 	path = tilde_expand_filename(file, pw->pw_uid);
23226888a9beSDag-Erling Smørgrav 	if (strcmp(path, "-") == 0) {
23236888a9beSDag-Erling Smørgrav 		krl_spec = stdin;
23246888a9beSDag-Erling Smørgrav 		free(path);
23256888a9beSDag-Erling Smørgrav 		path = xstrdup("(standard input)");
23266888a9beSDag-Erling Smørgrav 	} else if ((krl_spec = fopen(path, "r")) == NULL)
23276888a9beSDag-Erling Smørgrav 		fatal("fopen %s: %s", path, strerror(errno));
23286888a9beSDag-Erling Smørgrav 
23296888a9beSDag-Erling Smørgrav 	if (!quiet)
23306888a9beSDag-Erling Smørgrav 		printf("Revoking from %s\n", path);
2331190cef3dSDag-Erling Smørgrav 	while (getline(&line, &linesize, krl_spec) != -1) {
2332535af610SEd Maste 		if (linesize >= INT_MAX) {
2333535af610SEd Maste 			fatal_f("%s contains unparsable line, len=%zu",
2334535af610SEd Maste 			    path, linesize);
2335535af610SEd Maste 		}
2336190cef3dSDag-Erling Smørgrav 		lnum++;
23372f513db7SEd Maste 		was_explicit_key = was_sha1 = was_sha256 = was_hash = 0;
23386888a9beSDag-Erling Smørgrav 		cp = line + strspn(line, " \t");
23396888a9beSDag-Erling Smørgrav 		/* Trim trailing space, comments and strip \n */
23406888a9beSDag-Erling Smørgrav 		for (i = 0, r = -1; cp[i] != '\0'; i++) {
23416888a9beSDag-Erling Smørgrav 			if (cp[i] == '#' || cp[i] == '\n') {
23426888a9beSDag-Erling Smørgrav 				cp[i] = '\0';
23436888a9beSDag-Erling Smørgrav 				break;
23446888a9beSDag-Erling Smørgrav 			}
23456888a9beSDag-Erling Smørgrav 			if (cp[i] == ' ' || cp[i] == '\t') {
23466888a9beSDag-Erling Smørgrav 				/* Remember the start of a span of whitespace */
23476888a9beSDag-Erling Smørgrav 				if (r == -1)
23486888a9beSDag-Erling Smørgrav 					r = i;
23496888a9beSDag-Erling Smørgrav 			} else
23506888a9beSDag-Erling Smørgrav 				r = -1;
23516888a9beSDag-Erling Smørgrav 		}
23526888a9beSDag-Erling Smørgrav 		if (r != -1)
23536888a9beSDag-Erling Smørgrav 			cp[r] = '\0';
23546888a9beSDag-Erling Smørgrav 		if (*cp == '\0')
23556888a9beSDag-Erling Smørgrav 			continue;
23566888a9beSDag-Erling Smørgrav 		if (strncasecmp(cp, "serial:", 7) == 0) {
2357bc5531deSDag-Erling Smørgrav 			if (ca == NULL && !wild_ca) {
2358f7167e0eSDag-Erling Smørgrav 				fatal("revoking certificates by serial number "
23596888a9beSDag-Erling Smørgrav 				    "requires specification of a CA key");
23606888a9beSDag-Erling Smørgrav 			}
23616888a9beSDag-Erling Smørgrav 			cp += 7;
23626888a9beSDag-Erling Smørgrav 			cp = cp + strspn(cp, " \t");
23636888a9beSDag-Erling Smørgrav 			errno = 0;
23646888a9beSDag-Erling Smørgrav 			serial = strtoull(cp, &ep, 0);
23656888a9beSDag-Erling Smørgrav 			if (*cp == '\0' || (*ep != '\0' && *ep != '-'))
23666888a9beSDag-Erling Smørgrav 				fatal("%s:%lu: invalid serial \"%s\"",
23676888a9beSDag-Erling Smørgrav 				    path, lnum, cp);
23686888a9beSDag-Erling Smørgrav 			if (errno == ERANGE && serial == ULLONG_MAX)
23696888a9beSDag-Erling Smørgrav 				fatal("%s:%lu: serial out of range",
23706888a9beSDag-Erling Smørgrav 				    path, lnum);
23716888a9beSDag-Erling Smørgrav 			serial2 = serial;
23726888a9beSDag-Erling Smørgrav 			if (*ep == '-') {
23736888a9beSDag-Erling Smørgrav 				cp = ep + 1;
23746888a9beSDag-Erling Smørgrav 				errno = 0;
23756888a9beSDag-Erling Smørgrav 				serial2 = strtoull(cp, &ep, 0);
23766888a9beSDag-Erling Smørgrav 				if (*cp == '\0' || *ep != '\0')
23776888a9beSDag-Erling Smørgrav 					fatal("%s:%lu: invalid serial \"%s\"",
23786888a9beSDag-Erling Smørgrav 					    path, lnum, cp);
23796888a9beSDag-Erling Smørgrav 				if (errno == ERANGE && serial2 == ULLONG_MAX)
23806888a9beSDag-Erling Smørgrav 					fatal("%s:%lu: serial out of range",
23816888a9beSDag-Erling Smørgrav 					    path, lnum);
23826888a9beSDag-Erling Smørgrav 				if (serial2 <= serial)
23836888a9beSDag-Erling Smørgrav 					fatal("%s:%lu: invalid serial range "
23846888a9beSDag-Erling Smørgrav 					    "%llu:%llu", path, lnum,
23856888a9beSDag-Erling Smørgrav 					    (unsigned long long)serial,
23866888a9beSDag-Erling Smørgrav 					    (unsigned long long)serial2);
23876888a9beSDag-Erling Smørgrav 			}
23886888a9beSDag-Erling Smørgrav 			if (ssh_krl_revoke_cert_by_serial_range(krl,
23896888a9beSDag-Erling Smørgrav 			    ca, serial, serial2) != 0) {
239019261079SEd Maste 				fatal_f("revoke serial failed");
23916888a9beSDag-Erling Smørgrav 			}
23926888a9beSDag-Erling Smørgrav 		} else if (strncasecmp(cp, "id:", 3) == 0) {
2393bc5531deSDag-Erling Smørgrav 			if (ca == NULL && !wild_ca) {
2394f7167e0eSDag-Erling Smørgrav 				fatal("revoking certificates by key ID "
23956888a9beSDag-Erling Smørgrav 				    "requires specification of a CA key");
23966888a9beSDag-Erling Smørgrav 			}
23976888a9beSDag-Erling Smørgrav 			cp += 3;
23986888a9beSDag-Erling Smørgrav 			cp = cp + strspn(cp, " \t");
23996888a9beSDag-Erling Smørgrav 			if (ssh_krl_revoke_cert_by_key_id(krl, ca, cp) != 0)
240019261079SEd Maste 				fatal_f("revoke key ID failed");
24012f513db7SEd Maste 		} else if (strncasecmp(cp, "hash:", 5) == 0) {
24022f513db7SEd Maste 			cp += 5;
24032f513db7SEd Maste 			cp = cp + strspn(cp, " \t");
24042f513db7SEd Maste 			hash_to_blob(cp, &blob, &blen, file, lnum);
24052f513db7SEd Maste 			r = ssh_krl_revoke_key_sha256(krl, blob, blen);
240619261079SEd Maste 			if (r != 0)
240719261079SEd Maste 				fatal_fr(r, "revoke key failed");
24086888a9beSDag-Erling Smørgrav 		} else {
24096888a9beSDag-Erling Smørgrav 			if (strncasecmp(cp, "key:", 4) == 0) {
24106888a9beSDag-Erling Smørgrav 				cp += 4;
24116888a9beSDag-Erling Smørgrav 				cp = cp + strspn(cp, " \t");
24126888a9beSDag-Erling Smørgrav 				was_explicit_key = 1;
24136888a9beSDag-Erling Smørgrav 			} else if (strncasecmp(cp, "sha1:", 5) == 0) {
24146888a9beSDag-Erling Smørgrav 				cp += 5;
24156888a9beSDag-Erling Smørgrav 				cp = cp + strspn(cp, " \t");
24166888a9beSDag-Erling Smørgrav 				was_sha1 = 1;
24172f513db7SEd Maste 			} else if (strncasecmp(cp, "sha256:", 7) == 0) {
24182f513db7SEd Maste 				cp += 7;
24192f513db7SEd Maste 				cp = cp + strspn(cp, " \t");
24202f513db7SEd Maste 				was_sha256 = 1;
24216888a9beSDag-Erling Smørgrav 				/*
24226888a9beSDag-Erling Smørgrav 				 * Just try to process the line as a key.
24236888a9beSDag-Erling Smørgrav 				 * Parsing will fail if it isn't.
24246888a9beSDag-Erling Smørgrav 				 */
24256888a9beSDag-Erling Smørgrav 			}
2426bc5531deSDag-Erling Smørgrav 			if ((key = sshkey_new(KEY_UNSPEC)) == NULL)
24274f52dfbbSDag-Erling Smørgrav 				fatal("sshkey_new");
2428bc5531deSDag-Erling Smørgrav 			if ((r = sshkey_read(key, &cp)) != 0)
242919261079SEd Maste 				fatal_r(r, "%s:%lu: invalid key", path, lnum);
24306888a9beSDag-Erling Smørgrav 			if (was_explicit_key)
24316888a9beSDag-Erling Smørgrav 				r = ssh_krl_revoke_key_explicit(krl, key);
24322f513db7SEd Maste 			else if (was_sha1) {
24332f513db7SEd Maste 				if (sshkey_fingerprint_raw(key,
24342f513db7SEd Maste 				    SSH_DIGEST_SHA1, &blob, &blen) != 0) {
24352f513db7SEd Maste 					fatal("%s:%lu: fingerprint failed",
24362f513db7SEd Maste 					    file, lnum);
24372f513db7SEd Maste 				}
24382f513db7SEd Maste 				r = ssh_krl_revoke_key_sha1(krl, blob, blen);
24392f513db7SEd Maste 			} else if (was_sha256) {
24402f513db7SEd Maste 				if (sshkey_fingerprint_raw(key,
24412f513db7SEd Maste 				    SSH_DIGEST_SHA256, &blob, &blen) != 0) {
24422f513db7SEd Maste 					fatal("%s:%lu: fingerprint failed",
24432f513db7SEd Maste 					    file, lnum);
24442f513db7SEd Maste 				}
24452f513db7SEd Maste 				r = ssh_krl_revoke_key_sha256(krl, blob, blen);
24462f513db7SEd Maste 			} else
24476888a9beSDag-Erling Smørgrav 				r = ssh_krl_revoke_key(krl, key);
24486888a9beSDag-Erling Smørgrav 			if (r != 0)
244919261079SEd Maste 				fatal_fr(r, "revoke key failed");
24502f513db7SEd Maste 			freezero(blob, blen);
24512f513db7SEd Maste 			blob = NULL;
24522f513db7SEd Maste 			blen = 0;
2453bc5531deSDag-Erling Smørgrav 			sshkey_free(key);
24546888a9beSDag-Erling Smørgrav 		}
24556888a9beSDag-Erling Smørgrav 	}
24566888a9beSDag-Erling Smørgrav 	if (strcmp(path, "-") != 0)
24576888a9beSDag-Erling Smørgrav 		fclose(krl_spec);
2458190cef3dSDag-Erling Smørgrav 	free(line);
2459e4a9863fSDag-Erling Smørgrav 	free(path);
24606888a9beSDag-Erling Smørgrav }
24616888a9beSDag-Erling Smørgrav 
24626888a9beSDag-Erling Smørgrav 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)246319261079SEd Maste do_gen_krl(struct passwd *pw, int updating, const char *ca_key_path,
246419261079SEd Maste     unsigned long long krl_version, const char *krl_comment,
246519261079SEd Maste     int argc, char **argv)
24666888a9beSDag-Erling Smørgrav {
24676888a9beSDag-Erling Smørgrav 	struct ssh_krl *krl;
24686888a9beSDag-Erling Smørgrav 	struct stat sb;
2469bc5531deSDag-Erling Smørgrav 	struct sshkey *ca = NULL;
247019261079SEd Maste 	int i, r, wild_ca = 0;
24716888a9beSDag-Erling Smørgrav 	char *tmp;
2472bc5531deSDag-Erling Smørgrav 	struct sshbuf *kbuf;
24736888a9beSDag-Erling Smørgrav 
24746888a9beSDag-Erling Smørgrav 	if (*identity_file == '\0')
24756888a9beSDag-Erling Smørgrav 		fatal("KRL generation requires an output file");
24766888a9beSDag-Erling Smørgrav 	if (stat(identity_file, &sb) == -1) {
24776888a9beSDag-Erling Smørgrav 		if (errno != ENOENT)
24786888a9beSDag-Erling Smørgrav 			fatal("Cannot access KRL \"%s\": %s",
24796888a9beSDag-Erling Smørgrav 			    identity_file, strerror(errno));
24806888a9beSDag-Erling Smørgrav 		if (updating)
24816888a9beSDag-Erling Smørgrav 			fatal("KRL \"%s\" does not exist", identity_file);
24826888a9beSDag-Erling Smørgrav 	}
24836888a9beSDag-Erling Smørgrav 	if (ca_key_path != NULL) {
2484bc5531deSDag-Erling Smørgrav 		if (strcasecmp(ca_key_path, "none") == 0)
2485bc5531deSDag-Erling Smørgrav 			wild_ca = 1;
2486bc5531deSDag-Erling Smørgrav 		else {
24876888a9beSDag-Erling Smørgrav 			tmp = tilde_expand_filename(ca_key_path, pw->pw_uid);
2488bc5531deSDag-Erling Smørgrav 			if ((r = sshkey_load_public(tmp, &ca, NULL)) != 0)
248919261079SEd Maste 				fatal_r(r, "Cannot load CA public key %s", tmp);
2490e4a9863fSDag-Erling Smørgrav 			free(tmp);
24916888a9beSDag-Erling Smørgrav 		}
2492bc5531deSDag-Erling Smørgrav 	}
24936888a9beSDag-Erling Smørgrav 
24946888a9beSDag-Erling Smørgrav 	if (updating)
24956888a9beSDag-Erling Smørgrav 		load_krl(identity_file, &krl);
24966888a9beSDag-Erling Smørgrav 	else if ((krl = ssh_krl_init()) == NULL)
24976888a9beSDag-Erling Smørgrav 		fatal("couldn't create KRL");
24986888a9beSDag-Erling Smørgrav 
249919261079SEd Maste 	if (krl_version != 0)
250019261079SEd Maste 		ssh_krl_set_version(krl, krl_version);
250119261079SEd Maste 	if (krl_comment != NULL)
250219261079SEd Maste 		ssh_krl_set_comment(krl, krl_comment);
25036888a9beSDag-Erling Smørgrav 
25046888a9beSDag-Erling Smørgrav 	for (i = 0; i < argc; i++)
2505bc5531deSDag-Erling Smørgrav 		update_krl_from_file(pw, argv[i], wild_ca, ca, krl);
25066888a9beSDag-Erling Smørgrav 
2507bc5531deSDag-Erling Smørgrav 	if ((kbuf = sshbuf_new()) == NULL)
2508bc5531deSDag-Erling Smørgrav 		fatal("sshbuf_new failed");
2509535af610SEd Maste 	if (ssh_krl_to_blob(krl, kbuf) != 0)
25106888a9beSDag-Erling Smørgrav 		fatal("Couldn't generate KRL");
251119261079SEd Maste 	if ((r = sshbuf_write_file(identity_file, kbuf)) != 0)
25126888a9beSDag-Erling Smørgrav 		fatal("write %s: %s", identity_file, strerror(errno));
2513bc5531deSDag-Erling Smørgrav 	sshbuf_free(kbuf);
25146888a9beSDag-Erling Smørgrav 	ssh_krl_free(krl);
2515bc5531deSDag-Erling Smørgrav 	sshkey_free(ca);
25166888a9beSDag-Erling Smørgrav }
25176888a9beSDag-Erling Smørgrav 
25186888a9beSDag-Erling Smørgrav static void
do_check_krl(struct passwd * pw,int print_krl,int argc,char ** argv)251919261079SEd Maste do_check_krl(struct passwd *pw, int print_krl, int argc, char **argv)
25206888a9beSDag-Erling Smørgrav {
25216888a9beSDag-Erling Smørgrav 	int i, r, ret = 0;
25226888a9beSDag-Erling Smørgrav 	char *comment;
25236888a9beSDag-Erling Smørgrav 	struct ssh_krl *krl;
2524bc5531deSDag-Erling Smørgrav 	struct sshkey *k;
25256888a9beSDag-Erling Smørgrav 
25266888a9beSDag-Erling Smørgrav 	if (*identity_file == '\0')
25276888a9beSDag-Erling Smørgrav 		fatal("KRL checking requires an input file");
25286888a9beSDag-Erling Smørgrav 	load_krl(identity_file, &krl);
252919261079SEd Maste 	if (print_krl)
253019261079SEd Maste 		krl_dump(krl, stdout);
25316888a9beSDag-Erling Smørgrav 	for (i = 0; i < argc; i++) {
2532bc5531deSDag-Erling Smørgrav 		if ((r = sshkey_load_public(argv[i], &k, &comment)) != 0)
253319261079SEd Maste 			fatal_r(r, "Cannot load public key %s", argv[i]);
25346888a9beSDag-Erling Smørgrav 		r = ssh_krl_check_key(krl, k);
25356888a9beSDag-Erling Smørgrav 		printf("%s%s%s%s: %s\n", argv[i],
25366888a9beSDag-Erling Smørgrav 		    *comment ? " (" : "", comment, *comment ? ")" : "",
25376888a9beSDag-Erling Smørgrav 		    r == 0 ? "ok" : "REVOKED");
25386888a9beSDag-Erling Smørgrav 		if (r != 0)
25396888a9beSDag-Erling Smørgrav 			ret = 1;
2540bc5531deSDag-Erling Smørgrav 		sshkey_free(k);
25416888a9beSDag-Erling Smørgrav 		free(comment);
25426888a9beSDag-Erling Smørgrav 	}
25436888a9beSDag-Erling Smørgrav 	ssh_krl_free(krl);
25446888a9beSDag-Erling Smørgrav 	exit(ret);
25456888a9beSDag-Erling Smørgrav }
25466888a9beSDag-Erling Smørgrav 
254719261079SEd Maste static struct sshkey *
load_sign_key(const char * keypath,const struct sshkey * pubkey)254819261079SEd Maste load_sign_key(const char *keypath, const struct sshkey *pubkey)
254919261079SEd Maste {
255019261079SEd Maste 	size_t i, slen, plen = strlen(keypath);
255119261079SEd Maste 	char *privpath = xstrdup(keypath);
25521323ec57SEd Maste 	static const char * const suffixes[] = { "-cert.pub", ".pub", NULL };
255319261079SEd Maste 	struct sshkey *ret = NULL, *privkey = NULL;
255438a52bd3SEd Maste 	int r, waspub = 0;
255538a52bd3SEd Maste 	struct stat st;
255619261079SEd Maste 
255719261079SEd Maste 	/*
255819261079SEd Maste 	 * If passed a public key filename, then try to locate the corresponding
255919261079SEd Maste 	 * private key. This lets us specify certificates on the command-line
256019261079SEd Maste 	 * and have ssh-keygen find the appropriate private key.
256119261079SEd Maste 	 */
256219261079SEd Maste 	for (i = 0; suffixes[i]; i++) {
256319261079SEd Maste 		slen = strlen(suffixes[i]);
256419261079SEd Maste 		if (plen <= slen ||
256519261079SEd Maste 		    strcmp(privpath + plen - slen, suffixes[i]) != 0)
256619261079SEd Maste 			continue;
256719261079SEd Maste 		privpath[plen - slen] = '\0';
256819261079SEd Maste 		debug_f("%s looks like a public key, using private key "
256919261079SEd Maste 		    "path %s instead", keypath, privpath);
257038a52bd3SEd Maste 		waspub = 1;
257119261079SEd Maste 	}
257238a52bd3SEd Maste 	if (waspub && stat(privpath, &st) != 0 && errno == ENOENT)
257338a52bd3SEd Maste 		fatal("No private key found for public key \"%s\"", keypath);
257438a52bd3SEd Maste 	if ((r = sshkey_load_private(privpath, "", &privkey, NULL)) != 0 &&
257538a52bd3SEd Maste 	    (r != SSH_ERR_KEY_WRONG_PASSPHRASE)) {
257638a52bd3SEd Maste 		debug_fr(r, "load private key \"%s\"", privpath);
257738a52bd3SEd Maste 		fatal("No private key found for \"%s\"", privpath);
257838a52bd3SEd Maste 	} else if (privkey == NULL)
257938a52bd3SEd Maste 		privkey = load_identity(privpath, NULL);
258038a52bd3SEd Maste 
258119261079SEd Maste 	if (!sshkey_equal_public(pubkey, privkey)) {
258219261079SEd Maste 		error("Public key %s doesn't match private %s",
258319261079SEd Maste 		    keypath, privpath);
258419261079SEd Maste 		goto done;
258519261079SEd Maste 	}
258619261079SEd Maste 	if (sshkey_is_cert(pubkey) && !sshkey_is_cert(privkey)) {
258719261079SEd Maste 		/*
258819261079SEd Maste 		 * Graft the certificate onto the private key to make
258919261079SEd Maste 		 * it capable of signing.
259019261079SEd Maste 		 */
259119261079SEd Maste 		if ((r = sshkey_to_certified(privkey)) != 0) {
259219261079SEd Maste 			error_fr(r, "sshkey_to_certified");
259319261079SEd Maste 			goto done;
259419261079SEd Maste 		}
259519261079SEd Maste 		if ((r = sshkey_cert_copy(pubkey, privkey)) != 0) {
259619261079SEd Maste 			error_fr(r, "sshkey_cert_copy");
259719261079SEd Maste 			goto done;
259819261079SEd Maste 		}
259919261079SEd Maste 	}
260019261079SEd Maste 	/* success */
260119261079SEd Maste 	ret = privkey;
260219261079SEd Maste 	privkey = NULL;
260319261079SEd Maste  done:
260419261079SEd Maste 	sshkey_free(privkey);
260519261079SEd Maste 	free(privpath);
260619261079SEd Maste 	return ret;
260719261079SEd Maste }
260819261079SEd Maste 
260919261079SEd Maste 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)261019261079SEd Maste sign_one(struct sshkey *signkey, const char *filename, int fd,
26111323ec57SEd Maste     const char *sig_namespace, const char *hashalg, sshsig_signer *signer,
26121323ec57SEd Maste     void *signer_ctx)
261319261079SEd Maste {
261419261079SEd Maste 	struct sshbuf *sigbuf = NULL, *abuf = NULL;
261519261079SEd Maste 	int r = SSH_ERR_INTERNAL_ERROR, wfd = -1, oerrno;
261619261079SEd Maste 	char *wfile = NULL, *asig = NULL, *fp = NULL;
261719261079SEd Maste 	char *pin = NULL, *prompt = NULL;
261819261079SEd Maste 
261919261079SEd Maste 	if (!quiet) {
262019261079SEd Maste 		if (fd == STDIN_FILENO)
262119261079SEd Maste 			fprintf(stderr, "Signing data on standard input\n");
262219261079SEd Maste 		else
262319261079SEd Maste 			fprintf(stderr, "Signing file %s\n", filename);
262419261079SEd Maste 	}
262519261079SEd Maste 	if (signer == NULL && sshkey_is_sk(signkey)) {
262619261079SEd Maste 		if ((signkey->sk_flags & SSH_SK_USER_VERIFICATION_REQD)) {
262719261079SEd Maste 			xasprintf(&prompt, "Enter PIN for %s key: ",
262819261079SEd Maste 			    sshkey_type(signkey));
262919261079SEd Maste 			if ((pin = read_passphrase(prompt,
263019261079SEd Maste 			    RP_ALLOW_STDIN)) == NULL)
263119261079SEd Maste 				fatal_f("couldn't read PIN");
263219261079SEd Maste 		}
263319261079SEd Maste 		if ((signkey->sk_flags & SSH_SK_USER_PRESENCE_REQD)) {
263419261079SEd Maste 			if ((fp = sshkey_fingerprint(signkey, fingerprint_hash,
263519261079SEd Maste 			    SSH_FP_DEFAULT)) == NULL)
263619261079SEd Maste 				fatal_f("fingerprint failed");
263719261079SEd Maste 			fprintf(stderr, "Confirm user presence for key %s %s\n",
263819261079SEd Maste 			    sshkey_type(signkey), fp);
263919261079SEd Maste 			free(fp);
264019261079SEd Maste 		}
264119261079SEd Maste 	}
26421323ec57SEd Maste 	if ((r = sshsig_sign_fd(signkey, hashalg, sk_provider, pin,
264319261079SEd Maste 	    fd, sig_namespace, &sigbuf, signer, signer_ctx)) != 0) {
264419261079SEd Maste 		error_r(r, "Signing %s failed", filename);
264519261079SEd Maste 		goto out;
264619261079SEd Maste 	}
264719261079SEd Maste 	if ((r = sshsig_armor(sigbuf, &abuf)) != 0) {
264819261079SEd Maste 		error_fr(r, "sshsig_armor");
264919261079SEd Maste 		goto out;
265019261079SEd Maste 	}
265119261079SEd Maste 	if ((asig = sshbuf_dup_string(abuf)) == NULL) {
265219261079SEd Maste 		error_f("buffer error");
265319261079SEd Maste 		r = SSH_ERR_ALLOC_FAIL;
265419261079SEd Maste 		goto out;
265519261079SEd Maste 	}
265619261079SEd Maste 
265719261079SEd Maste 	if (fd == STDIN_FILENO) {
265819261079SEd Maste 		fputs(asig, stdout);
265919261079SEd Maste 		fflush(stdout);
266019261079SEd Maste 	} else {
266119261079SEd Maste 		xasprintf(&wfile, "%s.sig", filename);
266219261079SEd Maste 		if (confirm_overwrite(wfile)) {
266319261079SEd Maste 			if ((wfd = open(wfile, O_WRONLY|O_CREAT|O_TRUNC,
266419261079SEd Maste 			    0666)) == -1) {
266519261079SEd Maste 				oerrno = errno;
266619261079SEd Maste 				error("Cannot open %s: %s",
266719261079SEd Maste 				    wfile, strerror(errno));
266819261079SEd Maste 				errno = oerrno;
266919261079SEd Maste 				r = SSH_ERR_SYSTEM_ERROR;
267019261079SEd Maste 				goto out;
267119261079SEd Maste 			}
267219261079SEd Maste 			if (atomicio(vwrite, wfd, asig,
267319261079SEd Maste 			    strlen(asig)) != strlen(asig)) {
267419261079SEd Maste 				oerrno = errno;
267519261079SEd Maste 				error("Cannot write to %s: %s",
267619261079SEd Maste 				    wfile, strerror(errno));
267719261079SEd Maste 				errno = oerrno;
267819261079SEd Maste 				r = SSH_ERR_SYSTEM_ERROR;
267919261079SEd Maste 				goto out;
268019261079SEd Maste 			}
268119261079SEd Maste 			if (!quiet) {
268219261079SEd Maste 				fprintf(stderr, "Write signature to %s\n",
268319261079SEd Maste 				    wfile);
268419261079SEd Maste 			}
268519261079SEd Maste 		}
268619261079SEd Maste 	}
268719261079SEd Maste 	/* success */
268819261079SEd Maste 	r = 0;
268919261079SEd Maste  out:
269019261079SEd Maste 	free(wfile);
269119261079SEd Maste 	free(prompt);
269219261079SEd Maste 	free(asig);
269319261079SEd Maste 	if (pin != NULL)
269419261079SEd Maste 		freezero(pin, strlen(pin));
269519261079SEd Maste 	sshbuf_free(abuf);
269619261079SEd Maste 	sshbuf_free(sigbuf);
269719261079SEd Maste 	if (wfd != -1)
269819261079SEd Maste 		close(wfd);
269919261079SEd Maste 	return r;
270019261079SEd Maste }
270119261079SEd Maste 
270219261079SEd Maste static int
sig_process_opts(char * const * opts,size_t nopts,char ** hashalgp,uint64_t * verify_timep,int * print_pubkey)27031323ec57SEd Maste sig_process_opts(char * const *opts, size_t nopts, char **hashalgp,
27041323ec57SEd Maste     uint64_t *verify_timep, int *print_pubkey)
27051323ec57SEd Maste {
27061323ec57SEd Maste 	size_t i;
27071323ec57SEd Maste 	time_t now;
27081323ec57SEd Maste 
27091323ec57SEd Maste 	if (verify_timep != NULL)
27101323ec57SEd Maste 		*verify_timep = 0;
27111323ec57SEd Maste 	if (print_pubkey != NULL)
27121323ec57SEd Maste 		*print_pubkey = 0;
27131323ec57SEd Maste 	if (hashalgp != NULL)
27141323ec57SEd Maste 		*hashalgp = NULL;
27151323ec57SEd Maste 	for (i = 0; i < nopts; i++) {
27161323ec57SEd Maste 		if (hashalgp != NULL &&
27171323ec57SEd Maste 		    strncasecmp(opts[i], "hashalg=", 8) == 0) {
27181323ec57SEd Maste 			*hashalgp = xstrdup(opts[i] + 8);
27191323ec57SEd Maste 		} else if (verify_timep &&
27201323ec57SEd Maste 		    strncasecmp(opts[i], "verify-time=", 12) == 0) {
27211323ec57SEd Maste 			if (parse_absolute_time(opts[i] + 12,
27221323ec57SEd Maste 			    verify_timep) != 0 || *verify_timep == 0) {
27231323ec57SEd Maste 				error("Invalid \"verify-time\" option");
27241323ec57SEd Maste 				return SSH_ERR_INVALID_ARGUMENT;
27251323ec57SEd Maste 			}
27261323ec57SEd Maste 		} else if (print_pubkey &&
27271323ec57SEd Maste 		    strcasecmp(opts[i], "print-pubkey") == 0) {
27281323ec57SEd Maste 			*print_pubkey = 1;
27291323ec57SEd Maste 		} else {
27301323ec57SEd Maste 			error("Invalid option \"%s\"", opts[i]);
27311323ec57SEd Maste 			return SSH_ERR_INVALID_ARGUMENT;
27321323ec57SEd Maste 		}
27331323ec57SEd Maste 	}
27341323ec57SEd Maste 	if (verify_timep && *verify_timep == 0) {
27351323ec57SEd Maste 		if ((now = time(NULL)) < 0) {
27361323ec57SEd Maste 			error("Time is before epoch");
27371323ec57SEd Maste 			return SSH_ERR_INVALID_ARGUMENT;
27381323ec57SEd Maste 		}
27391323ec57SEd Maste 		*verify_timep = (uint64_t)now;
27401323ec57SEd Maste 	}
27411323ec57SEd Maste 	return 0;
27421323ec57SEd Maste }
27431323ec57SEd Maste 
27441323ec57SEd Maste 
27451323ec57SEd Maste static int
sig_sign(const char * keypath,const char * sig_namespace,int require_agent,int argc,char ** argv,char * const * opts,size_t nopts)274638a52bd3SEd Maste sig_sign(const char *keypath, const char *sig_namespace, int require_agent,
274738a52bd3SEd Maste     int argc, char **argv, char * const *opts, size_t nopts)
274819261079SEd Maste {
274919261079SEd Maste 	int i, fd = -1, r, ret = -1;
275019261079SEd Maste 	int agent_fd = -1;
275119261079SEd Maste 	struct sshkey *pubkey = NULL, *privkey = NULL, *signkey = NULL;
275219261079SEd Maste 	sshsig_signer *signer = NULL;
27531323ec57SEd Maste 	char *hashalg = NULL;
275419261079SEd Maste 
275519261079SEd Maste 	/* Check file arguments. */
275619261079SEd Maste 	for (i = 0; i < argc; i++) {
275719261079SEd Maste 		if (strcmp(argv[i], "-") != 0)
275819261079SEd Maste 			continue;
275919261079SEd Maste 		if (i > 0 || argc > 1)
276019261079SEd Maste 			fatal("Cannot sign mix of paths and standard input");
276119261079SEd Maste 	}
276219261079SEd Maste 
27631323ec57SEd Maste 	if (sig_process_opts(opts, nopts, &hashalg, NULL, NULL) != 0)
27641323ec57SEd Maste 		goto done; /* error already logged */
27651323ec57SEd Maste 
276619261079SEd Maste 	if ((r = sshkey_load_public(keypath, &pubkey, NULL)) != 0) {
276719261079SEd Maste 		error_r(r, "Couldn't load public key %s", keypath);
276819261079SEd Maste 		goto done;
276919261079SEd Maste 	}
277019261079SEd Maste 
277138a52bd3SEd Maste 	if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) {
277238a52bd3SEd Maste 		if (require_agent)
277338a52bd3SEd Maste 			fatal("Couldn't get agent socket");
277419261079SEd Maste 		debug_r(r, "Couldn't get agent socket");
277538a52bd3SEd Maste 	} else {
277619261079SEd Maste 		if ((r = ssh_agent_has_key(agent_fd, pubkey)) == 0)
277719261079SEd Maste 			signer = agent_signer;
277838a52bd3SEd Maste 		else {
277938a52bd3SEd Maste 			if (require_agent)
278038a52bd3SEd Maste 				fatal("Couldn't find key in agent");
278119261079SEd Maste 			debug_r(r, "Couldn't find key in agent");
278219261079SEd Maste 		}
278338a52bd3SEd Maste 	}
278419261079SEd Maste 
278519261079SEd Maste 	if (signer == NULL) {
278619261079SEd Maste 		/* Not using agent - try to load private key */
278719261079SEd Maste 		if ((privkey = load_sign_key(keypath, pubkey)) == NULL)
278819261079SEd Maste 			goto done;
278919261079SEd Maste 		signkey = privkey;
279019261079SEd Maste 	} else {
279119261079SEd Maste 		/* Will use key in agent */
279219261079SEd Maste 		signkey = pubkey;
279319261079SEd Maste 	}
279419261079SEd Maste 
279519261079SEd Maste 	if (argc == 0) {
279619261079SEd Maste 		if ((r = sign_one(signkey, "(stdin)", STDIN_FILENO,
27971323ec57SEd Maste 		    sig_namespace, hashalg, signer, &agent_fd)) != 0)
279819261079SEd Maste 			goto done;
279919261079SEd Maste 	} else {
280019261079SEd Maste 		for (i = 0; i < argc; i++) {
280119261079SEd Maste 			if (strcmp(argv[i], "-") == 0)
280219261079SEd Maste 				fd = STDIN_FILENO;
280319261079SEd Maste 			else if ((fd = open(argv[i], O_RDONLY)) == -1) {
280419261079SEd Maste 				error("Cannot open %s for signing: %s",
280519261079SEd Maste 				    argv[i], strerror(errno));
280619261079SEd Maste 				goto done;
280719261079SEd Maste 			}
280819261079SEd Maste 			if ((r = sign_one(signkey, argv[i], fd, sig_namespace,
28091323ec57SEd Maste 			    hashalg, signer, &agent_fd)) != 0)
281019261079SEd Maste 				goto done;
281119261079SEd Maste 			if (fd != STDIN_FILENO)
281219261079SEd Maste 				close(fd);
281319261079SEd Maste 			fd = -1;
281419261079SEd Maste 		}
281519261079SEd Maste 	}
281619261079SEd Maste 
281719261079SEd Maste 	ret = 0;
281819261079SEd Maste done:
281919261079SEd Maste 	if (fd != -1 && fd != STDIN_FILENO)
282019261079SEd Maste 		close(fd);
282119261079SEd Maste 	sshkey_free(pubkey);
282219261079SEd Maste 	sshkey_free(privkey);
28231323ec57SEd Maste 	free(hashalg);
282419261079SEd Maste 	return ret;
282519261079SEd Maste }
282619261079SEd Maste 
282719261079SEd Maste 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)282819261079SEd Maste sig_verify(const char *signature, const char *sig_namespace,
282919261079SEd Maste     const char *principal, const char *allowed_keys, const char *revoked_keys,
283019261079SEd Maste     char * const *opts, size_t nopts)
283119261079SEd Maste {
283219261079SEd Maste 	int r, ret = -1;
283319261079SEd Maste 	int print_pubkey = 0;
283419261079SEd Maste 	struct sshbuf *sigbuf = NULL, *abuf = NULL;
283519261079SEd Maste 	struct sshkey *sign_key = NULL;
283619261079SEd Maste 	char *fp = NULL;
283719261079SEd Maste 	struct sshkey_sig_details *sig_details = NULL;
283819261079SEd Maste 	uint64_t verify_time = 0;
283919261079SEd Maste 
28401323ec57SEd Maste 	if (sig_process_opts(opts, nopts, NULL, &verify_time,
28411323ec57SEd Maste 	    &print_pubkey) != 0)
284219261079SEd Maste 		goto done; /* error already logged */
284319261079SEd Maste 
284419261079SEd Maste 	memset(&sig_details, 0, sizeof(sig_details));
284519261079SEd Maste 	if ((r = sshbuf_load_file(signature, &abuf)) != 0) {
284619261079SEd Maste 		error_r(r, "Couldn't read signature file");
284719261079SEd Maste 		goto done;
284819261079SEd Maste 	}
284919261079SEd Maste 
285019261079SEd Maste 	if ((r = sshsig_dearmor(abuf, &sigbuf)) != 0) {
285119261079SEd Maste 		error_fr(r, "sshsig_armor");
285219261079SEd Maste 		goto done;
285319261079SEd Maste 	}
285419261079SEd Maste 	if ((r = sshsig_verify_fd(sigbuf, STDIN_FILENO, sig_namespace,
285519261079SEd Maste 	    &sign_key, &sig_details)) != 0)
285619261079SEd Maste 		goto done; /* sshsig_verify() prints error */
285719261079SEd Maste 
285819261079SEd Maste 	if ((fp = sshkey_fingerprint(sign_key, fingerprint_hash,
285919261079SEd Maste 	    SSH_FP_DEFAULT)) == NULL)
286019261079SEd Maste 		fatal_f("sshkey_fingerprint failed");
286119261079SEd Maste 	debug("Valid (unverified) signature from key %s", fp);
286219261079SEd Maste 	if (sig_details != NULL) {
286319261079SEd Maste 		debug2_f("signature details: counter = %u, flags = 0x%02x",
286419261079SEd Maste 		    sig_details->sk_counter, sig_details->sk_flags);
286519261079SEd Maste 	}
286619261079SEd Maste 	free(fp);
286719261079SEd Maste 	fp = NULL;
286819261079SEd Maste 
286919261079SEd Maste 	if (revoked_keys != NULL) {
287019261079SEd Maste 		if ((r = sshkey_check_revoked(sign_key, revoked_keys)) != 0) {
287119261079SEd Maste 			debug3_fr(r, "sshkey_check_revoked");
287219261079SEd Maste 			goto done;
287319261079SEd Maste 		}
287419261079SEd Maste 	}
287519261079SEd Maste 
287619261079SEd Maste 	if (allowed_keys != NULL && (r = sshsig_check_allowed_keys(allowed_keys,
287719261079SEd Maste 	    sign_key, principal, sig_namespace, verify_time)) != 0) {
287819261079SEd Maste 		debug3_fr(r, "sshsig_check_allowed_keys");
287919261079SEd Maste 		goto done;
288019261079SEd Maste 	}
288119261079SEd Maste 	/* success */
288219261079SEd Maste 	ret = 0;
288319261079SEd Maste done:
288419261079SEd Maste 	if (!quiet) {
288519261079SEd Maste 		if (ret == 0) {
288619261079SEd Maste 			if ((fp = sshkey_fingerprint(sign_key, fingerprint_hash,
288719261079SEd Maste 			    SSH_FP_DEFAULT)) == NULL)
288819261079SEd Maste 				fatal_f("sshkey_fingerprint failed");
288919261079SEd Maste 			if (principal == NULL) {
289019261079SEd Maste 				printf("Good \"%s\" signature with %s key %s\n",
289119261079SEd Maste 				    sig_namespace, sshkey_type(sign_key), fp);
289219261079SEd Maste 
289319261079SEd Maste 			} else {
289419261079SEd Maste 				printf("Good \"%s\" signature for %s with %s key %s\n",
289519261079SEd Maste 				    sig_namespace, principal,
289619261079SEd Maste 				    sshkey_type(sign_key), fp);
289719261079SEd Maste 			}
289819261079SEd Maste 		} else {
289919261079SEd Maste 			printf("Could not verify signature.\n");
290019261079SEd Maste 		}
290119261079SEd Maste 	}
290219261079SEd Maste 	/* Print the signature key if requested */
290319261079SEd Maste 	if (ret == 0 && print_pubkey && sign_key != NULL) {
290419261079SEd Maste 		if ((r = sshkey_write(sign_key, stdout)) == 0)
290519261079SEd Maste 			fputc('\n', stdout);
290619261079SEd Maste 		else {
290719261079SEd Maste 			error_r(r, "Could not print public key.\n");
290819261079SEd Maste 			ret = -1;
290919261079SEd Maste 		}
291019261079SEd Maste 	}
291119261079SEd Maste 	sshbuf_free(sigbuf);
291219261079SEd Maste 	sshbuf_free(abuf);
291319261079SEd Maste 	sshkey_free(sign_key);
291419261079SEd Maste 	sshkey_sig_details_free(sig_details);
291519261079SEd Maste 	free(fp);
291619261079SEd Maste 	return ret;
291719261079SEd Maste }
291819261079SEd Maste 
291919261079SEd Maste static int
sig_find_principals(const char * signature,const char * allowed_keys,char * const * opts,size_t nopts)292019261079SEd Maste sig_find_principals(const char *signature, const char *allowed_keys,
292119261079SEd Maste     char * const *opts, size_t nopts)
292219261079SEd Maste {
292319261079SEd Maste 	int r, ret = -1;
292419261079SEd Maste 	struct sshbuf *sigbuf = NULL, *abuf = NULL;
292519261079SEd Maste 	struct sshkey *sign_key = NULL;
292619261079SEd Maste 	char *principals = NULL, *cp, *tmp;
292719261079SEd Maste 	uint64_t verify_time = 0;
292819261079SEd Maste 
29291323ec57SEd Maste 	if (sig_process_opts(opts, nopts, NULL, &verify_time, NULL) != 0)
293019261079SEd Maste 		goto done; /* error already logged */
293119261079SEd Maste 
293219261079SEd Maste 	if ((r = sshbuf_load_file(signature, &abuf)) != 0) {
293319261079SEd Maste 		error_r(r, "Couldn't read signature file");
293419261079SEd Maste 		goto done;
293519261079SEd Maste 	}
293619261079SEd Maste 	if ((r = sshsig_dearmor(abuf, &sigbuf)) != 0) {
293719261079SEd Maste 		error_fr(r, "sshsig_armor");
293819261079SEd Maste 		goto done;
293919261079SEd Maste 	}
294019261079SEd Maste 	if ((r = sshsig_get_pubkey(sigbuf, &sign_key)) != 0) {
294119261079SEd Maste 		error_fr(r, "sshsig_get_pubkey");
294219261079SEd Maste 		goto done;
294319261079SEd Maste 	}
294419261079SEd Maste 	if ((r = sshsig_find_principals(allowed_keys, sign_key,
294519261079SEd Maste 	    verify_time, &principals)) != 0) {
294619261079SEd Maste 		if (r != SSH_ERR_KEY_NOT_FOUND)
294719261079SEd Maste 			error_fr(r, "sshsig_find_principal");
294819261079SEd Maste 		goto done;
294919261079SEd Maste 	}
295019261079SEd Maste 	ret = 0;
295119261079SEd Maste done:
295219261079SEd Maste 	if (ret == 0 ) {
295319261079SEd Maste 		/* Emit matching principals one per line */
295419261079SEd Maste 		tmp = principals;
295519261079SEd Maste 		while ((cp = strsep(&tmp, ",")) != NULL && *cp != '\0')
295619261079SEd Maste 			puts(cp);
295719261079SEd Maste 	} else {
295819261079SEd Maste 		fprintf(stderr, "No principal matched.\n");
295919261079SEd Maste 	}
296019261079SEd Maste 	sshbuf_free(sigbuf);
296119261079SEd Maste 	sshbuf_free(abuf);
296219261079SEd Maste 	sshkey_free(sign_key);
296319261079SEd Maste 	free(principals);
296419261079SEd Maste 	return ret;
296519261079SEd Maste }
296619261079SEd Maste 
29671323ec57SEd Maste static int
sig_match_principals(const char * allowed_keys,char * principal,char * const * opts,size_t nopts)29681323ec57SEd Maste sig_match_principals(const char *allowed_keys, char *principal,
29691323ec57SEd Maste 	char * const *opts, size_t nopts)
29701323ec57SEd Maste {
29711323ec57SEd Maste 	int r;
29721323ec57SEd Maste 	char **principals = NULL;
29731323ec57SEd Maste 	size_t i, nprincipals = 0;
29741323ec57SEd Maste 
29751323ec57SEd Maste 	if ((r = sig_process_opts(opts, nopts, NULL, NULL, NULL)) != 0)
29761323ec57SEd Maste 		return r; /* error already logged */
29771323ec57SEd Maste 
29781323ec57SEd Maste 	if ((r = sshsig_match_principals(allowed_keys, principal,
29791323ec57SEd Maste 	    &principals, &nprincipals)) != 0) {
29801323ec57SEd Maste 		debug_f("match: %s", ssh_err(r));
29811323ec57SEd Maste 		fprintf(stderr, "No principal matched.\n");
29821323ec57SEd Maste 		return r;
29831323ec57SEd Maste 	}
29841323ec57SEd Maste 	for (i = 0; i < nprincipals; i++) {
29851323ec57SEd Maste 		printf("%s\n", principals[i]);
29861323ec57SEd Maste 		free(principals[i]);
29871323ec57SEd Maste 	}
29881323ec57SEd Maste 	free(principals);
29891323ec57SEd Maste 
29901323ec57SEd Maste 	return 0;
29911323ec57SEd Maste }
29921323ec57SEd Maste 
299319261079SEd Maste static void
do_moduli_gen(const char * out_file,char ** opts,size_t nopts)299419261079SEd Maste do_moduli_gen(const char *out_file, char **opts, size_t nopts)
299519261079SEd Maste {
299619261079SEd Maste #ifdef WITH_OPENSSL
299719261079SEd Maste 	/* Moduli generation/screening */
299819261079SEd Maste 	u_int32_t memory = 0;
299919261079SEd Maste 	BIGNUM *start = NULL;
300019261079SEd Maste 	int moduli_bits = 0;
300119261079SEd Maste 	FILE *out;
300219261079SEd Maste 	size_t i;
300319261079SEd Maste 	const char *errstr;
300419261079SEd Maste 
300519261079SEd Maste 	/* Parse options */
300619261079SEd Maste 	for (i = 0; i < nopts; i++) {
300719261079SEd Maste 		if (strncmp(opts[i], "memory=", 7) == 0) {
300819261079SEd Maste 			memory = (u_int32_t)strtonum(opts[i]+7, 1,
300919261079SEd Maste 			    UINT_MAX, &errstr);
301019261079SEd Maste 			if (errstr) {
301119261079SEd Maste 				fatal("Memory limit is %s: %s",
301219261079SEd Maste 				    errstr, opts[i]+7);
301319261079SEd Maste 			}
301419261079SEd Maste 		} else if (strncmp(opts[i], "start=", 6) == 0) {
301519261079SEd Maste 			/* XXX - also compare length against bits */
301619261079SEd Maste 			if (BN_hex2bn(&start, opts[i]+6) == 0)
301719261079SEd Maste 				fatal("Invalid start point.");
301819261079SEd Maste 		} else if (strncmp(opts[i], "bits=", 5) == 0) {
301919261079SEd Maste 			moduli_bits = (int)strtonum(opts[i]+5, 1,
302019261079SEd Maste 			    INT_MAX, &errstr);
302119261079SEd Maste 			if (errstr) {
302219261079SEd Maste 				fatal("Invalid number: %s (%s)",
302319261079SEd Maste 					opts[i]+12, errstr);
302419261079SEd Maste 			}
302519261079SEd Maste 		} else {
302619261079SEd Maste 			fatal("Option \"%s\" is unsupported for moduli "
302719261079SEd Maste 			    "generation", opts[i]);
302819261079SEd Maste 		}
302919261079SEd Maste 	}
303019261079SEd Maste 
303119261079SEd Maste 	if ((out = fopen(out_file, "w")) == NULL) {
303219261079SEd Maste 		fatal("Couldn't open modulus candidate file \"%s\": %s",
303319261079SEd Maste 		    out_file, strerror(errno));
303419261079SEd Maste 	}
303519261079SEd Maste 	setvbuf(out, NULL, _IOLBF, 0);
303619261079SEd Maste 
303719261079SEd Maste 	if (moduli_bits == 0)
303819261079SEd Maste 		moduli_bits = DEFAULT_BITS;
303919261079SEd Maste 	if (gen_candidates(out, memory, moduli_bits, start) != 0)
304019261079SEd Maste 		fatal("modulus candidate generation failed");
304119261079SEd Maste #else /* WITH_OPENSSL */
304219261079SEd Maste 	fatal("Moduli generation is not supported");
304319261079SEd Maste #endif /* WITH_OPENSSL */
304419261079SEd Maste }
304519261079SEd Maste 
304619261079SEd Maste static void
do_moduli_screen(const char * out_file,char ** opts,size_t nopts)304719261079SEd Maste do_moduli_screen(const char *out_file, char **opts, size_t nopts)
304819261079SEd Maste {
304919261079SEd Maste #ifdef WITH_OPENSSL
305019261079SEd Maste 	/* Moduli generation/screening */
305119261079SEd Maste 	char *checkpoint = NULL;
305219261079SEd Maste 	u_int32_t generator_wanted = 0;
305319261079SEd Maste 	unsigned long start_lineno = 0, lines_to_process = 0;
305419261079SEd Maste 	int prime_tests = 0;
305519261079SEd Maste 	FILE *out, *in = stdin;
305619261079SEd Maste 	size_t i;
305719261079SEd Maste 	const char *errstr;
305819261079SEd Maste 
305919261079SEd Maste 	/* Parse options */
306019261079SEd Maste 	for (i = 0; i < nopts; i++) {
306119261079SEd Maste 		if (strncmp(opts[i], "lines=", 6) == 0) {
306219261079SEd Maste 			lines_to_process = strtoul(opts[i]+6, NULL, 10);
306319261079SEd Maste 		} else if (strncmp(opts[i], "start-line=", 11) == 0) {
306419261079SEd Maste 			start_lineno = strtoul(opts[i]+11, NULL, 10);
306519261079SEd Maste 		} else if (strncmp(opts[i], "checkpoint=", 11) == 0) {
30664d3fc8b0SEd Maste 			free(checkpoint);
306719261079SEd Maste 			checkpoint = xstrdup(opts[i]+11);
306819261079SEd Maste 		} else if (strncmp(opts[i], "generator=", 10) == 0) {
306919261079SEd Maste 			generator_wanted = (u_int32_t)strtonum(
307019261079SEd Maste 			    opts[i]+10, 1, UINT_MAX, &errstr);
307119261079SEd Maste 			if (errstr != NULL) {
307219261079SEd Maste 				fatal("Generator invalid: %s (%s)",
307319261079SEd Maste 				    opts[i]+10, errstr);
307419261079SEd Maste 			}
307519261079SEd Maste 		} else if (strncmp(opts[i], "prime-tests=", 12) == 0) {
307619261079SEd Maste 			prime_tests = (int)strtonum(opts[i]+12, 1,
307719261079SEd Maste 			    INT_MAX, &errstr);
307819261079SEd Maste 			if (errstr) {
307919261079SEd Maste 				fatal("Invalid number: %s (%s)",
308019261079SEd Maste 					opts[i]+12, errstr);
308119261079SEd Maste 			}
308219261079SEd Maste 		} else {
308319261079SEd Maste 			fatal("Option \"%s\" is unsupported for moduli "
308419261079SEd Maste 			    "screening", opts[i]);
308519261079SEd Maste 		}
308619261079SEd Maste 	}
308719261079SEd Maste 
308819261079SEd Maste 	if (have_identity && strcmp(identity_file, "-") != 0) {
308919261079SEd Maste 		if ((in = fopen(identity_file, "r")) == NULL) {
309019261079SEd Maste 			fatal("Couldn't open modulus candidate "
309119261079SEd Maste 			    "file \"%s\": %s", identity_file,
309219261079SEd Maste 			    strerror(errno));
309319261079SEd Maste 		}
309419261079SEd Maste 	}
309519261079SEd Maste 
309619261079SEd Maste 	if ((out = fopen(out_file, "a")) == NULL) {
309719261079SEd Maste 		fatal("Couldn't open moduli file \"%s\": %s",
309819261079SEd Maste 		    out_file, strerror(errno));
309919261079SEd Maste 	}
310019261079SEd Maste 	setvbuf(out, NULL, _IOLBF, 0);
310119261079SEd Maste 	if (prime_test(in, out, prime_tests == 0 ? 100 : prime_tests,
310219261079SEd Maste 	    generator_wanted, checkpoint,
310319261079SEd Maste 	    start_lineno, lines_to_process) != 0)
310419261079SEd Maste 		fatal("modulus screening failed");
31054d3fc8b0SEd Maste 	if (in != stdin)
31064d3fc8b0SEd Maste 		(void)fclose(in);
31074d3fc8b0SEd Maste 	free(checkpoint);
310819261079SEd Maste #else /* WITH_OPENSSL */
310919261079SEd Maste 	fatal("Moduli screening is not supported");
311019261079SEd Maste #endif /* WITH_OPENSSL */
311119261079SEd Maste }
311219261079SEd Maste 
311338a52bd3SEd Maste /* Read and confirm a passphrase */
311419261079SEd Maste static char *
read_check_passphrase(const char * prompt1,const char * prompt2,const char * retry_prompt)311538a52bd3SEd Maste read_check_passphrase(const char *prompt1, const char *prompt2,
311638a52bd3SEd Maste     const char *retry_prompt)
311719261079SEd Maste {
311819261079SEd Maste 	char *passphrase1, *passphrase2;
311919261079SEd Maste 
312038a52bd3SEd Maste 	for (;;) {
312138a52bd3SEd Maste 		passphrase1 = read_passphrase(prompt1, RP_ALLOW_STDIN);
312238a52bd3SEd Maste 		passphrase2 = read_passphrase(prompt2, RP_ALLOW_STDIN);
312338a52bd3SEd Maste 		if (strcmp(passphrase1, passphrase2) == 0) {
312438a52bd3SEd Maste 			freezero(passphrase2, strlen(passphrase2));
312538a52bd3SEd Maste 			return passphrase1;
312638a52bd3SEd Maste 		}
312738a52bd3SEd Maste 		/* The passphrases do not match. Clear them and retry. */
312819261079SEd Maste 		freezero(passphrase1, strlen(passphrase1));
312919261079SEd Maste 		freezero(passphrase2, strlen(passphrase2));
313038a52bd3SEd Maste 		fputs(retry_prompt, stdout);
313138a52bd3SEd Maste 		fputc('\n', stdout);
313238a52bd3SEd Maste 		fflush(stdout);
313319261079SEd Maste 	}
313438a52bd3SEd Maste 	/* NOTREACHED */
313538a52bd3SEd Maste 	return NULL;
313619261079SEd Maste }
313738a52bd3SEd Maste 
313838a52bd3SEd Maste static char *
private_key_passphrase(const char * path)3139*3d9fd9fcSEd Maste private_key_passphrase(const char *path)
314038a52bd3SEd Maste {
3141*3d9fd9fcSEd Maste 	char *prompt, *ret;
3142*3d9fd9fcSEd Maste 
314338a52bd3SEd Maste 	if (identity_passphrase)
314438a52bd3SEd Maste 		return xstrdup(identity_passphrase);
314538a52bd3SEd Maste 	if (identity_new_passphrase)
314638a52bd3SEd Maste 		return xstrdup(identity_new_passphrase);
314738a52bd3SEd Maste 
3148*3d9fd9fcSEd Maste 	xasprintf(&prompt, "Enter passphrase for \"%s\" "
3149*3d9fd9fcSEd Maste 	    "(empty for no passphrase): ", path);
3150*3d9fd9fcSEd Maste 	ret = read_check_passphrase(prompt,
315138a52bd3SEd Maste 	    "Enter same passphrase again: ",
315238a52bd3SEd Maste 	    "Passphrases do not match.  Try again.");
3153*3d9fd9fcSEd Maste 	free(prompt);
3154*3d9fd9fcSEd Maste 	return ret;
315519261079SEd Maste }
315619261079SEd Maste 
31571323ec57SEd Maste static char *
sk_suffix(const char * application,const uint8_t * user,size_t userlen)31581323ec57SEd Maste sk_suffix(const char *application, const uint8_t *user, size_t userlen)
315919261079SEd Maste {
31601323ec57SEd Maste 	char *ret, *cp;
31611323ec57SEd Maste 	size_t slen, i;
31621323ec57SEd Maste 
31631323ec57SEd Maste 	/* Trim off URL-like preamble */
31641323ec57SEd Maste 	if (strncmp(application, "ssh://", 6) == 0)
31651323ec57SEd Maste 		ret =  xstrdup(application + 6);
31661323ec57SEd Maste 	else if (strncmp(application, "ssh:", 4) == 0)
31671323ec57SEd Maste 		ret =  xstrdup(application + 4);
31681323ec57SEd Maste 	else
31691323ec57SEd Maste 		ret = xstrdup(application);
31701323ec57SEd Maste 
31711323ec57SEd Maste 	/* Count trailing zeros in user */
31721323ec57SEd Maste 	for (i = 0; i < userlen; i++) {
31731323ec57SEd Maste 		if (user[userlen - i - 1] != 0)
31741323ec57SEd Maste 			break;
31751323ec57SEd Maste 	}
31761323ec57SEd Maste 	if (i >= userlen)
31771323ec57SEd Maste 		return ret; /* user-id was default all-zeros */
31781323ec57SEd Maste 
31791323ec57SEd Maste 	/* Append user-id, escaping non-UTF-8 characters */
31801323ec57SEd Maste 	slen = userlen - i;
31811323ec57SEd Maste 	if (asmprintf(&cp, INT_MAX, NULL, "%.*s", (int)slen, user) == -1)
31821323ec57SEd Maste 		fatal_f("asmprintf failed");
31831323ec57SEd Maste 	/* Don't emit a user-id that contains path or control characters */
31841323ec57SEd Maste 	if (strchr(cp, '/') != NULL || strstr(cp, "..") != NULL ||
31851323ec57SEd Maste 	    strchr(cp, '\\') != NULL) {
31861323ec57SEd Maste 		free(cp);
31871323ec57SEd Maste 		cp = tohex(user, slen);
31881323ec57SEd Maste 	}
31891323ec57SEd Maste 	xextendf(&ret, "_", "%s", cp);
31901323ec57SEd Maste 	free(cp);
31911323ec57SEd Maste 	return ret;
319219261079SEd Maste }
319319261079SEd Maste 
319419261079SEd Maste static int
do_download_sk(const char * skprovider,const char * device)319519261079SEd Maste do_download_sk(const char *skprovider, const char *device)
319619261079SEd Maste {
31971323ec57SEd Maste 	struct sshsk_resident_key **srks;
31981323ec57SEd Maste 	size_t nsrks, i;
319919261079SEd Maste 	int r, ret = -1;
320019261079SEd Maste 	char *fp, *pin = NULL, *pass = NULL, *path, *pubpath;
320119261079SEd Maste 	const char *ext;
32021323ec57SEd Maste 	struct sshkey *key;
320319261079SEd Maste 
320419261079SEd Maste 	if (skprovider == NULL)
320519261079SEd Maste 		fatal("Cannot download keys without provider");
320619261079SEd Maste 
320719261079SEd Maste 	pin = read_passphrase("Enter PIN for authenticator: ", RP_ALLOW_STDIN);
320819261079SEd Maste 	if (!quiet) {
320919261079SEd Maste 		printf("You may need to touch your authenticator "
321019261079SEd Maste 		    "to authorize key download.\n");
321119261079SEd Maste 	}
32121323ec57SEd Maste 	if ((r = sshsk_load_resident(skprovider, device, pin, 0,
32131323ec57SEd Maste 	    &srks, &nsrks)) != 0) {
321419261079SEd Maste 		if (pin != NULL)
321519261079SEd Maste 			freezero(pin, strlen(pin));
321619261079SEd Maste 		error_r(r, "Unable to load resident keys");
321719261079SEd Maste 		return -1;
321819261079SEd Maste 	}
32191323ec57SEd Maste 	if (nsrks == 0)
322019261079SEd Maste 		logit("No keys to download");
322119261079SEd Maste 	if (pin != NULL)
322219261079SEd Maste 		freezero(pin, strlen(pin));
322319261079SEd Maste 
32241323ec57SEd Maste 	for (i = 0; i < nsrks; i++) {
32251323ec57SEd Maste 		key = srks[i]->key;
32261323ec57SEd Maste 		if (key->type != KEY_ECDSA_SK && key->type != KEY_ED25519_SK) {
322719261079SEd Maste 			error("Unsupported key type %s (%d)",
32281323ec57SEd Maste 			    sshkey_type(key), key->type);
322919261079SEd Maste 			continue;
323019261079SEd Maste 		}
32311323ec57SEd Maste 		if ((fp = sshkey_fingerprint(key, fingerprint_hash,
32321323ec57SEd Maste 		    SSH_FP_DEFAULT)) == NULL)
323319261079SEd Maste 			fatal_f("sshkey_fingerprint failed");
323419261079SEd Maste 		debug_f("key %zu: %s %s %s (flags 0x%02x)", i,
32351323ec57SEd Maste 		    sshkey_type(key), fp, key->sk_application, key->sk_flags);
32361323ec57SEd Maste 		ext = sk_suffix(key->sk_application,
32371323ec57SEd Maste 		    srks[i]->user_id, srks[i]->user_id_len);
323819261079SEd Maste 		xasprintf(&path, "id_%s_rk%s%s",
32391323ec57SEd Maste 		    key->type == KEY_ECDSA_SK ? "ecdsa_sk" : "ed25519_sk",
324019261079SEd Maste 		    *ext == '\0' ? "" : "_", ext);
324119261079SEd Maste 
324219261079SEd Maste 		/* If the file already exists, ask the user to confirm. */
324319261079SEd Maste 		if (!confirm_overwrite(path)) {
324419261079SEd Maste 			free(path);
324519261079SEd Maste 			break;
324619261079SEd Maste 		}
324719261079SEd Maste 
324819261079SEd Maste 		/* Save the key with the application string as the comment */
324919261079SEd Maste 		if (pass == NULL)
3250*3d9fd9fcSEd Maste 			pass = private_key_passphrase(path);
32511323ec57SEd Maste 		if ((r = sshkey_save_private(key, path, pass,
32521323ec57SEd Maste 		    key->sk_application, private_key_format,
325319261079SEd Maste 		    openssh_format_cipher, rounds)) != 0) {
325419261079SEd Maste 			error_r(r, "Saving key \"%s\" failed", path);
325519261079SEd Maste 			free(path);
325619261079SEd Maste 			break;
325719261079SEd Maste 		}
325819261079SEd Maste 		if (!quiet) {
32591323ec57SEd Maste 			printf("Saved %s key%s%s to %s\n", sshkey_type(key),
326019261079SEd Maste 			    *ext != '\0' ? " " : "",
32611323ec57SEd Maste 			    *ext != '\0' ? key->sk_application : "",
326219261079SEd Maste 			    path);
326319261079SEd Maste 		}
326419261079SEd Maste 
326519261079SEd Maste 		/* Save public key too */
326619261079SEd Maste 		xasprintf(&pubpath, "%s.pub", path);
326719261079SEd Maste 		free(path);
32681323ec57SEd Maste 		if ((r = sshkey_save_public(key, pubpath,
32691323ec57SEd Maste 		    key->sk_application)) != 0) {
327019261079SEd Maste 			error_r(r, "Saving public key \"%s\" failed", pubpath);
327119261079SEd Maste 			free(pubpath);
327219261079SEd Maste 			break;
327319261079SEd Maste 		}
327419261079SEd Maste 		free(pubpath);
327519261079SEd Maste 	}
327619261079SEd Maste 
32771323ec57SEd Maste 	if (i >= nsrks)
327819261079SEd Maste 		ret = 0; /* success */
327919261079SEd Maste 	if (pass != NULL)
328019261079SEd Maste 		freezero(pass, strlen(pass));
32811323ec57SEd Maste 	sshsk_free_resident_keys(srks, nsrks);
328219261079SEd Maste 	return ret;
328319261079SEd Maste }
328419261079SEd Maste 
328519261079SEd Maste static void
save_attestation(struct sshbuf * attest,const char * path)328619261079SEd Maste save_attestation(struct sshbuf *attest, const char *path)
328719261079SEd Maste {
328819261079SEd Maste 	mode_t omask;
328919261079SEd Maste 	int r;
329019261079SEd Maste 
329119261079SEd Maste 	if (path == NULL)
329219261079SEd Maste 		return; /* nothing to do */
329319261079SEd Maste 	if (attest == NULL || sshbuf_len(attest) == 0)
329419261079SEd Maste 		fatal("Enrollment did not return attestation data");
329519261079SEd Maste 	omask = umask(077);
329619261079SEd Maste 	r = sshbuf_write_file(path, attest);
329719261079SEd Maste 	umask(omask);
329819261079SEd Maste 	if (r != 0)
329919261079SEd Maste 		fatal_r(r, "Unable to write attestation data \"%s\"", path);
330019261079SEd Maste 	if (!quiet)
330119261079SEd Maste 		printf("Your FIDO attestation certificate has been saved in "
330219261079SEd Maste 		    "%s\n", path);
330319261079SEd Maste }
330419261079SEd Maste 
330538a52bd3SEd Maste static int
confirm_sk_overwrite(const char * application,const char * user)330638a52bd3SEd Maste confirm_sk_overwrite(const char *application, const char *user)
330738a52bd3SEd Maste {
330838a52bd3SEd Maste 	char yesno[3];
330938a52bd3SEd Maste 
331038a52bd3SEd Maste 	printf("A resident key scoped to '%s' with user id '%s' already "
331138a52bd3SEd Maste 	    "exists.\n", application == NULL ? "ssh:" : application,
331238a52bd3SEd Maste 	    user == NULL ? "null" : user);
331338a52bd3SEd Maste 	printf("Overwrite key in token (y/n)? ");
331438a52bd3SEd Maste 	fflush(stdout);
331538a52bd3SEd Maste 	if (fgets(yesno, sizeof(yesno), stdin) == NULL)
331638a52bd3SEd Maste 		return 0;
331738a52bd3SEd Maste 	if (yesno[0] != 'y' && yesno[0] != 'Y')
331838a52bd3SEd Maste 		return 0;
331938a52bd3SEd Maste 	return 1;
332038a52bd3SEd Maste }
332138a52bd3SEd Maste 
33226888a9beSDag-Erling Smørgrav static void
usage(void)3323511b41d2SMark Murray usage(void)
3324511b41d2SMark Murray {
3325a0ee8cc6SDag-Erling Smørgrav 	fprintf(stderr,
332619261079SEd Maste 	    "usage: ssh-keygen [-q] [-a rounds] [-b bits] [-C comment] [-f output_keyfile]\n"
332719261079SEd Maste 	    "                  [-m format] [-N new_passphrase] [-O option]\n"
332819261079SEd Maste 	    "                  [-t dsa | ecdsa | ecdsa-sk | ed25519 | ed25519-sk | rsa]\n"
332919261079SEd Maste 	    "                  [-w provider] [-Z cipher]\n"
333019261079SEd Maste 	    "       ssh-keygen -p [-a rounds] [-f keyfile] [-m format] [-N new_passphrase]\n"
333119261079SEd Maste 	    "                   [-P old_passphrase] [-Z cipher]\n"
333219261079SEd Maste #ifdef WITH_OPENSSL
333319261079SEd Maste 	    "       ssh-keygen -i [-f input_keyfile] [-m key_format]\n"
333419261079SEd Maste 	    "       ssh-keygen -e [-f input_keyfile] [-m key_format]\n"
333519261079SEd Maste #endif
3336a0ee8cc6SDag-Erling Smørgrav 	    "       ssh-keygen -y [-f input_keyfile]\n"
333719261079SEd Maste 	    "       ssh-keygen -c [-a rounds] [-C comment] [-f keyfile] [-P passphrase]\n"
3338bc5531deSDag-Erling Smørgrav 	    "       ssh-keygen -l [-v] [-E fingerprint_hash] [-f input_keyfile]\n"
33394f52dfbbSDag-Erling Smørgrav 	    "       ssh-keygen -B [-f input_keyfile]\n");
3340b15c8340SDag-Erling Smørgrav #ifdef ENABLE_PKCS11
3341a0ee8cc6SDag-Erling Smørgrav 	fprintf(stderr,
3342a0ee8cc6SDag-Erling Smørgrav 	    "       ssh-keygen -D pkcs11\n");
3343b15c8340SDag-Erling Smørgrav #endif
3344a0ee8cc6SDag-Erling Smørgrav 	fprintf(stderr,
334519261079SEd Maste 	    "       ssh-keygen -F hostname [-lv] [-f known_hosts_file]\n"
3346a0ee8cc6SDag-Erling Smørgrav 	    "       ssh-keygen -H [-f known_hosts_file]\n"
334719261079SEd Maste 	    "       ssh-keygen -K [-a rounds] [-w provider]\n"
3348a0ee8cc6SDag-Erling Smørgrav 	    "       ssh-keygen -R hostname [-f known_hosts_file]\n"
334919261079SEd Maste 	    "       ssh-keygen -r hostname [-g] [-f input_keyfile]\n"
3350557f75e5SDag-Erling Smørgrav #ifdef WITH_OPENSSL
335119261079SEd Maste 	    "       ssh-keygen -M generate [-O option] output_file\n"
335219261079SEd Maste 	    "       ssh-keygen -M screen [-f input_file] [-O option] output_file\n"
3353557f75e5SDag-Erling Smørgrav #endif
335419261079SEd Maste 	    "       ssh-keygen -I certificate_identity -s ca_key [-hU] [-D pkcs11_provider]\n"
335519261079SEd Maste 	    "                  [-n principals] [-O option] [-V validity_interval]\n"
335619261079SEd Maste 	    "                  [-z serial_number] file ...\n"
3357a0ee8cc6SDag-Erling Smørgrav 	    "       ssh-keygen -L [-f input_keyfile]\n"
335819261079SEd Maste 	    "       ssh-keygen -A [-a rounds] [-f prefix_path]\n"
3359a0ee8cc6SDag-Erling Smørgrav 	    "       ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n"
3360a0ee8cc6SDag-Erling Smørgrav 	    "                  file ...\n"
336119261079SEd Maste 	    "       ssh-keygen -Q [-l] -f krl_file [file ...]\n"
336219261079SEd Maste 	    "       ssh-keygen -Y find-principals -s signature_file -f allowed_signers_file\n"
33631323ec57SEd Maste 	    "       ssh-keygen -Y match-principals -I signer_identity -f allowed_signers_file\n"
336419261079SEd Maste 	    "       ssh-keygen -Y check-novalidate -n namespace -s signature_file\n"
33651323ec57SEd Maste 	    "       ssh-keygen -Y sign -f key_file -n namespace file [-O option] ...\n"
336619261079SEd Maste 	    "       ssh-keygen -Y verify -f allowed_signers_file -I signer_identity\n"
33671323ec57SEd Maste 	    "                  -n namespace -s signature_file [-r krl_file] [-O option]\n");
3368511b41d2SMark Murray 	exit(1);
3369511b41d2SMark Murray }
3370511b41d2SMark Murray 
3371511b41d2SMark Murray /*
3372511b41d2SMark Murray  * Main program for key management.
3373511b41d2SMark Murray  */
3374511b41d2SMark Murray int
main(int argc,char ** argv)3375d4af9e69SDag-Erling Smørgrav main(int argc, char **argv)
3376511b41d2SMark Murray {
337738a52bd3SEd Maste 	char comment[1024], *passphrase = NULL;
3378557f75e5SDag-Erling Smørgrav 	char *rr_hostname = NULL, *ep, *fp, *ra;
3379bc5531deSDag-Erling Smørgrav 	struct sshkey *private, *public;
3380511b41d2SMark Murray 	struct passwd *pw;
338119261079SEd Maste 	int r, opt, type;
338219261079SEd Maste 	int change_passphrase = 0, change_comment = 0, show_cert = 0;
338319261079SEd Maste 	int find_host = 0, delete_host = 0, hash_hosts = 0;
33846888a9beSDag-Erling Smørgrav 	int gen_all_hostkeys = 0, gen_krl = 0, update_krl = 0, check_krl = 0;
338519261079SEd Maste 	int prefer_agent = 0, convert_to = 0, convert_from = 0;
338619261079SEd Maste 	int print_public = 0, print_generic = 0, cert_serial_autoinc = 0;
338719261079SEd Maste 	int do_gen_candidates = 0, do_screen_candidates = 0, download_sk = 0;
338819261079SEd Maste 	unsigned long long cert_serial = 0;
338919261079SEd Maste 	char *identity_comment = NULL, *ca_key_path = NULL, **opts = NULL;
339019261079SEd Maste 	char *sk_application = NULL, *sk_device = NULL, *sk_user = NULL;
339119261079SEd Maste 	char *sk_attestation_path = NULL;
339219261079SEd Maste 	struct sshbuf *challenge = NULL, *attest = NULL;
339319261079SEd Maste 	size_t i, nopts = 0;
339419261079SEd Maste 	u_int32_t bits = 0;
339519261079SEd Maste 	uint8_t sk_flags = SSH_SK_USER_PRESENCE_REQD;
3396043840dfSDag-Erling Smørgrav 	const char *errstr;
339719261079SEd Maste 	int log_level = SYSLOG_LEVEL_INFO;
339819261079SEd Maste 	char *sign_op = NULL;
33991e8db6e2SBrian Feldman 
3400511b41d2SMark Murray 	extern int optind;
3401511b41d2SMark Murray 	extern char *optarg;
3402511b41d2SMark Murray 
3403021d409fSDag-Erling Smørgrav 	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
3404021d409fSDag-Erling Smørgrav 	sanitise_stdfd();
3405021d409fSDag-Erling Smørgrav 
3406d4af9e69SDag-Erling Smørgrav 	__progname = ssh_get_progname(argv[0]);
340783d2307dSDag-Erling Smørgrav 
34084b17dab0SDag-Erling Smørgrav 	seed_rng();
3409a04a10f8SKris Kennaway 
341019261079SEd Maste 	log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1);
341119261079SEd Maste 
3412d93a896eSDag-Erling Smørgrav 	msetlocale();
3413d93a896eSDag-Erling Smørgrav 
3414511b41d2SMark Murray 	/* we need this for the home * directory.  */
3415511b41d2SMark Murray 	pw = getpwuid(getuid());
3416557f75e5SDag-Erling Smørgrav 	if (!pw)
3417557f75e5SDag-Erling Smørgrav 		fatal("No user exists for uid %lu", (u_long)getuid());
341819261079SEd Maste 	pw = pwcopy(pw);
341919261079SEd Maste 	if (gethostname(hostname, sizeof(hostname)) == -1)
3420557f75e5SDag-Erling Smørgrav 		fatal("gethostname: %s", strerror(errno));
3421511b41d2SMark Murray 
342219261079SEd Maste 	sk_provider = getenv("SSH_SK_PROVIDER");
342319261079SEd Maste 
342419261079SEd Maste 	/* Remaining characters: dGjJSTWx */
342519261079SEd Maste 	while ((opt = getopt(argc, argv, "ABHKLQUXceghiklopquvy"
342619261079SEd Maste 	    "C:D:E:F:I:M:N:O:P:R:V:Y:Z:"
342719261079SEd Maste 	    "a:b:f:g:m:n:r:s:t:w:z:")) != -1) {
3428511b41d2SMark Murray 		switch (opt) {
3429e146993eSDag-Erling Smørgrav 		case 'A':
3430e146993eSDag-Erling Smørgrav 			gen_all_hostkeys = 1;
3431e146993eSDag-Erling Smørgrav 			break;
3432511b41d2SMark Murray 		case 'b':
343319261079SEd Maste 			bits = (u_int32_t)strtonum(optarg, 1, UINT32_MAX,
343419261079SEd Maste 			    &errstr);
3435043840dfSDag-Erling Smørgrav 			if (errstr)
3436043840dfSDag-Erling Smørgrav 				fatal("Bits has bad value %s (%s)",
3437043840dfSDag-Erling Smørgrav 					optarg, errstr);
3438511b41d2SMark Murray 			break;
3439bc5531deSDag-Erling Smørgrav 		case 'E':
3440bc5531deSDag-Erling Smørgrav 			fingerprint_hash = ssh_digest_alg_by_name(optarg);
3441bc5531deSDag-Erling Smørgrav 			if (fingerprint_hash == -1)
3442bc5531deSDag-Erling Smørgrav 				fatal("Invalid hash algorithm \"%s\"", optarg);
3443bc5531deSDag-Erling Smørgrav 			break;
34445e8dbd04SDag-Erling Smørgrav 		case 'F':
34455e8dbd04SDag-Erling Smørgrav 			find_host = 1;
34465e8dbd04SDag-Erling Smørgrav 			rr_hostname = optarg;
34475e8dbd04SDag-Erling Smørgrav 			break;
34485e8dbd04SDag-Erling Smørgrav 		case 'H':
34495e8dbd04SDag-Erling Smørgrav 			hash_hosts = 1;
34505e8dbd04SDag-Erling Smørgrav 			break;
3451b15c8340SDag-Erling Smørgrav 		case 'I':
3452b15c8340SDag-Erling Smørgrav 			cert_key_id = optarg;
3453b15c8340SDag-Erling Smørgrav 			break;
34545e8dbd04SDag-Erling Smørgrav 		case 'R':
34555e8dbd04SDag-Erling Smørgrav 			delete_host = 1;
34565e8dbd04SDag-Erling Smørgrav 			rr_hostname = optarg;
34575e8dbd04SDag-Erling Smørgrav 			break;
3458b15c8340SDag-Erling Smørgrav 		case 'L':
3459b15c8340SDag-Erling Smørgrav 			show_cert = 1;
3460b15c8340SDag-Erling Smørgrav 			break;
3461511b41d2SMark Murray 		case 'l':
3462511b41d2SMark Murray 			print_fingerprint = 1;
3463511b41d2SMark Murray 			break;
34641e8db6e2SBrian Feldman 		case 'B':
34651e8db6e2SBrian Feldman 			print_bubblebabble = 1;
34661e8db6e2SBrian Feldman 			break;
3467e2f6069cSDag-Erling Smørgrav 		case 'm':
3468e2f6069cSDag-Erling Smørgrav 			if (strcasecmp(optarg, "RFC4716") == 0 ||
3469e2f6069cSDag-Erling Smørgrav 			    strcasecmp(optarg, "ssh2") == 0) {
3470e2f6069cSDag-Erling Smørgrav 				convert_format = FMT_RFC4716;
3471e2f6069cSDag-Erling Smørgrav 				break;
3472e2f6069cSDag-Erling Smørgrav 			}
3473e2f6069cSDag-Erling Smørgrav 			if (strcasecmp(optarg, "PKCS8") == 0) {
3474e2f6069cSDag-Erling Smørgrav 				convert_format = FMT_PKCS8;
347519261079SEd Maste 				private_key_format = SSHKEY_PRIVATE_PKCS8;
3476e2f6069cSDag-Erling Smørgrav 				break;
3477e2f6069cSDag-Erling Smørgrav 			}
3478e2f6069cSDag-Erling Smørgrav 			if (strcasecmp(optarg, "PEM") == 0) {
3479e2f6069cSDag-Erling Smørgrav 				convert_format = FMT_PEM;
348019261079SEd Maste 				private_key_format = SSHKEY_PRIVATE_PEM;
3481e2f6069cSDag-Erling Smørgrav 				break;
3482e2f6069cSDag-Erling Smørgrav 			}
3483e2f6069cSDag-Erling Smørgrav 			fatal("Unsupported conversion format \"%s\"", optarg);
3484b15c8340SDag-Erling Smørgrav 		case 'n':
3485b15c8340SDag-Erling Smørgrav 			cert_principals = optarg;
3486b15c8340SDag-Erling Smørgrav 			break;
3487f7167e0eSDag-Erling Smørgrav 		case 'o':
3488190cef3dSDag-Erling Smørgrav 			/* no-op; new format is already the default */
3489f7167e0eSDag-Erling Smørgrav 			break;
3490511b41d2SMark Murray 		case 'p':
3491511b41d2SMark Murray 			change_passphrase = 1;
3492511b41d2SMark Murray 			break;
3493511b41d2SMark Murray 		case 'c':
3494511b41d2SMark Murray 			change_comment = 1;
3495511b41d2SMark Murray 			break;
3496511b41d2SMark Murray 		case 'f':
3497557f75e5SDag-Erling Smørgrav 			if (strlcpy(identity_file, optarg,
3498557f75e5SDag-Erling Smørgrav 			    sizeof(identity_file)) >= sizeof(identity_file))
3499043840dfSDag-Erling Smørgrav 				fatal("Identity filename too long");
3500511b41d2SMark Murray 			have_identity = 1;
3501511b41d2SMark Murray 			break;
3502d95e11bfSDag-Erling Smørgrav 		case 'g':
3503d95e11bfSDag-Erling Smørgrav 			print_generic = 1;
3504d95e11bfSDag-Erling Smørgrav 			break;
350519261079SEd Maste 		case 'K':
350619261079SEd Maste 			download_sk = 1;
350719261079SEd Maste 			break;
3508511b41d2SMark Murray 		case 'P':
3509511b41d2SMark Murray 			identity_passphrase = optarg;
3510511b41d2SMark Murray 			break;
3511511b41d2SMark Murray 		case 'N':
3512511b41d2SMark Murray 			identity_new_passphrase = optarg;
3513511b41d2SMark Murray 			break;
35146888a9beSDag-Erling Smørgrav 		case 'Q':
35156888a9beSDag-Erling Smørgrav 			check_krl = 1;
35166888a9beSDag-Erling Smørgrav 			break;
3517b15c8340SDag-Erling Smørgrav 		case 'O':
351819261079SEd Maste 			opts = xrecallocarray(opts, nopts, nopts + 1,
351919261079SEd Maste 			    sizeof(*opts));
352019261079SEd Maste 			opts[nopts++] = xstrdup(optarg);
3521b15c8340SDag-Erling Smørgrav 			break;
3522f7167e0eSDag-Erling Smørgrav 		case 'Z':
352319261079SEd Maste 			openssh_format_cipher = optarg;
352419261079SEd Maste 			if (cipher_by_name(openssh_format_cipher) == NULL)
352519261079SEd Maste 				fatal("Invalid OpenSSH-format cipher '%s'",
352619261079SEd Maste 				    openssh_format_cipher);
3527f7167e0eSDag-Erling Smørgrav 			break;
3528511b41d2SMark Murray 		case 'C':
3529511b41d2SMark Murray 			identity_comment = optarg;
3530511b41d2SMark Murray 			break;
3531511b41d2SMark Murray 		case 'q':
3532511b41d2SMark Murray 			quiet = 1;
3533511b41d2SMark Murray 			break;
35341e8db6e2SBrian Feldman 		case 'e':
35351e8db6e2SBrian Feldman 			/* export key */
3536e2f6069cSDag-Erling Smørgrav 			convert_to = 1;
3537a04a10f8SKris Kennaway 			break;
3538b15c8340SDag-Erling Smørgrav 		case 'h':
3539b15c8340SDag-Erling Smørgrav 			cert_key_type = SSH2_CERT_TYPE_HOST;
3540e2f6069cSDag-Erling Smørgrav 			certflags_flags = 0;
3541b15c8340SDag-Erling Smørgrav 			break;
35426888a9beSDag-Erling Smørgrav 		case 'k':
35436888a9beSDag-Erling Smørgrav 			gen_krl = 1;
35446888a9beSDag-Erling Smørgrav 			break;
35451e8db6e2SBrian Feldman 		case 'i':
3546a04a10f8SKris Kennaway 		case 'X':
35471e8db6e2SBrian Feldman 			/* import key */
3548e2f6069cSDag-Erling Smørgrav 			convert_from = 1;
3549a04a10f8SKris Kennaway 			break;
3550a04a10f8SKris Kennaway 		case 'y':
3551a04a10f8SKris Kennaway 			print_public = 1;
3552a04a10f8SKris Kennaway 			break;
3553b15c8340SDag-Erling Smørgrav 		case 's':
3554b15c8340SDag-Erling Smørgrav 			ca_key_path = optarg;
3555b15c8340SDag-Erling Smørgrav 			break;
35561e8db6e2SBrian Feldman 		case 't':
35571e8db6e2SBrian Feldman 			key_type_name = optarg;
3558a04a10f8SKris Kennaway 			break;
3559ae1f160dSDag-Erling Smørgrav 		case 'D':
3560b15c8340SDag-Erling Smørgrav 			pkcs11provider = optarg;
3561ae1f160dSDag-Erling Smørgrav 			break;
35624f52dfbbSDag-Erling Smørgrav 		case 'U':
35634f52dfbbSDag-Erling Smørgrav 			prefer_agent = 1;
35644f52dfbbSDag-Erling Smørgrav 			break;
35656888a9beSDag-Erling Smørgrav 		case 'u':
35666888a9beSDag-Erling Smørgrav 			update_krl = 1;
35676888a9beSDag-Erling Smørgrav 			break;
3568efcad6b7SDag-Erling Smørgrav 		case 'v':
3569efcad6b7SDag-Erling Smørgrav 			if (log_level == SYSLOG_LEVEL_INFO)
3570efcad6b7SDag-Erling Smørgrav 				log_level = SYSLOG_LEVEL_DEBUG1;
3571efcad6b7SDag-Erling Smørgrav 			else {
3572efcad6b7SDag-Erling Smørgrav 				if (log_level >= SYSLOG_LEVEL_DEBUG1 &&
3573efcad6b7SDag-Erling Smørgrav 				    log_level < SYSLOG_LEVEL_DEBUG3)
3574efcad6b7SDag-Erling Smørgrav 					log_level++;
3575efcad6b7SDag-Erling Smørgrav 			}
3576efcad6b7SDag-Erling Smørgrav 			break;
3577d95e11bfSDag-Erling Smørgrav 		case 'r':
35785e8dbd04SDag-Erling Smørgrav 			rr_hostname = optarg;
3579d95e11bfSDag-Erling Smørgrav 			break;
3580557f75e5SDag-Erling Smørgrav 		case 'a':
3581557f75e5SDag-Erling Smørgrav 			rounds = (int)strtonum(optarg, 1, INT_MAX, &errstr);
3582557f75e5SDag-Erling Smørgrav 			if (errstr)
3583557f75e5SDag-Erling Smørgrav 				fatal("Invalid number: %s (%s)",
3584557f75e5SDag-Erling Smørgrav 					optarg, errstr);
3585557f75e5SDag-Erling Smørgrav 			break;
3586557f75e5SDag-Erling Smørgrav 		case 'V':
3587557f75e5SDag-Erling Smørgrav 			parse_cert_times(optarg);
3588557f75e5SDag-Erling Smørgrav 			break;
358919261079SEd Maste 		case 'Y':
359019261079SEd Maste 			sign_op = optarg;
359119261079SEd Maste 			break;
359219261079SEd Maste 		case 'w':
359319261079SEd Maste 			sk_provider = optarg;
359419261079SEd Maste 			break;
3595557f75e5SDag-Erling Smørgrav 		case 'z':
3596557f75e5SDag-Erling Smørgrav 			errno = 0;
359719261079SEd Maste 			if (*optarg == '+') {
359819261079SEd Maste 				cert_serial_autoinc = 1;
359919261079SEd Maste 				optarg++;
360019261079SEd Maste 			}
3601557f75e5SDag-Erling Smørgrav 			cert_serial = strtoull(optarg, &ep, 10);
3602557f75e5SDag-Erling Smørgrav 			if (*optarg < '0' || *optarg > '9' || *ep != '\0' ||
3603557f75e5SDag-Erling Smørgrav 			    (errno == ERANGE && cert_serial == ULLONG_MAX))
3604557f75e5SDag-Erling Smørgrav 				fatal("Invalid serial number \"%s\"", optarg);
3605557f75e5SDag-Erling Smørgrav 			break;
3606ca86bcf2SDag-Erling Smørgrav 		case 'M':
360719261079SEd Maste 			if (strcmp(optarg, "generate") == 0)
360819261079SEd Maste 				do_gen_candidates = 1;
360919261079SEd Maste 			else if (strcmp(optarg, "screen") == 0)
3610d95e11bfSDag-Erling Smørgrav 				do_screen_candidates = 1;
361119261079SEd Maste 			else
361219261079SEd Maste 				fatal("Unsupported moduli option %s", optarg);
3613d95e11bfSDag-Erling Smørgrav 			break;
3614511b41d2SMark Murray 		default:
3615511b41d2SMark Murray 			usage();
3616511b41d2SMark Murray 		}
3617511b41d2SMark Murray 	}
3618efcad6b7SDag-Erling Smørgrav 
361919261079SEd Maste #ifdef ENABLE_SK_INTERNAL
362019261079SEd Maste 	if (sk_provider == NULL)
362119261079SEd Maste 		sk_provider = "internal";
362219261079SEd Maste #endif
362319261079SEd Maste 
3624efcad6b7SDag-Erling Smørgrav 	/* reinit */
3625d4af9e69SDag-Erling Smørgrav 	log_init(argv[0], log_level, SYSLOG_FACILITY_USER, 1);
3626efcad6b7SDag-Erling Smørgrav 
3627b15c8340SDag-Erling Smørgrav 	argv += optind;
3628b15c8340SDag-Erling Smørgrav 	argc -= optind;
3629b15c8340SDag-Erling Smørgrav 
363019261079SEd Maste 	if (sign_op != NULL) {
363119261079SEd Maste 		if (strncmp(sign_op, "find-principals", 15) == 0) {
363219261079SEd Maste 			if (ca_key_path == NULL) {
363319261079SEd Maste 				error("Too few arguments for find-principals:"
363419261079SEd Maste 				    "missing signature file");
363519261079SEd Maste 				exit(1);
363619261079SEd Maste 			}
363719261079SEd Maste 			if (!have_identity) {
363819261079SEd Maste 				error("Too few arguments for find-principals:"
363919261079SEd Maste 				    "missing allowed keys file");
364019261079SEd Maste 				exit(1);
364119261079SEd Maste 			}
364219261079SEd Maste 			return sig_find_principals(ca_key_path, identity_file,
364319261079SEd Maste 			    opts, nopts);
36441323ec57SEd Maste 		} else if (strncmp(sign_op, "match-principals", 16) == 0) {
36451323ec57SEd Maste 			if (!have_identity) {
36461323ec57SEd Maste 				error("Too few arguments for match-principals:"
36471323ec57SEd Maste 				    "missing allowed keys file");
36481323ec57SEd Maste 				exit(1);
36491323ec57SEd Maste 			}
36501323ec57SEd Maste 			if (cert_key_id == NULL) {
36511323ec57SEd Maste 				error("Too few arguments for match-principals: "
36521323ec57SEd Maste 				    "missing principal ID");
36531323ec57SEd Maste 				exit(1);
36541323ec57SEd Maste 			}
36551323ec57SEd Maste 			return sig_match_principals(identity_file, cert_key_id,
36561323ec57SEd Maste 			    opts, nopts);
365719261079SEd Maste 		} else if (strncmp(sign_op, "sign", 4) == 0) {
36581323ec57SEd Maste 			/* NB. cert_principals is actually namespace, via -n */
365919261079SEd Maste 			if (cert_principals == NULL ||
366019261079SEd Maste 			    *cert_principals == '\0') {
366119261079SEd Maste 				error("Too few arguments for sign: "
366219261079SEd Maste 				    "missing namespace");
366319261079SEd Maste 				exit(1);
366419261079SEd Maste 			}
366519261079SEd Maste 			if (!have_identity) {
366619261079SEd Maste 				error("Too few arguments for sign: "
366719261079SEd Maste 				    "missing key");
366819261079SEd Maste 				exit(1);
366919261079SEd Maste 			}
367019261079SEd Maste 			return sig_sign(identity_file, cert_principals,
367138a52bd3SEd Maste 			    prefer_agent, argc, argv, opts, nopts);
367219261079SEd Maste 		} else if (strncmp(sign_op, "check-novalidate", 16) == 0) {
367387c1498dSEd Maste 			/* NB. cert_principals is actually namespace, via -n */
367487c1498dSEd Maste 			if (cert_principals == NULL ||
367587c1498dSEd Maste 			    *cert_principals == '\0') {
367687c1498dSEd Maste 				error("Too few arguments for check-novalidate: "
367787c1498dSEd Maste 				    "missing namespace");
367887c1498dSEd Maste 				exit(1);
367987c1498dSEd Maste 			}
368019261079SEd Maste 			if (ca_key_path == NULL) {
368119261079SEd Maste 				error("Too few arguments for check-novalidate: "
368219261079SEd Maste 				    "missing signature file");
368319261079SEd Maste 				exit(1);
368419261079SEd Maste 			}
368519261079SEd Maste 			return sig_verify(ca_key_path, cert_principals,
368619261079SEd Maste 			    NULL, NULL, NULL, opts, nopts);
368719261079SEd Maste 		} else if (strncmp(sign_op, "verify", 6) == 0) {
36881323ec57SEd Maste 			/* NB. cert_principals is actually namespace, via -n */
368919261079SEd Maste 			if (cert_principals == NULL ||
369019261079SEd Maste 			    *cert_principals == '\0') {
369119261079SEd Maste 				error("Too few arguments for verify: "
369219261079SEd Maste 				    "missing namespace");
369319261079SEd Maste 				exit(1);
369419261079SEd Maste 			}
369519261079SEd Maste 			if (ca_key_path == NULL) {
369619261079SEd Maste 				error("Too few arguments for verify: "
369719261079SEd Maste 				    "missing signature file");
369819261079SEd Maste 				exit(1);
369919261079SEd Maste 			}
370019261079SEd Maste 			if (!have_identity) {
370119261079SEd Maste 				error("Too few arguments for sign: "
370219261079SEd Maste 				    "missing allowed keys file");
370319261079SEd Maste 				exit(1);
370419261079SEd Maste 			}
370519261079SEd Maste 			if (cert_key_id == NULL) {
370619261079SEd Maste 				error("Too few arguments for verify: "
37071323ec57SEd Maste 				    "missing principal identity");
370819261079SEd Maste 				exit(1);
370919261079SEd Maste 			}
371019261079SEd Maste 			return sig_verify(ca_key_path, cert_principals,
371119261079SEd Maste 			    cert_key_id, identity_file, rr_hostname,
371219261079SEd Maste 			    opts, nopts);
371319261079SEd Maste 		}
371419261079SEd Maste 		error("Unsupported operation for -Y: \"%s\"", sign_op);
371519261079SEd Maste 		usage();
371619261079SEd Maste 		/* NOTREACHED */
371719261079SEd Maste 	}
371819261079SEd Maste 
3719b15c8340SDag-Erling Smørgrav 	if (ca_key_path != NULL) {
37206888a9beSDag-Erling Smørgrav 		if (argc < 1 && !gen_krl) {
3721557f75e5SDag-Erling Smørgrav 			error("Too few arguments.");
3722b15c8340SDag-Erling Smørgrav 			usage();
3723b15c8340SDag-Erling Smørgrav 		}
372419261079SEd Maste 	} else if (argc > 0 && !gen_krl && !check_krl &&
372519261079SEd Maste 	    !do_gen_candidates && !do_screen_candidates) {
3726557f75e5SDag-Erling Smørgrav 		error("Too many arguments.");
3727511b41d2SMark Murray 		usage();
3728511b41d2SMark Murray 	}
3729511b41d2SMark Murray 	if (change_passphrase && change_comment) {
3730557f75e5SDag-Erling Smørgrav 		error("Can only have one of -p and -c.");
3731511b41d2SMark Murray 		usage();
3732511b41d2SMark Murray 	}
3733d4af9e69SDag-Erling Smørgrav 	if (print_fingerprint && (delete_host || hash_hosts)) {
3734557f75e5SDag-Erling Smørgrav 		error("Cannot use -l with -H or -R.");
3735d4af9e69SDag-Erling Smørgrav 		usage();
3736d4af9e69SDag-Erling Smørgrav 	}
37376888a9beSDag-Erling Smørgrav 	if (gen_krl) {
373819261079SEd Maste 		do_gen_krl(pw, update_krl, ca_key_path,
373919261079SEd Maste 		    cert_serial, identity_comment, argc, argv);
37406888a9beSDag-Erling Smørgrav 		return (0);
37416888a9beSDag-Erling Smørgrav 	}
37426888a9beSDag-Erling Smørgrav 	if (check_krl) {
374319261079SEd Maste 		do_check_krl(pw, print_fingerprint, argc, argv);
37446888a9beSDag-Erling Smørgrav 		return (0);
37456888a9beSDag-Erling Smørgrav 	}
3746b15c8340SDag-Erling Smørgrav 	if (ca_key_path != NULL) {
3747b15c8340SDag-Erling Smørgrav 		if (cert_key_id == NULL)
3748b15c8340SDag-Erling Smørgrav 			fatal("Must specify key id (-I) when certifying");
374919261079SEd Maste 		for (i = 0; i < nopts; i++)
375019261079SEd Maste 			add_cert_option(opts[i]);
375119261079SEd Maste 		do_ca_sign(pw, ca_key_path, prefer_agent,
375219261079SEd Maste 		    cert_serial, cert_serial_autoinc, argc, argv);
3753b15c8340SDag-Erling Smørgrav 	}
3754b15c8340SDag-Erling Smørgrav 	if (show_cert)
3755b15c8340SDag-Erling Smørgrav 		do_show_cert(pw);
375619261079SEd Maste 	if (delete_host || hash_hosts || find_host) {
375719261079SEd Maste 		do_known_hosts(pw, rr_hostname, find_host,
375819261079SEd Maste 		    delete_host, hash_hosts);
375919261079SEd Maste 	}
37606888a9beSDag-Erling Smørgrav 	if (pkcs11provider != NULL)
37616888a9beSDag-Erling Smørgrav 		do_download(pw);
376219261079SEd Maste 	if (download_sk) {
376319261079SEd Maste 		for (i = 0; i < nopts; i++) {
376419261079SEd Maste 			if (strncasecmp(opts[i], "device=", 7) == 0) {
376519261079SEd Maste 				sk_device = xstrdup(opts[i] + 7);
376619261079SEd Maste 			} else {
376719261079SEd Maste 				fatal("Option \"%s\" is unsupported for "
376819261079SEd Maste 				    "FIDO authenticator download", opts[i]);
376919261079SEd Maste 			}
377019261079SEd Maste 		}
377119261079SEd Maste 		return do_download_sk(sk_provider, sk_device);
377219261079SEd Maste 	}
37731e8db6e2SBrian Feldman 	if (print_fingerprint || print_bubblebabble)
3774511b41d2SMark Murray 		do_fingerprint(pw);
3775511b41d2SMark Murray 	if (change_passphrase)
3776511b41d2SMark Murray 		do_change_passphrase(pw);
377783d2307dSDag-Erling Smørgrav 	if (change_comment)
377819261079SEd Maste 		do_change_comment(pw, identity_comment);
3779a0ee8cc6SDag-Erling Smørgrav #ifdef WITH_OPENSSL
3780e2f6069cSDag-Erling Smørgrav 	if (convert_to)
3781e2f6069cSDag-Erling Smørgrav 		do_convert_to(pw);
3782e2f6069cSDag-Erling Smørgrav 	if (convert_from)
3783e2f6069cSDag-Erling Smørgrav 		do_convert_from(pw);
378419261079SEd Maste #else /* WITH_OPENSSL */
378519261079SEd Maste 	if (convert_to || convert_from)
378619261079SEd Maste 		fatal("key conversion disabled at compile time");
378719261079SEd Maste #endif /* WITH_OPENSSL */
3788a04a10f8SKris Kennaway 	if (print_public)
3789a04a10f8SKris Kennaway 		do_print_public(pw);
37905e8dbd04SDag-Erling Smørgrav 	if (rr_hostname != NULL) {
3791761efaa7SDag-Erling Smørgrav 		unsigned int n = 0;
3792761efaa7SDag-Erling Smørgrav 
3793761efaa7SDag-Erling Smørgrav 		if (have_identity) {
379419261079SEd Maste 			n = do_print_resource_record(pw, identity_file,
37954d3fc8b0SEd Maste 			    rr_hostname, print_generic, opts, nopts);
3796557f75e5SDag-Erling Smørgrav 			if (n == 0)
3797557f75e5SDag-Erling Smørgrav 				fatal("%s: %s", identity_file, strerror(errno));
3798761efaa7SDag-Erling Smørgrav 			exit(0);
3799761efaa7SDag-Erling Smørgrav 		} else {
3800761efaa7SDag-Erling Smørgrav 
3801761efaa7SDag-Erling Smørgrav 			n += do_print_resource_record(pw,
380219261079SEd Maste 			    _PATH_HOST_RSA_KEY_FILE, rr_hostname,
38034d3fc8b0SEd Maste 			    print_generic, opts, nopts);
3804a91a2465SEd Maste #ifdef WITH_DSA
3805761efaa7SDag-Erling Smørgrav 			n += do_print_resource_record(pw,
380619261079SEd Maste 			    _PATH_HOST_DSA_KEY_FILE, rr_hostname,
38074d3fc8b0SEd Maste 			    print_generic, opts, nopts);
3808a91a2465SEd Maste #endif
3809462c32cbSDag-Erling Smørgrav 			n += do_print_resource_record(pw,
381019261079SEd Maste 			    _PATH_HOST_ECDSA_KEY_FILE, rr_hostname,
38114d3fc8b0SEd Maste 			    print_generic, opts, nopts);
3812a0ee8cc6SDag-Erling Smørgrav 			n += do_print_resource_record(pw,
381319261079SEd Maste 			    _PATH_HOST_ED25519_KEY_FILE, rr_hostname,
38144d3fc8b0SEd Maste 			    print_generic, opts, nopts);
381547dd1d1bSDag-Erling Smørgrav 			n += do_print_resource_record(pw,
381619261079SEd Maste 			    _PATH_HOST_XMSS_KEY_FILE, rr_hostname,
38174d3fc8b0SEd Maste 			    print_generic, opts, nopts);
3818761efaa7SDag-Erling Smørgrav 			if (n == 0)
3819761efaa7SDag-Erling Smørgrav 				fatal("no keys found.");
3820761efaa7SDag-Erling Smørgrav 			exit(0);
3821761efaa7SDag-Erling Smørgrav 		}
3822d95e11bfSDag-Erling Smørgrav 	}
3823511b41d2SMark Murray 
382419261079SEd Maste 	if (do_gen_candidates || do_screen_candidates) {
382519261079SEd Maste 		if (argc <= 0)
382619261079SEd Maste 			fatal("No output file specified");
382719261079SEd Maste 		else if (argc > 1)
382819261079SEd Maste 			fatal("Too many output files specified");
382919261079SEd Maste 	}
3830d95e11bfSDag-Erling Smørgrav 	if (do_gen_candidates) {
383119261079SEd Maste 		do_moduli_gen(argv[0], opts, nopts);
383219261079SEd Maste 		return 0;
3833d95e11bfSDag-Erling Smørgrav 	}
3834d95e11bfSDag-Erling Smørgrav 	if (do_screen_candidates) {
383519261079SEd Maste 		do_moduli_screen(argv[0], opts, nopts);
383619261079SEd Maste 		return 0;
3837d95e11bfSDag-Erling Smørgrav 	}
3838d95e11bfSDag-Erling Smørgrav 
3839e146993eSDag-Erling Smørgrav 	if (gen_all_hostkeys) {
3840e146993eSDag-Erling Smørgrav 		do_gen_all_hostkeys(pw);
3841e146993eSDag-Erling Smørgrav 		return (0);
3842e146993eSDag-Erling Smørgrav 	}
3843e146993eSDag-Erling Smørgrav 
3844021d409fSDag-Erling Smørgrav 	if (key_type_name == NULL)
3845557f75e5SDag-Erling Smørgrav 		key_type_name = DEFAULT_KEY_TYPE_NAME;
3846021d409fSDag-Erling Smørgrav 
3847*3d9fd9fcSEd Maste 	type = sshkey_type_from_shortname(key_type_name);
3848bc5531deSDag-Erling Smørgrav 	type_bits_valid(type, key_type_name, &bits);
3849e146993eSDag-Erling Smørgrav 
38501e8db6e2SBrian Feldman 	if (!quiet)
3851bc5531deSDag-Erling Smørgrav 		printf("Generating public/private %s key pair.\n",
3852bc5531deSDag-Erling Smørgrav 		    key_type_name);
385319261079SEd Maste 	switch (type) {
385419261079SEd Maste 	case KEY_ECDSA_SK:
385519261079SEd Maste 	case KEY_ED25519_SK:
385619261079SEd Maste 		for (i = 0; i < nopts; i++) {
385719261079SEd Maste 			if (strcasecmp(opts[i], "no-touch-required") == 0) {
385819261079SEd Maste 				sk_flags &= ~SSH_SK_USER_PRESENCE_REQD;
385919261079SEd Maste 			} else if (strcasecmp(opts[i], "verify-required") == 0) {
386019261079SEd Maste 				sk_flags |= SSH_SK_USER_VERIFICATION_REQD;
386119261079SEd Maste 			} else if (strcasecmp(opts[i], "resident") == 0) {
386219261079SEd Maste 				sk_flags |= SSH_SK_RESIDENT_KEY;
386319261079SEd Maste 			} else if (strncasecmp(opts[i], "device=", 7) == 0) {
386419261079SEd Maste 				sk_device = xstrdup(opts[i] + 7);
386519261079SEd Maste 			} else if (strncasecmp(opts[i], "user=", 5) == 0) {
386619261079SEd Maste 				sk_user = xstrdup(opts[i] + 5);
386719261079SEd Maste 			} else if (strncasecmp(opts[i], "challenge=", 10) == 0) {
386819261079SEd Maste 				if ((r = sshbuf_load_file(opts[i] + 10,
386919261079SEd Maste 				    &challenge)) != 0) {
387019261079SEd Maste 					fatal_r(r, "Unable to load FIDO "
387119261079SEd Maste 					    "enrollment challenge \"%s\"",
387219261079SEd Maste 					    opts[i] + 10);
387319261079SEd Maste 				}
387419261079SEd Maste 			} else if (strncasecmp(opts[i],
387519261079SEd Maste 			    "write-attestation=", 18) == 0) {
387619261079SEd Maste 				sk_attestation_path = opts[i] + 18;
387719261079SEd Maste 			} else if (strncasecmp(opts[i],
387819261079SEd Maste 			    "application=", 12) == 0) {
387919261079SEd Maste 				sk_application = xstrdup(opts[i] + 12);
388019261079SEd Maste 				if (strncmp(sk_application, "ssh:", 4) != 0) {
388119261079SEd Maste 					fatal("FIDO application string must "
388219261079SEd Maste 					    "begin with \"ssh:\"");
388319261079SEd Maste 				}
388419261079SEd Maste 			} else {
388519261079SEd Maste 				fatal("Option \"%s\" is unsupported for "
388619261079SEd Maste 				    "FIDO authenticator enrollment", opts[i]);
388719261079SEd Maste 			}
388819261079SEd Maste 		}
388919261079SEd Maste 		if ((attest = sshbuf_new()) == NULL)
389019261079SEd Maste 			fatal("sshbuf_new failed");
389138a52bd3SEd Maste 		r = 0;
389238a52bd3SEd Maste 		for (i = 0 ;;) {
389338a52bd3SEd Maste 			if (!quiet) {
389438a52bd3SEd Maste 				printf("You may need to touch your "
389538a52bd3SEd Maste 				    "authenticator%s to authorize key "
389638a52bd3SEd Maste 				    "generation.\n",
389738a52bd3SEd Maste 				    r == 0 ? "" : " again");
389819261079SEd Maste 			}
389919261079SEd Maste 			fflush(stdout);
390019261079SEd Maste 			r = sshsk_enroll(type, sk_provider, sk_device,
390119261079SEd Maste 			    sk_application == NULL ? "ssh:" : sk_application,
390219261079SEd Maste 			    sk_user, sk_flags, passphrase, challenge,
390319261079SEd Maste 			    &private, attest);
390419261079SEd Maste 			if (r == 0)
390519261079SEd Maste 				break;
390638a52bd3SEd Maste 			if (r == SSH_ERR_KEY_BAD_PERMISSIONS &&
390738a52bd3SEd Maste 			    (sk_flags & SSH_SK_RESIDENT_KEY) != 0 &&
390838a52bd3SEd Maste 			    (sk_flags & SSH_SK_FORCE_OPERATION) == 0 &&
390938a52bd3SEd Maste 			    confirm_sk_overwrite(sk_application, sk_user)) {
391038a52bd3SEd Maste 				sk_flags |= SSH_SK_FORCE_OPERATION;
391138a52bd3SEd Maste 				continue;
391238a52bd3SEd Maste 			}
391319261079SEd Maste 			if (r != SSH_ERR_KEY_WRONG_PASSPHRASE)
391419261079SEd Maste 				fatal_r(r, "Key enrollment failed");
391519261079SEd Maste 			else if (passphrase != NULL) {
391619261079SEd Maste 				error("PIN incorrect");
391719261079SEd Maste 				freezero(passphrase, strlen(passphrase));
391819261079SEd Maste 				passphrase = NULL;
391919261079SEd Maste 			}
392038a52bd3SEd Maste 			if (++i >= 3)
392119261079SEd Maste 				fatal("Too many incorrect PINs");
392219261079SEd Maste 			passphrase = read_passphrase("Enter PIN for "
392319261079SEd Maste 			    "authenticator: ", RP_ALLOW_STDIN);
392419261079SEd Maste 		}
392519261079SEd Maste 		if (passphrase != NULL) {
392619261079SEd Maste 			freezero(passphrase, strlen(passphrase));
392719261079SEd Maste 			passphrase = NULL;
392819261079SEd Maste 		}
392919261079SEd Maste 		break;
393019261079SEd Maste 	default:
3931557f75e5SDag-Erling Smørgrav 		if ((r = sshkey_generate(type, bits, &private)) != 0)
39324f52dfbbSDag-Erling Smørgrav 			fatal("sshkey_generate failed");
393319261079SEd Maste 		break;
393419261079SEd Maste 	}
3935557f75e5SDag-Erling Smørgrav 	if ((r = sshkey_from_private(private, &public)) != 0)
393619261079SEd Maste 		fatal_r(r, "sshkey_from_private");
3937511b41d2SMark Murray 
3938511b41d2SMark Murray 	if (!have_identity)
3939511b41d2SMark Murray 		ask_filename(pw, "Enter file in which to save the key");
3940511b41d2SMark Murray 
3941021d409fSDag-Erling Smørgrav 	/* Create ~/.ssh directory if it doesn't already exist. */
394219261079SEd Maste 	hostfile_create_user_ssh_dir(identity_file, !quiet);
3943511b41d2SMark Murray 
394419261079SEd Maste 	/* If the file already exists, ask the user to confirm. */
394519261079SEd Maste 	if (!confirm_overwrite(identity_file))
394619261079SEd Maste 		exit(1);
394719261079SEd Maste 
394819261079SEd Maste 	/* Determine the passphrase for the private key */
3949*3d9fd9fcSEd Maste 	passphrase = private_key_passphrase(identity_file);
3950511b41d2SMark Murray 	if (identity_comment) {
3951511b41d2SMark Murray 		strlcpy(comment, identity_comment, sizeof(comment));
3952511b41d2SMark Murray 	} else {
3953cce7d346SDag-Erling Smørgrav 		/* Create default comment field for the passphrase. */
3954511b41d2SMark Murray 		snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname);
3955511b41d2SMark Murray 	}
3956511b41d2SMark Murray 
3957511b41d2SMark Murray 	/* Save the key with the given passphrase and comment. */
395819261079SEd Maste 	if ((r = sshkey_save_private(private, identity_file, passphrase,
395919261079SEd Maste 	    comment, private_key_format, openssh_format_cipher, rounds)) != 0) {
396019261079SEd Maste 		error_r(r, "Saving key \"%s\" failed", identity_file);
396119261079SEd Maste 		freezero(passphrase, strlen(passphrase));
3962511b41d2SMark Murray 		exit(1);
3963511b41d2SMark Murray 	}
396419261079SEd Maste 	freezero(passphrase, strlen(passphrase));
3965bc5531deSDag-Erling Smørgrav 	sshkey_free(private);
3966511b41d2SMark Murray 
396719261079SEd Maste 	if (!quiet) {
396819261079SEd Maste 		printf("Your identification has been saved in %s\n",
396919261079SEd Maste 		    identity_file);
397019261079SEd Maste 	}
3971511b41d2SMark Murray 
3972511b41d2SMark Murray 	strlcat(identity_file, ".pub", sizeof(identity_file));
397319261079SEd Maste 	if ((r = sshkey_save_public(public, identity_file, comment)) != 0)
397419261079SEd Maste 		fatal_r(r, "Unable to save public key to %s", identity_file);
3975511b41d2SMark Murray 
3976511b41d2SMark Murray 	if (!quiet) {
3977bc5531deSDag-Erling Smørgrav 		fp = sshkey_fingerprint(public, fingerprint_hash,
3978bc5531deSDag-Erling Smørgrav 		    SSH_FP_DEFAULT);
3979bc5531deSDag-Erling Smørgrav 		ra = sshkey_fingerprint(public, fingerprint_hash,
3980d4af9e69SDag-Erling Smørgrav 		    SSH_FP_RANDOMART);
3981bc5531deSDag-Erling Smørgrav 		if (fp == NULL || ra == NULL)
3982bc5531deSDag-Erling Smørgrav 			fatal("sshkey_fingerprint failed");
398319261079SEd Maste 		printf("Your public key has been saved in %s\n",
3984a04a10f8SKris Kennaway 		    identity_file);
3985511b41d2SMark Murray 		printf("The key fingerprint is:\n");
39861e8db6e2SBrian Feldman 		printf("%s %s\n", fp, comment);
3987d4af9e69SDag-Erling Smørgrav 		printf("The key's randomart image is:\n");
3988d4af9e69SDag-Erling Smørgrav 		printf("%s\n", ra);
3989e4a9863fSDag-Erling Smørgrav 		free(ra);
3990e4a9863fSDag-Erling Smørgrav 		free(fp);
3991511b41d2SMark Murray 	}
3992a04a10f8SKris Kennaway 
399319261079SEd Maste 	if (sk_attestation_path != NULL)
399419261079SEd Maste 		save_attestation(attest, sk_attestation_path);
399519261079SEd Maste 
399619261079SEd Maste 	sshbuf_free(attest);
3997bc5531deSDag-Erling Smørgrav 	sshkey_free(public);
399819261079SEd Maste 
3999511b41d2SMark Murray 	exit(0);
4000511b41d2SMark Murray }
4001