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