1*ba1276acSMatthew Dillon /* $OpenBSD: ssh-keygen.c,v 1.472 2024/01/11 01:45:36 djm Exp $ */
218de8d7fSPeter Avalos /*
318de8d7fSPeter Avalos * Author: Tatu Ylonen <ylo@cs.hut.fi>
418de8d7fSPeter Avalos * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
518de8d7fSPeter Avalos * All rights reserved
618de8d7fSPeter Avalos * Identity and host key generation and maintenance.
718de8d7fSPeter Avalos *
818de8d7fSPeter Avalos * As far as I am concerned, the code I have written for this software
918de8d7fSPeter Avalos * can be used freely for any purpose. Any derived versions of this
1018de8d7fSPeter Avalos * software must be clearly marked as such, and if the derived work is
1118de8d7fSPeter Avalos * incompatible with the protocol description in the RFC file, it must be
1218de8d7fSPeter Avalos * called by a name other than "ssh" or "Secure Shell".
1318de8d7fSPeter Avalos */
1418de8d7fSPeter Avalos
1518de8d7fSPeter Avalos #include "includes.h"
1618de8d7fSPeter Avalos
1718de8d7fSPeter Avalos #include <sys/types.h>
1818de8d7fSPeter Avalos #include <sys/socket.h>
1918de8d7fSPeter Avalos #include <sys/stat.h>
2018de8d7fSPeter Avalos
21e9778795SPeter Avalos #ifdef WITH_OPENSSL
2218de8d7fSPeter Avalos #include <openssl/evp.h>
2318de8d7fSPeter Avalos #include <openssl/pem.h>
2418de8d7fSPeter Avalos #include "openbsd-compat/openssl-compat.h"
25e9778795SPeter Avalos #endif
2618de8d7fSPeter Avalos
270cbfa66cSDaniel Fojt #ifdef HAVE_STDINT_H
280cbfa66cSDaniel Fojt # include <stdint.h>
290cbfa66cSDaniel Fojt #endif
3018de8d7fSPeter Avalos #include <errno.h>
3118de8d7fSPeter Avalos #include <fcntl.h>
3218de8d7fSPeter Avalos #include <netdb.h>
3318de8d7fSPeter Avalos #ifdef HAVE_PATHS_H
3418de8d7fSPeter Avalos # include <paths.h>
3518de8d7fSPeter Avalos #endif
3618de8d7fSPeter Avalos #include <pwd.h>
3718de8d7fSPeter Avalos #include <stdarg.h>
3818de8d7fSPeter Avalos #include <stdio.h>
3918de8d7fSPeter Avalos #include <stdlib.h>
4018de8d7fSPeter Avalos #include <string.h>
4118de8d7fSPeter Avalos #include <unistd.h>
42e9778795SPeter Avalos #include <limits.h>
43ce74bacaSMatthew Dillon #include <locale.h>
44664f4763Szrj #include <time.h>
4518de8d7fSPeter Avalos
4618de8d7fSPeter Avalos #include "xmalloc.h"
47e9778795SPeter Avalos #include "sshkey.h"
4818de8d7fSPeter Avalos #include "authfile.h"
49e9778795SPeter Avalos #include "sshbuf.h"
5018de8d7fSPeter Avalos #include "pathnames.h"
5118de8d7fSPeter Avalos #include "log.h"
5218de8d7fSPeter Avalos #include "misc.h"
5318de8d7fSPeter Avalos #include "match.h"
5418de8d7fSPeter Avalos #include "hostfile.h"
5518de8d7fSPeter Avalos #include "dns.h"
5636e94dc5SPeter Avalos #include "ssh.h"
57856ea928SPeter Avalos #include "ssh2.h"
58e9778795SPeter Avalos #include "ssherr.h"
59856ea928SPeter Avalos #include "ssh-pkcs11.h"
6036e94dc5SPeter Avalos #include "atomicio.h"
6136e94dc5SPeter Avalos #include "krl.h"
62e9778795SPeter Avalos #include "digest.h"
63ce74bacaSMatthew Dillon #include "utf8.h"
64ce74bacaSMatthew Dillon #include "authfd.h"
650cbfa66cSDaniel Fojt #include "sshsig.h"
660cbfa66cSDaniel Fojt #include "ssh-sk.h"
670cbfa66cSDaniel Fojt #include "sk-api.h" /* XXX for SSH_SK_USER_PRESENCE_REQD; remove */
6850a69bb5SSascha Wildner #include "cipher.h"
69e9778795SPeter Avalos
70e9778795SPeter Avalos #define DEFAULT_KEY_TYPE_NAME "ed25519"
7118de8d7fSPeter Avalos
72664f4763Szrj /*
73664f4763Szrj * Default number of bits in the RSA, DSA and ECDSA keys. These value can be
74664f4763Szrj * overridden on the command line.
75664f4763Szrj *
76664f4763Szrj * These values, with the exception of DSA, provide security equivalent to at
77664f4763Szrj * least 128 bits of security according to NIST Special Publication 800-57:
78664f4763Szrj * Recommendation for Key Management Part 1 rev 4 section 5.6.1.
79664f4763Szrj * For DSA it (and FIPS-186-4 section 4.2) specifies that the only size for
80664f4763Szrj * which a 160bit hash is acceptable is 1kbit, and since ssh-dss specifies only
81664f4763Szrj * SHA1 we limit the DSA key size 1k bits.
82664f4763Szrj */
83664f4763Szrj #define DEFAULT_BITS 3072
8418de8d7fSPeter Avalos #define DEFAULT_BITS_DSA 1024
859f304aafSPeter Avalos #define DEFAULT_BITS_ECDSA 256
8618de8d7fSPeter Avalos
87664f4763Szrj static int quiet = 0;
88856ea928SPeter Avalos
8918de8d7fSPeter Avalos /* Flag indicating that we just want to see the key fingerprint */
90664f4763Szrj static int print_fingerprint = 0;
91664f4763Szrj static int print_bubblebabble = 0;
9218de8d7fSPeter Avalos
93e9778795SPeter Avalos /* Hash algorithm to use for fingerprints. */
94664f4763Szrj static int fingerprint_hash = SSH_FP_HASH_DEFAULT;
95e9778795SPeter Avalos
9618de8d7fSPeter Avalos /* The identity file name, given on the command line or entered by the user. */
970cbfa66cSDaniel Fojt static char identity_file[PATH_MAX];
98664f4763Szrj static int have_identity = 0;
9918de8d7fSPeter Avalos
10018de8d7fSPeter Avalos /* This is set to the passphrase if given on the command line. */
101664f4763Szrj static char *identity_passphrase = NULL;
10218de8d7fSPeter Avalos
10318de8d7fSPeter Avalos /* This is set to the new passphrase if given on the command line. */
104664f4763Szrj static char *identity_new_passphrase = NULL;
105856ea928SPeter Avalos
106856ea928SPeter Avalos /* Key type when certifying */
107664f4763Szrj static u_int cert_key_type = SSH2_CERT_TYPE_USER;
108856ea928SPeter Avalos
109856ea928SPeter Avalos /* "key ID" of signed key */
110664f4763Szrj static char *cert_key_id = NULL;
111856ea928SPeter Avalos
112856ea928SPeter Avalos /* Comma-separated list of principal names for certifying keys */
113664f4763Szrj static char *cert_principals = NULL;
114856ea928SPeter Avalos
115856ea928SPeter Avalos /* Validity period for certificates */
116664f4763Szrj static u_int64_t cert_valid_from = 0;
117664f4763Szrj static u_int64_t cert_valid_to = ~0ULL;
118856ea928SPeter Avalos
119856ea928SPeter Avalos /* Certificate options */
120856ea928SPeter Avalos #define CERTOPT_X_FWD (1)
121856ea928SPeter Avalos #define CERTOPT_AGENT_FWD (1<<1)
122856ea928SPeter Avalos #define CERTOPT_PORT_FWD (1<<2)
123856ea928SPeter Avalos #define CERTOPT_PTY (1<<3)
124856ea928SPeter Avalos #define CERTOPT_USER_RC (1<<4)
1250cbfa66cSDaniel Fojt #define CERTOPT_NO_REQUIRE_USER_PRESENCE (1<<5)
126ee116499SAntonio Huete Jimenez #define CERTOPT_REQUIRE_VERIFY (1<<6)
127856ea928SPeter Avalos #define CERTOPT_DEFAULT (CERTOPT_X_FWD|CERTOPT_AGENT_FWD| \
128856ea928SPeter Avalos CERTOPT_PORT_FWD|CERTOPT_PTY|CERTOPT_USER_RC)
129664f4763Szrj static u_int32_t certflags_flags = CERTOPT_DEFAULT;
130664f4763Szrj static char *certflags_command = NULL;
131664f4763Szrj static char *certflags_src_addr = NULL;
132856ea928SPeter Avalos
133ce74bacaSMatthew Dillon /* Arbitrary extensions specified by user */
13450a69bb5SSascha Wildner struct cert_ext {
135ce74bacaSMatthew Dillon char *key;
136ce74bacaSMatthew Dillon char *val;
137ce74bacaSMatthew Dillon int crit;
138ce74bacaSMatthew Dillon };
13950a69bb5SSascha Wildner static struct cert_ext *cert_ext;
14050a69bb5SSascha Wildner static size_t ncert_ext;
141ce74bacaSMatthew Dillon
142856ea928SPeter Avalos /* Conversion to/from various formats */
143856ea928SPeter Avalos enum {
144856ea928SPeter Avalos FMT_RFC4716,
145856ea928SPeter Avalos FMT_PKCS8,
146856ea928SPeter Avalos FMT_PEM
147856ea928SPeter Avalos } convert_format = FMT_RFC4716;
14818de8d7fSPeter Avalos
149664f4763Szrj static char *key_type_name = NULL;
15018de8d7fSPeter Avalos
151856ea928SPeter Avalos /* Load key from this PKCS#11 provider */
152664f4763Szrj static char *pkcs11provider = NULL;
153856ea928SPeter Avalos
1540cbfa66cSDaniel Fojt /* FIDO/U2F provider to use */
1550cbfa66cSDaniel Fojt static char *sk_provider = NULL;
1560cbfa66cSDaniel Fojt
1570cbfa66cSDaniel Fojt /* Format for writing private keys */
1580cbfa66cSDaniel Fojt static int private_key_format = SSHKEY_PRIVATE_OPENSSH;
15936e94dc5SPeter Avalos
16036e94dc5SPeter Avalos /* Cipher for new-format private keys */
1610cbfa66cSDaniel Fojt static char *openssh_format_cipher = NULL;
16236e94dc5SPeter Avalos
1630cbfa66cSDaniel Fojt /* Number of KDF rounds to derive new format keys. */
164664f4763Szrj static int rounds = 0;
16536e94dc5SPeter Avalos
16618de8d7fSPeter Avalos /* argv0 */
16718de8d7fSPeter Avalos extern char *__progname;
16818de8d7fSPeter Avalos
169664f4763Szrj static char hostname[NI_MAXHOST];
17018de8d7fSPeter Avalos
171e9778795SPeter Avalos #ifdef WITH_OPENSSL
17218de8d7fSPeter Avalos /* moduli.c */
17318de8d7fSPeter Avalos int gen_candidates(FILE *, u_int32_t, u_int32_t, BIGNUM *);
17499e85e0dSPeter Avalos int prime_test(FILE *, FILE *, u_int32_t, u_int32_t, char *, unsigned long,
17599e85e0dSPeter Avalos unsigned long);
176e9778795SPeter Avalos #endif
17718de8d7fSPeter Avalos
17818de8d7fSPeter Avalos static void
type_bits_valid(int type,const char * name,u_int32_t * bitsp)179e9778795SPeter Avalos type_bits_valid(int type, const char *name, u_int32_t *bitsp)
1801c188a7fSPeter Avalos {
181e9778795SPeter Avalos if (type == KEY_UNSPEC)
182e9778795SPeter Avalos fatal("unknown key type %s", key_type_name);
1831c188a7fSPeter Avalos if (*bitsp == 0) {
184e9778795SPeter Avalos #ifdef WITH_OPENSSL
18550a69bb5SSascha Wildner int nid;
1860cbfa66cSDaniel Fojt
1870cbfa66cSDaniel Fojt switch(type) {
1880cbfa66cSDaniel Fojt case KEY_DSA:
1891c188a7fSPeter Avalos *bitsp = DEFAULT_BITS_DSA;
1900cbfa66cSDaniel Fojt break;
1910cbfa66cSDaniel Fojt case KEY_ECDSA:
192e9778795SPeter Avalos if (name != NULL &&
193e9778795SPeter Avalos (nid = sshkey_ecdsa_nid_from_name(name)) > 0)
194e9778795SPeter Avalos *bitsp = sshkey_curve_nid_to_bits(nid);
195e9778795SPeter Avalos if (*bitsp == 0)
1961c188a7fSPeter Avalos *bitsp = DEFAULT_BITS_ECDSA;
1970cbfa66cSDaniel Fojt break;
1980cbfa66cSDaniel Fojt case KEY_RSA:
1991c188a7fSPeter Avalos *bitsp = DEFAULT_BITS;
2000cbfa66cSDaniel Fojt break;
2010cbfa66cSDaniel Fojt }
2020cbfa66cSDaniel Fojt #endif
2031c188a7fSPeter Avalos }
204e9778795SPeter Avalos #ifdef WITH_OPENSSL
205ce74bacaSMatthew Dillon switch (type) {
206ce74bacaSMatthew Dillon case KEY_DSA:
207ce74bacaSMatthew Dillon if (*bitsp != 1024)
208ce74bacaSMatthew Dillon fatal("Invalid DSA key length: must be 1024 bits");
209ce74bacaSMatthew Dillon break;
210ce74bacaSMatthew Dillon case KEY_RSA:
211ce74bacaSMatthew Dillon if (*bitsp < SSH_RSA_MINIMUM_MODULUS_SIZE)
212ce74bacaSMatthew Dillon fatal("Invalid RSA key length: minimum is %d bits",
213ce74bacaSMatthew Dillon SSH_RSA_MINIMUM_MODULUS_SIZE);
2140cbfa66cSDaniel Fojt else if (*bitsp > OPENSSL_RSA_MAX_MODULUS_BITS)
2150cbfa66cSDaniel Fojt fatal("Invalid RSA key length: maximum is %d bits",
2160cbfa66cSDaniel Fojt OPENSSL_RSA_MAX_MODULUS_BITS);
217ce74bacaSMatthew Dillon break;
218ce74bacaSMatthew Dillon case KEY_ECDSA:
219ce74bacaSMatthew Dillon if (sshkey_ecdsa_bits_to_nid(*bitsp) == -1)
220664f4763Szrj #ifdef OPENSSL_HAS_NISTP521
22150a69bb5SSascha Wildner fatal("Invalid ECDSA key length: valid lengths are "
2221c188a7fSPeter Avalos "256, 384 or 521 bits");
223664f4763Szrj #else
22450a69bb5SSascha Wildner fatal("Invalid ECDSA key length: valid lengths are "
225664f4763Szrj "256 or 384 bits");
226664f4763Szrj #endif
227ce74bacaSMatthew Dillon }
22836e94dc5SPeter Avalos #endif
2291c188a7fSPeter Avalos }
2301c188a7fSPeter Avalos
2310cbfa66cSDaniel Fojt /*
2320cbfa66cSDaniel Fojt * Checks whether a file exists and, if so, asks the user whether they wish
2330cbfa66cSDaniel Fojt * to overwrite it.
2340cbfa66cSDaniel Fojt * Returns nonzero if the file does not already exist or if the user agrees to
2350cbfa66cSDaniel Fojt * overwrite, or zero otherwise.
2360cbfa66cSDaniel Fojt */
2370cbfa66cSDaniel Fojt static int
confirm_overwrite(const char * filename)2380cbfa66cSDaniel Fojt confirm_overwrite(const char *filename)
2390cbfa66cSDaniel Fojt {
2400cbfa66cSDaniel Fojt char yesno[3];
2410cbfa66cSDaniel Fojt struct stat st;
2420cbfa66cSDaniel Fojt
2430cbfa66cSDaniel Fojt if (stat(filename, &st) != 0)
2440cbfa66cSDaniel Fojt return 1;
2450cbfa66cSDaniel Fojt printf("%s already exists.\n", filename);
2460cbfa66cSDaniel Fojt printf("Overwrite (y/n)? ");
2470cbfa66cSDaniel Fojt fflush(stdout);
2480cbfa66cSDaniel Fojt if (fgets(yesno, sizeof(yesno), stdin) == NULL)
2490cbfa66cSDaniel Fojt return 0;
2500cbfa66cSDaniel Fojt if (yesno[0] != 'y' && yesno[0] != 'Y')
2510cbfa66cSDaniel Fojt return 0;
2520cbfa66cSDaniel Fojt return 1;
2530cbfa66cSDaniel Fojt }
2540cbfa66cSDaniel Fojt
2551c188a7fSPeter Avalos static void
ask_filename(struct passwd * pw,const char * prompt)25618de8d7fSPeter Avalos ask_filename(struct passwd *pw, const char *prompt)
25718de8d7fSPeter Avalos {
25818de8d7fSPeter Avalos char buf[1024];
25918de8d7fSPeter Avalos char *name = NULL;
26018de8d7fSPeter Avalos
26118de8d7fSPeter Avalos if (key_type_name == NULL)
262*ba1276acSMatthew Dillon name = _PATH_SSH_CLIENT_ID_ED25519;
26318de8d7fSPeter Avalos else {
264e9778795SPeter Avalos switch (sshkey_type_from_name(key_type_name)) {
265*ba1276acSMatthew Dillon #ifdef WITH_DSA
266856ea928SPeter Avalos case KEY_DSA_CERT:
26718de8d7fSPeter Avalos case KEY_DSA:
26818de8d7fSPeter Avalos name = _PATH_SSH_CLIENT_ID_DSA;
26918de8d7fSPeter Avalos break;
270*ba1276acSMatthew Dillon #endif
2719f304aafSPeter Avalos #ifdef OPENSSL_HAS_ECC
2729f304aafSPeter Avalos case KEY_ECDSA_CERT:
2739f304aafSPeter Avalos case KEY_ECDSA:
2749f304aafSPeter Avalos name = _PATH_SSH_CLIENT_ID_ECDSA;
2759f304aafSPeter Avalos break;
2760cbfa66cSDaniel Fojt case KEY_ECDSA_SK_CERT:
2770cbfa66cSDaniel Fojt case KEY_ECDSA_SK:
2780cbfa66cSDaniel Fojt name = _PATH_SSH_CLIENT_ID_ECDSA_SK;
2790cbfa66cSDaniel Fojt break;
2809f304aafSPeter Avalos #endif
281856ea928SPeter Avalos case KEY_RSA_CERT:
28218de8d7fSPeter Avalos case KEY_RSA:
28318de8d7fSPeter Avalos name = _PATH_SSH_CLIENT_ID_RSA;
28418de8d7fSPeter Avalos break;
28536e94dc5SPeter Avalos case KEY_ED25519:
28636e94dc5SPeter Avalos case KEY_ED25519_CERT:
28736e94dc5SPeter Avalos name = _PATH_SSH_CLIENT_ID_ED25519;
28836e94dc5SPeter Avalos break;
2890cbfa66cSDaniel Fojt case KEY_ED25519_SK:
2900cbfa66cSDaniel Fojt case KEY_ED25519_SK_CERT:
2910cbfa66cSDaniel Fojt name = _PATH_SSH_CLIENT_ID_ED25519_SK;
2920cbfa66cSDaniel Fojt break;
293664f4763Szrj case KEY_XMSS:
294664f4763Szrj case KEY_XMSS_CERT:
295664f4763Szrj name = _PATH_SSH_CLIENT_ID_XMSS;
296664f4763Szrj break;
29718de8d7fSPeter Avalos default:
298e9778795SPeter Avalos fatal("bad key type");
29918de8d7fSPeter Avalos }
30018de8d7fSPeter Avalos }
301e9778795SPeter Avalos snprintf(identity_file, sizeof(identity_file),
302e9778795SPeter Avalos "%s/%s", pw->pw_dir, name);
303e9778795SPeter Avalos printf("%s (%s): ", prompt, identity_file);
304e9778795SPeter Avalos fflush(stdout);
30518de8d7fSPeter Avalos if (fgets(buf, sizeof(buf), stdin) == NULL)
30618de8d7fSPeter Avalos exit(1);
30718de8d7fSPeter Avalos buf[strcspn(buf, "\n")] = '\0';
30818de8d7fSPeter Avalos if (strcmp(buf, "") != 0)
30918de8d7fSPeter Avalos strlcpy(identity_file, buf, sizeof(identity_file));
31018de8d7fSPeter Avalos have_identity = 1;
31118de8d7fSPeter Avalos }
31218de8d7fSPeter Avalos
313e9778795SPeter Avalos static struct sshkey *
load_identity(const char * filename,char ** commentp)3140cbfa66cSDaniel Fojt load_identity(const char *filename, char **commentp)
31518de8d7fSPeter Avalos {
31618de8d7fSPeter Avalos char *pass;
317e9778795SPeter Avalos struct sshkey *prv;
318e9778795SPeter Avalos int r;
31918de8d7fSPeter Avalos
3200cbfa66cSDaniel Fojt if (commentp != NULL)
3210cbfa66cSDaniel Fojt *commentp = NULL;
3220cbfa66cSDaniel Fojt if ((r = sshkey_load_private(filename, "", &prv, commentp)) == 0)
323e9778795SPeter Avalos return prv;
324e9778795SPeter Avalos if (r != SSH_ERR_KEY_WRONG_PASSPHRASE)
32550a69bb5SSascha Wildner fatal_r(r, "Load key \"%s\"", filename);
32618de8d7fSPeter Avalos if (identity_passphrase)
32718de8d7fSPeter Avalos pass = xstrdup(identity_passphrase);
32818de8d7fSPeter Avalos else
329e9778795SPeter Avalos pass = read_passphrase("Enter passphrase: ", RP_ALLOW_STDIN);
3300cbfa66cSDaniel Fojt r = sshkey_load_private(filename, pass, &prv, commentp);
3310cbfa66cSDaniel Fojt freezero(pass, strlen(pass));
332e9778795SPeter Avalos if (r != 0)
33350a69bb5SSascha Wildner fatal_r(r, "Load key \"%s\"", filename);
33418de8d7fSPeter Avalos return prv;
33518de8d7fSPeter Avalos }
33618de8d7fSPeter Avalos
33718de8d7fSPeter Avalos #define SSH_COM_PUBLIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----"
33818de8d7fSPeter Avalos #define SSH_COM_PUBLIC_END "---- END SSH2 PUBLIC KEY ----"
33918de8d7fSPeter Avalos #define SSH_COM_PRIVATE_BEGIN "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----"
34018de8d7fSPeter Avalos #define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb
34118de8d7fSPeter Avalos
34236e94dc5SPeter Avalos #ifdef WITH_OPENSSL
34318de8d7fSPeter Avalos static void
do_convert_to_ssh2(struct passwd * pw,struct sshkey * k)344e9778795SPeter Avalos do_convert_to_ssh2(struct passwd *pw, struct sshkey *k)
34518de8d7fSPeter Avalos {
3460cbfa66cSDaniel Fojt struct sshbuf *b;
3470cbfa66cSDaniel Fojt char comment[61], *b64;
348e9778795SPeter Avalos int r;
349856ea928SPeter Avalos
3500cbfa66cSDaniel Fojt if ((b = sshbuf_new()) == NULL)
35150a69bb5SSascha Wildner fatal_f("sshbuf_new failed");
3520cbfa66cSDaniel Fojt if ((r = sshkey_putb(k, b)) != 0)
35350a69bb5SSascha Wildner fatal_fr(r, "put key");
3540cbfa66cSDaniel Fojt if ((b64 = sshbuf_dtob64_string(b, 1)) == NULL)
35550a69bb5SSascha Wildner fatal_f("sshbuf_dtob64_string failed");
3560cbfa66cSDaniel Fojt
357856ea928SPeter Avalos /* Comment + surrounds must fit into 72 chars (RFC 4716 sec 3.3) */
358856ea928SPeter Avalos snprintf(comment, sizeof(comment),
359856ea928SPeter Avalos "%u-bit %s, converted by %s@%s from OpenSSH",
360e9778795SPeter Avalos sshkey_size(k), sshkey_type(k),
361856ea928SPeter Avalos pw->pw_name, hostname);
362856ea928SPeter Avalos
363e9778795SPeter Avalos sshkey_free(k);
3640cbfa66cSDaniel Fojt sshbuf_free(b);
3650cbfa66cSDaniel Fojt
3660cbfa66cSDaniel Fojt fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN);
3670cbfa66cSDaniel Fojt fprintf(stdout, "Comment: \"%s\"\n%s", comment, b64);
3680cbfa66cSDaniel Fojt fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END);
3690cbfa66cSDaniel Fojt free(b64);
370856ea928SPeter Avalos exit(0);
371856ea928SPeter Avalos }
372856ea928SPeter Avalos
373856ea928SPeter Avalos static void
do_convert_to_pkcs8(struct sshkey * k)374e9778795SPeter Avalos do_convert_to_pkcs8(struct sshkey *k)
375856ea928SPeter Avalos {
376e9778795SPeter Avalos switch (sshkey_type_plain(k->type)) {
377856ea928SPeter Avalos case KEY_RSA:
378856ea928SPeter Avalos if (!PEM_write_RSA_PUBKEY(stdout, k->rsa))
379856ea928SPeter Avalos fatal("PEM_write_RSA_PUBKEY failed");
380856ea928SPeter Avalos break;
381*ba1276acSMatthew Dillon #ifdef WITH_DSA
382856ea928SPeter Avalos case KEY_DSA:
383856ea928SPeter Avalos if (!PEM_write_DSA_PUBKEY(stdout, k->dsa))
384856ea928SPeter Avalos fatal("PEM_write_DSA_PUBKEY failed");
385856ea928SPeter Avalos break;
386*ba1276acSMatthew Dillon #endif
3879f304aafSPeter Avalos #ifdef OPENSSL_HAS_ECC
3889f304aafSPeter Avalos case KEY_ECDSA:
3899f304aafSPeter Avalos if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa))
3909f304aafSPeter Avalos fatal("PEM_write_EC_PUBKEY failed");
3919f304aafSPeter Avalos break;
3929f304aafSPeter Avalos #endif
393856ea928SPeter Avalos default:
39450a69bb5SSascha Wildner fatal_f("unsupported key type %s", sshkey_type(k));
395856ea928SPeter Avalos }
396856ea928SPeter Avalos exit(0);
397856ea928SPeter Avalos }
398856ea928SPeter Avalos
399856ea928SPeter Avalos static void
do_convert_to_pem(struct sshkey * k)400e9778795SPeter Avalos do_convert_to_pem(struct sshkey *k)
401856ea928SPeter Avalos {
402e9778795SPeter Avalos switch (sshkey_type_plain(k->type)) {
403856ea928SPeter Avalos case KEY_RSA:
404856ea928SPeter Avalos if (!PEM_write_RSAPublicKey(stdout, k->rsa))
405856ea928SPeter Avalos fatal("PEM_write_RSAPublicKey failed");
406856ea928SPeter Avalos break;
407*ba1276acSMatthew Dillon #ifdef WITH_DSA
4080cbfa66cSDaniel Fojt case KEY_DSA:
4090cbfa66cSDaniel Fojt if (!PEM_write_DSA_PUBKEY(stdout, k->dsa))
4100cbfa66cSDaniel Fojt fatal("PEM_write_DSA_PUBKEY failed");
4110cbfa66cSDaniel Fojt break;
412*ba1276acSMatthew Dillon #endif
4130cbfa66cSDaniel Fojt #ifdef OPENSSL_HAS_ECC
4140cbfa66cSDaniel Fojt case KEY_ECDSA:
4150cbfa66cSDaniel Fojt if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa))
4160cbfa66cSDaniel Fojt fatal("PEM_write_EC_PUBKEY failed");
4170cbfa66cSDaniel Fojt break;
4180cbfa66cSDaniel Fojt #endif
419856ea928SPeter Avalos default:
42050a69bb5SSascha Wildner fatal_f("unsupported key type %s", sshkey_type(k));
421856ea928SPeter Avalos }
422856ea928SPeter Avalos exit(0);
423856ea928SPeter Avalos }
424856ea928SPeter Avalos
425856ea928SPeter Avalos static void
do_convert_to(struct passwd * pw)426856ea928SPeter Avalos do_convert_to(struct passwd *pw)
427856ea928SPeter Avalos {
428e9778795SPeter Avalos struct sshkey *k;
42918de8d7fSPeter Avalos struct stat st;
430e9778795SPeter Avalos int r;
43118de8d7fSPeter Avalos
43218de8d7fSPeter Avalos if (!have_identity)
43318de8d7fSPeter Avalos ask_filename(pw, "Enter file in which the key is");
4340cbfa66cSDaniel Fojt if (stat(identity_file, &st) == -1)
435856ea928SPeter Avalos fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
436e9778795SPeter Avalos if ((r = sshkey_load_public(identity_file, &k, NULL)) != 0)
4370cbfa66cSDaniel Fojt k = load_identity(identity_file, NULL);
438856ea928SPeter Avalos switch (convert_format) {
439856ea928SPeter Avalos case FMT_RFC4716:
440856ea928SPeter Avalos do_convert_to_ssh2(pw, k);
441856ea928SPeter Avalos break;
442856ea928SPeter Avalos case FMT_PKCS8:
443856ea928SPeter Avalos do_convert_to_pkcs8(k);
444856ea928SPeter Avalos break;
445856ea928SPeter Avalos case FMT_PEM:
446856ea928SPeter Avalos do_convert_to_pem(k);
447856ea928SPeter Avalos break;
448856ea928SPeter Avalos default:
44950a69bb5SSascha Wildner fatal_f("unknown key format %d", convert_format);
45018de8d7fSPeter Avalos }
45118de8d7fSPeter Avalos exit(0);
45218de8d7fSPeter Avalos }
45318de8d7fSPeter Avalos
454e9778795SPeter Avalos /*
455e9778795SPeter Avalos * This is almost exactly the bignum1 encoding, but with 32 bit for length
456e9778795SPeter Avalos * instead of 16.
457e9778795SPeter Avalos */
45818de8d7fSPeter Avalos static void
buffer_get_bignum_bits(struct sshbuf * b,BIGNUM * value)459e9778795SPeter Avalos buffer_get_bignum_bits(struct sshbuf *b, BIGNUM *value)
46018de8d7fSPeter Avalos {
461e9778795SPeter Avalos u_int bytes, bignum_bits;
462e9778795SPeter Avalos int r;
46318de8d7fSPeter Avalos
464e9778795SPeter Avalos if ((r = sshbuf_get_u32(b, &bignum_bits)) != 0)
46550a69bb5SSascha Wildner fatal_fr(r, "parse");
466e9778795SPeter Avalos bytes = (bignum_bits + 7) / 8;
467e9778795SPeter Avalos if (sshbuf_len(b) < bytes)
46850a69bb5SSascha Wildner fatal_f("input buffer too small: need %d have %zu",
46950a69bb5SSascha Wildner bytes, sshbuf_len(b));
470e9778795SPeter Avalos if (BN_bin2bn(sshbuf_ptr(b), bytes, value) == NULL)
47150a69bb5SSascha Wildner fatal_f("BN_bin2bn failed");
472e9778795SPeter Avalos if ((r = sshbuf_consume(b, bytes)) != 0)
47350a69bb5SSascha Wildner fatal_fr(r, "consume");
47418de8d7fSPeter Avalos }
47518de8d7fSPeter Avalos
476e9778795SPeter Avalos static struct sshkey *
do_convert_private_ssh2(struct sshbuf * b)4770cbfa66cSDaniel Fojt do_convert_private_ssh2(struct sshbuf *b)
47818de8d7fSPeter Avalos {
479e9778795SPeter Avalos struct sshkey *key = NULL;
48018de8d7fSPeter Avalos char *type, *cipher;
481*ba1276acSMatthew Dillon const char *alg = NULL;
482e9778795SPeter Avalos u_char e1, e2, e3, *sig = NULL, data[] = "abcde12345";
483e9778795SPeter Avalos int r, rlen, ktype;
484e9778795SPeter Avalos u_int magic, i1, i2, i3, i4;
485e9778795SPeter Avalos size_t slen;
48618de8d7fSPeter Avalos u_long e;
487*ba1276acSMatthew Dillon #ifdef WITH_DSA
488664f4763Szrj BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL;
489664f4763Szrj BIGNUM *dsa_pub_key = NULL, *dsa_priv_key = NULL;
490*ba1276acSMatthew Dillon #endif
491664f4763Szrj BIGNUM *rsa_n = NULL, *rsa_e = NULL, *rsa_d = NULL;
492664f4763Szrj BIGNUM *rsa_p = NULL, *rsa_q = NULL, *rsa_iqmp = NULL;
4930cbfa66cSDaniel Fojt
494e9778795SPeter Avalos if ((r = sshbuf_get_u32(b, &magic)) != 0)
49550a69bb5SSascha Wildner fatal_fr(r, "parse magic");
49618de8d7fSPeter Avalos
49718de8d7fSPeter Avalos if (magic != SSH_COM_PRIVATE_KEY_MAGIC) {
498e9778795SPeter Avalos error("bad magic 0x%x != 0x%x", magic,
499e9778795SPeter Avalos SSH_COM_PRIVATE_KEY_MAGIC);
50018de8d7fSPeter Avalos return NULL;
50118de8d7fSPeter Avalos }
502e9778795SPeter Avalos if ((r = sshbuf_get_u32(b, &i1)) != 0 ||
503e9778795SPeter Avalos (r = sshbuf_get_cstring(b, &type, NULL)) != 0 ||
504e9778795SPeter Avalos (r = sshbuf_get_cstring(b, &cipher, NULL)) != 0 ||
505e9778795SPeter Avalos (r = sshbuf_get_u32(b, &i2)) != 0 ||
506e9778795SPeter Avalos (r = sshbuf_get_u32(b, &i3)) != 0 ||
507e9778795SPeter Avalos (r = sshbuf_get_u32(b, &i4)) != 0)
50850a69bb5SSascha Wildner fatal_fr(r, "parse");
50918de8d7fSPeter Avalos debug("ignore (%d %d %d %d)", i1, i2, i3, i4);
51018de8d7fSPeter Avalos if (strcmp(cipher, "none") != 0) {
51118de8d7fSPeter Avalos error("unsupported cipher %s", cipher);
51236e94dc5SPeter Avalos free(cipher);
51336e94dc5SPeter Avalos free(type);
51418de8d7fSPeter Avalos return NULL;
51518de8d7fSPeter Avalos }
51636e94dc5SPeter Avalos free(cipher);
51718de8d7fSPeter Avalos
518*ba1276acSMatthew Dillon if (strstr(type, "rsa")) {
51918de8d7fSPeter Avalos ktype = KEY_RSA;
520*ba1276acSMatthew Dillon #ifdef WITH_DSA
521*ba1276acSMatthew Dillon } else if (strstr(type, "dsa")) {
522*ba1276acSMatthew Dillon ktype = KEY_DSA;
523*ba1276acSMatthew Dillon #endif
52418de8d7fSPeter Avalos } else {
52536e94dc5SPeter Avalos free(type);
52618de8d7fSPeter Avalos return NULL;
52718de8d7fSPeter Avalos }
528664f4763Szrj if ((key = sshkey_new(ktype)) == NULL)
529664f4763Szrj fatal("sshkey_new failed");
53036e94dc5SPeter Avalos free(type);
53118de8d7fSPeter Avalos
53218de8d7fSPeter Avalos switch (key->type) {
533*ba1276acSMatthew Dillon #ifdef WITH_DSA
53418de8d7fSPeter Avalos case KEY_DSA:
535664f4763Szrj if ((dsa_p = BN_new()) == NULL ||
536664f4763Szrj (dsa_q = BN_new()) == NULL ||
537664f4763Szrj (dsa_g = BN_new()) == NULL ||
538664f4763Szrj (dsa_pub_key = BN_new()) == NULL ||
539664f4763Szrj (dsa_priv_key = BN_new()) == NULL)
54050a69bb5SSascha Wildner fatal_f("BN_new");
541664f4763Szrj buffer_get_bignum_bits(b, dsa_p);
542664f4763Szrj buffer_get_bignum_bits(b, dsa_g);
543664f4763Szrj buffer_get_bignum_bits(b, dsa_q);
544664f4763Szrj buffer_get_bignum_bits(b, dsa_pub_key);
545664f4763Szrj buffer_get_bignum_bits(b, dsa_priv_key);
546664f4763Szrj if (!DSA_set0_pqg(key->dsa, dsa_p, dsa_q, dsa_g))
54750a69bb5SSascha Wildner fatal_f("DSA_set0_pqg failed");
548664f4763Szrj dsa_p = dsa_q = dsa_g = NULL; /* transferred */
549664f4763Szrj if (!DSA_set0_key(key->dsa, dsa_pub_key, dsa_priv_key))
55050a69bb5SSascha Wildner fatal_f("DSA_set0_key failed");
551664f4763Szrj dsa_pub_key = dsa_priv_key = NULL; /* transferred */
55218de8d7fSPeter Avalos break;
553*ba1276acSMatthew Dillon #endif
55418de8d7fSPeter Avalos case KEY_RSA:
555e9778795SPeter Avalos if ((r = sshbuf_get_u8(b, &e1)) != 0 ||
556e9778795SPeter Avalos (e1 < 30 && (r = sshbuf_get_u8(b, &e2)) != 0) ||
557e9778795SPeter Avalos (e1 < 30 && (r = sshbuf_get_u8(b, &e3)) != 0))
55850a69bb5SSascha Wildner fatal_fr(r, "parse RSA");
559e9778795SPeter Avalos e = e1;
56018de8d7fSPeter Avalos debug("e %lx", e);
56118de8d7fSPeter Avalos if (e < 30) {
56218de8d7fSPeter Avalos e <<= 8;
563e9778795SPeter Avalos e += e2;
56418de8d7fSPeter Avalos debug("e %lx", e);
56518de8d7fSPeter Avalos e <<= 8;
566e9778795SPeter Avalos e += e3;
56718de8d7fSPeter Avalos debug("e %lx", e);
56818de8d7fSPeter Avalos }
569664f4763Szrj if ((rsa_e = BN_new()) == NULL)
57050a69bb5SSascha Wildner fatal_f("BN_new");
571664f4763Szrj if (!BN_set_word(rsa_e, e)) {
572664f4763Szrj BN_clear_free(rsa_e);
573e9778795SPeter Avalos sshkey_free(key);
57418de8d7fSPeter Avalos return NULL;
57518de8d7fSPeter Avalos }
576664f4763Szrj if ((rsa_n = BN_new()) == NULL ||
577664f4763Szrj (rsa_d = BN_new()) == NULL ||
578664f4763Szrj (rsa_p = BN_new()) == NULL ||
579664f4763Szrj (rsa_q = BN_new()) == NULL ||
580664f4763Szrj (rsa_iqmp = BN_new()) == NULL)
58150a69bb5SSascha Wildner fatal_f("BN_new");
582664f4763Szrj buffer_get_bignum_bits(b, rsa_d);
583664f4763Szrj buffer_get_bignum_bits(b, rsa_n);
584664f4763Szrj buffer_get_bignum_bits(b, rsa_iqmp);
585664f4763Szrj buffer_get_bignum_bits(b, rsa_q);
586664f4763Szrj buffer_get_bignum_bits(b, rsa_p);
587664f4763Szrj if (!RSA_set0_key(key->rsa, rsa_n, rsa_e, rsa_d))
58850a69bb5SSascha Wildner fatal_f("RSA_set0_key failed");
589664f4763Szrj rsa_n = rsa_e = rsa_d = NULL; /* transferred */
590664f4763Szrj if (!RSA_set0_factors(key->rsa, rsa_p, rsa_q))
59150a69bb5SSascha Wildner fatal_f("RSA_set0_factors failed");
592664f4763Szrj rsa_p = rsa_q = NULL; /* transferred */
593664f4763Szrj if ((r = ssh_rsa_complete_crt_parameters(key, rsa_iqmp)) != 0)
59450a69bb5SSascha Wildner fatal_fr(r, "generate RSA parameters");
595664f4763Szrj BN_clear_free(rsa_iqmp);
596*ba1276acSMatthew Dillon alg = "rsa-sha2-256";
59718de8d7fSPeter Avalos break;
59818de8d7fSPeter Avalos }
599e9778795SPeter Avalos rlen = sshbuf_len(b);
60018de8d7fSPeter Avalos if (rlen != 0)
60150a69bb5SSascha Wildner error_f("remaining bytes in key blob %d", rlen);
60218de8d7fSPeter Avalos
60318de8d7fSPeter Avalos /* try the key */
604ee116499SAntonio Huete Jimenez if ((r = sshkey_sign(key, &sig, &slen, data, sizeof(data),
605*ba1276acSMatthew Dillon alg, NULL, NULL, 0)) != 0)
606ee116499SAntonio Huete Jimenez error_fr(r, "signing with converted key failed");
607ee116499SAntonio Huete Jimenez else if ((r = sshkey_verify(key, sig, slen, data, sizeof(data),
608*ba1276acSMatthew Dillon alg, 0, NULL)) != 0)
609ee116499SAntonio Huete Jimenez error_fr(r, "verification with converted key failed");
610ee116499SAntonio Huete Jimenez if (r != 0) {
611e9778795SPeter Avalos sshkey_free(key);
612e9778795SPeter Avalos free(sig);
613e9778795SPeter Avalos return NULL;
614e9778795SPeter Avalos }
61536e94dc5SPeter Avalos free(sig);
61618de8d7fSPeter Avalos return key;
61718de8d7fSPeter Avalos }
61818de8d7fSPeter Avalos
61918de8d7fSPeter Avalos static int
get_line(FILE * fp,char * line,size_t len)62018de8d7fSPeter Avalos get_line(FILE *fp, char *line, size_t len)
62118de8d7fSPeter Avalos {
62218de8d7fSPeter Avalos int c;
62318de8d7fSPeter Avalos size_t pos = 0;
62418de8d7fSPeter Avalos
62518de8d7fSPeter Avalos line[0] = '\0';
62618de8d7fSPeter Avalos while ((c = fgetc(fp)) != EOF) {
627e9778795SPeter Avalos if (pos >= len - 1)
628e9778795SPeter Avalos fatal("input line too long.");
62918de8d7fSPeter Avalos switch (c) {
63018de8d7fSPeter Avalos case '\r':
63118de8d7fSPeter Avalos c = fgetc(fp);
632e9778795SPeter Avalos if (c != EOF && c != '\n' && ungetc(c, fp) == EOF)
633e9778795SPeter Avalos fatal("unget: %s", strerror(errno));
63418de8d7fSPeter Avalos return pos;
63518de8d7fSPeter Avalos case '\n':
63618de8d7fSPeter Avalos return pos;
63718de8d7fSPeter Avalos }
63818de8d7fSPeter Avalos line[pos++] = c;
63918de8d7fSPeter Avalos line[pos] = '\0';
64018de8d7fSPeter Avalos }
64118de8d7fSPeter Avalos /* We reached EOF */
64218de8d7fSPeter Avalos return -1;
64318de8d7fSPeter Avalos }
64418de8d7fSPeter Avalos
64518de8d7fSPeter Avalos static void
do_convert_from_ssh2(struct passwd * pw,struct sshkey ** k,int * private)646e9778795SPeter Avalos do_convert_from_ssh2(struct passwd *pw, struct sshkey **k, int *private)
64718de8d7fSPeter Avalos {
648e9778795SPeter Avalos int r, blen, escaped = 0;
64918de8d7fSPeter Avalos u_int len;
65018de8d7fSPeter Avalos char line[1024];
6510cbfa66cSDaniel Fojt struct sshbuf *buf;
65218de8d7fSPeter Avalos char encoded[8096];
65318de8d7fSPeter Avalos FILE *fp;
65418de8d7fSPeter Avalos
6550cbfa66cSDaniel Fojt if ((buf = sshbuf_new()) == NULL)
6560cbfa66cSDaniel Fojt fatal("sshbuf_new failed");
657856ea928SPeter Avalos if ((fp = fopen(identity_file, "r")) == NULL)
658856ea928SPeter Avalos fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
65918de8d7fSPeter Avalos encoded[0] = '\0';
66018de8d7fSPeter Avalos while ((blen = get_line(fp, line, sizeof(line))) != -1) {
66136e94dc5SPeter Avalos if (blen > 0 && line[blen - 1] == '\\')
66218de8d7fSPeter Avalos escaped++;
66318de8d7fSPeter Avalos if (strncmp(line, "----", 4) == 0 ||
66418de8d7fSPeter Avalos strstr(line, ": ") != NULL) {
66518de8d7fSPeter Avalos if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL)
666856ea928SPeter Avalos *private = 1;
66718de8d7fSPeter Avalos if (strstr(line, " END ") != NULL) {
66818de8d7fSPeter Avalos break;
66918de8d7fSPeter Avalos }
67018de8d7fSPeter Avalos /* fprintf(stderr, "ignore: %s", line); */
67118de8d7fSPeter Avalos continue;
67218de8d7fSPeter Avalos }
67318de8d7fSPeter Avalos if (escaped) {
67418de8d7fSPeter Avalos escaped--;
67518de8d7fSPeter Avalos /* fprintf(stderr, "escaped: %s", line); */
67618de8d7fSPeter Avalos continue;
67718de8d7fSPeter Avalos }
67818de8d7fSPeter Avalos strlcat(encoded, line, sizeof(encoded));
67918de8d7fSPeter Avalos }
68018de8d7fSPeter Avalos len = strlen(encoded);
68118de8d7fSPeter Avalos if (((len % 4) == 3) &&
68218de8d7fSPeter Avalos (encoded[len-1] == '=') &&
68318de8d7fSPeter Avalos (encoded[len-2] == '=') &&
68418de8d7fSPeter Avalos (encoded[len-3] == '='))
68518de8d7fSPeter Avalos encoded[len-3] = '\0';
6860cbfa66cSDaniel Fojt if ((r = sshbuf_b64tod(buf, encoded)) != 0)
68750a69bb5SSascha Wildner fatal_fr(r, "base64 decode");
6880cbfa66cSDaniel Fojt if (*private) {
6890cbfa66cSDaniel Fojt if ((*k = do_convert_private_ssh2(buf)) == NULL)
69050a69bb5SSascha Wildner fatal_f("private key conversion failed");
6910cbfa66cSDaniel Fojt } else if ((r = sshkey_fromb(buf, k)) != 0)
69250a69bb5SSascha Wildner fatal_fr(r, "parse key");
6930cbfa66cSDaniel Fojt sshbuf_free(buf);
694856ea928SPeter Avalos fclose(fp);
695856ea928SPeter Avalos }
696856ea928SPeter Avalos
697856ea928SPeter Avalos static void
do_convert_from_pkcs8(struct sshkey ** k,int * private)698e9778795SPeter Avalos do_convert_from_pkcs8(struct sshkey **k, int *private)
699856ea928SPeter Avalos {
700856ea928SPeter Avalos EVP_PKEY *pubkey;
701856ea928SPeter Avalos FILE *fp;
702856ea928SPeter Avalos
703856ea928SPeter Avalos if ((fp = fopen(identity_file, "r")) == NULL)
704856ea928SPeter Avalos fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
705856ea928SPeter Avalos if ((pubkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
70650a69bb5SSascha Wildner fatal_f("%s is not a recognised public key format",
707856ea928SPeter Avalos identity_file);
708856ea928SPeter Avalos }
709856ea928SPeter Avalos fclose(fp);
710664f4763Szrj switch (EVP_PKEY_base_id(pubkey)) {
711856ea928SPeter Avalos case EVP_PKEY_RSA:
712e9778795SPeter Avalos if ((*k = sshkey_new(KEY_UNSPEC)) == NULL)
713e9778795SPeter Avalos fatal("sshkey_new failed");
714856ea928SPeter Avalos (*k)->type = KEY_RSA;
715856ea928SPeter Avalos (*k)->rsa = EVP_PKEY_get1_RSA(pubkey);
716856ea928SPeter Avalos break;
717*ba1276acSMatthew Dillon #ifdef WITH_DSA
718856ea928SPeter Avalos case EVP_PKEY_DSA:
719e9778795SPeter Avalos if ((*k = sshkey_new(KEY_UNSPEC)) == NULL)
720e9778795SPeter Avalos fatal("sshkey_new failed");
721856ea928SPeter Avalos (*k)->type = KEY_DSA;
722856ea928SPeter Avalos (*k)->dsa = EVP_PKEY_get1_DSA(pubkey);
723856ea928SPeter Avalos break;
724*ba1276acSMatthew Dillon #endif
7259f304aafSPeter Avalos #ifdef OPENSSL_HAS_ECC
7269f304aafSPeter Avalos case EVP_PKEY_EC:
727e9778795SPeter Avalos if ((*k = sshkey_new(KEY_UNSPEC)) == NULL)
728e9778795SPeter Avalos fatal("sshkey_new failed");
7299f304aafSPeter Avalos (*k)->type = KEY_ECDSA;
7309f304aafSPeter Avalos (*k)->ecdsa = EVP_PKEY_get1_EC_KEY(pubkey);
731e9778795SPeter Avalos (*k)->ecdsa_nid = sshkey_ecdsa_key_to_nid((*k)->ecdsa);
7329f304aafSPeter Avalos break;
7339f304aafSPeter Avalos #endif
734856ea928SPeter Avalos default:
73550a69bb5SSascha Wildner fatal_f("unsupported pubkey type %d",
736664f4763Szrj EVP_PKEY_base_id(pubkey));
737856ea928SPeter Avalos }
738856ea928SPeter Avalos EVP_PKEY_free(pubkey);
739856ea928SPeter Avalos return;
740856ea928SPeter Avalos }
741856ea928SPeter Avalos
742856ea928SPeter Avalos static void
do_convert_from_pem(struct sshkey ** k,int * private)743e9778795SPeter Avalos do_convert_from_pem(struct sshkey **k, int *private)
744856ea928SPeter Avalos {
745856ea928SPeter Avalos FILE *fp;
746856ea928SPeter Avalos RSA *rsa;
747856ea928SPeter Avalos
748856ea928SPeter Avalos if ((fp = fopen(identity_file, "r")) == NULL)
749856ea928SPeter Avalos fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
750856ea928SPeter Avalos if ((rsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL)) != NULL) {
751e9778795SPeter Avalos if ((*k = sshkey_new(KEY_UNSPEC)) == NULL)
752e9778795SPeter Avalos fatal("sshkey_new failed");
753856ea928SPeter Avalos (*k)->type = KEY_RSA;
754856ea928SPeter Avalos (*k)->rsa = rsa;
755856ea928SPeter Avalos fclose(fp);
756856ea928SPeter Avalos return;
757856ea928SPeter Avalos }
75850a69bb5SSascha Wildner fatal_f("unrecognised raw private key format");
759856ea928SPeter Avalos }
760856ea928SPeter Avalos
761856ea928SPeter Avalos static void
do_convert_from(struct passwd * pw)762856ea928SPeter Avalos do_convert_from(struct passwd *pw)
763856ea928SPeter Avalos {
764e9778795SPeter Avalos struct sshkey *k = NULL;
765e9778795SPeter Avalos int r, private = 0, ok = 0;
766856ea928SPeter Avalos struct stat st;
767856ea928SPeter Avalos
768856ea928SPeter Avalos if (!have_identity)
769856ea928SPeter Avalos ask_filename(pw, "Enter file in which the key is");
7700cbfa66cSDaniel Fojt if (stat(identity_file, &st) == -1)
771856ea928SPeter Avalos fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
772856ea928SPeter Avalos
773856ea928SPeter Avalos switch (convert_format) {
774856ea928SPeter Avalos case FMT_RFC4716:
775856ea928SPeter Avalos do_convert_from_ssh2(pw, &k, &private);
776856ea928SPeter Avalos break;
777856ea928SPeter Avalos case FMT_PKCS8:
778856ea928SPeter Avalos do_convert_from_pkcs8(&k, &private);
779856ea928SPeter Avalos break;
780856ea928SPeter Avalos case FMT_PEM:
781856ea928SPeter Avalos do_convert_from_pem(&k, &private);
782856ea928SPeter Avalos break;
783856ea928SPeter Avalos default:
78450a69bb5SSascha Wildner fatal_f("unknown key format %d", convert_format);
785856ea928SPeter Avalos }
786856ea928SPeter Avalos
787e9778795SPeter Avalos if (!private) {
788e9778795SPeter Avalos if ((r = sshkey_write(k, stdout)) == 0)
789e9778795SPeter Avalos ok = 1;
790856ea928SPeter Avalos if (ok)
791856ea928SPeter Avalos fprintf(stdout, "\n");
792e9778795SPeter Avalos } else {
793856ea928SPeter Avalos switch (k->type) {
794*ba1276acSMatthew Dillon #ifdef WITH_DSA
795856ea928SPeter Avalos case KEY_DSA:
796856ea928SPeter Avalos ok = PEM_write_DSAPrivateKey(stdout, k->dsa, NULL,
797856ea928SPeter Avalos NULL, 0, NULL, NULL);
798856ea928SPeter Avalos break;
799*ba1276acSMatthew Dillon #endif
8009f304aafSPeter Avalos #ifdef OPENSSL_HAS_ECC
8019f304aafSPeter Avalos case KEY_ECDSA:
8029f304aafSPeter Avalos ok = PEM_write_ECPrivateKey(stdout, k->ecdsa, NULL,
8039f304aafSPeter Avalos NULL, 0, NULL, NULL);
8049f304aafSPeter Avalos break;
8059f304aafSPeter Avalos #endif
806856ea928SPeter Avalos case KEY_RSA:
807856ea928SPeter Avalos ok = PEM_write_RSAPrivateKey(stdout, k->rsa, NULL,
808856ea928SPeter Avalos NULL, 0, NULL, NULL);
809856ea928SPeter Avalos break;
810856ea928SPeter Avalos default:
81150a69bb5SSascha Wildner fatal_f("unsupported key type %s", sshkey_type(k));
812856ea928SPeter Avalos }
813856ea928SPeter Avalos }
814856ea928SPeter Avalos
815e9778795SPeter Avalos if (!ok)
816e9778795SPeter Avalos fatal("key write failed");
817e9778795SPeter Avalos sshkey_free(k);
81818de8d7fSPeter Avalos exit(0);
81918de8d7fSPeter Avalos }
82036e94dc5SPeter Avalos #endif
82118de8d7fSPeter Avalos
82218de8d7fSPeter Avalos static void
do_print_public(struct passwd * pw)82318de8d7fSPeter Avalos do_print_public(struct passwd *pw)
82418de8d7fSPeter Avalos {
825e9778795SPeter Avalos struct sshkey *prv;
82618de8d7fSPeter Avalos struct stat st;
827e9778795SPeter Avalos int r;
8280cbfa66cSDaniel Fojt char *comment = NULL;
82918de8d7fSPeter Avalos
83018de8d7fSPeter Avalos if (!have_identity)
83118de8d7fSPeter Avalos ask_filename(pw, "Enter file in which the key is");
8320cbfa66cSDaniel Fojt if (stat(identity_file, &st) == -1)
833e9778795SPeter Avalos fatal("%s: %s", identity_file, strerror(errno));
8340cbfa66cSDaniel Fojt prv = load_identity(identity_file, &comment);
835e9778795SPeter Avalos if ((r = sshkey_write(prv, stdout)) != 0)
83650a69bb5SSascha Wildner fatal_fr(r, "write key");
8370cbfa66cSDaniel Fojt if (comment != NULL && *comment != '\0')
8380cbfa66cSDaniel Fojt fprintf(stdout, " %s", comment);
83918de8d7fSPeter Avalos fprintf(stdout, "\n");
84050a69bb5SSascha Wildner if (sshkey_is_sk(prv)) {
84150a69bb5SSascha Wildner debug("sk_application: \"%s\", sk_flags 0x%02x",
84250a69bb5SSascha Wildner prv->sk_application, prv->sk_flags);
84350a69bb5SSascha Wildner }
84450a69bb5SSascha Wildner sshkey_free(prv);
8450cbfa66cSDaniel Fojt free(comment);
84618de8d7fSPeter Avalos exit(0);
84718de8d7fSPeter Avalos }
84818de8d7fSPeter Avalos
84918de8d7fSPeter Avalos static void
do_download(struct passwd * pw)850856ea928SPeter Avalos do_download(struct passwd *pw)
85118de8d7fSPeter Avalos {
852856ea928SPeter Avalos #ifdef ENABLE_PKCS11
853e9778795SPeter Avalos struct sshkey **keys = NULL;
854856ea928SPeter Avalos int i, nkeys;
855e9778795SPeter Avalos enum sshkey_fp_rep rep;
856e9778795SPeter Avalos int fptype;
8570cbfa66cSDaniel Fojt char *fp, *ra, **comments = NULL;
85836e94dc5SPeter Avalos
859e9778795SPeter Avalos fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash;
860e9778795SPeter Avalos rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT;
86118de8d7fSPeter Avalos
862664f4763Szrj pkcs11_init(1);
8630cbfa66cSDaniel Fojt nkeys = pkcs11_add_provider(pkcs11provider, NULL, &keys, &comments);
864856ea928SPeter Avalos if (nkeys <= 0)
865856ea928SPeter Avalos fatal("cannot read public key from pkcs11");
866856ea928SPeter Avalos for (i = 0; i < nkeys; i++) {
86736e94dc5SPeter Avalos if (print_fingerprint) {
868e9778795SPeter Avalos fp = sshkey_fingerprint(keys[i], fptype, rep);
869e9778795SPeter Avalos ra = sshkey_fingerprint(keys[i], fingerprint_hash,
87036e94dc5SPeter Avalos SSH_FP_RANDOMART);
871e9778795SPeter Avalos if (fp == NULL || ra == NULL)
87250a69bb5SSascha Wildner fatal_f("sshkey_fingerprint fail");
873e9778795SPeter Avalos printf("%u %s %s (PKCS11 key)\n", sshkey_size(keys[i]),
874e9778795SPeter Avalos fp, sshkey_type(keys[i]));
875664f4763Szrj if (log_level_get() >= SYSLOG_LEVEL_VERBOSE)
87636e94dc5SPeter Avalos printf("%s\n", ra);
87736e94dc5SPeter Avalos free(ra);
87836e94dc5SPeter Avalos free(fp);
87936e94dc5SPeter Avalos } else {
880e9778795SPeter Avalos (void) sshkey_write(keys[i], stdout); /* XXX check */
8810cbfa66cSDaniel Fojt fprintf(stdout, "%s%s\n",
8820cbfa66cSDaniel Fojt *(comments[i]) == '\0' ? "" : " ", comments[i]);
88318de8d7fSPeter Avalos }
8840cbfa66cSDaniel Fojt free(comments[i]);
885e9778795SPeter Avalos sshkey_free(keys[i]);
88636e94dc5SPeter Avalos }
8870cbfa66cSDaniel Fojt free(comments);
88836e94dc5SPeter Avalos free(keys);
889856ea928SPeter Avalos pkcs11_terminate();
89018de8d7fSPeter Avalos exit(0);
891856ea928SPeter Avalos #else
892856ea928SPeter Avalos fatal("no pkcs11 support");
893856ea928SPeter Avalos #endif /* ENABLE_PKCS11 */
89418de8d7fSPeter Avalos }
89518de8d7fSPeter Avalos
896e9778795SPeter Avalos static struct sshkey *
try_read_key(char ** cpp)897e9778795SPeter Avalos try_read_key(char **cpp)
898e9778795SPeter Avalos {
899e9778795SPeter Avalos struct sshkey *ret;
900e9778795SPeter Avalos int r;
901e9778795SPeter Avalos
902e9778795SPeter Avalos if ((ret = sshkey_new(KEY_UNSPEC)) == NULL)
903e9778795SPeter Avalos fatal("sshkey_new failed");
904e9778795SPeter Avalos if ((r = sshkey_read(ret, cpp)) == 0)
905e9778795SPeter Avalos return ret;
906e9778795SPeter Avalos /* Not a key */
907e9778795SPeter Avalos sshkey_free(ret);
908e9778795SPeter Avalos return NULL;
909e9778795SPeter Avalos }
910e9778795SPeter Avalos
911e9778795SPeter Avalos static void
fingerprint_one_key(const struct sshkey * public,const char * comment)912e9778795SPeter Avalos fingerprint_one_key(const struct sshkey *public, const char *comment)
913e9778795SPeter Avalos {
914e9778795SPeter Avalos char *fp = NULL, *ra = NULL;
915e9778795SPeter Avalos enum sshkey_fp_rep rep;
916e9778795SPeter Avalos int fptype;
917e9778795SPeter Avalos
918e9778795SPeter Avalos fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash;
919e9778795SPeter Avalos rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT;
920e9778795SPeter Avalos fp = sshkey_fingerprint(public, fptype, rep);
921e9778795SPeter Avalos ra = sshkey_fingerprint(public, fingerprint_hash, SSH_FP_RANDOMART);
922e9778795SPeter Avalos if (fp == NULL || ra == NULL)
92350a69bb5SSascha Wildner fatal_f("sshkey_fingerprint failed");
924ce74bacaSMatthew Dillon mprintf("%u %s %s (%s)\n", sshkey_size(public), fp,
925e9778795SPeter Avalos comment ? comment : "no comment", sshkey_type(public));
926664f4763Szrj if (log_level_get() >= SYSLOG_LEVEL_VERBOSE)
927e9778795SPeter Avalos printf("%s\n", ra);
928e9778795SPeter Avalos free(ra);
929e9778795SPeter Avalos free(fp);
930e9778795SPeter Avalos }
931e9778795SPeter Avalos
932e9778795SPeter Avalos static void
fingerprint_private(const char * path)933e9778795SPeter Avalos fingerprint_private(const char *path)
934e9778795SPeter Avalos {
935e9778795SPeter Avalos struct stat st;
936e9778795SPeter Avalos char *comment = NULL;
9370cbfa66cSDaniel Fojt struct sshkey *privkey = NULL, *pubkey = NULL;
938e9778795SPeter Avalos int r;
939e9778795SPeter Avalos
9400cbfa66cSDaniel Fojt if (stat(identity_file, &st) == -1)
941e9778795SPeter Avalos fatal("%s: %s", path, strerror(errno));
9420cbfa66cSDaniel Fojt if ((r = sshkey_load_public(path, &pubkey, &comment)) != 0)
94350a69bb5SSascha Wildner debug_r(r, "load public \"%s\"", path);
9440cbfa66cSDaniel Fojt if (pubkey == NULL || comment == NULL || *comment == '\0') {
9450cbfa66cSDaniel Fojt free(comment);
946e9778795SPeter Avalos if ((r = sshkey_load_private(path, NULL,
9470cbfa66cSDaniel Fojt &privkey, &comment)) != 0)
94850a69bb5SSascha Wildner debug_r(r, "load private \"%s\"", path);
9490cbfa66cSDaniel Fojt }
9500cbfa66cSDaniel Fojt if (pubkey == NULL && privkey == NULL)
951e9778795SPeter Avalos fatal("%s is not a key file.", path);
952e9778795SPeter Avalos
9530cbfa66cSDaniel Fojt fingerprint_one_key(pubkey == NULL ? privkey : pubkey, comment);
9540cbfa66cSDaniel Fojt sshkey_free(pubkey);
9550cbfa66cSDaniel Fojt sshkey_free(privkey);
956e9778795SPeter Avalos free(comment);
957e9778795SPeter Avalos }
958e9778795SPeter Avalos
95918de8d7fSPeter Avalos static void
do_fingerprint(struct passwd * pw)96018de8d7fSPeter Avalos do_fingerprint(struct passwd *pw)
96118de8d7fSPeter Avalos {
96218de8d7fSPeter Avalos FILE *f;
963e9778795SPeter Avalos struct sshkey *public = NULL;
964664f4763Szrj char *comment = NULL, *cp, *ep, *line = NULL;
965664f4763Szrj size_t linesize = 0;
966e9778795SPeter Avalos int i, invalid = 1;
967e9778795SPeter Avalos const char *path;
968e9778795SPeter Avalos u_long lnum = 0;
96918de8d7fSPeter Avalos
97018de8d7fSPeter Avalos if (!have_identity)
97118de8d7fSPeter Avalos ask_filename(pw, "Enter file in which the key is");
972e9778795SPeter Avalos path = identity_file;
973e9778795SPeter Avalos
974e9778795SPeter Avalos if (strcmp(identity_file, "-") == 0) {
975e9778795SPeter Avalos f = stdin;
976e9778795SPeter Avalos path = "(stdin)";
977e9778795SPeter Avalos } else if ((f = fopen(path, "r")) == NULL)
978e9778795SPeter Avalos fatal("%s: %s: %s", __progname, path, strerror(errno));
979e9778795SPeter Avalos
980664f4763Szrj while (getline(&line, &linesize, f) != -1) {
981664f4763Szrj lnum++;
982e9778795SPeter Avalos cp = line;
983e9778795SPeter Avalos cp[strcspn(cp, "\n")] = '\0';
984e9778795SPeter Avalos /* Trim leading space and comments */
985e9778795SPeter Avalos cp = line + strspn(line, " \t");
986e9778795SPeter Avalos if (*cp == '#' || *cp == '\0')
987e9778795SPeter Avalos continue;
988e9778795SPeter Avalos
989e9778795SPeter Avalos /*
990e9778795SPeter Avalos * Input may be plain keys, private keys, authorized_keys
991e9778795SPeter Avalos * or known_hosts.
992e9778795SPeter Avalos */
993e9778795SPeter Avalos
994e9778795SPeter Avalos /*
995e9778795SPeter Avalos * Try private keys first. Assume a key is private if
996e9778795SPeter Avalos * "SSH PRIVATE KEY" appears on the first line and we're
997e9778795SPeter Avalos * not reading from stdin (XXX support private keys on stdin).
998e9778795SPeter Avalos */
999e9778795SPeter Avalos if (lnum == 1 && strcmp(identity_file, "-") != 0 &&
1000e9778795SPeter Avalos strstr(cp, "PRIVATE KEY") != NULL) {
1001664f4763Szrj free(line);
1002e9778795SPeter Avalos fclose(f);
1003e9778795SPeter Avalos fingerprint_private(path);
100418de8d7fSPeter Avalos exit(0);
100518de8d7fSPeter Avalos }
100618de8d7fSPeter Avalos
1007e9778795SPeter Avalos /*
1008e9778795SPeter Avalos * If it's not a private key, then this must be prepared to
1009e9778795SPeter Avalos * accept a public key prefixed with a hostname or options.
1010e9778795SPeter Avalos * Try a bare key first, otherwise skip the leading stuff.
1011e9778795SPeter Avalos */
1012*ba1276acSMatthew Dillon comment = NULL;
1013e9778795SPeter Avalos if ((public = try_read_key(&cp)) == NULL) {
101418de8d7fSPeter Avalos i = strtol(cp, &ep, 10);
1015e9778795SPeter Avalos if (i == 0 || ep == NULL ||
1016e9778795SPeter Avalos (*ep != ' ' && *ep != '\t')) {
101718de8d7fSPeter Avalos int quoted = 0;
1018e9778795SPeter Avalos
101918de8d7fSPeter Avalos comment = cp;
102018de8d7fSPeter Avalos for (; *cp && (quoted || (*cp != ' ' &&
102118de8d7fSPeter Avalos *cp != '\t')); cp++) {
102218de8d7fSPeter Avalos if (*cp == '\\' && cp[1] == '"')
102318de8d7fSPeter Avalos cp++; /* Skip both */
102418de8d7fSPeter Avalos else if (*cp == '"')
102518de8d7fSPeter Avalos quoted = !quoted;
102618de8d7fSPeter Avalos }
102718de8d7fSPeter Avalos if (!*cp)
102818de8d7fSPeter Avalos continue;
102918de8d7fSPeter Avalos *cp++ = '\0';
103018de8d7fSPeter Avalos }
1031e9778795SPeter Avalos }
1032e9778795SPeter Avalos /* Retry after parsing leading hostname/key options */
1033e9778795SPeter Avalos if (public == NULL && (public = try_read_key(&cp)) == NULL) {
1034e9778795SPeter Avalos debug("%s:%lu: not a public key", path, lnum);
103518de8d7fSPeter Avalos continue;
103618de8d7fSPeter Avalos }
1037e9778795SPeter Avalos
1038e9778795SPeter Avalos /* Find trailing comment, if any */
1039e9778795SPeter Avalos for (; *cp == ' ' || *cp == '\t'; cp++)
1040e9778795SPeter Avalos ;
1041e9778795SPeter Avalos if (*cp != '\0' && *cp != '#')
1042e9778795SPeter Avalos comment = cp;
1043e9778795SPeter Avalos
1044e9778795SPeter Avalos fingerprint_one_key(public, comment);
1045e9778795SPeter Avalos sshkey_free(public);
1046e9778795SPeter Avalos invalid = 0; /* One good key in the file is sufficient */
104718de8d7fSPeter Avalos }
104818de8d7fSPeter Avalos fclose(f);
1049664f4763Szrj free(line);
1050856ea928SPeter Avalos
1051e9778795SPeter Avalos if (invalid)
1052e9778795SPeter Avalos fatal("%s is not a public key file.", path);
105318de8d7fSPeter Avalos exit(0);
105418de8d7fSPeter Avalos }
105518de8d7fSPeter Avalos
105618de8d7fSPeter Avalos static void
do_gen_all_hostkeys(struct passwd * pw)10571c188a7fSPeter Avalos do_gen_all_hostkeys(struct passwd *pw)
10581c188a7fSPeter Avalos {
10591c188a7fSPeter Avalos struct {
10601c188a7fSPeter Avalos char *key_type;
10611c188a7fSPeter Avalos char *key_type_display;
10621c188a7fSPeter Avalos char *path;
10631c188a7fSPeter Avalos } key_types[] = {
1064e9778795SPeter Avalos #ifdef WITH_OPENSSL
10651c188a7fSPeter Avalos { "rsa", "RSA" ,_PATH_HOST_RSA_KEY_FILE },
106699e85e0dSPeter Avalos #ifdef OPENSSL_HAS_ECC
10671c188a7fSPeter Avalos { "ecdsa", "ECDSA",_PATH_HOST_ECDSA_KEY_FILE },
1068e9778795SPeter Avalos #endif /* OPENSSL_HAS_ECC */
1069e9778795SPeter Avalos #endif /* WITH_OPENSSL */
107036e94dc5SPeter Avalos { "ed25519", "ED25519",_PATH_HOST_ED25519_KEY_FILE },
1071664f4763Szrj #ifdef WITH_XMSS
1072664f4763Szrj { "xmss", "XMSS",_PATH_HOST_XMSS_KEY_FILE },
1073664f4763Szrj #endif /* WITH_XMSS */
10741c188a7fSPeter Avalos { NULL, NULL, NULL }
10751c188a7fSPeter Avalos };
10761c188a7fSPeter Avalos
10770cbfa66cSDaniel Fojt u_int32_t bits = 0;
10781c188a7fSPeter Avalos int first = 0;
10791c188a7fSPeter Avalos struct stat st;
1080e9778795SPeter Avalos struct sshkey *private, *public;
1081ce74bacaSMatthew Dillon char comment[1024], *prv_tmp, *pub_tmp, *prv_file, *pub_file;
1082e9778795SPeter Avalos int i, type, fd, r;
10831c188a7fSPeter Avalos
10841c188a7fSPeter Avalos for (i = 0; key_types[i].key_type; i++) {
1085ce74bacaSMatthew Dillon public = private = NULL;
1086ce74bacaSMatthew Dillon prv_tmp = pub_tmp = prv_file = pub_file = NULL;
1087ce74bacaSMatthew Dillon
1088ce74bacaSMatthew Dillon xasprintf(&prv_file, "%s%s",
1089ce74bacaSMatthew Dillon identity_file, key_types[i].path);
1090ce74bacaSMatthew Dillon
1091ce74bacaSMatthew Dillon /* Check whether private key exists and is not zero-length */
1092ce74bacaSMatthew Dillon if (stat(prv_file, &st) == 0) {
1093ce74bacaSMatthew Dillon if (st.st_size != 0)
1094ce74bacaSMatthew Dillon goto next;
1095ce74bacaSMatthew Dillon } else if (errno != ENOENT) {
1096e9778795SPeter Avalos error("Could not stat %s: %s", key_types[i].path,
10971c188a7fSPeter Avalos strerror(errno));
1098ce74bacaSMatthew Dillon goto failnext;
10991c188a7fSPeter Avalos }
11001c188a7fSPeter Avalos
1101ce74bacaSMatthew Dillon /*
1102ce74bacaSMatthew Dillon * Private key doesn't exist or is invalid; proceed with
1103ce74bacaSMatthew Dillon * key generation.
1104ce74bacaSMatthew Dillon */
1105ce74bacaSMatthew Dillon xasprintf(&prv_tmp, "%s%s.XXXXXXXXXX",
1106ce74bacaSMatthew Dillon identity_file, key_types[i].path);
1107ce74bacaSMatthew Dillon xasprintf(&pub_tmp, "%s%s.pub.XXXXXXXXXX",
1108ce74bacaSMatthew Dillon identity_file, key_types[i].path);
1109ce74bacaSMatthew Dillon xasprintf(&pub_file, "%s%s.pub",
1110ce74bacaSMatthew Dillon identity_file, key_types[i].path);
1111ce74bacaSMatthew Dillon
11121c188a7fSPeter Avalos if (first == 0) {
11131c188a7fSPeter Avalos first = 1;
11141c188a7fSPeter Avalos printf("%s: generating new host keys: ", __progname);
11151c188a7fSPeter Avalos }
11161c188a7fSPeter Avalos printf("%s ", key_types[i].key_type_display);
11171c188a7fSPeter Avalos fflush(stdout);
1118e9778795SPeter Avalos type = sshkey_type_from_name(key_types[i].key_type);
1119ce74bacaSMatthew Dillon if ((fd = mkstemp(prv_tmp)) == -1) {
11200cbfa66cSDaniel Fojt error("Could not save your private key in %s: %s",
1121ce74bacaSMatthew Dillon prv_tmp, strerror(errno));
1122ce74bacaSMatthew Dillon goto failnext;
1123ce74bacaSMatthew Dillon }
11240cbfa66cSDaniel Fojt (void)close(fd); /* just using mkstemp() to reserve a name */
11251c188a7fSPeter Avalos bits = 0;
1126e9778795SPeter Avalos type_bits_valid(type, NULL, &bits);
1127e9778795SPeter Avalos if ((r = sshkey_generate(type, bits, &private)) != 0) {
112850a69bb5SSascha Wildner error_r(r, "sshkey_generate failed");
1129ce74bacaSMatthew Dillon goto failnext;
11301c188a7fSPeter Avalos }
1131e9778795SPeter Avalos if ((r = sshkey_from_private(private, &public)) != 0)
113250a69bb5SSascha Wildner fatal_fr(r, "sshkey_from_private");
11331c188a7fSPeter Avalos snprintf(comment, sizeof comment, "%s@%s", pw->pw_name,
11341c188a7fSPeter Avalos hostname);
1135ce74bacaSMatthew Dillon if ((r = sshkey_save_private(private, prv_tmp, "",
11360cbfa66cSDaniel Fojt comment, private_key_format, openssh_format_cipher,
11370cbfa66cSDaniel Fojt rounds)) != 0) {
113850a69bb5SSascha Wildner error_r(r, "Saving key \"%s\" failed", prv_tmp);
1139ce74bacaSMatthew Dillon goto failnext;
11401c188a7fSPeter Avalos }
1141ce74bacaSMatthew Dillon if ((fd = mkstemp(pub_tmp)) == -1) {
1142ce74bacaSMatthew Dillon error("Could not save your public key in %s: %s",
1143ce74bacaSMatthew Dillon pub_tmp, strerror(errno));
1144ce74bacaSMatthew Dillon goto failnext;
11451c188a7fSPeter Avalos }
1146ce74bacaSMatthew Dillon (void)fchmod(fd, 0644);
11470cbfa66cSDaniel Fojt (void)close(fd);
11480cbfa66cSDaniel Fojt if ((r = sshkey_save_public(public, pub_tmp, comment)) != 0) {
114950a69bb5SSascha Wildner error_r(r, "Unable to save public key to %s",
115050a69bb5SSascha Wildner identity_file);
1151ce74bacaSMatthew Dillon goto failnext;
1152ce74bacaSMatthew Dillon }
11531c188a7fSPeter Avalos
1154ce74bacaSMatthew Dillon /* Rename temporary files to their permanent locations. */
1155ce74bacaSMatthew Dillon if (rename(pub_tmp, pub_file) != 0) {
1156ce74bacaSMatthew Dillon error("Unable to move %s into position: %s",
1157ce74bacaSMatthew Dillon pub_file, strerror(errno));
1158ce74bacaSMatthew Dillon goto failnext;
1159ce74bacaSMatthew Dillon }
1160ce74bacaSMatthew Dillon if (rename(prv_tmp, prv_file) != 0) {
1161ce74bacaSMatthew Dillon error("Unable to move %s into position: %s",
1162ce74bacaSMatthew Dillon key_types[i].path, strerror(errno));
1163ce74bacaSMatthew Dillon failnext:
1164ce74bacaSMatthew Dillon first = 0;
1165ce74bacaSMatthew Dillon goto next;
1166ce74bacaSMatthew Dillon }
1167ce74bacaSMatthew Dillon next:
1168ce74bacaSMatthew Dillon sshkey_free(private);
1169ce74bacaSMatthew Dillon sshkey_free(public);
1170ce74bacaSMatthew Dillon free(prv_tmp);
1171ce74bacaSMatthew Dillon free(pub_tmp);
1172ce74bacaSMatthew Dillon free(prv_file);
1173ce74bacaSMatthew Dillon free(pub_file);
11741c188a7fSPeter Avalos }
11751c188a7fSPeter Avalos if (first != 0)
11761c188a7fSPeter Avalos printf("\n");
11771c188a7fSPeter Avalos }
11781c188a7fSPeter Avalos
1179e9778795SPeter Avalos struct known_hosts_ctx {
1180e9778795SPeter Avalos const char *host; /* Hostname searched for in find/delete case */
1181e9778795SPeter Avalos FILE *out; /* Output file, stdout for find_hosts case */
1182e9778795SPeter Avalos int has_unhashed; /* When hashing, original had unhashed hosts */
1183e9778795SPeter Avalos int found_key; /* For find/delete, host was found */
1184e9778795SPeter Avalos int invalid; /* File contained invalid items; don't delete */
1185664f4763Szrj int hash_hosts; /* Hash hostnames as we go */
1186664f4763Szrj int find_host; /* Search for specific hostname */
1187664f4763Szrj int delete_host; /* Delete host from known_hosts */
1188e9778795SPeter Avalos };
118918de8d7fSPeter Avalos
1190e9778795SPeter Avalos static int
known_hosts_hash(struct hostkey_foreach_line * l,void * _ctx)1191e9778795SPeter Avalos known_hosts_hash(struct hostkey_foreach_line *l, void *_ctx)
1192e9778795SPeter Avalos {
1193e9778795SPeter Avalos struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx;
1194e9778795SPeter Avalos char *hashed, *cp, *hosts, *ohosts;
1195e9778795SPeter Avalos int has_wild = l->hosts && strcspn(l->hosts, "*?!") != strlen(l->hosts);
1196ce74bacaSMatthew Dillon int was_hashed = l->hosts && l->hosts[0] == HASH_DELIM;
1197e9778795SPeter Avalos
1198e9778795SPeter Avalos switch (l->status) {
1199e9778795SPeter Avalos case HKF_STATUS_OK:
1200e9778795SPeter Avalos case HKF_STATUS_MATCHED:
1201e9778795SPeter Avalos /*
1202*ba1276acSMatthew Dillon * Don't hash hosts already hashed, with wildcard
1203e9778795SPeter Avalos * characters or a CA/revocation marker.
1204e9778795SPeter Avalos */
1205ce74bacaSMatthew Dillon if (was_hashed || has_wild || l->marker != MRK_NONE) {
1206e9778795SPeter Avalos fprintf(ctx->out, "%s\n", l->line);
1207664f4763Szrj if (has_wild && !ctx->find_host) {
1208ce74bacaSMatthew Dillon logit("%s:%lu: ignoring host name "
1209e9778795SPeter Avalos "with wildcard: %.64s", l->path,
1210e9778795SPeter Avalos l->linenum, l->hosts);
121118de8d7fSPeter Avalos }
1212e9778795SPeter Avalos return 0;
1213e9778795SPeter Avalos }
1214e9778795SPeter Avalos /*
1215e9778795SPeter Avalos * Split any comma-separated hostnames from the host list,
1216e9778795SPeter Avalos * hash and store separately.
1217e9778795SPeter Avalos */
1218e9778795SPeter Avalos ohosts = hosts = xstrdup(l->hosts);
1219e9778795SPeter Avalos while ((cp = strsep(&hosts, ",")) != NULL && *cp != '\0') {
1220ce74bacaSMatthew Dillon lowercase(cp);
1221e9778795SPeter Avalos if ((hashed = host_hash(cp, NULL, 0)) == NULL)
1222e9778795SPeter Avalos fatal("hash_host failed");
1223e9778795SPeter Avalos fprintf(ctx->out, "%s %s\n", hashed, l->rawkey);
1224ee116499SAntonio Huete Jimenez free(hashed);
1225e9778795SPeter Avalos ctx->has_unhashed = 1;
1226e9778795SPeter Avalos }
1227e9778795SPeter Avalos free(ohosts);
1228e9778795SPeter Avalos return 0;
1229e9778795SPeter Avalos case HKF_STATUS_INVALID:
1230e9778795SPeter Avalos /* Retain invalid lines, but mark file as invalid. */
1231e9778795SPeter Avalos ctx->invalid = 1;
1232ce74bacaSMatthew Dillon logit("%s:%lu: invalid line", l->path, l->linenum);
1233e9778795SPeter Avalos /* FALLTHROUGH */
1234e9778795SPeter Avalos default:
1235e9778795SPeter Avalos fprintf(ctx->out, "%s\n", l->line);
1236e9778795SPeter Avalos return 0;
1237e9778795SPeter Avalos }
1238e9778795SPeter Avalos /* NOTREACHED */
1239e9778795SPeter Avalos return -1;
1240e9778795SPeter Avalos }
1241e9778795SPeter Avalos
1242e9778795SPeter Avalos static int
known_hosts_find_delete(struct hostkey_foreach_line * l,void * _ctx)1243e9778795SPeter Avalos known_hosts_find_delete(struct hostkey_foreach_line *l, void *_ctx)
1244e9778795SPeter Avalos {
1245e9778795SPeter Avalos struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx;
1246e9778795SPeter Avalos enum sshkey_fp_rep rep;
1247e9778795SPeter Avalos int fptype;
12480cbfa66cSDaniel Fojt char *fp = NULL, *ra = NULL;
1249e9778795SPeter Avalos
1250e9778795SPeter Avalos fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash;
1251e9778795SPeter Avalos rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT;
1252e9778795SPeter Avalos
1253e9778795SPeter Avalos if (l->status == HKF_STATUS_MATCHED) {
1254664f4763Szrj if (ctx->delete_host) {
1255e9778795SPeter Avalos if (l->marker != MRK_NONE) {
1256e9778795SPeter Avalos /* Don't remove CA and revocation lines */
1257e9778795SPeter Avalos fprintf(ctx->out, "%s\n", l->line);
1258e9778795SPeter Avalos } else {
1259e9778795SPeter Avalos /*
1260e9778795SPeter Avalos * Hostname matches and has no CA/revoke
1261e9778795SPeter Avalos * marker, delete it by *not* writing the
1262e9778795SPeter Avalos * line to ctx->out.
1263e9778795SPeter Avalos */
1264e9778795SPeter Avalos ctx->found_key = 1;
1265e9778795SPeter Avalos if (!quiet)
1266ce74bacaSMatthew Dillon printf("# Host %s found: line %lu\n",
1267e9778795SPeter Avalos ctx->host, l->linenum);
1268e9778795SPeter Avalos }
1269e9778795SPeter Avalos return 0;
1270664f4763Szrj } else if (ctx->find_host) {
1271e9778795SPeter Avalos ctx->found_key = 1;
1272e9778795SPeter Avalos if (!quiet) {
1273ce74bacaSMatthew Dillon printf("# Host %s found: line %lu %s\n",
1274e9778795SPeter Avalos ctx->host,
1275e9778795SPeter Avalos l->linenum, l->marker == MRK_CA ? "CA" :
1276e9778795SPeter Avalos (l->marker == MRK_REVOKE ? "REVOKED" : ""));
1277e9778795SPeter Avalos }
1278664f4763Szrj if (ctx->hash_hosts)
1279e9778795SPeter Avalos known_hosts_hash(l, ctx);
1280e9778795SPeter Avalos else if (print_fingerprint) {
1281e9778795SPeter Avalos fp = sshkey_fingerprint(l->key, fptype, rep);
12820cbfa66cSDaniel Fojt ra = sshkey_fingerprint(l->key,
12830cbfa66cSDaniel Fojt fingerprint_hash, SSH_FP_RANDOMART);
12840cbfa66cSDaniel Fojt if (fp == NULL || ra == NULL)
128550a69bb5SSascha Wildner fatal_f("sshkey_fingerprint failed");
12860cbfa66cSDaniel Fojt mprintf("%s %s %s%s%s\n", ctx->host,
12870cbfa66cSDaniel Fojt sshkey_type(l->key), fp,
12880cbfa66cSDaniel Fojt l->comment[0] ? " " : "",
12890cbfa66cSDaniel Fojt l->comment);
12900cbfa66cSDaniel Fojt if (log_level_get() >= SYSLOG_LEVEL_VERBOSE)
12910cbfa66cSDaniel Fojt printf("%s\n", ra);
12920cbfa66cSDaniel Fojt free(ra);
1293e9778795SPeter Avalos free(fp);
1294e9778795SPeter Avalos } else
1295e9778795SPeter Avalos fprintf(ctx->out, "%s\n", l->line);
1296e9778795SPeter Avalos return 0;
1297e9778795SPeter Avalos }
1298664f4763Szrj } else if (ctx->delete_host) {
1299e9778795SPeter Avalos /* Retain non-matching hosts when deleting */
1300e9778795SPeter Avalos if (l->status == HKF_STATUS_INVALID) {
1301e9778795SPeter Avalos ctx->invalid = 1;
1302ce74bacaSMatthew Dillon logit("%s:%lu: invalid line", l->path, l->linenum);
1303e9778795SPeter Avalos }
1304e9778795SPeter Avalos fprintf(ctx->out, "%s\n", l->line);
1305e9778795SPeter Avalos }
1306e9778795SPeter Avalos return 0;
130718de8d7fSPeter Avalos }
130818de8d7fSPeter Avalos
130918de8d7fSPeter Avalos static void
do_known_hosts(struct passwd * pw,const char * name,int find_host,int delete_host,int hash_hosts)1310664f4763Szrj do_known_hosts(struct passwd *pw, const char *name, int find_host,
1311664f4763Szrj int delete_host, int hash_hosts)
131218de8d7fSPeter Avalos {
1313e9778795SPeter Avalos char *cp, tmp[PATH_MAX], old[PATH_MAX];
1314e9778795SPeter Avalos int r, fd, oerrno, inplace = 0;
1315e9778795SPeter Avalos struct known_hosts_ctx ctx;
1316e9778795SPeter Avalos u_int foreach_options;
131750a69bb5SSascha Wildner struct stat sb;
131818de8d7fSPeter Avalos
131918de8d7fSPeter Avalos if (!have_identity) {
132018de8d7fSPeter Avalos cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid);
132118de8d7fSPeter Avalos if (strlcpy(identity_file, cp, sizeof(identity_file)) >=
132218de8d7fSPeter Avalos sizeof(identity_file))
132318de8d7fSPeter Avalos fatal("Specified known hosts path too long");
132436e94dc5SPeter Avalos free(cp);
132518de8d7fSPeter Avalos have_identity = 1;
132618de8d7fSPeter Avalos }
132750a69bb5SSascha Wildner if (stat(identity_file, &sb) != 0)
132850a69bb5SSascha Wildner fatal("Cannot stat %s: %s", identity_file, strerror(errno));
132918de8d7fSPeter Avalos
1330e9778795SPeter Avalos memset(&ctx, 0, sizeof(ctx));
1331e9778795SPeter Avalos ctx.out = stdout;
1332e9778795SPeter Avalos ctx.host = name;
1333664f4763Szrj ctx.hash_hosts = hash_hosts;
1334664f4763Szrj ctx.find_host = find_host;
1335664f4763Szrj ctx.delete_host = delete_host;
1336e9778795SPeter Avalos
133718de8d7fSPeter Avalos /*
133818de8d7fSPeter Avalos * Find hosts goes to stdout, hash and deletions happen in-place
133918de8d7fSPeter Avalos * A corner case is ssh-keygen -HF foo, which should go to stdout
134018de8d7fSPeter Avalos */
134118de8d7fSPeter Avalos if (!find_host && (hash_hosts || delete_host)) {
134218de8d7fSPeter Avalos if (strlcpy(tmp, identity_file, sizeof(tmp)) >= sizeof(tmp) ||
134318de8d7fSPeter Avalos strlcat(tmp, ".XXXXXXXXXX", sizeof(tmp)) >= sizeof(tmp) ||
134418de8d7fSPeter Avalos strlcpy(old, identity_file, sizeof(old)) >= sizeof(old) ||
134518de8d7fSPeter Avalos strlcat(old, ".old", sizeof(old)) >= sizeof(old))
134618de8d7fSPeter Avalos fatal("known_hosts path too long");
134718de8d7fSPeter Avalos umask(077);
1348e9778795SPeter Avalos if ((fd = mkstemp(tmp)) == -1)
134918de8d7fSPeter Avalos fatal("mkstemp: %s", strerror(errno));
1350e9778795SPeter Avalos if ((ctx.out = fdopen(fd, "w")) == NULL) {
1351e9778795SPeter Avalos oerrno = errno;
135218de8d7fSPeter Avalos unlink(tmp);
1353e9778795SPeter Avalos fatal("fdopen: %s", strerror(oerrno));
135418de8d7fSPeter Avalos }
1355*ba1276acSMatthew Dillon (void)fchmod(fd, sb.st_mode & 0644);
135618de8d7fSPeter Avalos inplace = 1;
135718de8d7fSPeter Avalos }
1358e9778795SPeter Avalos /* XXX support identity_file == "-" for stdin */
1359e9778795SPeter Avalos foreach_options = find_host ? HKF_WANT_MATCH : 0;
1360e9778795SPeter Avalos foreach_options |= print_fingerprint ? HKF_WANT_PARSE_KEY : 0;
1361664f4763Szrj if ((r = hostkeys_foreach(identity_file, (find_host || !hash_hosts) ?
1362664f4763Szrj known_hosts_find_delete : known_hosts_hash, &ctx, name, NULL,
136350a69bb5SSascha Wildner foreach_options, 0)) != 0) {
136418de8d7fSPeter Avalos if (inplace)
1365e9778795SPeter Avalos unlink(tmp);
136650a69bb5SSascha Wildner fatal_fr(r, "hostkeys_foreach");
136718de8d7fSPeter Avalos }
136818de8d7fSPeter Avalos
1369e9778795SPeter Avalos if (inplace)
1370e9778795SPeter Avalos fclose(ctx.out);
137118de8d7fSPeter Avalos
1372e9778795SPeter Avalos if (ctx.invalid) {
1373e9778795SPeter Avalos error("%s is not a valid known_hosts file.", identity_file);
137418de8d7fSPeter Avalos if (inplace) {
1375e9778795SPeter Avalos error("Not replacing existing known_hosts "
1376e9778795SPeter Avalos "file because of errors");
137718de8d7fSPeter Avalos unlink(tmp);
137818de8d7fSPeter Avalos }
137918de8d7fSPeter Avalos exit(1);
1380e9778795SPeter Avalos } else if (delete_host && !ctx.found_key) {
1381e9778795SPeter Avalos logit("Host %s not found in %s", name, identity_file);
1382e9778795SPeter Avalos if (inplace)
1383e9778795SPeter Avalos unlink(tmp);
1384e9778795SPeter Avalos } else if (inplace) {
138518de8d7fSPeter Avalos /* Backup existing file */
138618de8d7fSPeter Avalos if (unlink(old) == -1 && errno != ENOENT)
138718de8d7fSPeter Avalos fatal("unlink %.100s: %s", old, strerror(errno));
138818de8d7fSPeter Avalos if (link(identity_file, old) == -1)
138918de8d7fSPeter Avalos fatal("link %.100s to %.100s: %s", identity_file, old,
139018de8d7fSPeter Avalos strerror(errno));
139118de8d7fSPeter Avalos /* Move new one into place */
139218de8d7fSPeter Avalos if (rename(tmp, identity_file) == -1) {
139318de8d7fSPeter Avalos error("rename\"%s\" to \"%s\": %s", tmp, identity_file,
139418de8d7fSPeter Avalos strerror(errno));
139518de8d7fSPeter Avalos unlink(tmp);
139618de8d7fSPeter Avalos unlink(old);
139718de8d7fSPeter Avalos exit(1);
139818de8d7fSPeter Avalos }
139918de8d7fSPeter Avalos
1400e9778795SPeter Avalos printf("%s updated.\n", identity_file);
1401e9778795SPeter Avalos printf("Original contents retained as %s\n", old);
1402e9778795SPeter Avalos if (ctx.has_unhashed) {
1403e9778795SPeter Avalos logit("WARNING: %s contains unhashed entries", old);
1404e9778795SPeter Avalos logit("Delete this file to ensure privacy "
1405e9778795SPeter Avalos "of hostnames");
140618de8d7fSPeter Avalos }
140718de8d7fSPeter Avalos }
140818de8d7fSPeter Avalos
1409e9778795SPeter Avalos exit (find_host && !ctx.found_key);
141018de8d7fSPeter Avalos }
141118de8d7fSPeter Avalos
141218de8d7fSPeter Avalos /*
141318de8d7fSPeter Avalos * Perform changing a passphrase. The argument is the passwd structure
141418de8d7fSPeter Avalos * for the current user.
141518de8d7fSPeter Avalos */
141618de8d7fSPeter Avalos static void
do_change_passphrase(struct passwd * pw)141718de8d7fSPeter Avalos do_change_passphrase(struct passwd *pw)
141818de8d7fSPeter Avalos {
141918de8d7fSPeter Avalos char *comment;
142018de8d7fSPeter Avalos char *old_passphrase, *passphrase1, *passphrase2;
142118de8d7fSPeter Avalos struct stat st;
1422e9778795SPeter Avalos struct sshkey *private;
1423e9778795SPeter Avalos int r;
142418de8d7fSPeter Avalos
142518de8d7fSPeter Avalos if (!have_identity)
142618de8d7fSPeter Avalos ask_filename(pw, "Enter file in which the key is");
14270cbfa66cSDaniel Fojt if (stat(identity_file, &st) == -1)
1428e9778795SPeter Avalos fatal("%s: %s", identity_file, strerror(errno));
142918de8d7fSPeter Avalos /* Try to load the file with empty passphrase. */
1430e9778795SPeter Avalos r = sshkey_load_private(identity_file, "", &private, &comment);
1431e9778795SPeter Avalos if (r == SSH_ERR_KEY_WRONG_PASSPHRASE) {
143218de8d7fSPeter Avalos if (identity_passphrase)
143318de8d7fSPeter Avalos old_passphrase = xstrdup(identity_passphrase);
143418de8d7fSPeter Avalos else
143518de8d7fSPeter Avalos old_passphrase =
143618de8d7fSPeter Avalos read_passphrase("Enter old passphrase: ",
143718de8d7fSPeter Avalos RP_ALLOW_STDIN);
1438e9778795SPeter Avalos r = sshkey_load_private(identity_file, old_passphrase,
1439e9778795SPeter Avalos &private, &comment);
14400cbfa66cSDaniel Fojt freezero(old_passphrase, strlen(old_passphrase));
1441e9778795SPeter Avalos if (r != 0)
1442e9778795SPeter Avalos goto badkey;
1443e9778795SPeter Avalos } else if (r != 0) {
1444e9778795SPeter Avalos badkey:
144550a69bb5SSascha Wildner fatal_r(r, "Failed to load key %s", identity_file);
144618de8d7fSPeter Avalos }
1447e9778795SPeter Avalos if (comment)
1448ce74bacaSMatthew Dillon mprintf("Key has comment '%s'\n", comment);
144918de8d7fSPeter Avalos
145018de8d7fSPeter Avalos /* Ask the new passphrase (twice). */
145118de8d7fSPeter Avalos if (identity_new_passphrase) {
145218de8d7fSPeter Avalos passphrase1 = xstrdup(identity_new_passphrase);
145318de8d7fSPeter Avalos passphrase2 = NULL;
145418de8d7fSPeter Avalos } else {
145518de8d7fSPeter Avalos passphrase1 =
145618de8d7fSPeter Avalos read_passphrase("Enter new passphrase (empty for no "
145718de8d7fSPeter Avalos "passphrase): ", RP_ALLOW_STDIN);
145818de8d7fSPeter Avalos passphrase2 = read_passphrase("Enter same passphrase again: ",
145918de8d7fSPeter Avalos RP_ALLOW_STDIN);
146018de8d7fSPeter Avalos
146118de8d7fSPeter Avalos /* Verify that they are the same. */
146218de8d7fSPeter Avalos if (strcmp(passphrase1, passphrase2) != 0) {
146336e94dc5SPeter Avalos explicit_bzero(passphrase1, strlen(passphrase1));
146436e94dc5SPeter Avalos explicit_bzero(passphrase2, strlen(passphrase2));
146536e94dc5SPeter Avalos free(passphrase1);
146636e94dc5SPeter Avalos free(passphrase2);
146718de8d7fSPeter Avalos printf("Pass phrases do not match. Try again.\n");
146818de8d7fSPeter Avalos exit(1);
146918de8d7fSPeter Avalos }
147018de8d7fSPeter Avalos /* Destroy the other copy. */
14710cbfa66cSDaniel Fojt freezero(passphrase2, strlen(passphrase2));
147218de8d7fSPeter Avalos }
147318de8d7fSPeter Avalos
147418de8d7fSPeter Avalos /* Save the file using the new passphrase. */
1475e9778795SPeter Avalos if ((r = sshkey_save_private(private, identity_file, passphrase1,
14760cbfa66cSDaniel Fojt comment, private_key_format, openssh_format_cipher, rounds)) != 0) {
147750a69bb5SSascha Wildner error_r(r, "Saving key \"%s\" failed", identity_file);
14780cbfa66cSDaniel Fojt freezero(passphrase1, strlen(passphrase1));
1479e9778795SPeter Avalos sshkey_free(private);
148036e94dc5SPeter Avalos free(comment);
148118de8d7fSPeter Avalos exit(1);
148218de8d7fSPeter Avalos }
148318de8d7fSPeter Avalos /* Destroy the passphrase and the copy of the key in memory. */
14840cbfa66cSDaniel Fojt freezero(passphrase1, strlen(passphrase1));
1485e9778795SPeter Avalos sshkey_free(private); /* Destroys contents */
148636e94dc5SPeter Avalos free(comment);
148718de8d7fSPeter Avalos
148818de8d7fSPeter Avalos printf("Your identification has been saved with the new passphrase.\n");
148918de8d7fSPeter Avalos exit(0);
149018de8d7fSPeter Avalos }
149118de8d7fSPeter Avalos
149218de8d7fSPeter Avalos /*
149318de8d7fSPeter Avalos * Print the SSHFP RR.
149418de8d7fSPeter Avalos */
149518de8d7fSPeter Avalos static int
do_print_resource_record(struct passwd * pw,char * fname,char * hname,int print_generic,char * const * opts,size_t nopts)1496664f4763Szrj do_print_resource_record(struct passwd *pw, char *fname, char *hname,
1497*ba1276acSMatthew Dillon int print_generic, char * const *opts, size_t nopts)
149818de8d7fSPeter Avalos {
1499e9778795SPeter Avalos struct sshkey *public;
150018de8d7fSPeter Avalos char *comment = NULL;
150118de8d7fSPeter Avalos struct stat st;
1502*ba1276acSMatthew Dillon int r, hash = -1;
1503*ba1276acSMatthew Dillon size_t i;
150418de8d7fSPeter Avalos
1505*ba1276acSMatthew Dillon for (i = 0; i < nopts; i++) {
1506*ba1276acSMatthew Dillon if (strncasecmp(opts[i], "hashalg=", 8) == 0) {
1507*ba1276acSMatthew Dillon if ((hash = ssh_digest_alg_by_name(opts[i] + 8)) == -1)
1508*ba1276acSMatthew Dillon fatal("Unsupported hash algorithm");
1509*ba1276acSMatthew Dillon } else {
1510*ba1276acSMatthew Dillon error("Invalid option \"%s\"", opts[i]);
1511*ba1276acSMatthew Dillon return SSH_ERR_INVALID_ARGUMENT;
1512*ba1276acSMatthew Dillon }
1513*ba1276acSMatthew Dillon }
151418de8d7fSPeter Avalos if (fname == NULL)
151550a69bb5SSascha Wildner fatal_f("no filename");
15160cbfa66cSDaniel Fojt if (stat(fname, &st) == -1) {
151718de8d7fSPeter Avalos if (errno == ENOENT)
151818de8d7fSPeter Avalos return 0;
1519e9778795SPeter Avalos fatal("%s: %s", fname, strerror(errno));
152018de8d7fSPeter Avalos }
1521e9778795SPeter Avalos if ((r = sshkey_load_public(fname, &public, &comment)) != 0)
152250a69bb5SSascha Wildner fatal_r(r, "Failed to read v2 public key from \"%s\"", fname);
1523*ba1276acSMatthew Dillon export_dns_rr(hname, public, stdout, print_generic, hash);
1524e9778795SPeter Avalos sshkey_free(public);
152536e94dc5SPeter Avalos free(comment);
152618de8d7fSPeter Avalos return 1;
152718de8d7fSPeter Avalos }
152818de8d7fSPeter Avalos
152918de8d7fSPeter Avalos /*
153018de8d7fSPeter Avalos * Change the comment of a private key file.
153118de8d7fSPeter Avalos */
153218de8d7fSPeter Avalos static void
do_change_comment(struct passwd * pw,const char * identity_comment)1533664f4763Szrj do_change_comment(struct passwd *pw, const char *identity_comment)
153418de8d7fSPeter Avalos {
153518de8d7fSPeter Avalos char new_comment[1024], *comment, *passphrase;
1536e9778795SPeter Avalos struct sshkey *private;
1537e9778795SPeter Avalos struct sshkey *public;
153818de8d7fSPeter Avalos struct stat st;
15390cbfa66cSDaniel Fojt int r;
154018de8d7fSPeter Avalos
154118de8d7fSPeter Avalos if (!have_identity)
154218de8d7fSPeter Avalos ask_filename(pw, "Enter file in which the key is");
15430cbfa66cSDaniel Fojt if (stat(identity_file, &st) == -1)
1544e9778795SPeter Avalos fatal("%s: %s", identity_file, strerror(errno));
1545e9778795SPeter Avalos if ((r = sshkey_load_private(identity_file, "",
1546e9778795SPeter Avalos &private, &comment)) == 0)
1547e9778795SPeter Avalos passphrase = xstrdup("");
1548e9778795SPeter Avalos else if (r != SSH_ERR_KEY_WRONG_PASSPHRASE)
154950a69bb5SSascha Wildner fatal_r(r, "Cannot load private key \"%s\"", identity_file);
1550e9778795SPeter Avalos else {
155118de8d7fSPeter Avalos if (identity_passphrase)
155218de8d7fSPeter Avalos passphrase = xstrdup(identity_passphrase);
155318de8d7fSPeter Avalos else if (identity_new_passphrase)
155418de8d7fSPeter Avalos passphrase = xstrdup(identity_new_passphrase);
155518de8d7fSPeter Avalos else
155618de8d7fSPeter Avalos passphrase = read_passphrase("Enter passphrase: ",
155718de8d7fSPeter Avalos RP_ALLOW_STDIN);
155818de8d7fSPeter Avalos /* Try to load using the passphrase. */
1559e9778795SPeter Avalos if ((r = sshkey_load_private(identity_file, passphrase,
1560e9778795SPeter Avalos &private, &comment)) != 0) {
15610cbfa66cSDaniel Fojt freezero(passphrase, strlen(passphrase));
156250a69bb5SSascha Wildner fatal_r(r, "Cannot load private key \"%s\"",
156350a69bb5SSascha Wildner identity_file);
156418de8d7fSPeter Avalos }
156518de8d7fSPeter Avalos }
1566e9778795SPeter Avalos
1567664f4763Szrj if (private->type != KEY_ED25519 && private->type != KEY_XMSS &&
15680cbfa66cSDaniel Fojt private_key_format != SSHKEY_PRIVATE_OPENSSH) {
1569ce74bacaSMatthew Dillon error("Comments are only supported for keys stored in "
1570e9778795SPeter Avalos "the new format (-o).");
1571e9778795SPeter Avalos explicit_bzero(passphrase, strlen(passphrase));
1572e9778795SPeter Avalos sshkey_free(private);
157318de8d7fSPeter Avalos exit(1);
157418de8d7fSPeter Avalos }
1575ce74bacaSMatthew Dillon if (comment)
15760cbfa66cSDaniel Fojt printf("Old comment: %s\n", comment);
1577ce74bacaSMatthew Dillon else
15780cbfa66cSDaniel Fojt printf("No existing comment\n");
157918de8d7fSPeter Avalos
158018de8d7fSPeter Avalos if (identity_comment) {
158118de8d7fSPeter Avalos strlcpy(new_comment, identity_comment, sizeof(new_comment));
158218de8d7fSPeter Avalos } else {
15830cbfa66cSDaniel Fojt printf("New comment: ");
158418de8d7fSPeter Avalos fflush(stdout);
158518de8d7fSPeter Avalos if (!fgets(new_comment, sizeof(new_comment), stdin)) {
158636e94dc5SPeter Avalos explicit_bzero(passphrase, strlen(passphrase));
1587e9778795SPeter Avalos sshkey_free(private);
158818de8d7fSPeter Avalos exit(1);
158918de8d7fSPeter Avalos }
159018de8d7fSPeter Avalos new_comment[strcspn(new_comment, "\n")] = '\0';
159118de8d7fSPeter Avalos }
15920cbfa66cSDaniel Fojt if (comment != NULL && strcmp(comment, new_comment) == 0) {
15930cbfa66cSDaniel Fojt printf("No change to comment\n");
15940cbfa66cSDaniel Fojt free(passphrase);
15950cbfa66cSDaniel Fojt sshkey_free(private);
15960cbfa66cSDaniel Fojt free(comment);
15970cbfa66cSDaniel Fojt exit(0);
15980cbfa66cSDaniel Fojt }
159918de8d7fSPeter Avalos
160018de8d7fSPeter Avalos /* Save the file using the new passphrase. */
1601e9778795SPeter Avalos if ((r = sshkey_save_private(private, identity_file, passphrase,
16020cbfa66cSDaniel Fojt new_comment, private_key_format, openssh_format_cipher,
16030cbfa66cSDaniel Fojt rounds)) != 0) {
160450a69bb5SSascha Wildner error_r(r, "Saving key \"%s\" failed", identity_file);
16050cbfa66cSDaniel Fojt freezero(passphrase, strlen(passphrase));
1606e9778795SPeter Avalos sshkey_free(private);
160736e94dc5SPeter Avalos free(comment);
160818de8d7fSPeter Avalos exit(1);
160918de8d7fSPeter Avalos }
16100cbfa66cSDaniel Fojt freezero(passphrase, strlen(passphrase));
1611e9778795SPeter Avalos if ((r = sshkey_from_private(private, &public)) != 0)
161250a69bb5SSascha Wildner fatal_fr(r, "sshkey_from_private");
1613e9778795SPeter Avalos sshkey_free(private);
161418de8d7fSPeter Avalos
161518de8d7fSPeter Avalos strlcat(identity_file, ".pub", sizeof(identity_file));
161650a69bb5SSascha Wildner if ((r = sshkey_save_public(public, identity_file, new_comment)) != 0)
161750a69bb5SSascha Wildner fatal_r(r, "Unable to save public key to %s", identity_file);
1618e9778795SPeter Avalos sshkey_free(public);
161936e94dc5SPeter Avalos free(comment);
162018de8d7fSPeter Avalos
16210cbfa66cSDaniel Fojt if (strlen(new_comment) > 0)
16220cbfa66cSDaniel Fojt printf("Comment '%s' applied\n", new_comment);
16230cbfa66cSDaniel Fojt else
16240cbfa66cSDaniel Fojt printf("Comment removed\n");
16250cbfa66cSDaniel Fojt
162618de8d7fSPeter Avalos exit(0);
162718de8d7fSPeter Avalos }
162818de8d7fSPeter Avalos
1629856ea928SPeter Avalos static void
cert_ext_add(const char * key,const char * value,int iscrit)163050a69bb5SSascha Wildner cert_ext_add(const char *key, const char *value, int iscrit)
1631856ea928SPeter Avalos {
163250a69bb5SSascha Wildner cert_ext = xreallocarray(cert_ext, ncert_ext + 1, sizeof(*cert_ext));
163350a69bb5SSascha Wildner cert_ext[ncert_ext].key = xstrdup(key);
163450a69bb5SSascha Wildner cert_ext[ncert_ext].val = value == NULL ? NULL : xstrdup(value);
163550a69bb5SSascha Wildner cert_ext[ncert_ext].crit = iscrit;
163650a69bb5SSascha Wildner ncert_ext++;
1637856ea928SPeter Avalos }
1638856ea928SPeter Avalos
163950a69bb5SSascha Wildner /* qsort(3) comparison function for certificate extensions */
164050a69bb5SSascha Wildner static int
cert_ext_cmp(const void * _a,const void * _b)164150a69bb5SSascha Wildner cert_ext_cmp(const void *_a, const void *_b)
1642856ea928SPeter Avalos {
164350a69bb5SSascha Wildner const struct cert_ext *a = (const struct cert_ext *)_a;
164450a69bb5SSascha Wildner const struct cert_ext *b = (const struct cert_ext *)_b;
1645e9778795SPeter Avalos int r;
1646856ea928SPeter Avalos
164750a69bb5SSascha Wildner if (a->crit != b->crit)
164850a69bb5SSascha Wildner return (a->crit < b->crit) ? -1 : 1;
164950a69bb5SSascha Wildner if ((r = strcmp(a->key, b->key)) != 0)
165050a69bb5SSascha Wildner return r;
165150a69bb5SSascha Wildner if ((a->val == NULL) != (b->val == NULL))
165250a69bb5SSascha Wildner return (a->val == NULL) ? -1 : 1;
165350a69bb5SSascha Wildner if (a->val != NULL && (r = strcmp(a->val, b->val)) != 0)
165450a69bb5SSascha Wildner return r;
165550a69bb5SSascha Wildner return 0;
1656856ea928SPeter Avalos }
1657856ea928SPeter Avalos
1658856ea928SPeter Avalos #define OPTIONS_CRITICAL 1
1659856ea928SPeter Avalos #define OPTIONS_EXTENSIONS 2
1660856ea928SPeter Avalos static void
prepare_options_buf(struct sshbuf * c,int which)1661e9778795SPeter Avalos prepare_options_buf(struct sshbuf *c, int which)
1662856ea928SPeter Avalos {
166350a69bb5SSascha Wildner struct sshbuf *b;
1664ce74bacaSMatthew Dillon size_t i;
166550a69bb5SSascha Wildner int r;
166650a69bb5SSascha Wildner const struct cert_ext *ext;
1667ce74bacaSMatthew Dillon
166850a69bb5SSascha Wildner if ((b = sshbuf_new()) == NULL)
166950a69bb5SSascha Wildner fatal_f("sshbuf_new failed");
1670e9778795SPeter Avalos sshbuf_reset(c);
167150a69bb5SSascha Wildner for (i = 0; i < ncert_ext; i++) {
167250a69bb5SSascha Wildner ext = &cert_ext[i];
167350a69bb5SSascha Wildner if ((ext->crit && (which & OPTIONS_EXTENSIONS)) ||
167450a69bb5SSascha Wildner (!ext->crit && (which & OPTIONS_CRITICAL)))
1675ce74bacaSMatthew Dillon continue;
167650a69bb5SSascha Wildner if (ext->val == NULL) {
167750a69bb5SSascha Wildner /* flag option */
167850a69bb5SSascha Wildner debug3_f("%s", ext->key);
167950a69bb5SSascha Wildner if ((r = sshbuf_put_cstring(c, ext->key)) != 0 ||
168050a69bb5SSascha Wildner (r = sshbuf_put_string(c, NULL, 0)) != 0)
168150a69bb5SSascha Wildner fatal_fr(r, "prepare flag");
168250a69bb5SSascha Wildner } else {
168350a69bb5SSascha Wildner /* key/value option */
168450a69bb5SSascha Wildner debug3_f("%s=%s", ext->key, ext->val);
168550a69bb5SSascha Wildner sshbuf_reset(b);
168650a69bb5SSascha Wildner if ((r = sshbuf_put_cstring(c, ext->key)) != 0 ||
168750a69bb5SSascha Wildner (r = sshbuf_put_cstring(b, ext->val)) != 0 ||
168850a69bb5SSascha Wildner (r = sshbuf_put_stringb(c, b)) != 0)
168950a69bb5SSascha Wildner fatal_fr(r, "prepare k/v");
1690ce74bacaSMatthew Dillon }
1691ce74bacaSMatthew Dillon }
169250a69bb5SSascha Wildner sshbuf_free(b);
169350a69bb5SSascha Wildner }
169450a69bb5SSascha Wildner
169550a69bb5SSascha Wildner static void
finalise_cert_exts(void)169650a69bb5SSascha Wildner finalise_cert_exts(void)
169750a69bb5SSascha Wildner {
169850a69bb5SSascha Wildner /* critical options */
169950a69bb5SSascha Wildner if (certflags_command != NULL)
170050a69bb5SSascha Wildner cert_ext_add("force-command", certflags_command, 1);
170150a69bb5SSascha Wildner if (certflags_src_addr != NULL)
170250a69bb5SSascha Wildner cert_ext_add("source-address", certflags_src_addr, 1);
1703ee116499SAntonio Huete Jimenez if ((certflags_flags & CERTOPT_REQUIRE_VERIFY) != 0)
1704ee116499SAntonio Huete Jimenez cert_ext_add("verify-required", NULL, 1);
170550a69bb5SSascha Wildner /* extensions */
170650a69bb5SSascha Wildner if ((certflags_flags & CERTOPT_X_FWD) != 0)
170750a69bb5SSascha Wildner cert_ext_add("permit-X11-forwarding", NULL, 0);
170850a69bb5SSascha Wildner if ((certflags_flags & CERTOPT_AGENT_FWD) != 0)
170950a69bb5SSascha Wildner cert_ext_add("permit-agent-forwarding", NULL, 0);
171050a69bb5SSascha Wildner if ((certflags_flags & CERTOPT_PORT_FWD) != 0)
171150a69bb5SSascha Wildner cert_ext_add("permit-port-forwarding", NULL, 0);
171250a69bb5SSascha Wildner if ((certflags_flags & CERTOPT_PTY) != 0)
171350a69bb5SSascha Wildner cert_ext_add("permit-pty", NULL, 0);
171450a69bb5SSascha Wildner if ((certflags_flags & CERTOPT_USER_RC) != 0)
171550a69bb5SSascha Wildner cert_ext_add("permit-user-rc", NULL, 0);
171650a69bb5SSascha Wildner if ((certflags_flags & CERTOPT_NO_REQUIRE_USER_PRESENCE) != 0)
171750a69bb5SSascha Wildner cert_ext_add("no-touch-required", NULL, 0);
171850a69bb5SSascha Wildner /* order lexically by key */
171950a69bb5SSascha Wildner if (ncert_ext > 0)
172050a69bb5SSascha Wildner qsort(cert_ext, ncert_ext, sizeof(*cert_ext), cert_ext_cmp);
1721856ea928SPeter Avalos }
1722856ea928SPeter Avalos
1723e9778795SPeter Avalos static struct sshkey *
load_pkcs11_key(char * path)1724856ea928SPeter Avalos load_pkcs11_key(char *path)
1725856ea928SPeter Avalos {
1726856ea928SPeter Avalos #ifdef ENABLE_PKCS11
1727e9778795SPeter Avalos struct sshkey **keys = NULL, *public, *private = NULL;
1728e9778795SPeter Avalos int r, i, nkeys;
1729856ea928SPeter Avalos
1730e9778795SPeter Avalos if ((r = sshkey_load_public(path, &public, NULL)) != 0)
173150a69bb5SSascha Wildner fatal_r(r, "Couldn't load CA public key \"%s\"", path);
1732856ea928SPeter Avalos
17330cbfa66cSDaniel Fojt nkeys = pkcs11_add_provider(pkcs11provider, identity_passphrase,
17340cbfa66cSDaniel Fojt &keys, NULL);
173550a69bb5SSascha Wildner debug3_f("%d keys", nkeys);
1736856ea928SPeter Avalos if (nkeys <= 0)
1737856ea928SPeter Avalos fatal("cannot read public key from pkcs11");
1738856ea928SPeter Avalos for (i = 0; i < nkeys; i++) {
1739e9778795SPeter Avalos if (sshkey_equal_public(public, keys[i])) {
1740856ea928SPeter Avalos private = keys[i];
1741856ea928SPeter Avalos continue;
1742856ea928SPeter Avalos }
1743e9778795SPeter Avalos sshkey_free(keys[i]);
1744856ea928SPeter Avalos }
174536e94dc5SPeter Avalos free(keys);
1746e9778795SPeter Avalos sshkey_free(public);
1747856ea928SPeter Avalos return private;
1748856ea928SPeter Avalos #else
1749856ea928SPeter Avalos fatal("no pkcs11 support");
1750856ea928SPeter Avalos #endif /* ENABLE_PKCS11 */
1751856ea928SPeter Avalos }
1752856ea928SPeter Avalos
1753ce74bacaSMatthew Dillon /* Signer for sshkey_certify_custom that uses the agent */
1754ce74bacaSMatthew Dillon static int
agent_signer(struct sshkey * key,u_char ** sigp,size_t * lenp,const u_char * data,size_t datalen,const char * alg,const char * provider,const char * pin,u_int compat,void * ctx)17550cbfa66cSDaniel Fojt agent_signer(struct sshkey *key, u_char **sigp, size_t *lenp,
1756ce74bacaSMatthew Dillon const u_char *data, size_t datalen,
175750a69bb5SSascha Wildner const char *alg, const char *provider, const char *pin,
175850a69bb5SSascha Wildner u_int compat, void *ctx)
1759ce74bacaSMatthew Dillon {
1760ce74bacaSMatthew Dillon int *agent_fdp = (int *)ctx;
1761ce74bacaSMatthew Dillon
1762ce74bacaSMatthew Dillon return ssh_agent_sign(*agent_fdp, key, sigp, lenp,
1763ce74bacaSMatthew Dillon data, datalen, alg, compat);
1764ce74bacaSMatthew Dillon }
1765ce74bacaSMatthew Dillon
1766856ea928SPeter Avalos static void
do_ca_sign(struct passwd * pw,const char * ca_key_path,int prefer_agent,unsigned long long cert_serial,int cert_serial_autoinc,int argc,char ** argv)1767664f4763Szrj do_ca_sign(struct passwd *pw, const char *ca_key_path, int prefer_agent,
1768664f4763Szrj unsigned long long cert_serial, int cert_serial_autoinc,
1769664f4763Szrj int argc, char **argv)
1770856ea928SPeter Avalos {
17710cbfa66cSDaniel Fojt int r, i, found, agent_fd = -1;
1772856ea928SPeter Avalos u_int n;
1773e9778795SPeter Avalos struct sshkey *ca, *public;
17740cbfa66cSDaniel Fojt char valid[64], *otmp, *tmp, *cp, *out, *comment;
177550a69bb5SSascha Wildner char *ca_fp = NULL, **plist = NULL, *pin = NULL;
1776ce74bacaSMatthew Dillon struct ssh_identitylist *agent_ids;
1777ce74bacaSMatthew Dillon size_t j;
17780cbfa66cSDaniel Fojt struct notifier_ctx *notifier = NULL;
1779856ea928SPeter Avalos
178036e94dc5SPeter Avalos #ifdef ENABLE_PKCS11
1781856ea928SPeter Avalos pkcs11_init(1);
178236e94dc5SPeter Avalos #endif
1783856ea928SPeter Avalos tmp = tilde_expand_filename(ca_key_path, pw->pw_uid);
1784856ea928SPeter Avalos if (pkcs11provider != NULL) {
1785ce74bacaSMatthew Dillon /* If a PKCS#11 token was specified then try to use it */
1786856ea928SPeter Avalos if ((ca = load_pkcs11_key(tmp)) == NULL)
1787856ea928SPeter Avalos fatal("No PKCS#11 key matching %s found", ca_key_path);
1788ce74bacaSMatthew Dillon } else if (prefer_agent) {
1789ce74bacaSMatthew Dillon /*
1790ce74bacaSMatthew Dillon * Agent signature requested. Try to use agent after making
1791ce74bacaSMatthew Dillon * sure the public key specified is actually present in the
1792ce74bacaSMatthew Dillon * agent.
1793ce74bacaSMatthew Dillon */
1794ce74bacaSMatthew Dillon if ((r = sshkey_load_public(tmp, &ca, NULL)) != 0)
179550a69bb5SSascha Wildner fatal_r(r, "Cannot load CA public key %s", tmp);
1796ce74bacaSMatthew Dillon if ((r = ssh_get_authentication_socket(&agent_fd)) != 0)
179750a69bb5SSascha Wildner fatal_r(r, "Cannot use public key for CA signature");
1798ce74bacaSMatthew Dillon if ((r = ssh_fetch_identitylist(agent_fd, &agent_ids)) != 0)
179950a69bb5SSascha Wildner fatal_r(r, "Retrieve agent key list");
1800ce74bacaSMatthew Dillon found = 0;
1801ce74bacaSMatthew Dillon for (j = 0; j < agent_ids->nkeys; j++) {
1802ce74bacaSMatthew Dillon if (sshkey_equal(ca, agent_ids->keys[j])) {
1803ce74bacaSMatthew Dillon found = 1;
1804ce74bacaSMatthew Dillon break;
1805ce74bacaSMatthew Dillon }
1806ce74bacaSMatthew Dillon }
1807ce74bacaSMatthew Dillon if (!found)
1808ce74bacaSMatthew Dillon fatal("CA key %s not found in agent", tmp);
1809ce74bacaSMatthew Dillon ssh_free_identitylist(agent_ids);
1810ce74bacaSMatthew Dillon ca->flags |= SSHKEY_FLAG_EXT;
1811ce74bacaSMatthew Dillon } else {
1812ce74bacaSMatthew Dillon /* CA key is assumed to be a private key on the filesystem */
18130cbfa66cSDaniel Fojt ca = load_identity(tmp, NULL);
181450a69bb5SSascha Wildner if (sshkey_is_sk(ca) &&
181550a69bb5SSascha Wildner (ca->sk_flags & SSH_SK_USER_VERIFICATION_REQD)) {
181650a69bb5SSascha Wildner if ((pin = read_passphrase("Enter PIN for CA key: ",
181750a69bb5SSascha Wildner RP_ALLOW_STDIN)) == NULL)
181850a69bb5SSascha Wildner fatal_f("couldn't read PIN");
181950a69bb5SSascha Wildner }
1820ce74bacaSMatthew Dillon }
182136e94dc5SPeter Avalos free(tmp);
1822856ea928SPeter Avalos
18230cbfa66cSDaniel Fojt if (key_type_name != NULL) {
18240cbfa66cSDaniel Fojt if (sshkey_type_from_name(key_type_name) != ca->type) {
1825e9778795SPeter Avalos fatal("CA key type %s doesn't match specified %s",
1826e9778795SPeter Avalos sshkey_ssh_name(ca), key_type_name);
1827e9778795SPeter Avalos }
18280cbfa66cSDaniel Fojt } else if (ca->type == KEY_RSA) {
18290cbfa66cSDaniel Fojt /* Default to a good signature algorithm */
18300cbfa66cSDaniel Fojt key_type_name = "rsa-sha2-512";
18310cbfa66cSDaniel Fojt }
18320cbfa66cSDaniel Fojt ca_fp = sshkey_fingerprint(ca, fingerprint_hash, SSH_FP_DEFAULT);
1833e9778795SPeter Avalos
183450a69bb5SSascha Wildner finalise_cert_exts();
1835856ea928SPeter Avalos for (i = 0; i < argc; i++) {
1836856ea928SPeter Avalos /* Split list of principals */
1837856ea928SPeter Avalos n = 0;
1838856ea928SPeter Avalos if (cert_principals != NULL) {
1839856ea928SPeter Avalos otmp = tmp = xstrdup(cert_principals);
1840856ea928SPeter Avalos plist = NULL;
1841856ea928SPeter Avalos for (; (cp = strsep(&tmp, ",")) != NULL; n++) {
1842e9778795SPeter Avalos plist = xreallocarray(plist, n + 1, sizeof(*plist));
1843856ea928SPeter Avalos if (*(plist[n] = xstrdup(cp)) == '\0')
1844856ea928SPeter Avalos fatal("Empty principal name");
1845856ea928SPeter Avalos }
184636e94dc5SPeter Avalos free(otmp);
1847856ea928SPeter Avalos }
1848664f4763Szrj if (n > SSHKEY_CERT_MAX_PRINCIPALS)
1849664f4763Szrj fatal("Too many certificate principals specified");
1850856ea928SPeter Avalos
1851856ea928SPeter Avalos tmp = tilde_expand_filename(argv[i], pw->pw_uid);
1852e9778795SPeter Avalos if ((r = sshkey_load_public(tmp, &public, &comment)) != 0)
185350a69bb5SSascha Wildner fatal_r(r, "load pubkey \"%s\"", tmp);
18540cbfa66cSDaniel Fojt if (sshkey_is_cert(public))
185550a69bb5SSascha Wildner fatal_f("key \"%s\" type %s cannot be certified",
185650a69bb5SSascha Wildner tmp, sshkey_type(public));
1857856ea928SPeter Avalos
1858856ea928SPeter Avalos /* Prepare certificate to sign */
1859e9778795SPeter Avalos if ((r = sshkey_to_certified(public)) != 0)
186050a69bb5SSascha Wildner fatal_r(r, "Could not upgrade key %s to certificate", tmp);
1861856ea928SPeter Avalos public->cert->type = cert_key_type;
1862856ea928SPeter Avalos public->cert->serial = (u_int64_t)cert_serial;
1863856ea928SPeter Avalos public->cert->key_id = xstrdup(cert_key_id);
1864856ea928SPeter Avalos public->cert->nprincipals = n;
1865856ea928SPeter Avalos public->cert->principals = plist;
1866856ea928SPeter Avalos public->cert->valid_after = cert_valid_from;
1867856ea928SPeter Avalos public->cert->valid_before = cert_valid_to;
1868e9778795SPeter Avalos prepare_options_buf(public->cert->critical, OPTIONS_CRITICAL);
186936e94dc5SPeter Avalos prepare_options_buf(public->cert->extensions,
1870856ea928SPeter Avalos OPTIONS_EXTENSIONS);
1871e9778795SPeter Avalos if ((r = sshkey_from_private(ca,
1872e9778795SPeter Avalos &public->cert->signature_key)) != 0)
187350a69bb5SSascha Wildner fatal_r(r, "sshkey_from_private (ca key)");
1874856ea928SPeter Avalos
1875ce74bacaSMatthew Dillon if (agent_fd != -1 && (ca->flags & SSHKEY_FLAG_EXT) != 0) {
1876ce74bacaSMatthew Dillon if ((r = sshkey_certify_custom(public, ca,
187750a69bb5SSascha Wildner key_type_name, sk_provider, NULL, agent_signer,
18780cbfa66cSDaniel Fojt &agent_fd)) != 0)
187950a69bb5SSascha Wildner fatal_r(r, "Couldn't certify %s via agent", tmp);
1880ce74bacaSMatthew Dillon } else {
18810cbfa66cSDaniel Fojt if (sshkey_is_sk(ca) &&
18820cbfa66cSDaniel Fojt (ca->sk_flags & SSH_SK_USER_PRESENCE_REQD)) {
18830cbfa66cSDaniel Fojt notifier = notify_start(0,
18840cbfa66cSDaniel Fojt "Confirm user presence for key %s %s",
18850cbfa66cSDaniel Fojt sshkey_type(ca), ca_fp);
18860cbfa66cSDaniel Fojt }
18870cbfa66cSDaniel Fojt r = sshkey_certify(public, ca, key_type_name,
188850a69bb5SSascha Wildner sk_provider, pin);
188950a69bb5SSascha Wildner notify_complete(notifier, "User presence confirmed");
18900cbfa66cSDaniel Fojt if (r != 0)
189150a69bb5SSascha Wildner fatal_r(r, "Couldn't certify key %s", tmp);
1892ce74bacaSMatthew Dillon }
1893856ea928SPeter Avalos
1894856ea928SPeter Avalos if ((cp = strrchr(tmp, '.')) != NULL && strcmp(cp, ".pub") == 0)
1895856ea928SPeter Avalos *cp = '\0';
1896856ea928SPeter Avalos xasprintf(&out, "%s-cert.pub", tmp);
189736e94dc5SPeter Avalos free(tmp);
1898856ea928SPeter Avalos
18990cbfa66cSDaniel Fojt if ((r = sshkey_save_public(public, out, comment)) != 0) {
190050a69bb5SSascha Wildner fatal_r(r, "Unable to save public key to %s",
190150a69bb5SSascha Wildner identity_file);
19020cbfa66cSDaniel Fojt }
1903856ea928SPeter Avalos
1904856ea928SPeter Avalos if (!quiet) {
1905e9778795SPeter Avalos sshkey_format_cert_validity(public->cert,
1906e9778795SPeter Avalos valid, sizeof(valid));
1907856ea928SPeter Avalos logit("Signed %s key %s: id \"%s\" serial %llu%s%s "
1908e9778795SPeter Avalos "valid %s", sshkey_cert_type(public),
19099f304aafSPeter Avalos out, public->cert->key_id,
19109f304aafSPeter Avalos (unsigned long long)public->cert->serial,
1911856ea928SPeter Avalos cert_principals != NULL ? " for " : "",
1912856ea928SPeter Avalos cert_principals != NULL ? cert_principals : "",
1913e9778795SPeter Avalos valid);
1914856ea928SPeter Avalos }
1915856ea928SPeter Avalos
1916e9778795SPeter Avalos sshkey_free(public);
191736e94dc5SPeter Avalos free(out);
1918664f4763Szrj if (cert_serial_autoinc)
1919664f4763Szrj cert_serial++;
1920856ea928SPeter Avalos }
192150a69bb5SSascha Wildner if (pin != NULL)
192250a69bb5SSascha Wildner freezero(pin, strlen(pin));
19230cbfa66cSDaniel Fojt free(ca_fp);
192436e94dc5SPeter Avalos #ifdef ENABLE_PKCS11
1925856ea928SPeter Avalos pkcs11_terminate();
192636e94dc5SPeter Avalos #endif
1927856ea928SPeter Avalos exit(0);
1928856ea928SPeter Avalos }
1929856ea928SPeter Avalos
1930856ea928SPeter Avalos static u_int64_t
parse_relative_time(const char * s,time_t now)1931856ea928SPeter Avalos parse_relative_time(const char *s, time_t now)
1932856ea928SPeter Avalos {
1933856ea928SPeter Avalos int64_t mul, secs;
1934856ea928SPeter Avalos
1935856ea928SPeter Avalos mul = *s == '-' ? -1 : 1;
1936856ea928SPeter Avalos
1937856ea928SPeter Avalos if ((secs = convtime(s + 1)) == -1)
1938856ea928SPeter Avalos fatal("Invalid relative certificate time %s", s);
1939856ea928SPeter Avalos if (mul == -1 && secs > now)
1940856ea928SPeter Avalos fatal("Certificate time %s cannot be represented", s);
1941856ea928SPeter Avalos return now + (u_int64_t)(secs * mul);
1942856ea928SPeter Avalos }
1943856ea928SPeter Avalos
1944856ea928SPeter Avalos static void
parse_hex_u64(const char * s,uint64_t * up)1945ee116499SAntonio Huete Jimenez parse_hex_u64(const char *s, uint64_t *up)
1946ee116499SAntonio Huete Jimenez {
1947ee116499SAntonio Huete Jimenez char *ep;
1948ee116499SAntonio Huete Jimenez unsigned long long ull;
1949ee116499SAntonio Huete Jimenez
1950ee116499SAntonio Huete Jimenez errno = 0;
1951ee116499SAntonio Huete Jimenez ull = strtoull(s, &ep, 16);
1952ee116499SAntonio Huete Jimenez if (*s == '\0' || *ep != '\0')
1953ee116499SAntonio Huete Jimenez fatal("Invalid certificate time: not a number");
1954ee116499SAntonio Huete Jimenez if (errno == ERANGE && ull == ULONG_MAX)
1955ee116499SAntonio Huete Jimenez fatal_fr(SSH_ERR_SYSTEM_ERROR, "Invalid certificate time");
1956ee116499SAntonio Huete Jimenez *up = (uint64_t)ull;
1957ee116499SAntonio Huete Jimenez }
1958ee116499SAntonio Huete Jimenez
1959ee116499SAntonio Huete Jimenez static void
parse_cert_times(char * timespec)1960856ea928SPeter Avalos parse_cert_times(char *timespec)
1961856ea928SPeter Avalos {
1962856ea928SPeter Avalos char *from, *to;
1963856ea928SPeter Avalos time_t now = time(NULL);
1964856ea928SPeter Avalos int64_t secs;
1965856ea928SPeter Avalos
1966856ea928SPeter Avalos /* +timespec relative to now */
1967856ea928SPeter Avalos if (*timespec == '+' && strchr(timespec, ':') == NULL) {
1968856ea928SPeter Avalos if ((secs = convtime(timespec + 1)) == -1)
1969856ea928SPeter Avalos fatal("Invalid relative certificate life %s", timespec);
1970856ea928SPeter Avalos cert_valid_to = now + secs;
1971856ea928SPeter Avalos /*
1972856ea928SPeter Avalos * Backdate certificate one minute to avoid problems on hosts
1973856ea928SPeter Avalos * with poorly-synchronised clocks.
1974856ea928SPeter Avalos */
1975856ea928SPeter Avalos cert_valid_from = ((now - 59)/ 60) * 60;
1976856ea928SPeter Avalos return;
1977856ea928SPeter Avalos }
1978856ea928SPeter Avalos
1979856ea928SPeter Avalos /*
1980856ea928SPeter Avalos * from:to, where
1981ee116499SAntonio Huete Jimenez * from := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS | 0x... | "always"
1982ee116499SAntonio Huete Jimenez * to := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS | 0x... | "forever"
1983856ea928SPeter Avalos */
1984856ea928SPeter Avalos from = xstrdup(timespec);
1985856ea928SPeter Avalos to = strchr(from, ':');
1986856ea928SPeter Avalos if (to == NULL || from == to || *(to + 1) == '\0')
1987856ea928SPeter Avalos fatal("Invalid certificate life specification %s", timespec);
1988856ea928SPeter Avalos *to++ = '\0';
1989856ea928SPeter Avalos
1990856ea928SPeter Avalos if (*from == '-' || *from == '+')
1991856ea928SPeter Avalos cert_valid_from = parse_relative_time(from, now);
1992664f4763Szrj else if (strcmp(from, "always") == 0)
1993664f4763Szrj cert_valid_from = 0;
1994ee116499SAntonio Huete Jimenez else if (strncmp(from, "0x", 2) == 0)
1995ee116499SAntonio Huete Jimenez parse_hex_u64(from, &cert_valid_from);
1996664f4763Szrj else if (parse_absolute_time(from, &cert_valid_from) != 0)
1997664f4763Szrj fatal("Invalid from time \"%s\"", from);
1998856ea928SPeter Avalos
1999856ea928SPeter Avalos if (*to == '-' || *to == '+')
200036e94dc5SPeter Avalos cert_valid_to = parse_relative_time(to, now);
2001664f4763Szrj else if (strcmp(to, "forever") == 0)
2002664f4763Szrj cert_valid_to = ~(u_int64_t)0;
2003*ba1276acSMatthew Dillon else if (strncmp(to, "0x", 2) == 0)
2004ee116499SAntonio Huete Jimenez parse_hex_u64(to, &cert_valid_to);
2005664f4763Szrj else if (parse_absolute_time(to, &cert_valid_to) != 0)
2006664f4763Szrj fatal("Invalid to time \"%s\"", to);
2007856ea928SPeter Avalos
2008856ea928SPeter Avalos if (cert_valid_to <= cert_valid_from)
2009856ea928SPeter Avalos fatal("Empty certificate validity interval");
201036e94dc5SPeter Avalos free(from);
2011856ea928SPeter Avalos }
2012856ea928SPeter Avalos
2013856ea928SPeter Avalos static void
add_cert_option(char * opt)2014856ea928SPeter Avalos add_cert_option(char *opt)
2015856ea928SPeter Avalos {
2016ce74bacaSMatthew Dillon char *val, *cp;
2017ce74bacaSMatthew Dillon int iscrit = 0;
2018856ea928SPeter Avalos
20191c188a7fSPeter Avalos if (strcasecmp(opt, "clear") == 0)
2020856ea928SPeter Avalos certflags_flags = 0;
2021856ea928SPeter Avalos else if (strcasecmp(opt, "no-x11-forwarding") == 0)
2022856ea928SPeter Avalos certflags_flags &= ~CERTOPT_X_FWD;
2023856ea928SPeter Avalos else if (strcasecmp(opt, "permit-x11-forwarding") == 0)
2024856ea928SPeter Avalos certflags_flags |= CERTOPT_X_FWD;
2025856ea928SPeter Avalos else if (strcasecmp(opt, "no-agent-forwarding") == 0)
2026856ea928SPeter Avalos certflags_flags &= ~CERTOPT_AGENT_FWD;
2027856ea928SPeter Avalos else if (strcasecmp(opt, "permit-agent-forwarding") == 0)
2028856ea928SPeter Avalos certflags_flags |= CERTOPT_AGENT_FWD;
2029856ea928SPeter Avalos else if (strcasecmp(opt, "no-port-forwarding") == 0)
2030856ea928SPeter Avalos certflags_flags &= ~CERTOPT_PORT_FWD;
2031856ea928SPeter Avalos else if (strcasecmp(opt, "permit-port-forwarding") == 0)
2032856ea928SPeter Avalos certflags_flags |= CERTOPT_PORT_FWD;
2033856ea928SPeter Avalos else if (strcasecmp(opt, "no-pty") == 0)
2034856ea928SPeter Avalos certflags_flags &= ~CERTOPT_PTY;
2035856ea928SPeter Avalos else if (strcasecmp(opt, "permit-pty") == 0)
2036856ea928SPeter Avalos certflags_flags |= CERTOPT_PTY;
2037856ea928SPeter Avalos else if (strcasecmp(opt, "no-user-rc") == 0)
2038856ea928SPeter Avalos certflags_flags &= ~CERTOPT_USER_RC;
2039856ea928SPeter Avalos else if (strcasecmp(opt, "permit-user-rc") == 0)
2040856ea928SPeter Avalos certflags_flags |= CERTOPT_USER_RC;
20410cbfa66cSDaniel Fojt else if (strcasecmp(opt, "touch-required") == 0)
20420cbfa66cSDaniel Fojt certflags_flags &= ~CERTOPT_NO_REQUIRE_USER_PRESENCE;
20430cbfa66cSDaniel Fojt else if (strcasecmp(opt, "no-touch-required") == 0)
20440cbfa66cSDaniel Fojt certflags_flags |= CERTOPT_NO_REQUIRE_USER_PRESENCE;
2045ee116499SAntonio Huete Jimenez else if (strcasecmp(opt, "no-verify-required") == 0)
2046ee116499SAntonio Huete Jimenez certflags_flags &= ~CERTOPT_REQUIRE_VERIFY;
2047ee116499SAntonio Huete Jimenez else if (strcasecmp(opt, "verify-required") == 0)
2048ee116499SAntonio Huete Jimenez certflags_flags |= CERTOPT_REQUIRE_VERIFY;
2049856ea928SPeter Avalos else if (strncasecmp(opt, "force-command=", 14) == 0) {
2050856ea928SPeter Avalos val = opt + 14;
2051856ea928SPeter Avalos if (*val == '\0')
2052856ea928SPeter Avalos fatal("Empty force-command option");
2053856ea928SPeter Avalos if (certflags_command != NULL)
2054856ea928SPeter Avalos fatal("force-command already specified");
2055856ea928SPeter Avalos certflags_command = xstrdup(val);
2056856ea928SPeter Avalos } else if (strncasecmp(opt, "source-address=", 15) == 0) {
2057856ea928SPeter Avalos val = opt + 15;
2058856ea928SPeter Avalos if (*val == '\0')
2059856ea928SPeter Avalos fatal("Empty source-address option");
2060856ea928SPeter Avalos if (certflags_src_addr != NULL)
2061856ea928SPeter Avalos fatal("source-address already specified");
2062856ea928SPeter Avalos if (addr_match_cidr_list(NULL, val) != 0)
2063856ea928SPeter Avalos fatal("Invalid source-address list");
2064856ea928SPeter Avalos certflags_src_addr = xstrdup(val);
2065ce74bacaSMatthew Dillon } else if (strncasecmp(opt, "extension:", 10) == 0 ||
2066ce74bacaSMatthew Dillon (iscrit = (strncasecmp(opt, "critical:", 9) == 0))) {
2067ce74bacaSMatthew Dillon val = xstrdup(strchr(opt, ':') + 1);
2068ce74bacaSMatthew Dillon if ((cp = strchr(val, '=')) != NULL)
2069ce74bacaSMatthew Dillon *cp++ = '\0';
207050a69bb5SSascha Wildner cert_ext_add(val, cp, iscrit);
207150a69bb5SSascha Wildner free(val);
2072856ea928SPeter Avalos } else
2073856ea928SPeter Avalos fatal("Unsupported certificate option \"%s\"", opt);
2074856ea928SPeter Avalos }
2075856ea928SPeter Avalos
2076856ea928SPeter Avalos static void
show_options(struct sshbuf * optbuf,int in_critical)2077e9778795SPeter Avalos show_options(struct sshbuf *optbuf, int in_critical)
2078856ea928SPeter Avalos {
207950a69bb5SSascha Wildner char *name, *arg, *hex;
2080e9778795SPeter Avalos struct sshbuf *options, *option = NULL;
2081e9778795SPeter Avalos int r;
2082856ea928SPeter Avalos
2083e9778795SPeter Avalos if ((options = sshbuf_fromb(optbuf)) == NULL)
208450a69bb5SSascha Wildner fatal_f("sshbuf_fromb failed");
2085e9778795SPeter Avalos while (sshbuf_len(options) != 0) {
2086e9778795SPeter Avalos sshbuf_free(option);
2087e9778795SPeter Avalos option = NULL;
2088e9778795SPeter Avalos if ((r = sshbuf_get_cstring(options, &name, NULL)) != 0 ||
2089e9778795SPeter Avalos (r = sshbuf_froms(options, &option)) != 0)
209050a69bb5SSascha Wildner fatal_fr(r, "parse option");
2091856ea928SPeter Avalos printf(" %s", name);
2092e9778795SPeter Avalos if (!in_critical &&
2093856ea928SPeter Avalos (strcmp(name, "permit-X11-forwarding") == 0 ||
2094856ea928SPeter Avalos strcmp(name, "permit-agent-forwarding") == 0 ||
2095856ea928SPeter Avalos strcmp(name, "permit-port-forwarding") == 0 ||
2096856ea928SPeter Avalos strcmp(name, "permit-pty") == 0 ||
20970cbfa66cSDaniel Fojt strcmp(name, "permit-user-rc") == 0 ||
20980cbfa66cSDaniel Fojt strcmp(name, "no-touch-required") == 0)) {
2099856ea928SPeter Avalos printf("\n");
21000cbfa66cSDaniel Fojt } else if (in_critical &&
2101856ea928SPeter Avalos (strcmp(name, "force-command") == 0 ||
2102856ea928SPeter Avalos strcmp(name, "source-address") == 0)) {
2103e9778795SPeter Avalos if ((r = sshbuf_get_cstring(option, &arg, NULL)) != 0)
210450a69bb5SSascha Wildner fatal_fr(r, "parse critical");
210536e94dc5SPeter Avalos printf(" %s\n", arg);
210636e94dc5SPeter Avalos free(arg);
2107ee116499SAntonio Huete Jimenez } else if (in_critical &&
2108ee116499SAntonio Huete Jimenez strcmp(name, "verify-required") == 0) {
2109ee116499SAntonio Huete Jimenez printf("\n");
211050a69bb5SSascha Wildner } else if (sshbuf_len(option) > 0) {
211150a69bb5SSascha Wildner hex = sshbuf_dtob16(option);
211250a69bb5SSascha Wildner printf(" UNKNOWN OPTION: %s (len %zu)\n",
211350a69bb5SSascha Wildner hex, sshbuf_len(option));
2114e9778795SPeter Avalos sshbuf_reset(option);
211550a69bb5SSascha Wildner free(hex);
211650a69bb5SSascha Wildner } else
211750a69bb5SSascha Wildner printf(" UNKNOWN FLAG OPTION\n");
211836e94dc5SPeter Avalos free(name);
2119e9778795SPeter Avalos if (sshbuf_len(option) != 0)
2120856ea928SPeter Avalos fatal("Option corrupt: extra data at end");
2121856ea928SPeter Avalos }
2122e9778795SPeter Avalos sshbuf_free(option);
2123e9778795SPeter Avalos sshbuf_free(options);
2124856ea928SPeter Avalos }
2125856ea928SPeter Avalos
2126856ea928SPeter Avalos static void
print_cert(struct sshkey * key)2127e9778795SPeter Avalos print_cert(struct sshkey *key)
2128856ea928SPeter Avalos {
2129e9778795SPeter Avalos char valid[64], *key_fp, *ca_fp;
2130e9778795SPeter Avalos u_int i;
2131856ea928SPeter Avalos
2132e9778795SPeter Avalos key_fp = sshkey_fingerprint(key, fingerprint_hash, SSH_FP_DEFAULT);
2133e9778795SPeter Avalos ca_fp = sshkey_fingerprint(key->cert->signature_key,
2134e9778795SPeter Avalos fingerprint_hash, SSH_FP_DEFAULT);
2135e9778795SPeter Avalos if (key_fp == NULL || ca_fp == NULL)
213650a69bb5SSascha Wildner fatal_f("sshkey_fingerprint fail");
2137e9778795SPeter Avalos sshkey_format_cert_validity(key->cert, valid, sizeof(valid));
2138856ea928SPeter Avalos
2139e9778795SPeter Avalos printf(" Type: %s %s certificate\n", sshkey_ssh_name(key),
2140e9778795SPeter Avalos sshkey_cert_type(key));
2141e9778795SPeter Avalos printf(" Public key: %s %s\n", sshkey_type(key), key_fp);
2142664f4763Szrj printf(" Signing CA: %s %s (using %s)\n",
2143664f4763Szrj sshkey_type(key->cert->signature_key), ca_fp,
2144664f4763Szrj key->cert->signature_type);
2145856ea928SPeter Avalos printf(" Key ID: \"%s\"\n", key->cert->key_id);
2146e9778795SPeter Avalos printf(" Serial: %llu\n", (unsigned long long)key->cert->serial);
2147e9778795SPeter Avalos printf(" Valid: %s\n", valid);
2148856ea928SPeter Avalos printf(" Principals: ");
2149856ea928SPeter Avalos if (key->cert->nprincipals == 0)
2150856ea928SPeter Avalos printf("(none)\n");
2151856ea928SPeter Avalos else {
2152856ea928SPeter Avalos for (i = 0; i < key->cert->nprincipals; i++)
2153856ea928SPeter Avalos printf("\n %s",
2154856ea928SPeter Avalos key->cert->principals[i]);
2155856ea928SPeter Avalos printf("\n");
2156856ea928SPeter Avalos }
2157856ea928SPeter Avalos printf(" Critical Options: ");
2158e9778795SPeter Avalos if (sshbuf_len(key->cert->critical) == 0)
2159856ea928SPeter Avalos printf("(none)\n");
2160856ea928SPeter Avalos else {
2161856ea928SPeter Avalos printf("\n");
2162e9778795SPeter Avalos show_options(key->cert->critical, 1);
2163856ea928SPeter Avalos }
2164856ea928SPeter Avalos printf(" Extensions: ");
2165e9778795SPeter Avalos if (sshbuf_len(key->cert->extensions) == 0)
2166856ea928SPeter Avalos printf("(none)\n");
2167856ea928SPeter Avalos else {
2168856ea928SPeter Avalos printf("\n");
2169e9778795SPeter Avalos show_options(key->cert->extensions, 0);
2170856ea928SPeter Avalos }
2171856ea928SPeter Avalos }
2172856ea928SPeter Avalos
2173e9778795SPeter Avalos static void
do_show_cert(struct passwd * pw)2174e9778795SPeter Avalos do_show_cert(struct passwd *pw)
2175e9778795SPeter Avalos {
2176e9778795SPeter Avalos struct sshkey *key = NULL;
2177e9778795SPeter Avalos struct stat st;
2178e9778795SPeter Avalos int r, is_stdin = 0, ok = 0;
2179e9778795SPeter Avalos FILE *f;
2180664f4763Szrj char *cp, *line = NULL;
2181e9778795SPeter Avalos const char *path;
2182664f4763Szrj size_t linesize = 0;
2183e9778795SPeter Avalos u_long lnum = 0;
2184e9778795SPeter Avalos
2185e9778795SPeter Avalos if (!have_identity)
2186e9778795SPeter Avalos ask_filename(pw, "Enter file in which the key is");
21870cbfa66cSDaniel Fojt if (strcmp(identity_file, "-") != 0 && stat(identity_file, &st) == -1)
2188e9778795SPeter Avalos fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
2189e9778795SPeter Avalos
2190e9778795SPeter Avalos path = identity_file;
2191e9778795SPeter Avalos if (strcmp(path, "-") == 0) {
2192e9778795SPeter Avalos f = stdin;
2193e9778795SPeter Avalos path = "(stdin)";
2194e9778795SPeter Avalos is_stdin = 1;
2195e9778795SPeter Avalos } else if ((f = fopen(identity_file, "r")) == NULL)
2196e9778795SPeter Avalos fatal("fopen %s: %s", identity_file, strerror(errno));
2197e9778795SPeter Avalos
2198664f4763Szrj while (getline(&line, &linesize, f) != -1) {
2199664f4763Szrj lnum++;
2200e9778795SPeter Avalos sshkey_free(key);
2201e9778795SPeter Avalos key = NULL;
2202e9778795SPeter Avalos /* Trim leading space and comments */
2203e9778795SPeter Avalos cp = line + strspn(line, " \t");
2204e9778795SPeter Avalos if (*cp == '#' || *cp == '\0')
2205e9778795SPeter Avalos continue;
2206e9778795SPeter Avalos if ((key = sshkey_new(KEY_UNSPEC)) == NULL)
2207ce74bacaSMatthew Dillon fatal("sshkey_new");
2208e9778795SPeter Avalos if ((r = sshkey_read(key, &cp)) != 0) {
220950a69bb5SSascha Wildner error_r(r, "%s:%lu: invalid key", path, lnum);
2210e9778795SPeter Avalos continue;
2211e9778795SPeter Avalos }
2212e9778795SPeter Avalos if (!sshkey_is_cert(key)) {
2213e9778795SPeter Avalos error("%s:%lu is not a certificate", path, lnum);
2214e9778795SPeter Avalos continue;
2215e9778795SPeter Avalos }
2216e9778795SPeter Avalos ok = 1;
2217e9778795SPeter Avalos if (!is_stdin && lnum == 1)
2218e9778795SPeter Avalos printf("%s:\n", path);
2219e9778795SPeter Avalos else
2220e9778795SPeter Avalos printf("%s:%lu:\n", path, lnum);
2221e9778795SPeter Avalos print_cert(key);
2222e9778795SPeter Avalos }
2223664f4763Szrj free(line);
2224e9778795SPeter Avalos sshkey_free(key);
2225e9778795SPeter Avalos fclose(f);
2226e9778795SPeter Avalos exit(ok ? 0 : 1);
2227e9778795SPeter Avalos }
2228e9778795SPeter Avalos
222936e94dc5SPeter Avalos static void
load_krl(const char * path,struct ssh_krl ** krlp)223036e94dc5SPeter Avalos load_krl(const char *path, struct ssh_krl **krlp)
223136e94dc5SPeter Avalos {
2232e9778795SPeter Avalos struct sshbuf *krlbuf;
22330cbfa66cSDaniel Fojt int r;
223436e94dc5SPeter Avalos
22350cbfa66cSDaniel Fojt if ((r = sshbuf_load_file(path, &krlbuf)) != 0)
223650a69bb5SSascha Wildner fatal_r(r, "Unable to load KRL %s", path);
223736e94dc5SPeter Avalos /* XXX check sigs */
2238*ba1276acSMatthew Dillon if ((r = ssh_krl_from_blob(krlbuf, krlp)) != 0 ||
223936e94dc5SPeter Avalos *krlp == NULL)
224050a69bb5SSascha Wildner fatal_r(r, "Invalid KRL file %s", path);
2241e9778795SPeter Avalos sshbuf_free(krlbuf);
224236e94dc5SPeter Avalos }
224336e94dc5SPeter Avalos
224436e94dc5SPeter Avalos static void
hash_to_blob(const char * cp,u_char ** blobp,size_t * lenp,const char * file,u_long lnum)2245664f4763Szrj hash_to_blob(const char *cp, u_char **blobp, size_t *lenp,
2246664f4763Szrj const char *file, u_long lnum)
2247664f4763Szrj {
2248664f4763Szrj char *tmp;
2249664f4763Szrj size_t tlen;
2250664f4763Szrj struct sshbuf *b;
2251664f4763Szrj int r;
2252664f4763Szrj
2253664f4763Szrj if (strncmp(cp, "SHA256:", 7) != 0)
2254664f4763Szrj fatal("%s:%lu: unsupported hash algorithm", file, lnum);
2255664f4763Szrj cp += 7;
2256664f4763Szrj
2257664f4763Szrj /*
2258664f4763Szrj * OpenSSH base64 hashes omit trailing '='
2259664f4763Szrj * characters; put them back for decode.
2260664f4763Szrj */
2261*ba1276acSMatthew Dillon if ((tlen = strlen(cp)) >= SIZE_MAX - 5)
2262*ba1276acSMatthew Dillon fatal_f("hash too long: %zu bytes", tlen);
2263664f4763Szrj tmp = xmalloc(tlen + 4 + 1);
2264664f4763Szrj strlcpy(tmp, cp, tlen + 1);
2265664f4763Szrj while ((tlen % 4) != 0) {
2266664f4763Szrj tmp[tlen++] = '=';
2267664f4763Szrj tmp[tlen] = '\0';
2268664f4763Szrj }
2269664f4763Szrj if ((b = sshbuf_new()) == NULL)
227050a69bb5SSascha Wildner fatal_f("sshbuf_new failed");
2271664f4763Szrj if ((r = sshbuf_b64tod(b, tmp)) != 0)
227250a69bb5SSascha Wildner fatal_r(r, "%s:%lu: decode hash failed", file, lnum);
2273664f4763Szrj free(tmp);
2274664f4763Szrj *lenp = sshbuf_len(b);
2275664f4763Szrj *blobp = xmalloc(*lenp);
2276664f4763Szrj memcpy(*blobp, sshbuf_ptr(b), *lenp);
2277664f4763Szrj sshbuf_free(b);
2278664f4763Szrj }
2279664f4763Szrj
2280664f4763Szrj static void
update_krl_from_file(struct passwd * pw,const char * file,int wild_ca,const struct sshkey * ca,struct ssh_krl * krl)2281e9778795SPeter Avalos update_krl_from_file(struct passwd *pw, const char *file, int wild_ca,
2282e9778795SPeter Avalos const struct sshkey *ca, struct ssh_krl *krl)
228336e94dc5SPeter Avalos {
2284e9778795SPeter Avalos struct sshkey *key = NULL;
228536e94dc5SPeter Avalos u_long lnum = 0;
2286664f4763Szrj char *path, *cp, *ep, *line = NULL;
2287664f4763Szrj u_char *blob = NULL;
2288664f4763Szrj size_t blen = 0, linesize = 0;
228936e94dc5SPeter Avalos unsigned long long serial, serial2;
2290664f4763Szrj int i, was_explicit_key, was_sha1, was_sha256, was_hash, r;
229136e94dc5SPeter Avalos FILE *krl_spec;
229236e94dc5SPeter Avalos
229336e94dc5SPeter Avalos path = tilde_expand_filename(file, pw->pw_uid);
229436e94dc5SPeter Avalos if (strcmp(path, "-") == 0) {
229536e94dc5SPeter Avalos krl_spec = stdin;
229636e94dc5SPeter Avalos free(path);
229736e94dc5SPeter Avalos path = xstrdup("(standard input)");
229836e94dc5SPeter Avalos } else if ((krl_spec = fopen(path, "r")) == NULL)
229936e94dc5SPeter Avalos fatal("fopen %s: %s", path, strerror(errno));
230036e94dc5SPeter Avalos
230136e94dc5SPeter Avalos if (!quiet)
230236e94dc5SPeter Avalos printf("Revoking from %s\n", path);
2303664f4763Szrj while (getline(&line, &linesize, krl_spec) != -1) {
2304*ba1276acSMatthew Dillon if (linesize >= INT_MAX) {
2305*ba1276acSMatthew Dillon fatal_f("%s contains unparsable line, len=%zu",
2306*ba1276acSMatthew Dillon path, linesize);
2307*ba1276acSMatthew Dillon }
2308664f4763Szrj lnum++;
2309664f4763Szrj was_explicit_key = was_sha1 = was_sha256 = was_hash = 0;
231036e94dc5SPeter Avalos cp = line + strspn(line, " \t");
231136e94dc5SPeter Avalos /* Trim trailing space, comments and strip \n */
231236e94dc5SPeter Avalos for (i = 0, r = -1; cp[i] != '\0'; i++) {
231336e94dc5SPeter Avalos if (cp[i] == '#' || cp[i] == '\n') {
231436e94dc5SPeter Avalos cp[i] = '\0';
231536e94dc5SPeter Avalos break;
231636e94dc5SPeter Avalos }
231736e94dc5SPeter Avalos if (cp[i] == ' ' || cp[i] == '\t') {
231836e94dc5SPeter Avalos /* Remember the start of a span of whitespace */
231936e94dc5SPeter Avalos if (r == -1)
232036e94dc5SPeter Avalos r = i;
232136e94dc5SPeter Avalos } else
232236e94dc5SPeter Avalos r = -1;
232336e94dc5SPeter Avalos }
232436e94dc5SPeter Avalos if (r != -1)
232536e94dc5SPeter Avalos cp[r] = '\0';
232636e94dc5SPeter Avalos if (*cp == '\0')
232736e94dc5SPeter Avalos continue;
232836e94dc5SPeter Avalos if (strncasecmp(cp, "serial:", 7) == 0) {
2329e9778795SPeter Avalos if (ca == NULL && !wild_ca) {
233036e94dc5SPeter Avalos fatal("revoking certificates by serial number "
233136e94dc5SPeter Avalos "requires specification of a CA key");
233236e94dc5SPeter Avalos }
233336e94dc5SPeter Avalos cp += 7;
233436e94dc5SPeter Avalos cp = cp + strspn(cp, " \t");
233536e94dc5SPeter Avalos errno = 0;
233636e94dc5SPeter Avalos serial = strtoull(cp, &ep, 0);
233736e94dc5SPeter Avalos if (*cp == '\0' || (*ep != '\0' && *ep != '-'))
233836e94dc5SPeter Avalos fatal("%s:%lu: invalid serial \"%s\"",
233936e94dc5SPeter Avalos path, lnum, cp);
234036e94dc5SPeter Avalos if (errno == ERANGE && serial == ULLONG_MAX)
234136e94dc5SPeter Avalos fatal("%s:%lu: serial out of range",
234236e94dc5SPeter Avalos path, lnum);
234336e94dc5SPeter Avalos serial2 = serial;
234436e94dc5SPeter Avalos if (*ep == '-') {
234536e94dc5SPeter Avalos cp = ep + 1;
234636e94dc5SPeter Avalos errno = 0;
234736e94dc5SPeter Avalos serial2 = strtoull(cp, &ep, 0);
234836e94dc5SPeter Avalos if (*cp == '\0' || *ep != '\0')
234936e94dc5SPeter Avalos fatal("%s:%lu: invalid serial \"%s\"",
235036e94dc5SPeter Avalos path, lnum, cp);
235136e94dc5SPeter Avalos if (errno == ERANGE && serial2 == ULLONG_MAX)
235236e94dc5SPeter Avalos fatal("%s:%lu: serial out of range",
235336e94dc5SPeter Avalos path, lnum);
235436e94dc5SPeter Avalos if (serial2 <= serial)
235536e94dc5SPeter Avalos fatal("%s:%lu: invalid serial range "
235636e94dc5SPeter Avalos "%llu:%llu", path, lnum,
235736e94dc5SPeter Avalos (unsigned long long)serial,
235836e94dc5SPeter Avalos (unsigned long long)serial2);
235936e94dc5SPeter Avalos }
236036e94dc5SPeter Avalos if (ssh_krl_revoke_cert_by_serial_range(krl,
236136e94dc5SPeter Avalos ca, serial, serial2) != 0) {
236250a69bb5SSascha Wildner fatal_f("revoke serial failed");
236336e94dc5SPeter Avalos }
236436e94dc5SPeter Avalos } else if (strncasecmp(cp, "id:", 3) == 0) {
2365e9778795SPeter Avalos if (ca == NULL && !wild_ca) {
236636e94dc5SPeter Avalos fatal("revoking certificates by key ID "
236736e94dc5SPeter Avalos "requires specification of a CA key");
236836e94dc5SPeter Avalos }
236936e94dc5SPeter Avalos cp += 3;
237036e94dc5SPeter Avalos cp = cp + strspn(cp, " \t");
237136e94dc5SPeter Avalos if (ssh_krl_revoke_cert_by_key_id(krl, ca, cp) != 0)
237250a69bb5SSascha Wildner fatal_f("revoke key ID failed");
2373664f4763Szrj } else if (strncasecmp(cp, "hash:", 5) == 0) {
2374664f4763Szrj cp += 5;
2375664f4763Szrj cp = cp + strspn(cp, " \t");
2376664f4763Szrj hash_to_blob(cp, &blob, &blen, file, lnum);
2377664f4763Szrj r = ssh_krl_revoke_key_sha256(krl, blob, blen);
23780cbfa66cSDaniel Fojt if (r != 0)
237950a69bb5SSascha Wildner fatal_fr(r, "revoke key failed");
238036e94dc5SPeter Avalos } else {
238136e94dc5SPeter Avalos if (strncasecmp(cp, "key:", 4) == 0) {
238236e94dc5SPeter Avalos cp += 4;
238336e94dc5SPeter Avalos cp = cp + strspn(cp, " \t");
238436e94dc5SPeter Avalos was_explicit_key = 1;
238536e94dc5SPeter Avalos } else if (strncasecmp(cp, "sha1:", 5) == 0) {
238636e94dc5SPeter Avalos cp += 5;
238736e94dc5SPeter Avalos cp = cp + strspn(cp, " \t");
238836e94dc5SPeter Avalos was_sha1 = 1;
2389664f4763Szrj } else if (strncasecmp(cp, "sha256:", 7) == 0) {
2390664f4763Szrj cp += 7;
2391664f4763Szrj cp = cp + strspn(cp, " \t");
2392664f4763Szrj was_sha256 = 1;
239336e94dc5SPeter Avalos /*
239436e94dc5SPeter Avalos * Just try to process the line as a key.
239536e94dc5SPeter Avalos * Parsing will fail if it isn't.
239636e94dc5SPeter Avalos */
239736e94dc5SPeter Avalos }
2398e9778795SPeter Avalos if ((key = sshkey_new(KEY_UNSPEC)) == NULL)
2399ce74bacaSMatthew Dillon fatal("sshkey_new");
2400e9778795SPeter Avalos if ((r = sshkey_read(key, &cp)) != 0)
240150a69bb5SSascha Wildner fatal_r(r, "%s:%lu: invalid key", path, lnum);
240236e94dc5SPeter Avalos if (was_explicit_key)
240336e94dc5SPeter Avalos r = ssh_krl_revoke_key_explicit(krl, key);
2404664f4763Szrj else if (was_sha1) {
2405664f4763Szrj if (sshkey_fingerprint_raw(key,
2406664f4763Szrj SSH_DIGEST_SHA1, &blob, &blen) != 0) {
2407664f4763Szrj fatal("%s:%lu: fingerprint failed",
2408664f4763Szrj file, lnum);
2409664f4763Szrj }
2410664f4763Szrj r = ssh_krl_revoke_key_sha1(krl, blob, blen);
2411664f4763Szrj } else if (was_sha256) {
2412664f4763Szrj if (sshkey_fingerprint_raw(key,
2413664f4763Szrj SSH_DIGEST_SHA256, &blob, &blen) != 0) {
2414664f4763Szrj fatal("%s:%lu: fingerprint failed",
2415664f4763Szrj file, lnum);
2416664f4763Szrj }
2417664f4763Szrj r = ssh_krl_revoke_key_sha256(krl, blob, blen);
2418664f4763Szrj } else
241936e94dc5SPeter Avalos r = ssh_krl_revoke_key(krl, key);
242036e94dc5SPeter Avalos if (r != 0)
242150a69bb5SSascha Wildner fatal_fr(r, "revoke key failed");
2422664f4763Szrj freezero(blob, blen);
2423664f4763Szrj blob = NULL;
2424664f4763Szrj blen = 0;
2425e9778795SPeter Avalos sshkey_free(key);
242636e94dc5SPeter Avalos }
242736e94dc5SPeter Avalos }
242836e94dc5SPeter Avalos if (strcmp(path, "-") != 0)
242936e94dc5SPeter Avalos fclose(krl_spec);
2430664f4763Szrj free(line);
243136e94dc5SPeter Avalos free(path);
243236e94dc5SPeter Avalos }
243336e94dc5SPeter Avalos
243436e94dc5SPeter Avalos static void
do_gen_krl(struct passwd * pw,int updating,const char * ca_key_path,unsigned long long krl_version,const char * krl_comment,int argc,char ** argv)2435664f4763Szrj do_gen_krl(struct passwd *pw, int updating, const char *ca_key_path,
2436664f4763Szrj unsigned long long krl_version, const char *krl_comment,
2437664f4763Szrj int argc, char **argv)
243836e94dc5SPeter Avalos {
243936e94dc5SPeter Avalos struct ssh_krl *krl;
244036e94dc5SPeter Avalos struct stat sb;
2441e9778795SPeter Avalos struct sshkey *ca = NULL;
24420cbfa66cSDaniel Fojt int i, r, wild_ca = 0;
244336e94dc5SPeter Avalos char *tmp;
2444e9778795SPeter Avalos struct sshbuf *kbuf;
244536e94dc5SPeter Avalos
244636e94dc5SPeter Avalos if (*identity_file == '\0')
244736e94dc5SPeter Avalos fatal("KRL generation requires an output file");
244836e94dc5SPeter Avalos if (stat(identity_file, &sb) == -1) {
244936e94dc5SPeter Avalos if (errno != ENOENT)
245036e94dc5SPeter Avalos fatal("Cannot access KRL \"%s\": %s",
245136e94dc5SPeter Avalos identity_file, strerror(errno));
245236e94dc5SPeter Avalos if (updating)
245336e94dc5SPeter Avalos fatal("KRL \"%s\" does not exist", identity_file);
245436e94dc5SPeter Avalos }
245536e94dc5SPeter Avalos if (ca_key_path != NULL) {
2456e9778795SPeter Avalos if (strcasecmp(ca_key_path, "none") == 0)
2457e9778795SPeter Avalos wild_ca = 1;
2458e9778795SPeter Avalos else {
245936e94dc5SPeter Avalos tmp = tilde_expand_filename(ca_key_path, pw->pw_uid);
2460e9778795SPeter Avalos if ((r = sshkey_load_public(tmp, &ca, NULL)) != 0)
246150a69bb5SSascha Wildner fatal_r(r, "Cannot load CA public key %s", tmp);
246236e94dc5SPeter Avalos free(tmp);
246336e94dc5SPeter Avalos }
2464e9778795SPeter Avalos }
246536e94dc5SPeter Avalos
246636e94dc5SPeter Avalos if (updating)
246736e94dc5SPeter Avalos load_krl(identity_file, &krl);
246836e94dc5SPeter Avalos else if ((krl = ssh_krl_init()) == NULL)
246936e94dc5SPeter Avalos fatal("couldn't create KRL");
247036e94dc5SPeter Avalos
2471664f4763Szrj if (krl_version != 0)
2472664f4763Szrj ssh_krl_set_version(krl, krl_version);
2473664f4763Szrj if (krl_comment != NULL)
2474664f4763Szrj ssh_krl_set_comment(krl, krl_comment);
247536e94dc5SPeter Avalos
247636e94dc5SPeter Avalos for (i = 0; i < argc; i++)
2477e9778795SPeter Avalos update_krl_from_file(pw, argv[i], wild_ca, ca, krl);
247836e94dc5SPeter Avalos
2479e9778795SPeter Avalos if ((kbuf = sshbuf_new()) == NULL)
2480e9778795SPeter Avalos fatal("sshbuf_new failed");
2481*ba1276acSMatthew Dillon if (ssh_krl_to_blob(krl, kbuf) != 0)
248236e94dc5SPeter Avalos fatal("Couldn't generate KRL");
24830cbfa66cSDaniel Fojt if ((r = sshbuf_write_file(identity_file, kbuf)) != 0)
248436e94dc5SPeter Avalos fatal("write %s: %s", identity_file, strerror(errno));
2485e9778795SPeter Avalos sshbuf_free(kbuf);
248636e94dc5SPeter Avalos ssh_krl_free(krl);
2487e9778795SPeter Avalos sshkey_free(ca);
248836e94dc5SPeter Avalos }
248936e94dc5SPeter Avalos
249036e94dc5SPeter Avalos static void
do_check_krl(struct passwd * pw,int print_krl,int argc,char ** argv)24910cbfa66cSDaniel Fojt do_check_krl(struct passwd *pw, int print_krl, int argc, char **argv)
249236e94dc5SPeter Avalos {
249336e94dc5SPeter Avalos int i, r, ret = 0;
249436e94dc5SPeter Avalos char *comment;
249536e94dc5SPeter Avalos struct ssh_krl *krl;
2496e9778795SPeter Avalos struct sshkey *k;
249736e94dc5SPeter Avalos
249836e94dc5SPeter Avalos if (*identity_file == '\0')
249936e94dc5SPeter Avalos fatal("KRL checking requires an input file");
250036e94dc5SPeter Avalos load_krl(identity_file, &krl);
25010cbfa66cSDaniel Fojt if (print_krl)
25020cbfa66cSDaniel Fojt krl_dump(krl, stdout);
250336e94dc5SPeter Avalos for (i = 0; i < argc; i++) {
2504e9778795SPeter Avalos if ((r = sshkey_load_public(argv[i], &k, &comment)) != 0)
250550a69bb5SSascha Wildner fatal_r(r, "Cannot load public key %s", argv[i]);
250636e94dc5SPeter Avalos r = ssh_krl_check_key(krl, k);
250736e94dc5SPeter Avalos printf("%s%s%s%s: %s\n", argv[i],
250836e94dc5SPeter Avalos *comment ? " (" : "", comment, *comment ? ")" : "",
250936e94dc5SPeter Avalos r == 0 ? "ok" : "REVOKED");
251036e94dc5SPeter Avalos if (r != 0)
251136e94dc5SPeter Avalos ret = 1;
2512e9778795SPeter Avalos sshkey_free(k);
251336e94dc5SPeter Avalos free(comment);
251436e94dc5SPeter Avalos }
251536e94dc5SPeter Avalos ssh_krl_free(krl);
251636e94dc5SPeter Avalos exit(ret);
251736e94dc5SPeter Avalos }
251836e94dc5SPeter Avalos
25190cbfa66cSDaniel Fojt static struct sshkey *
load_sign_key(const char * keypath,const struct sshkey * pubkey)25200cbfa66cSDaniel Fojt load_sign_key(const char *keypath, const struct sshkey *pubkey)
25210cbfa66cSDaniel Fojt {
25220cbfa66cSDaniel Fojt size_t i, slen, plen = strlen(keypath);
25230cbfa66cSDaniel Fojt char *privpath = xstrdup(keypath);
2524ee116499SAntonio Huete Jimenez static const char * const suffixes[] = { "-cert.pub", ".pub", NULL };
25250cbfa66cSDaniel Fojt struct sshkey *ret = NULL, *privkey = NULL;
2526ee116499SAntonio Huete Jimenez int r, waspub = 0;
2527ee116499SAntonio Huete Jimenez struct stat st;
25280cbfa66cSDaniel Fojt
25290cbfa66cSDaniel Fojt /*
25300cbfa66cSDaniel Fojt * If passed a public key filename, then try to locate the corresponding
25310cbfa66cSDaniel Fojt * private key. This lets us specify certificates on the command-line
25320cbfa66cSDaniel Fojt * and have ssh-keygen find the appropriate private key.
25330cbfa66cSDaniel Fojt */
25340cbfa66cSDaniel Fojt for (i = 0; suffixes[i]; i++) {
25350cbfa66cSDaniel Fojt slen = strlen(suffixes[i]);
25360cbfa66cSDaniel Fojt if (plen <= slen ||
25370cbfa66cSDaniel Fojt strcmp(privpath + plen - slen, suffixes[i]) != 0)
25380cbfa66cSDaniel Fojt continue;
25390cbfa66cSDaniel Fojt privpath[plen - slen] = '\0';
254050a69bb5SSascha Wildner debug_f("%s looks like a public key, using private key "
254150a69bb5SSascha Wildner "path %s instead", keypath, privpath);
2542ee116499SAntonio Huete Jimenez waspub = 1;
25430cbfa66cSDaniel Fojt }
2544ee116499SAntonio Huete Jimenez if (waspub && stat(privpath, &st) != 0 && errno == ENOENT)
2545ee116499SAntonio Huete Jimenez fatal("No private key found for public key \"%s\"", keypath);
2546ee116499SAntonio Huete Jimenez if ((r = sshkey_load_private(privpath, "", &privkey, NULL)) != 0 &&
2547ee116499SAntonio Huete Jimenez (r != SSH_ERR_KEY_WRONG_PASSPHRASE)) {
2548ee116499SAntonio Huete Jimenez debug_fr(r, "load private key \"%s\"", privpath);
2549ee116499SAntonio Huete Jimenez fatal("No private key found for \"%s\"", privpath);
2550ee116499SAntonio Huete Jimenez } else if (privkey == NULL)
2551ee116499SAntonio Huete Jimenez privkey = load_identity(privpath, NULL);
2552ee116499SAntonio Huete Jimenez
25530cbfa66cSDaniel Fojt if (!sshkey_equal_public(pubkey, privkey)) {
25540cbfa66cSDaniel Fojt error("Public key %s doesn't match private %s",
25550cbfa66cSDaniel Fojt keypath, privpath);
25560cbfa66cSDaniel Fojt goto done;
25570cbfa66cSDaniel Fojt }
25580cbfa66cSDaniel Fojt if (sshkey_is_cert(pubkey) && !sshkey_is_cert(privkey)) {
25590cbfa66cSDaniel Fojt /*
25600cbfa66cSDaniel Fojt * Graft the certificate onto the private key to make
25610cbfa66cSDaniel Fojt * it capable of signing.
25620cbfa66cSDaniel Fojt */
25630cbfa66cSDaniel Fojt if ((r = sshkey_to_certified(privkey)) != 0) {
256450a69bb5SSascha Wildner error_fr(r, "sshkey_to_certified");
25650cbfa66cSDaniel Fojt goto done;
25660cbfa66cSDaniel Fojt }
25670cbfa66cSDaniel Fojt if ((r = sshkey_cert_copy(pubkey, privkey)) != 0) {
256850a69bb5SSascha Wildner error_fr(r, "sshkey_cert_copy");
25690cbfa66cSDaniel Fojt goto done;
25700cbfa66cSDaniel Fojt }
25710cbfa66cSDaniel Fojt }
25720cbfa66cSDaniel Fojt /* success */
25730cbfa66cSDaniel Fojt ret = privkey;
25740cbfa66cSDaniel Fojt privkey = NULL;
25750cbfa66cSDaniel Fojt done:
25760cbfa66cSDaniel Fojt sshkey_free(privkey);
25770cbfa66cSDaniel Fojt free(privpath);
25780cbfa66cSDaniel Fojt return ret;
25790cbfa66cSDaniel Fojt }
25800cbfa66cSDaniel Fojt
25810cbfa66cSDaniel Fojt static int
sign_one(struct sshkey * signkey,const char * filename,int fd,const char * sig_namespace,const char * hashalg,sshsig_signer * signer,void * signer_ctx)25820cbfa66cSDaniel Fojt sign_one(struct sshkey *signkey, const char *filename, int fd,
2583ee116499SAntonio Huete Jimenez const char *sig_namespace, const char *hashalg, sshsig_signer *signer,
2584ee116499SAntonio Huete Jimenez void *signer_ctx)
25850cbfa66cSDaniel Fojt {
25860cbfa66cSDaniel Fojt struct sshbuf *sigbuf = NULL, *abuf = NULL;
25870cbfa66cSDaniel Fojt int r = SSH_ERR_INTERNAL_ERROR, wfd = -1, oerrno;
25880cbfa66cSDaniel Fojt char *wfile = NULL, *asig = NULL, *fp = NULL;
258950a69bb5SSascha Wildner char *pin = NULL, *prompt = NULL;
25900cbfa66cSDaniel Fojt
25910cbfa66cSDaniel Fojt if (!quiet) {
25920cbfa66cSDaniel Fojt if (fd == STDIN_FILENO)
25930cbfa66cSDaniel Fojt fprintf(stderr, "Signing data on standard input\n");
25940cbfa66cSDaniel Fojt else
25950cbfa66cSDaniel Fojt fprintf(stderr, "Signing file %s\n", filename);
25960cbfa66cSDaniel Fojt }
259750a69bb5SSascha Wildner if (signer == NULL && sshkey_is_sk(signkey)) {
259850a69bb5SSascha Wildner if ((signkey->sk_flags & SSH_SK_USER_VERIFICATION_REQD)) {
259950a69bb5SSascha Wildner xasprintf(&prompt, "Enter PIN for %s key: ",
260050a69bb5SSascha Wildner sshkey_type(signkey));
260150a69bb5SSascha Wildner if ((pin = read_passphrase(prompt,
260250a69bb5SSascha Wildner RP_ALLOW_STDIN)) == NULL)
260350a69bb5SSascha Wildner fatal_f("couldn't read PIN");
260450a69bb5SSascha Wildner }
260550a69bb5SSascha Wildner if ((signkey->sk_flags & SSH_SK_USER_PRESENCE_REQD)) {
26060cbfa66cSDaniel Fojt if ((fp = sshkey_fingerprint(signkey, fingerprint_hash,
26070cbfa66cSDaniel Fojt SSH_FP_DEFAULT)) == NULL)
260850a69bb5SSascha Wildner fatal_f("fingerprint failed");
26090cbfa66cSDaniel Fojt fprintf(stderr, "Confirm user presence for key %s %s\n",
26100cbfa66cSDaniel Fojt sshkey_type(signkey), fp);
26110cbfa66cSDaniel Fojt free(fp);
26120cbfa66cSDaniel Fojt }
261350a69bb5SSascha Wildner }
2614ee116499SAntonio Huete Jimenez if ((r = sshsig_sign_fd(signkey, hashalg, sk_provider, pin,
261550a69bb5SSascha Wildner fd, sig_namespace, &sigbuf, signer, signer_ctx)) != 0) {
261650a69bb5SSascha Wildner error_r(r, "Signing %s failed", filename);
26170cbfa66cSDaniel Fojt goto out;
26180cbfa66cSDaniel Fojt }
26190cbfa66cSDaniel Fojt if ((r = sshsig_armor(sigbuf, &abuf)) != 0) {
262050a69bb5SSascha Wildner error_fr(r, "sshsig_armor");
26210cbfa66cSDaniel Fojt goto out;
26220cbfa66cSDaniel Fojt }
26230cbfa66cSDaniel Fojt if ((asig = sshbuf_dup_string(abuf)) == NULL) {
262450a69bb5SSascha Wildner error_f("buffer error");
26250cbfa66cSDaniel Fojt r = SSH_ERR_ALLOC_FAIL;
26260cbfa66cSDaniel Fojt goto out;
26270cbfa66cSDaniel Fojt }
26280cbfa66cSDaniel Fojt
26290cbfa66cSDaniel Fojt if (fd == STDIN_FILENO) {
26300cbfa66cSDaniel Fojt fputs(asig, stdout);
26310cbfa66cSDaniel Fojt fflush(stdout);
26320cbfa66cSDaniel Fojt } else {
26330cbfa66cSDaniel Fojt xasprintf(&wfile, "%s.sig", filename);
26340cbfa66cSDaniel Fojt if (confirm_overwrite(wfile)) {
26350cbfa66cSDaniel Fojt if ((wfd = open(wfile, O_WRONLY|O_CREAT|O_TRUNC,
26360cbfa66cSDaniel Fojt 0666)) == -1) {
26370cbfa66cSDaniel Fojt oerrno = errno;
26380cbfa66cSDaniel Fojt error("Cannot open %s: %s",
26390cbfa66cSDaniel Fojt wfile, strerror(errno));
26400cbfa66cSDaniel Fojt errno = oerrno;
26410cbfa66cSDaniel Fojt r = SSH_ERR_SYSTEM_ERROR;
26420cbfa66cSDaniel Fojt goto out;
26430cbfa66cSDaniel Fojt }
26440cbfa66cSDaniel Fojt if (atomicio(vwrite, wfd, asig,
26450cbfa66cSDaniel Fojt strlen(asig)) != strlen(asig)) {
26460cbfa66cSDaniel Fojt oerrno = errno;
26470cbfa66cSDaniel Fojt error("Cannot write to %s: %s",
26480cbfa66cSDaniel Fojt wfile, strerror(errno));
26490cbfa66cSDaniel Fojt errno = oerrno;
26500cbfa66cSDaniel Fojt r = SSH_ERR_SYSTEM_ERROR;
26510cbfa66cSDaniel Fojt goto out;
26520cbfa66cSDaniel Fojt }
26530cbfa66cSDaniel Fojt if (!quiet) {
26540cbfa66cSDaniel Fojt fprintf(stderr, "Write signature to %s\n",
26550cbfa66cSDaniel Fojt wfile);
26560cbfa66cSDaniel Fojt }
26570cbfa66cSDaniel Fojt }
26580cbfa66cSDaniel Fojt }
26590cbfa66cSDaniel Fojt /* success */
26600cbfa66cSDaniel Fojt r = 0;
26610cbfa66cSDaniel Fojt out:
26620cbfa66cSDaniel Fojt free(wfile);
266350a69bb5SSascha Wildner free(prompt);
26640cbfa66cSDaniel Fojt free(asig);
266550a69bb5SSascha Wildner if (pin != NULL)
266650a69bb5SSascha Wildner freezero(pin, strlen(pin));
26670cbfa66cSDaniel Fojt sshbuf_free(abuf);
26680cbfa66cSDaniel Fojt sshbuf_free(sigbuf);
26690cbfa66cSDaniel Fojt if (wfd != -1)
26700cbfa66cSDaniel Fojt close(wfd);
26710cbfa66cSDaniel Fojt return r;
26720cbfa66cSDaniel Fojt }
26730cbfa66cSDaniel Fojt
26740cbfa66cSDaniel Fojt static int
sig_process_opts(char * const * opts,size_t nopts,char ** hashalgp,uint64_t * verify_timep,int * print_pubkey)2675ee116499SAntonio Huete Jimenez sig_process_opts(char * const *opts, size_t nopts, char **hashalgp,
2676ee116499SAntonio Huete Jimenez uint64_t *verify_timep, int *print_pubkey)
2677ee116499SAntonio Huete Jimenez {
2678ee116499SAntonio Huete Jimenez size_t i;
2679ee116499SAntonio Huete Jimenez time_t now;
2680ee116499SAntonio Huete Jimenez
2681ee116499SAntonio Huete Jimenez if (verify_timep != NULL)
2682ee116499SAntonio Huete Jimenez *verify_timep = 0;
2683ee116499SAntonio Huete Jimenez if (print_pubkey != NULL)
2684ee116499SAntonio Huete Jimenez *print_pubkey = 0;
2685ee116499SAntonio Huete Jimenez if (hashalgp != NULL)
2686ee116499SAntonio Huete Jimenez *hashalgp = NULL;
2687ee116499SAntonio Huete Jimenez for (i = 0; i < nopts; i++) {
2688ee116499SAntonio Huete Jimenez if (hashalgp != NULL &&
2689ee116499SAntonio Huete Jimenez strncasecmp(opts[i], "hashalg=", 8) == 0) {
2690ee116499SAntonio Huete Jimenez *hashalgp = xstrdup(opts[i] + 8);
2691ee116499SAntonio Huete Jimenez } else if (verify_timep &&
2692ee116499SAntonio Huete Jimenez strncasecmp(opts[i], "verify-time=", 12) == 0) {
2693ee116499SAntonio Huete Jimenez if (parse_absolute_time(opts[i] + 12,
2694ee116499SAntonio Huete Jimenez verify_timep) != 0 || *verify_timep == 0) {
2695ee116499SAntonio Huete Jimenez error("Invalid \"verify-time\" option");
2696ee116499SAntonio Huete Jimenez return SSH_ERR_INVALID_ARGUMENT;
2697ee116499SAntonio Huete Jimenez }
2698ee116499SAntonio Huete Jimenez } else if (print_pubkey &&
2699ee116499SAntonio Huete Jimenez strcasecmp(opts[i], "print-pubkey") == 0) {
2700ee116499SAntonio Huete Jimenez *print_pubkey = 1;
2701ee116499SAntonio Huete Jimenez } else {
2702ee116499SAntonio Huete Jimenez error("Invalid option \"%s\"", opts[i]);
2703ee116499SAntonio Huete Jimenez return SSH_ERR_INVALID_ARGUMENT;
2704ee116499SAntonio Huete Jimenez }
2705ee116499SAntonio Huete Jimenez }
2706ee116499SAntonio Huete Jimenez if (verify_timep && *verify_timep == 0) {
2707ee116499SAntonio Huete Jimenez if ((now = time(NULL)) < 0) {
2708ee116499SAntonio Huete Jimenez error("Time is before epoch");
2709ee116499SAntonio Huete Jimenez return SSH_ERR_INVALID_ARGUMENT;
2710ee116499SAntonio Huete Jimenez }
2711ee116499SAntonio Huete Jimenez *verify_timep = (uint64_t)now;
2712ee116499SAntonio Huete Jimenez }
2713ee116499SAntonio Huete Jimenez return 0;
2714ee116499SAntonio Huete Jimenez }
2715ee116499SAntonio Huete Jimenez
2716ee116499SAntonio Huete Jimenez
2717ee116499SAntonio Huete Jimenez static int
sig_sign(const char * keypath,const char * sig_namespace,int require_agent,int argc,char ** argv,char * const * opts,size_t nopts)2718ee116499SAntonio Huete Jimenez sig_sign(const char *keypath, const char *sig_namespace, int require_agent,
2719ee116499SAntonio Huete Jimenez int argc, char **argv, char * const *opts, size_t nopts)
27200cbfa66cSDaniel Fojt {
27210cbfa66cSDaniel Fojt int i, fd = -1, r, ret = -1;
27220cbfa66cSDaniel Fojt int agent_fd = -1;
27230cbfa66cSDaniel Fojt struct sshkey *pubkey = NULL, *privkey = NULL, *signkey = NULL;
27240cbfa66cSDaniel Fojt sshsig_signer *signer = NULL;
2725ee116499SAntonio Huete Jimenez char *hashalg = NULL;
27260cbfa66cSDaniel Fojt
27270cbfa66cSDaniel Fojt /* Check file arguments. */
27280cbfa66cSDaniel Fojt for (i = 0; i < argc; i++) {
27290cbfa66cSDaniel Fojt if (strcmp(argv[i], "-") != 0)
27300cbfa66cSDaniel Fojt continue;
27310cbfa66cSDaniel Fojt if (i > 0 || argc > 1)
27320cbfa66cSDaniel Fojt fatal("Cannot sign mix of paths and standard input");
27330cbfa66cSDaniel Fojt }
27340cbfa66cSDaniel Fojt
2735ee116499SAntonio Huete Jimenez if (sig_process_opts(opts, nopts, &hashalg, NULL, NULL) != 0)
2736ee116499SAntonio Huete Jimenez goto done; /* error already logged */
2737ee116499SAntonio Huete Jimenez
27380cbfa66cSDaniel Fojt if ((r = sshkey_load_public(keypath, &pubkey, NULL)) != 0) {
273950a69bb5SSascha Wildner error_r(r, "Couldn't load public key %s", keypath);
27400cbfa66cSDaniel Fojt goto done;
27410cbfa66cSDaniel Fojt }
27420cbfa66cSDaniel Fojt
2743ee116499SAntonio Huete Jimenez if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) {
2744ee116499SAntonio Huete Jimenez if (require_agent)
2745ee116499SAntonio Huete Jimenez fatal("Couldn't get agent socket");
274650a69bb5SSascha Wildner debug_r(r, "Couldn't get agent socket");
2747ee116499SAntonio Huete Jimenez } else {
27480cbfa66cSDaniel Fojt if ((r = ssh_agent_has_key(agent_fd, pubkey)) == 0)
27490cbfa66cSDaniel Fojt signer = agent_signer;
2750ee116499SAntonio Huete Jimenez else {
2751ee116499SAntonio Huete Jimenez if (require_agent)
2752ee116499SAntonio Huete Jimenez fatal("Couldn't find key in agent");
275350a69bb5SSascha Wildner debug_r(r, "Couldn't find key in agent");
27540cbfa66cSDaniel Fojt }
2755ee116499SAntonio Huete Jimenez }
27560cbfa66cSDaniel Fojt
27570cbfa66cSDaniel Fojt if (signer == NULL) {
27580cbfa66cSDaniel Fojt /* Not using agent - try to load private key */
27590cbfa66cSDaniel Fojt if ((privkey = load_sign_key(keypath, pubkey)) == NULL)
27600cbfa66cSDaniel Fojt goto done;
27610cbfa66cSDaniel Fojt signkey = privkey;
27620cbfa66cSDaniel Fojt } else {
27630cbfa66cSDaniel Fojt /* Will use key in agent */
27640cbfa66cSDaniel Fojt signkey = pubkey;
27650cbfa66cSDaniel Fojt }
27660cbfa66cSDaniel Fojt
27670cbfa66cSDaniel Fojt if (argc == 0) {
27680cbfa66cSDaniel Fojt if ((r = sign_one(signkey, "(stdin)", STDIN_FILENO,
2769ee116499SAntonio Huete Jimenez sig_namespace, hashalg, signer, &agent_fd)) != 0)
27700cbfa66cSDaniel Fojt goto done;
27710cbfa66cSDaniel Fojt } else {
27720cbfa66cSDaniel Fojt for (i = 0; i < argc; i++) {
27730cbfa66cSDaniel Fojt if (strcmp(argv[i], "-") == 0)
27740cbfa66cSDaniel Fojt fd = STDIN_FILENO;
27750cbfa66cSDaniel Fojt else if ((fd = open(argv[i], O_RDONLY)) == -1) {
27760cbfa66cSDaniel Fojt error("Cannot open %s for signing: %s",
27770cbfa66cSDaniel Fojt argv[i], strerror(errno));
27780cbfa66cSDaniel Fojt goto done;
27790cbfa66cSDaniel Fojt }
27800cbfa66cSDaniel Fojt if ((r = sign_one(signkey, argv[i], fd, sig_namespace,
2781ee116499SAntonio Huete Jimenez hashalg, signer, &agent_fd)) != 0)
27820cbfa66cSDaniel Fojt goto done;
27830cbfa66cSDaniel Fojt if (fd != STDIN_FILENO)
27840cbfa66cSDaniel Fojt close(fd);
27850cbfa66cSDaniel Fojt fd = -1;
27860cbfa66cSDaniel Fojt }
27870cbfa66cSDaniel Fojt }
27880cbfa66cSDaniel Fojt
27890cbfa66cSDaniel Fojt ret = 0;
27900cbfa66cSDaniel Fojt done:
27910cbfa66cSDaniel Fojt if (fd != -1 && fd != STDIN_FILENO)
27920cbfa66cSDaniel Fojt close(fd);
27930cbfa66cSDaniel Fojt sshkey_free(pubkey);
27940cbfa66cSDaniel Fojt sshkey_free(privkey);
2795ee116499SAntonio Huete Jimenez free(hashalg);
27960cbfa66cSDaniel Fojt return ret;
27970cbfa66cSDaniel Fojt }
27980cbfa66cSDaniel Fojt
27990cbfa66cSDaniel Fojt static int
sig_verify(const char * signature,const char * sig_namespace,const char * principal,const char * allowed_keys,const char * revoked_keys,char * const * opts,size_t nopts)28000cbfa66cSDaniel Fojt sig_verify(const char *signature, const char *sig_namespace,
280150a69bb5SSascha Wildner const char *principal, const char *allowed_keys, const char *revoked_keys,
280250a69bb5SSascha Wildner char * const *opts, size_t nopts)
28030cbfa66cSDaniel Fojt {
28040cbfa66cSDaniel Fojt int r, ret = -1;
280550a69bb5SSascha Wildner int print_pubkey = 0;
28060cbfa66cSDaniel Fojt struct sshbuf *sigbuf = NULL, *abuf = NULL;
28070cbfa66cSDaniel Fojt struct sshkey *sign_key = NULL;
28080cbfa66cSDaniel Fojt char *fp = NULL;
28090cbfa66cSDaniel Fojt struct sshkey_sig_details *sig_details = NULL;
281050a69bb5SSascha Wildner uint64_t verify_time = 0;
281150a69bb5SSascha Wildner
2812ee116499SAntonio Huete Jimenez if (sig_process_opts(opts, nopts, NULL, &verify_time,
2813ee116499SAntonio Huete Jimenez &print_pubkey) != 0)
281450a69bb5SSascha Wildner goto done; /* error already logged */
28150cbfa66cSDaniel Fojt
28160cbfa66cSDaniel Fojt memset(&sig_details, 0, sizeof(sig_details));
28170cbfa66cSDaniel Fojt if ((r = sshbuf_load_file(signature, &abuf)) != 0) {
281850a69bb5SSascha Wildner error_r(r, "Couldn't read signature file");
28190cbfa66cSDaniel Fojt goto done;
28200cbfa66cSDaniel Fojt }
28210cbfa66cSDaniel Fojt
28220cbfa66cSDaniel Fojt if ((r = sshsig_dearmor(abuf, &sigbuf)) != 0) {
282350a69bb5SSascha Wildner error_fr(r, "sshsig_armor");
28240cbfa66cSDaniel Fojt goto done;
28250cbfa66cSDaniel Fojt }
28260cbfa66cSDaniel Fojt if ((r = sshsig_verify_fd(sigbuf, STDIN_FILENO, sig_namespace,
28270cbfa66cSDaniel Fojt &sign_key, &sig_details)) != 0)
28280cbfa66cSDaniel Fojt goto done; /* sshsig_verify() prints error */
28290cbfa66cSDaniel Fojt
28300cbfa66cSDaniel Fojt if ((fp = sshkey_fingerprint(sign_key, fingerprint_hash,
28310cbfa66cSDaniel Fojt SSH_FP_DEFAULT)) == NULL)
283250a69bb5SSascha Wildner fatal_f("sshkey_fingerprint failed");
28330cbfa66cSDaniel Fojt debug("Valid (unverified) signature from key %s", fp);
28340cbfa66cSDaniel Fojt if (sig_details != NULL) {
283550a69bb5SSascha Wildner debug2_f("signature details: counter = %u, flags = 0x%02x",
283650a69bb5SSascha Wildner sig_details->sk_counter, sig_details->sk_flags);
28370cbfa66cSDaniel Fojt }
28380cbfa66cSDaniel Fojt free(fp);
28390cbfa66cSDaniel Fojt fp = NULL;
28400cbfa66cSDaniel Fojt
28410cbfa66cSDaniel Fojt if (revoked_keys != NULL) {
28420cbfa66cSDaniel Fojt if ((r = sshkey_check_revoked(sign_key, revoked_keys)) != 0) {
284350a69bb5SSascha Wildner debug3_fr(r, "sshkey_check_revoked");
28440cbfa66cSDaniel Fojt goto done;
28450cbfa66cSDaniel Fojt }
28460cbfa66cSDaniel Fojt }
28470cbfa66cSDaniel Fojt
284850a69bb5SSascha Wildner if (allowed_keys != NULL && (r = sshsig_check_allowed_keys(allowed_keys,
284950a69bb5SSascha Wildner sign_key, principal, sig_namespace, verify_time)) != 0) {
285050a69bb5SSascha Wildner debug3_fr(r, "sshsig_check_allowed_keys");
28510cbfa66cSDaniel Fojt goto done;
28520cbfa66cSDaniel Fojt }
28530cbfa66cSDaniel Fojt /* success */
28540cbfa66cSDaniel Fojt ret = 0;
28550cbfa66cSDaniel Fojt done:
28560cbfa66cSDaniel Fojt if (!quiet) {
28570cbfa66cSDaniel Fojt if (ret == 0) {
28580cbfa66cSDaniel Fojt if ((fp = sshkey_fingerprint(sign_key, fingerprint_hash,
285950a69bb5SSascha Wildner SSH_FP_DEFAULT)) == NULL)
286050a69bb5SSascha Wildner fatal_f("sshkey_fingerprint failed");
28610cbfa66cSDaniel Fojt if (principal == NULL) {
28620cbfa66cSDaniel Fojt printf("Good \"%s\" signature with %s key %s\n",
28630cbfa66cSDaniel Fojt sig_namespace, sshkey_type(sign_key), fp);
28640cbfa66cSDaniel Fojt
28650cbfa66cSDaniel Fojt } else {
28660cbfa66cSDaniel Fojt printf("Good \"%s\" signature for %s with %s key %s\n",
28670cbfa66cSDaniel Fojt sig_namespace, principal,
28680cbfa66cSDaniel Fojt sshkey_type(sign_key), fp);
28690cbfa66cSDaniel Fojt }
28700cbfa66cSDaniel Fojt } else {
28710cbfa66cSDaniel Fojt printf("Could not verify signature.\n");
28720cbfa66cSDaniel Fojt }
28730cbfa66cSDaniel Fojt }
287450a69bb5SSascha Wildner /* Print the signature key if requested */
287550a69bb5SSascha Wildner if (ret == 0 && print_pubkey && sign_key != NULL) {
287650a69bb5SSascha Wildner if ((r = sshkey_write(sign_key, stdout)) == 0)
287750a69bb5SSascha Wildner fputc('\n', stdout);
287850a69bb5SSascha Wildner else {
287950a69bb5SSascha Wildner error_r(r, "Could not print public key.\n");
288050a69bb5SSascha Wildner ret = -1;
288150a69bb5SSascha Wildner }
288250a69bb5SSascha Wildner }
28830cbfa66cSDaniel Fojt sshbuf_free(sigbuf);
28840cbfa66cSDaniel Fojt sshbuf_free(abuf);
28850cbfa66cSDaniel Fojt sshkey_free(sign_key);
28860cbfa66cSDaniel Fojt sshkey_sig_details_free(sig_details);
28870cbfa66cSDaniel Fojt free(fp);
28880cbfa66cSDaniel Fojt return ret;
28890cbfa66cSDaniel Fojt }
28900cbfa66cSDaniel Fojt
28910cbfa66cSDaniel Fojt static int
sig_find_principals(const char * signature,const char * allowed_keys,char * const * opts,size_t nopts)289250a69bb5SSascha Wildner sig_find_principals(const char *signature, const char *allowed_keys,
289350a69bb5SSascha Wildner char * const *opts, size_t nopts)
289450a69bb5SSascha Wildner {
28950cbfa66cSDaniel Fojt int r, ret = -1;
28960cbfa66cSDaniel Fojt struct sshbuf *sigbuf = NULL, *abuf = NULL;
28970cbfa66cSDaniel Fojt struct sshkey *sign_key = NULL;
28980cbfa66cSDaniel Fojt char *principals = NULL, *cp, *tmp;
289950a69bb5SSascha Wildner uint64_t verify_time = 0;
290050a69bb5SSascha Wildner
2901ee116499SAntonio Huete Jimenez if (sig_process_opts(opts, nopts, NULL, &verify_time, NULL) != 0)
290250a69bb5SSascha Wildner goto done; /* error already logged */
29030cbfa66cSDaniel Fojt
29040cbfa66cSDaniel Fojt if ((r = sshbuf_load_file(signature, &abuf)) != 0) {
290550a69bb5SSascha Wildner error_r(r, "Couldn't read signature file");
29060cbfa66cSDaniel Fojt goto done;
29070cbfa66cSDaniel Fojt }
29080cbfa66cSDaniel Fojt if ((r = sshsig_dearmor(abuf, &sigbuf)) != 0) {
290950a69bb5SSascha Wildner error_fr(r, "sshsig_armor");
29100cbfa66cSDaniel Fojt goto done;
29110cbfa66cSDaniel Fojt }
29120cbfa66cSDaniel Fojt if ((r = sshsig_get_pubkey(sigbuf, &sign_key)) != 0) {
291350a69bb5SSascha Wildner error_fr(r, "sshsig_get_pubkey");
29140cbfa66cSDaniel Fojt goto done;
29150cbfa66cSDaniel Fojt }
29160cbfa66cSDaniel Fojt if ((r = sshsig_find_principals(allowed_keys, sign_key,
291750a69bb5SSascha Wildner verify_time, &principals)) != 0) {
291850a69bb5SSascha Wildner if (r != SSH_ERR_KEY_NOT_FOUND)
291950a69bb5SSascha Wildner error_fr(r, "sshsig_find_principal");
29200cbfa66cSDaniel Fojt goto done;
29210cbfa66cSDaniel Fojt }
29220cbfa66cSDaniel Fojt ret = 0;
29230cbfa66cSDaniel Fojt done:
29240cbfa66cSDaniel Fojt if (ret == 0 ) {
29250cbfa66cSDaniel Fojt /* Emit matching principals one per line */
29260cbfa66cSDaniel Fojt tmp = principals;
29270cbfa66cSDaniel Fojt while ((cp = strsep(&tmp, ",")) != NULL && *cp != '\0')
29280cbfa66cSDaniel Fojt puts(cp);
29290cbfa66cSDaniel Fojt } else {
29300cbfa66cSDaniel Fojt fprintf(stderr, "No principal matched.\n");
29310cbfa66cSDaniel Fojt }
29320cbfa66cSDaniel Fojt sshbuf_free(sigbuf);
29330cbfa66cSDaniel Fojt sshbuf_free(abuf);
29340cbfa66cSDaniel Fojt sshkey_free(sign_key);
29350cbfa66cSDaniel Fojt free(principals);
29360cbfa66cSDaniel Fojt return ret;
29370cbfa66cSDaniel Fojt }
29380cbfa66cSDaniel Fojt
2939ee116499SAntonio Huete Jimenez static int
sig_match_principals(const char * allowed_keys,char * principal,char * const * opts,size_t nopts)2940ee116499SAntonio Huete Jimenez sig_match_principals(const char *allowed_keys, char *principal,
2941ee116499SAntonio Huete Jimenez char * const *opts, size_t nopts)
2942ee116499SAntonio Huete Jimenez {
2943ee116499SAntonio Huete Jimenez int r;
2944ee116499SAntonio Huete Jimenez char **principals = NULL;
2945ee116499SAntonio Huete Jimenez size_t i, nprincipals = 0;
2946ee116499SAntonio Huete Jimenez
2947ee116499SAntonio Huete Jimenez if ((r = sig_process_opts(opts, nopts, NULL, NULL, NULL)) != 0)
2948ee116499SAntonio Huete Jimenez return r; /* error already logged */
2949ee116499SAntonio Huete Jimenez
2950ee116499SAntonio Huete Jimenez if ((r = sshsig_match_principals(allowed_keys, principal,
2951ee116499SAntonio Huete Jimenez &principals, &nprincipals)) != 0) {
2952ee116499SAntonio Huete Jimenez debug_f("match: %s", ssh_err(r));
2953ee116499SAntonio Huete Jimenez fprintf(stderr, "No principal matched.\n");
2954ee116499SAntonio Huete Jimenez return r;
2955ee116499SAntonio Huete Jimenez }
2956ee116499SAntonio Huete Jimenez for (i = 0; i < nprincipals; i++) {
2957ee116499SAntonio Huete Jimenez printf("%s\n", principals[i]);
2958ee116499SAntonio Huete Jimenez free(principals[i]);
2959ee116499SAntonio Huete Jimenez }
2960ee116499SAntonio Huete Jimenez free(principals);
2961ee116499SAntonio Huete Jimenez
2962ee116499SAntonio Huete Jimenez return 0;
2963ee116499SAntonio Huete Jimenez }
2964ee116499SAntonio Huete Jimenez
29650cbfa66cSDaniel Fojt static void
do_moduli_gen(const char * out_file,char ** opts,size_t nopts)29660cbfa66cSDaniel Fojt do_moduli_gen(const char *out_file, char **opts, size_t nopts)
29670cbfa66cSDaniel Fojt {
29680cbfa66cSDaniel Fojt #ifdef WITH_OPENSSL
29690cbfa66cSDaniel Fojt /* Moduli generation/screening */
29700cbfa66cSDaniel Fojt u_int32_t memory = 0;
29710cbfa66cSDaniel Fojt BIGNUM *start = NULL;
29720cbfa66cSDaniel Fojt int moduli_bits = 0;
29730cbfa66cSDaniel Fojt FILE *out;
29740cbfa66cSDaniel Fojt size_t i;
29750cbfa66cSDaniel Fojt const char *errstr;
29760cbfa66cSDaniel Fojt
29770cbfa66cSDaniel Fojt /* Parse options */
29780cbfa66cSDaniel Fojt for (i = 0; i < nopts; i++) {
29790cbfa66cSDaniel Fojt if (strncmp(opts[i], "memory=", 7) == 0) {
29800cbfa66cSDaniel Fojt memory = (u_int32_t)strtonum(opts[i]+7, 1,
29810cbfa66cSDaniel Fojt UINT_MAX, &errstr);
29820cbfa66cSDaniel Fojt if (errstr) {
29830cbfa66cSDaniel Fojt fatal("Memory limit is %s: %s",
29840cbfa66cSDaniel Fojt errstr, opts[i]+7);
29850cbfa66cSDaniel Fojt }
29860cbfa66cSDaniel Fojt } else if (strncmp(opts[i], "start=", 6) == 0) {
29870cbfa66cSDaniel Fojt /* XXX - also compare length against bits */
29880cbfa66cSDaniel Fojt if (BN_hex2bn(&start, opts[i]+6) == 0)
29890cbfa66cSDaniel Fojt fatal("Invalid start point.");
29900cbfa66cSDaniel Fojt } else if (strncmp(opts[i], "bits=", 5) == 0) {
29910cbfa66cSDaniel Fojt moduli_bits = (int)strtonum(opts[i]+5, 1,
29920cbfa66cSDaniel Fojt INT_MAX, &errstr);
29930cbfa66cSDaniel Fojt if (errstr) {
29940cbfa66cSDaniel Fojt fatal("Invalid number: %s (%s)",
29950cbfa66cSDaniel Fojt opts[i]+12, errstr);
29960cbfa66cSDaniel Fojt }
29970cbfa66cSDaniel Fojt } else {
29980cbfa66cSDaniel Fojt fatal("Option \"%s\" is unsupported for moduli "
29990cbfa66cSDaniel Fojt "generation", opts[i]);
30000cbfa66cSDaniel Fojt }
30010cbfa66cSDaniel Fojt }
30020cbfa66cSDaniel Fojt
30030cbfa66cSDaniel Fojt if ((out = fopen(out_file, "w")) == NULL) {
30040cbfa66cSDaniel Fojt fatal("Couldn't open modulus candidate file \"%s\": %s",
30050cbfa66cSDaniel Fojt out_file, strerror(errno));
30060cbfa66cSDaniel Fojt }
30070cbfa66cSDaniel Fojt setvbuf(out, NULL, _IOLBF, 0);
30080cbfa66cSDaniel Fojt
30090cbfa66cSDaniel Fojt if (moduli_bits == 0)
30100cbfa66cSDaniel Fojt moduli_bits = DEFAULT_BITS;
30110cbfa66cSDaniel Fojt if (gen_candidates(out, memory, moduli_bits, start) != 0)
30120cbfa66cSDaniel Fojt fatal("modulus candidate generation failed");
30130cbfa66cSDaniel Fojt #else /* WITH_OPENSSL */
30140cbfa66cSDaniel Fojt fatal("Moduli generation is not supported");
30150cbfa66cSDaniel Fojt #endif /* WITH_OPENSSL */
30160cbfa66cSDaniel Fojt }
30170cbfa66cSDaniel Fojt
30180cbfa66cSDaniel Fojt static void
do_moduli_screen(const char * out_file,char ** opts,size_t nopts)30190cbfa66cSDaniel Fojt do_moduli_screen(const char *out_file, char **opts, size_t nopts)
30200cbfa66cSDaniel Fojt {
30210cbfa66cSDaniel Fojt #ifdef WITH_OPENSSL
30220cbfa66cSDaniel Fojt /* Moduli generation/screening */
30230cbfa66cSDaniel Fojt char *checkpoint = NULL;
30240cbfa66cSDaniel Fojt u_int32_t generator_wanted = 0;
30250cbfa66cSDaniel Fojt unsigned long start_lineno = 0, lines_to_process = 0;
30260cbfa66cSDaniel Fojt int prime_tests = 0;
30270cbfa66cSDaniel Fojt FILE *out, *in = stdin;
30280cbfa66cSDaniel Fojt size_t i;
30290cbfa66cSDaniel Fojt const char *errstr;
30300cbfa66cSDaniel Fojt
30310cbfa66cSDaniel Fojt /* Parse options */
30320cbfa66cSDaniel Fojt for (i = 0; i < nopts; i++) {
30330cbfa66cSDaniel Fojt if (strncmp(opts[i], "lines=", 6) == 0) {
30340cbfa66cSDaniel Fojt lines_to_process = strtoul(opts[i]+6, NULL, 10);
30350cbfa66cSDaniel Fojt } else if (strncmp(opts[i], "start-line=", 11) == 0) {
30360cbfa66cSDaniel Fojt start_lineno = strtoul(opts[i]+11, NULL, 10);
30370cbfa66cSDaniel Fojt } else if (strncmp(opts[i], "checkpoint=", 11) == 0) {
3038*ba1276acSMatthew Dillon free(checkpoint);
30390cbfa66cSDaniel Fojt checkpoint = xstrdup(opts[i]+11);
30400cbfa66cSDaniel Fojt } else if (strncmp(opts[i], "generator=", 10) == 0) {
30410cbfa66cSDaniel Fojt generator_wanted = (u_int32_t)strtonum(
30420cbfa66cSDaniel Fojt opts[i]+10, 1, UINT_MAX, &errstr);
30430cbfa66cSDaniel Fojt if (errstr != NULL) {
30440cbfa66cSDaniel Fojt fatal("Generator invalid: %s (%s)",
30450cbfa66cSDaniel Fojt opts[i]+10, errstr);
30460cbfa66cSDaniel Fojt }
30470cbfa66cSDaniel Fojt } else if (strncmp(opts[i], "prime-tests=", 12) == 0) {
30480cbfa66cSDaniel Fojt prime_tests = (int)strtonum(opts[i]+12, 1,
30490cbfa66cSDaniel Fojt INT_MAX, &errstr);
30500cbfa66cSDaniel Fojt if (errstr) {
30510cbfa66cSDaniel Fojt fatal("Invalid number: %s (%s)",
30520cbfa66cSDaniel Fojt opts[i]+12, errstr);
30530cbfa66cSDaniel Fojt }
30540cbfa66cSDaniel Fojt } else {
30550cbfa66cSDaniel Fojt fatal("Option \"%s\" is unsupported for moduli "
30560cbfa66cSDaniel Fojt "screening", opts[i]);
30570cbfa66cSDaniel Fojt }
30580cbfa66cSDaniel Fojt }
30590cbfa66cSDaniel Fojt
30600cbfa66cSDaniel Fojt if (have_identity && strcmp(identity_file, "-") != 0) {
30610cbfa66cSDaniel Fojt if ((in = fopen(identity_file, "r")) == NULL) {
30620cbfa66cSDaniel Fojt fatal("Couldn't open modulus candidate "
30630cbfa66cSDaniel Fojt "file \"%s\": %s", identity_file,
30640cbfa66cSDaniel Fojt strerror(errno));
30650cbfa66cSDaniel Fojt }
30660cbfa66cSDaniel Fojt }
30670cbfa66cSDaniel Fojt
30680cbfa66cSDaniel Fojt if ((out = fopen(out_file, "a")) == NULL) {
30690cbfa66cSDaniel Fojt fatal("Couldn't open moduli file \"%s\": %s",
30700cbfa66cSDaniel Fojt out_file, strerror(errno));
30710cbfa66cSDaniel Fojt }
30720cbfa66cSDaniel Fojt setvbuf(out, NULL, _IOLBF, 0);
30730cbfa66cSDaniel Fojt if (prime_test(in, out, prime_tests == 0 ? 100 : prime_tests,
30740cbfa66cSDaniel Fojt generator_wanted, checkpoint,
30750cbfa66cSDaniel Fojt start_lineno, lines_to_process) != 0)
30760cbfa66cSDaniel Fojt fatal("modulus screening failed");
3077*ba1276acSMatthew Dillon if (in != stdin)
3078*ba1276acSMatthew Dillon (void)fclose(in);
3079*ba1276acSMatthew Dillon free(checkpoint);
30800cbfa66cSDaniel Fojt #else /* WITH_OPENSSL */
30810cbfa66cSDaniel Fojt fatal("Moduli screening is not supported");
30820cbfa66cSDaniel Fojt #endif /* WITH_OPENSSL */
30830cbfa66cSDaniel Fojt }
30840cbfa66cSDaniel Fojt
3085ee116499SAntonio Huete Jimenez /* Read and confirm a passphrase */
30860cbfa66cSDaniel Fojt static char *
read_check_passphrase(const char * prompt1,const char * prompt2,const char * retry_prompt)3087ee116499SAntonio Huete Jimenez read_check_passphrase(const char *prompt1, const char *prompt2,
3088ee116499SAntonio Huete Jimenez const char *retry_prompt)
30890cbfa66cSDaniel Fojt {
30900cbfa66cSDaniel Fojt char *passphrase1, *passphrase2;
30910cbfa66cSDaniel Fojt
3092ee116499SAntonio Huete Jimenez for (;;) {
3093ee116499SAntonio Huete Jimenez passphrase1 = read_passphrase(prompt1, RP_ALLOW_STDIN);
3094ee116499SAntonio Huete Jimenez passphrase2 = read_passphrase(prompt2, RP_ALLOW_STDIN);
3095ee116499SAntonio Huete Jimenez if (strcmp(passphrase1, passphrase2) == 0) {
30960cbfa66cSDaniel Fojt freezero(passphrase2, strlen(passphrase2));
30970cbfa66cSDaniel Fojt return passphrase1;
30980cbfa66cSDaniel Fojt }
3099ee116499SAntonio Huete Jimenez /* The passphrases do not match. Clear them and retry. */
3100ee116499SAntonio Huete Jimenez freezero(passphrase1, strlen(passphrase1));
3101ee116499SAntonio Huete Jimenez freezero(passphrase2, strlen(passphrase2));
3102ee116499SAntonio Huete Jimenez fputs(retry_prompt, stdout);
3103ee116499SAntonio Huete Jimenez fputc('\n', stdout);
3104ee116499SAntonio Huete Jimenez fflush(stdout);
3105ee116499SAntonio Huete Jimenez }
3106ee116499SAntonio Huete Jimenez /* NOTREACHED */
3107ee116499SAntonio Huete Jimenez return NULL;
3108ee116499SAntonio Huete Jimenez }
31090cbfa66cSDaniel Fojt
3110ee116499SAntonio Huete Jimenez static char *
private_key_passphrase(void)3111ee116499SAntonio Huete Jimenez private_key_passphrase(void)
31120cbfa66cSDaniel Fojt {
3113ee116499SAntonio Huete Jimenez if (identity_passphrase)
3114ee116499SAntonio Huete Jimenez return xstrdup(identity_passphrase);
3115ee116499SAntonio Huete Jimenez if (identity_new_passphrase)
3116ee116499SAntonio Huete Jimenez return xstrdup(identity_new_passphrase);
3117ee116499SAntonio Huete Jimenez
3118ee116499SAntonio Huete Jimenez return read_check_passphrase(
3119ee116499SAntonio Huete Jimenez "Enter passphrase (empty for no passphrase): ",
3120ee116499SAntonio Huete Jimenez "Enter same passphrase again: ",
3121ee116499SAntonio Huete Jimenez "Passphrases do not match. Try again.");
3122ee116499SAntonio Huete Jimenez }
3123ee116499SAntonio Huete Jimenez
3124ee116499SAntonio Huete Jimenez static char *
sk_suffix(const char * application,const uint8_t * user,size_t userlen)3125ee116499SAntonio Huete Jimenez sk_suffix(const char *application, const uint8_t *user, size_t userlen)
3126ee116499SAntonio Huete Jimenez {
3127ee116499SAntonio Huete Jimenez char *ret, *cp;
3128ee116499SAntonio Huete Jimenez size_t slen, i;
3129ee116499SAntonio Huete Jimenez
3130ee116499SAntonio Huete Jimenez /* Trim off URL-like preamble */
3131ee116499SAntonio Huete Jimenez if (strncmp(application, "ssh://", 6) == 0)
3132ee116499SAntonio Huete Jimenez ret = xstrdup(application + 6);
3133ee116499SAntonio Huete Jimenez else if (strncmp(application, "ssh:", 4) == 0)
3134ee116499SAntonio Huete Jimenez ret = xstrdup(application + 4);
3135ee116499SAntonio Huete Jimenez else
3136ee116499SAntonio Huete Jimenez ret = xstrdup(application);
3137ee116499SAntonio Huete Jimenez
3138ee116499SAntonio Huete Jimenez /* Count trailing zeros in user */
3139ee116499SAntonio Huete Jimenez for (i = 0; i < userlen; i++) {
3140ee116499SAntonio Huete Jimenez if (user[userlen - i - 1] != 0)
3141ee116499SAntonio Huete Jimenez break;
3142ee116499SAntonio Huete Jimenez }
3143ee116499SAntonio Huete Jimenez if (i >= userlen)
3144ee116499SAntonio Huete Jimenez return ret; /* user-id was default all-zeros */
3145ee116499SAntonio Huete Jimenez
3146ee116499SAntonio Huete Jimenez /* Append user-id, escaping non-UTF-8 characters */
3147ee116499SAntonio Huete Jimenez slen = userlen - i;
3148ee116499SAntonio Huete Jimenez if (asmprintf(&cp, INT_MAX, NULL, "%.*s", (int)slen, user) == -1)
3149ee116499SAntonio Huete Jimenez fatal_f("asmprintf failed");
3150ee116499SAntonio Huete Jimenez /* Don't emit a user-id that contains path or control characters */
3151ee116499SAntonio Huete Jimenez if (strchr(cp, '/') != NULL || strstr(cp, "..") != NULL ||
3152ee116499SAntonio Huete Jimenez strchr(cp, '\\') != NULL) {
3153ee116499SAntonio Huete Jimenez free(cp);
3154ee116499SAntonio Huete Jimenez cp = tohex(user, slen);
3155ee116499SAntonio Huete Jimenez }
3156ee116499SAntonio Huete Jimenez xextendf(&ret, "_", "%s", cp);
3157ee116499SAntonio Huete Jimenez free(cp);
3158ee116499SAntonio Huete Jimenez return ret;
31590cbfa66cSDaniel Fojt }
31600cbfa66cSDaniel Fojt
31610cbfa66cSDaniel Fojt static int
do_download_sk(const char * skprovider,const char * device)31620cbfa66cSDaniel Fojt do_download_sk(const char *skprovider, const char *device)
31630cbfa66cSDaniel Fojt {
3164ee116499SAntonio Huete Jimenez struct sshsk_resident_key **srks;
3165ee116499SAntonio Huete Jimenez size_t nsrks, i;
316650a69bb5SSascha Wildner int r, ret = -1;
31670cbfa66cSDaniel Fojt char *fp, *pin = NULL, *pass = NULL, *path, *pubpath;
31680cbfa66cSDaniel Fojt const char *ext;
3169ee116499SAntonio Huete Jimenez struct sshkey *key;
31700cbfa66cSDaniel Fojt
31710cbfa66cSDaniel Fojt if (skprovider == NULL)
31720cbfa66cSDaniel Fojt fatal("Cannot download keys without provider");
31730cbfa66cSDaniel Fojt
317450a69bb5SSascha Wildner pin = read_passphrase("Enter PIN for authenticator: ", RP_ALLOW_STDIN);
317550a69bb5SSascha Wildner if (!quiet) {
317650a69bb5SSascha Wildner printf("You may need to touch your authenticator "
317750a69bb5SSascha Wildner "to authorize key download.\n");
31780cbfa66cSDaniel Fojt }
3179ee116499SAntonio Huete Jimenez if ((r = sshsk_load_resident(skprovider, device, pin, 0,
3180ee116499SAntonio Huete Jimenez &srks, &nsrks)) != 0) {
31810cbfa66cSDaniel Fojt if (pin != NULL)
31820cbfa66cSDaniel Fojt freezero(pin, strlen(pin));
318350a69bb5SSascha Wildner error_r(r, "Unable to load resident keys");
31840cbfa66cSDaniel Fojt return -1;
31850cbfa66cSDaniel Fojt }
3186ee116499SAntonio Huete Jimenez if (nsrks == 0)
31870cbfa66cSDaniel Fojt logit("No keys to download");
31880cbfa66cSDaniel Fojt if (pin != NULL)
31890cbfa66cSDaniel Fojt freezero(pin, strlen(pin));
31900cbfa66cSDaniel Fojt
3191ee116499SAntonio Huete Jimenez for (i = 0; i < nsrks; i++) {
3192ee116499SAntonio Huete Jimenez key = srks[i]->key;
3193ee116499SAntonio Huete Jimenez if (key->type != KEY_ECDSA_SK && key->type != KEY_ED25519_SK) {
31940cbfa66cSDaniel Fojt error("Unsupported key type %s (%d)",
3195ee116499SAntonio Huete Jimenez sshkey_type(key), key->type);
31960cbfa66cSDaniel Fojt continue;
31970cbfa66cSDaniel Fojt }
3198ee116499SAntonio Huete Jimenez if ((fp = sshkey_fingerprint(key, fingerprint_hash,
3199ee116499SAntonio Huete Jimenez SSH_FP_DEFAULT)) == NULL)
320050a69bb5SSascha Wildner fatal_f("sshkey_fingerprint failed");
320150a69bb5SSascha Wildner debug_f("key %zu: %s %s %s (flags 0x%02x)", i,
3202ee116499SAntonio Huete Jimenez sshkey_type(key), fp, key->sk_application, key->sk_flags);
3203ee116499SAntonio Huete Jimenez ext = sk_suffix(key->sk_application,
3204ee116499SAntonio Huete Jimenez srks[i]->user_id, srks[i]->user_id_len);
32050cbfa66cSDaniel Fojt xasprintf(&path, "id_%s_rk%s%s",
3206ee116499SAntonio Huete Jimenez key->type == KEY_ECDSA_SK ? "ecdsa_sk" : "ed25519_sk",
32070cbfa66cSDaniel Fojt *ext == '\0' ? "" : "_", ext);
32080cbfa66cSDaniel Fojt
32090cbfa66cSDaniel Fojt /* If the file already exists, ask the user to confirm. */
32100cbfa66cSDaniel Fojt if (!confirm_overwrite(path)) {
32110cbfa66cSDaniel Fojt free(path);
32120cbfa66cSDaniel Fojt break;
32130cbfa66cSDaniel Fojt }
32140cbfa66cSDaniel Fojt
32150cbfa66cSDaniel Fojt /* Save the key with the application string as the comment */
32160cbfa66cSDaniel Fojt if (pass == NULL)
32170cbfa66cSDaniel Fojt pass = private_key_passphrase();
3218ee116499SAntonio Huete Jimenez if ((r = sshkey_save_private(key, path, pass,
3219ee116499SAntonio Huete Jimenez key->sk_application, private_key_format,
32200cbfa66cSDaniel Fojt openssh_format_cipher, rounds)) != 0) {
322150a69bb5SSascha Wildner error_r(r, "Saving key \"%s\" failed", path);
32220cbfa66cSDaniel Fojt free(path);
32230cbfa66cSDaniel Fojt break;
32240cbfa66cSDaniel Fojt }
32250cbfa66cSDaniel Fojt if (!quiet) {
3226ee116499SAntonio Huete Jimenez printf("Saved %s key%s%s to %s\n", sshkey_type(key),
32270cbfa66cSDaniel Fojt *ext != '\0' ? " " : "",
3228ee116499SAntonio Huete Jimenez *ext != '\0' ? key->sk_application : "",
32290cbfa66cSDaniel Fojt path);
32300cbfa66cSDaniel Fojt }
32310cbfa66cSDaniel Fojt
32320cbfa66cSDaniel Fojt /* Save public key too */
32330cbfa66cSDaniel Fojt xasprintf(&pubpath, "%s.pub", path);
32340cbfa66cSDaniel Fojt free(path);
3235ee116499SAntonio Huete Jimenez if ((r = sshkey_save_public(key, pubpath,
3236ee116499SAntonio Huete Jimenez key->sk_application)) != 0) {
323750a69bb5SSascha Wildner error_r(r, "Saving public key \"%s\" failed", pubpath);
32380cbfa66cSDaniel Fojt free(pubpath);
32390cbfa66cSDaniel Fojt break;
32400cbfa66cSDaniel Fojt }
32410cbfa66cSDaniel Fojt free(pubpath);
32420cbfa66cSDaniel Fojt }
32430cbfa66cSDaniel Fojt
3244ee116499SAntonio Huete Jimenez if (i >= nsrks)
324550a69bb5SSascha Wildner ret = 0; /* success */
32460cbfa66cSDaniel Fojt if (pass != NULL)
32470cbfa66cSDaniel Fojt freezero(pass, strlen(pass));
3248ee116499SAntonio Huete Jimenez sshsk_free_resident_keys(srks, nsrks);
324950a69bb5SSascha Wildner return ret;
325050a69bb5SSascha Wildner }
325150a69bb5SSascha Wildner
325250a69bb5SSascha Wildner static void
save_attestation(struct sshbuf * attest,const char * path)325350a69bb5SSascha Wildner save_attestation(struct sshbuf *attest, const char *path)
325450a69bb5SSascha Wildner {
325550a69bb5SSascha Wildner mode_t omask;
325650a69bb5SSascha Wildner int r;
325750a69bb5SSascha Wildner
325850a69bb5SSascha Wildner if (path == NULL)
325950a69bb5SSascha Wildner return; /* nothing to do */
326050a69bb5SSascha Wildner if (attest == NULL || sshbuf_len(attest) == 0)
326150a69bb5SSascha Wildner fatal("Enrollment did not return attestation data");
326250a69bb5SSascha Wildner omask = umask(077);
326350a69bb5SSascha Wildner r = sshbuf_write_file(path, attest);
326450a69bb5SSascha Wildner umask(omask);
326550a69bb5SSascha Wildner if (r != 0)
326650a69bb5SSascha Wildner fatal_r(r, "Unable to write attestation data \"%s\"", path);
326750a69bb5SSascha Wildner if (!quiet)
326850a69bb5SSascha Wildner printf("Your FIDO attestation certificate has been saved in "
326950a69bb5SSascha Wildner "%s\n", path);
32700cbfa66cSDaniel Fojt }
32710cbfa66cSDaniel Fojt
3272ee116499SAntonio Huete Jimenez static int
confirm_sk_overwrite(const char * application,const char * user)3273ee116499SAntonio Huete Jimenez confirm_sk_overwrite(const char *application, const char *user)
3274ee116499SAntonio Huete Jimenez {
3275ee116499SAntonio Huete Jimenez char yesno[3];
3276ee116499SAntonio Huete Jimenez
3277ee116499SAntonio Huete Jimenez printf("A resident key scoped to '%s' with user id '%s' already "
3278ee116499SAntonio Huete Jimenez "exists.\n", application == NULL ? "ssh:" : application,
3279ee116499SAntonio Huete Jimenez user == NULL ? "null" : user);
3280ee116499SAntonio Huete Jimenez printf("Overwrite key in token (y/n)? ");
3281ee116499SAntonio Huete Jimenez fflush(stdout);
3282ee116499SAntonio Huete Jimenez if (fgets(yesno, sizeof(yesno), stdin) == NULL)
3283ee116499SAntonio Huete Jimenez return 0;
3284ee116499SAntonio Huete Jimenez if (yesno[0] != 'y' && yesno[0] != 'Y')
3285ee116499SAntonio Huete Jimenez return 0;
3286ee116499SAntonio Huete Jimenez return 1;
3287ee116499SAntonio Huete Jimenez }
3288ee116499SAntonio Huete Jimenez
328918de8d7fSPeter Avalos static void
usage(void)329018de8d7fSPeter Avalos usage(void)
329118de8d7fSPeter Avalos {
329236e94dc5SPeter Avalos fprintf(stderr,
329350a69bb5SSascha Wildner "usage: ssh-keygen [-q] [-a rounds] [-b bits] [-C comment] [-f output_keyfile]\n"
329450a69bb5SSascha Wildner " [-m format] [-N new_passphrase] [-O option]\n"
32950cbfa66cSDaniel Fojt " [-t dsa | ecdsa | ecdsa-sk | ed25519 | ed25519-sk | rsa]\n"
329650a69bb5SSascha Wildner " [-w provider] [-Z cipher]\n"
329750a69bb5SSascha Wildner " ssh-keygen -p [-a rounds] [-f keyfile] [-m format] [-N new_passphrase]\n"
329850a69bb5SSascha Wildner " [-P old_passphrase] [-Z cipher]\n"
329950a69bb5SSascha Wildner #ifdef WITH_OPENSSL
33000cbfa66cSDaniel Fojt " ssh-keygen -i [-f input_keyfile] [-m key_format]\n"
33010cbfa66cSDaniel Fojt " ssh-keygen -e [-f input_keyfile] [-m key_format]\n"
330250a69bb5SSascha Wildner #endif
330336e94dc5SPeter Avalos " ssh-keygen -y [-f input_keyfile]\n"
330450a69bb5SSascha Wildner " ssh-keygen -c [-a rounds] [-C comment] [-f keyfile] [-P passphrase]\n"
3305e9778795SPeter Avalos " ssh-keygen -l [-v] [-E fingerprint_hash] [-f input_keyfile]\n"
330636e94dc5SPeter Avalos " ssh-keygen -B [-f input_keyfile]\n");
3307856ea928SPeter Avalos #ifdef ENABLE_PKCS11
330836e94dc5SPeter Avalos fprintf(stderr,
330936e94dc5SPeter Avalos " ssh-keygen -D pkcs11\n");
3310856ea928SPeter Avalos #endif
331136e94dc5SPeter Avalos fprintf(stderr,
33120cbfa66cSDaniel Fojt " ssh-keygen -F hostname [-lv] [-f known_hosts_file]\n"
331336e94dc5SPeter Avalos " ssh-keygen -H [-f known_hosts_file]\n"
331450a69bb5SSascha Wildner " ssh-keygen -K [-a rounds] [-w provider]\n"
331536e94dc5SPeter Avalos " ssh-keygen -R hostname [-f known_hosts_file]\n"
33160cbfa66cSDaniel Fojt " ssh-keygen -r hostname [-g] [-f input_keyfile]\n"
3317e9778795SPeter Avalos #ifdef WITH_OPENSSL
33180cbfa66cSDaniel Fojt " ssh-keygen -M generate [-O option] output_file\n"
33190cbfa66cSDaniel Fojt " ssh-keygen -M screen [-f input_file] [-O option] output_file\n"
3320e9778795SPeter Avalos #endif
33210cbfa66cSDaniel Fojt " ssh-keygen -I certificate_identity -s ca_key [-hU] [-D pkcs11_provider]\n"
33220cbfa66cSDaniel Fojt " [-n principals] [-O option] [-V validity_interval]\n"
33230cbfa66cSDaniel Fojt " [-z serial_number] file ...\n"
332436e94dc5SPeter Avalos " ssh-keygen -L [-f input_keyfile]\n"
332550a69bb5SSascha Wildner " ssh-keygen -A [-a rounds] [-f prefix_path]\n"
332636e94dc5SPeter Avalos " ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n"
332736e94dc5SPeter Avalos " file ...\n"
33280cbfa66cSDaniel Fojt " ssh-keygen -Q [-l] -f krl_file [file ...]\n"
33290cbfa66cSDaniel Fojt " ssh-keygen -Y find-principals -s signature_file -f allowed_signers_file\n"
3330ee116499SAntonio Huete Jimenez " ssh-keygen -Y match-principals -I signer_identity -f allowed_signers_file\n"
33310cbfa66cSDaniel Fojt " ssh-keygen -Y check-novalidate -n namespace -s signature_file\n"
3332ee116499SAntonio Huete Jimenez " ssh-keygen -Y sign -f key_file -n namespace file [-O option] ...\n"
33330cbfa66cSDaniel Fojt " ssh-keygen -Y verify -f allowed_signers_file -I signer_identity\n"
3334ee116499SAntonio Huete Jimenez " -n namespace -s signature_file [-r krl_file] [-O option]\n");
333518de8d7fSPeter Avalos exit(1);
333618de8d7fSPeter Avalos }
333718de8d7fSPeter Avalos
333818de8d7fSPeter Avalos /*
333918de8d7fSPeter Avalos * Main program for key management.
334018de8d7fSPeter Avalos */
334118de8d7fSPeter Avalos int
main(int argc,char ** argv)334218de8d7fSPeter Avalos main(int argc, char **argv)
334318de8d7fSPeter Avalos {
3344ee116499SAntonio Huete Jimenez char comment[1024], *passphrase = NULL;
3345e9778795SPeter Avalos char *rr_hostname = NULL, *ep, *fp, *ra;
3346e9778795SPeter Avalos struct sshkey *private, *public;
334718de8d7fSPeter Avalos struct passwd *pw;
33480cbfa66cSDaniel Fojt int r, opt, type;
3349664f4763Szrj int change_passphrase = 0, change_comment = 0, show_cert = 0;
3350664f4763Szrj int find_host = 0, delete_host = 0, hash_hosts = 0;
335136e94dc5SPeter Avalos int gen_all_hostkeys = 0, gen_krl = 0, update_krl = 0, check_krl = 0;
3352664f4763Szrj int prefer_agent = 0, convert_to = 0, convert_from = 0;
3353664f4763Szrj int print_public = 0, print_generic = 0, cert_serial_autoinc = 0;
33540cbfa66cSDaniel Fojt int do_gen_candidates = 0, do_screen_candidates = 0, download_sk = 0;
3355664f4763Szrj unsigned long long cert_serial = 0;
33560cbfa66cSDaniel Fojt char *identity_comment = NULL, *ca_key_path = NULL, **opts = NULL;
33570cbfa66cSDaniel Fojt char *sk_application = NULL, *sk_device = NULL, *sk_user = NULL;
335850a69bb5SSascha Wildner char *sk_attestation_path = NULL;
33590cbfa66cSDaniel Fojt struct sshbuf *challenge = NULL, *attest = NULL;
33600cbfa66cSDaniel Fojt size_t i, nopts = 0;
33610cbfa66cSDaniel Fojt u_int32_t bits = 0;
33620cbfa66cSDaniel Fojt uint8_t sk_flags = SSH_SK_USER_PRESENCE_REQD;
336318de8d7fSPeter Avalos const char *errstr;
3364664f4763Szrj int log_level = SYSLOG_LEVEL_INFO;
33650cbfa66cSDaniel Fojt char *sign_op = NULL;
336618de8d7fSPeter Avalos
336718de8d7fSPeter Avalos extern int optind;
336818de8d7fSPeter Avalos extern char *optarg;
336918de8d7fSPeter Avalos
337018de8d7fSPeter Avalos /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
337118de8d7fSPeter Avalos sanitise_stdfd();
337218de8d7fSPeter Avalos
337318de8d7fSPeter Avalos __progname = ssh_get_progname(argv[0]);
337418de8d7fSPeter Avalos
337518de8d7fSPeter Avalos seed_rng();
337618de8d7fSPeter Avalos
3377664f4763Szrj log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1);
3378664f4763Szrj
3379ce74bacaSMatthew Dillon msetlocale();
3380ce74bacaSMatthew Dillon
338118de8d7fSPeter Avalos /* we need this for the home * directory. */
338218de8d7fSPeter Avalos pw = getpwuid(getuid());
3383e9778795SPeter Avalos if (!pw)
3384e9778795SPeter Avalos fatal("No user exists for uid %lu", (u_long)getuid());
338550a69bb5SSascha Wildner pw = pwcopy(pw);
33860cbfa66cSDaniel Fojt if (gethostname(hostname, sizeof(hostname)) == -1)
3387e9778795SPeter Avalos fatal("gethostname: %s", strerror(errno));
338818de8d7fSPeter Avalos
33890cbfa66cSDaniel Fojt sk_provider = getenv("SSH_SK_PROVIDER");
33900cbfa66cSDaniel Fojt
33910cbfa66cSDaniel Fojt /* Remaining characters: dGjJSTWx */
33920cbfa66cSDaniel Fojt while ((opt = getopt(argc, argv, "ABHKLQUXceghiklopquvy"
33930cbfa66cSDaniel Fojt "C:D:E:F:I:M:N:O:P:R:V:Y:Z:"
33940cbfa66cSDaniel Fojt "a:b:f:g:m:n:r:s:t:w:z:")) != -1) {
339518de8d7fSPeter Avalos switch (opt) {
33961c188a7fSPeter Avalos case 'A':
33971c188a7fSPeter Avalos gen_all_hostkeys = 1;
33981c188a7fSPeter Avalos break;
339918de8d7fSPeter Avalos case 'b':
34000cbfa66cSDaniel Fojt bits = (u_int32_t)strtonum(optarg, 1, UINT32_MAX,
34010cbfa66cSDaniel Fojt &errstr);
340218de8d7fSPeter Avalos if (errstr)
340318de8d7fSPeter Avalos fatal("Bits has bad value %s (%s)",
340418de8d7fSPeter Avalos optarg, errstr);
340518de8d7fSPeter Avalos break;
3406e9778795SPeter Avalos case 'E':
3407e9778795SPeter Avalos fingerprint_hash = ssh_digest_alg_by_name(optarg);
3408e9778795SPeter Avalos if (fingerprint_hash == -1)
3409e9778795SPeter Avalos fatal("Invalid hash algorithm \"%s\"", optarg);
3410e9778795SPeter Avalos break;
341118de8d7fSPeter Avalos case 'F':
341218de8d7fSPeter Avalos find_host = 1;
341318de8d7fSPeter Avalos rr_hostname = optarg;
341418de8d7fSPeter Avalos break;
341518de8d7fSPeter Avalos case 'H':
341618de8d7fSPeter Avalos hash_hosts = 1;
341718de8d7fSPeter Avalos break;
3418856ea928SPeter Avalos case 'I':
3419856ea928SPeter Avalos cert_key_id = optarg;
3420856ea928SPeter Avalos break;
342118de8d7fSPeter Avalos case 'R':
342218de8d7fSPeter Avalos delete_host = 1;
342318de8d7fSPeter Avalos rr_hostname = optarg;
342418de8d7fSPeter Avalos break;
3425856ea928SPeter Avalos case 'L':
3426856ea928SPeter Avalos show_cert = 1;
3427856ea928SPeter Avalos break;
342818de8d7fSPeter Avalos case 'l':
342918de8d7fSPeter Avalos print_fingerprint = 1;
343018de8d7fSPeter Avalos break;
343118de8d7fSPeter Avalos case 'B':
343218de8d7fSPeter Avalos print_bubblebabble = 1;
343318de8d7fSPeter Avalos break;
3434856ea928SPeter Avalos case 'm':
3435856ea928SPeter Avalos if (strcasecmp(optarg, "RFC4716") == 0 ||
3436856ea928SPeter Avalos strcasecmp(optarg, "ssh2") == 0) {
3437856ea928SPeter Avalos convert_format = FMT_RFC4716;
3438856ea928SPeter Avalos break;
3439856ea928SPeter Avalos }
3440856ea928SPeter Avalos if (strcasecmp(optarg, "PKCS8") == 0) {
3441856ea928SPeter Avalos convert_format = FMT_PKCS8;
34420cbfa66cSDaniel Fojt private_key_format = SSHKEY_PRIVATE_PKCS8;
3443856ea928SPeter Avalos break;
3444856ea928SPeter Avalos }
3445856ea928SPeter Avalos if (strcasecmp(optarg, "PEM") == 0) {
3446856ea928SPeter Avalos convert_format = FMT_PEM;
34470cbfa66cSDaniel Fojt private_key_format = SSHKEY_PRIVATE_PEM;
3448856ea928SPeter Avalos break;
3449856ea928SPeter Avalos }
3450856ea928SPeter Avalos fatal("Unsupported conversion format \"%s\"", optarg);
3451856ea928SPeter Avalos case 'n':
3452856ea928SPeter Avalos cert_principals = optarg;
3453856ea928SPeter Avalos break;
345436e94dc5SPeter Avalos case 'o':
3455664f4763Szrj /* no-op; new format is already the default */
345636e94dc5SPeter Avalos break;
345718de8d7fSPeter Avalos case 'p':
345818de8d7fSPeter Avalos change_passphrase = 1;
345918de8d7fSPeter Avalos break;
346018de8d7fSPeter Avalos case 'c':
346118de8d7fSPeter Avalos change_comment = 1;
346218de8d7fSPeter Avalos break;
346318de8d7fSPeter Avalos case 'f':
3464e9778795SPeter Avalos if (strlcpy(identity_file, optarg,
3465e9778795SPeter Avalos sizeof(identity_file)) >= sizeof(identity_file))
346618de8d7fSPeter Avalos fatal("Identity filename too long");
346718de8d7fSPeter Avalos have_identity = 1;
346818de8d7fSPeter Avalos break;
346918de8d7fSPeter Avalos case 'g':
347018de8d7fSPeter Avalos print_generic = 1;
347118de8d7fSPeter Avalos break;
34720cbfa66cSDaniel Fojt case 'K':
34730cbfa66cSDaniel Fojt download_sk = 1;
34740cbfa66cSDaniel Fojt break;
347518de8d7fSPeter Avalos case 'P':
347618de8d7fSPeter Avalos identity_passphrase = optarg;
347718de8d7fSPeter Avalos break;
347818de8d7fSPeter Avalos case 'N':
347918de8d7fSPeter Avalos identity_new_passphrase = optarg;
348018de8d7fSPeter Avalos break;
348136e94dc5SPeter Avalos case 'Q':
348236e94dc5SPeter Avalos check_krl = 1;
348336e94dc5SPeter Avalos break;
3484856ea928SPeter Avalos case 'O':
34850cbfa66cSDaniel Fojt opts = xrecallocarray(opts, nopts, nopts + 1,
34860cbfa66cSDaniel Fojt sizeof(*opts));
34870cbfa66cSDaniel Fojt opts[nopts++] = xstrdup(optarg);
3488856ea928SPeter Avalos break;
348936e94dc5SPeter Avalos case 'Z':
34900cbfa66cSDaniel Fojt openssh_format_cipher = optarg;
349150a69bb5SSascha Wildner if (cipher_by_name(openssh_format_cipher) == NULL)
349250a69bb5SSascha Wildner fatal("Invalid OpenSSH-format cipher '%s'",
349350a69bb5SSascha Wildner openssh_format_cipher);
349436e94dc5SPeter Avalos break;
349518de8d7fSPeter Avalos case 'C':
349618de8d7fSPeter Avalos identity_comment = optarg;
349718de8d7fSPeter Avalos break;
349818de8d7fSPeter Avalos case 'q':
349918de8d7fSPeter Avalos quiet = 1;
350018de8d7fSPeter Avalos break;
350118de8d7fSPeter Avalos case 'e':
350218de8d7fSPeter Avalos /* export key */
3503856ea928SPeter Avalos convert_to = 1;
3504856ea928SPeter Avalos break;
3505856ea928SPeter Avalos case 'h':
3506856ea928SPeter Avalos cert_key_type = SSH2_CERT_TYPE_HOST;
3507856ea928SPeter Avalos certflags_flags = 0;
350818de8d7fSPeter Avalos break;
350936e94dc5SPeter Avalos case 'k':
351036e94dc5SPeter Avalos gen_krl = 1;
351136e94dc5SPeter Avalos break;
351218de8d7fSPeter Avalos case 'i':
351318de8d7fSPeter Avalos case 'X':
351418de8d7fSPeter Avalos /* import key */
3515856ea928SPeter Avalos convert_from = 1;
351618de8d7fSPeter Avalos break;
351718de8d7fSPeter Avalos case 'y':
351818de8d7fSPeter Avalos print_public = 1;
351918de8d7fSPeter Avalos break;
3520856ea928SPeter Avalos case 's':
3521856ea928SPeter Avalos ca_key_path = optarg;
3522856ea928SPeter Avalos break;
352318de8d7fSPeter Avalos case 't':
352418de8d7fSPeter Avalos key_type_name = optarg;
352518de8d7fSPeter Avalos break;
352618de8d7fSPeter Avalos case 'D':
3527856ea928SPeter Avalos pkcs11provider = optarg;
352818de8d7fSPeter Avalos break;
3529ce74bacaSMatthew Dillon case 'U':
3530ce74bacaSMatthew Dillon prefer_agent = 1;
3531ce74bacaSMatthew Dillon break;
353236e94dc5SPeter Avalos case 'u':
353336e94dc5SPeter Avalos update_krl = 1;
353436e94dc5SPeter Avalos break;
353518de8d7fSPeter Avalos case 'v':
353618de8d7fSPeter Avalos if (log_level == SYSLOG_LEVEL_INFO)
353718de8d7fSPeter Avalos log_level = SYSLOG_LEVEL_DEBUG1;
353818de8d7fSPeter Avalos else {
353918de8d7fSPeter Avalos if (log_level >= SYSLOG_LEVEL_DEBUG1 &&
354018de8d7fSPeter Avalos log_level < SYSLOG_LEVEL_DEBUG3)
354118de8d7fSPeter Avalos log_level++;
354218de8d7fSPeter Avalos }
354318de8d7fSPeter Avalos break;
354418de8d7fSPeter Avalos case 'r':
354518de8d7fSPeter Avalos rr_hostname = optarg;
354618de8d7fSPeter Avalos break;
3547e9778795SPeter Avalos case 'a':
3548e9778795SPeter Avalos rounds = (int)strtonum(optarg, 1, INT_MAX, &errstr);
3549e9778795SPeter Avalos if (errstr)
3550e9778795SPeter Avalos fatal("Invalid number: %s (%s)",
3551e9778795SPeter Avalos optarg, errstr);
3552e9778795SPeter Avalos break;
3553e9778795SPeter Avalos case 'V':
3554e9778795SPeter Avalos parse_cert_times(optarg);
3555e9778795SPeter Avalos break;
35560cbfa66cSDaniel Fojt case 'Y':
35570cbfa66cSDaniel Fojt sign_op = optarg;
35580cbfa66cSDaniel Fojt break;
35590cbfa66cSDaniel Fojt case 'w':
35600cbfa66cSDaniel Fojt sk_provider = optarg;
35610cbfa66cSDaniel Fojt break;
3562e9778795SPeter Avalos case 'z':
3563e9778795SPeter Avalos errno = 0;
3564664f4763Szrj if (*optarg == '+') {
3565664f4763Szrj cert_serial_autoinc = 1;
3566664f4763Szrj optarg++;
3567664f4763Szrj }
3568e9778795SPeter Avalos cert_serial = strtoull(optarg, &ep, 10);
3569e9778795SPeter Avalos if (*optarg < '0' || *optarg > '9' || *ep != '\0' ||
3570e9778795SPeter Avalos (errno == ERANGE && cert_serial == ULLONG_MAX))
3571e9778795SPeter Avalos fatal("Invalid serial number \"%s\"", optarg);
3572e9778795SPeter Avalos break;
3573ce74bacaSMatthew Dillon case 'M':
35740cbfa66cSDaniel Fojt if (strcmp(optarg, "generate") == 0)
35750cbfa66cSDaniel Fojt do_gen_candidates = 1;
35760cbfa66cSDaniel Fojt else if (strcmp(optarg, "screen") == 0)
357718de8d7fSPeter Avalos do_screen_candidates = 1;
35780cbfa66cSDaniel Fojt else
35790cbfa66cSDaniel Fojt fatal("Unsupported moduli option %s", optarg);
358018de8d7fSPeter Avalos break;
358118de8d7fSPeter Avalos default:
358218de8d7fSPeter Avalos usage();
358318de8d7fSPeter Avalos }
358418de8d7fSPeter Avalos }
358518de8d7fSPeter Avalos
35860cbfa66cSDaniel Fojt #ifdef ENABLE_SK_INTERNAL
35870cbfa66cSDaniel Fojt if (sk_provider == NULL)
35880cbfa66cSDaniel Fojt sk_provider = "internal";
35890cbfa66cSDaniel Fojt #endif
35900cbfa66cSDaniel Fojt
359118de8d7fSPeter Avalos /* reinit */
359218de8d7fSPeter Avalos log_init(argv[0], log_level, SYSLOG_FACILITY_USER, 1);
359318de8d7fSPeter Avalos
3594856ea928SPeter Avalos argv += optind;
3595856ea928SPeter Avalos argc -= optind;
3596856ea928SPeter Avalos
35970cbfa66cSDaniel Fojt if (sign_op != NULL) {
35980cbfa66cSDaniel Fojt if (strncmp(sign_op, "find-principals", 15) == 0) {
35990cbfa66cSDaniel Fojt if (ca_key_path == NULL) {
36000cbfa66cSDaniel Fojt error("Too few arguments for find-principals:"
36010cbfa66cSDaniel Fojt "missing signature file");
36020cbfa66cSDaniel Fojt exit(1);
36030cbfa66cSDaniel Fojt }
36040cbfa66cSDaniel Fojt if (!have_identity) {
36050cbfa66cSDaniel Fojt error("Too few arguments for find-principals:"
36060cbfa66cSDaniel Fojt "missing allowed keys file");
36070cbfa66cSDaniel Fojt exit(1);
36080cbfa66cSDaniel Fojt }
360950a69bb5SSascha Wildner return sig_find_principals(ca_key_path, identity_file,
361050a69bb5SSascha Wildner opts, nopts);
3611ee116499SAntonio Huete Jimenez } else if (strncmp(sign_op, "match-principals", 16) == 0) {
3612ee116499SAntonio Huete Jimenez if (!have_identity) {
3613ee116499SAntonio Huete Jimenez error("Too few arguments for match-principals:"
3614ee116499SAntonio Huete Jimenez "missing allowed keys file");
3615ee116499SAntonio Huete Jimenez exit(1);
3616ee116499SAntonio Huete Jimenez }
3617ee116499SAntonio Huete Jimenez if (cert_key_id == NULL) {
3618ee116499SAntonio Huete Jimenez error("Too few arguments for match-principals: "
3619ee116499SAntonio Huete Jimenez "missing principal ID");
3620ee116499SAntonio Huete Jimenez exit(1);
3621ee116499SAntonio Huete Jimenez }
3622ee116499SAntonio Huete Jimenez return sig_match_principals(identity_file, cert_key_id,
3623ee116499SAntonio Huete Jimenez opts, nopts);
36240cbfa66cSDaniel Fojt } else if (strncmp(sign_op, "sign", 4) == 0) {
3625ee116499SAntonio Huete Jimenez /* NB. cert_principals is actually namespace, via -n */
36260cbfa66cSDaniel Fojt if (cert_principals == NULL ||
36270cbfa66cSDaniel Fojt *cert_principals == '\0') {
36280cbfa66cSDaniel Fojt error("Too few arguments for sign: "
36290cbfa66cSDaniel Fojt "missing namespace");
36300cbfa66cSDaniel Fojt exit(1);
36310cbfa66cSDaniel Fojt }
36320cbfa66cSDaniel Fojt if (!have_identity) {
36330cbfa66cSDaniel Fojt error("Too few arguments for sign: "
36340cbfa66cSDaniel Fojt "missing key");
36350cbfa66cSDaniel Fojt exit(1);
36360cbfa66cSDaniel Fojt }
36370cbfa66cSDaniel Fojt return sig_sign(identity_file, cert_principals,
3638ee116499SAntonio Huete Jimenez prefer_agent, argc, argv, opts, nopts);
36390cbfa66cSDaniel Fojt } else if (strncmp(sign_op, "check-novalidate", 16) == 0) {
3640ee116499SAntonio Huete Jimenez /* NB. cert_principals is actually namespace, via -n */
3641ee116499SAntonio Huete Jimenez if (cert_principals == NULL ||
3642ee116499SAntonio Huete Jimenez *cert_principals == '\0') {
3643ee116499SAntonio Huete Jimenez error("Too few arguments for check-novalidate: "
3644ee116499SAntonio Huete Jimenez "missing namespace");
3645ee116499SAntonio Huete Jimenez exit(1);
3646ee116499SAntonio Huete Jimenez }
36470cbfa66cSDaniel Fojt if (ca_key_path == NULL) {
36480cbfa66cSDaniel Fojt error("Too few arguments for check-novalidate: "
36490cbfa66cSDaniel Fojt "missing signature file");
36500cbfa66cSDaniel Fojt exit(1);
36510cbfa66cSDaniel Fojt }
36520cbfa66cSDaniel Fojt return sig_verify(ca_key_path, cert_principals,
365350a69bb5SSascha Wildner NULL, NULL, NULL, opts, nopts);
36540cbfa66cSDaniel Fojt } else if (strncmp(sign_op, "verify", 6) == 0) {
3655ee116499SAntonio Huete Jimenez /* NB. cert_principals is actually namespace, via -n */
36560cbfa66cSDaniel Fojt if (cert_principals == NULL ||
36570cbfa66cSDaniel Fojt *cert_principals == '\0') {
36580cbfa66cSDaniel Fojt error("Too few arguments for verify: "
36590cbfa66cSDaniel Fojt "missing namespace");
36600cbfa66cSDaniel Fojt exit(1);
36610cbfa66cSDaniel Fojt }
36620cbfa66cSDaniel Fojt if (ca_key_path == NULL) {
36630cbfa66cSDaniel Fojt error("Too few arguments for verify: "
36640cbfa66cSDaniel Fojt "missing signature file");
36650cbfa66cSDaniel Fojt exit(1);
36660cbfa66cSDaniel Fojt }
36670cbfa66cSDaniel Fojt if (!have_identity) {
36680cbfa66cSDaniel Fojt error("Too few arguments for sign: "
36690cbfa66cSDaniel Fojt "missing allowed keys file");
36700cbfa66cSDaniel Fojt exit(1);
36710cbfa66cSDaniel Fojt }
36720cbfa66cSDaniel Fojt if (cert_key_id == NULL) {
36730cbfa66cSDaniel Fojt error("Too few arguments for verify: "
3674ee116499SAntonio Huete Jimenez "missing principal identity");
36750cbfa66cSDaniel Fojt exit(1);
36760cbfa66cSDaniel Fojt }
36770cbfa66cSDaniel Fojt return sig_verify(ca_key_path, cert_principals,
367850a69bb5SSascha Wildner cert_key_id, identity_file, rr_hostname,
367950a69bb5SSascha Wildner opts, nopts);
36800cbfa66cSDaniel Fojt }
36810cbfa66cSDaniel Fojt error("Unsupported operation for -Y: \"%s\"", sign_op);
36820cbfa66cSDaniel Fojt usage();
36830cbfa66cSDaniel Fojt /* NOTREACHED */
36840cbfa66cSDaniel Fojt }
36850cbfa66cSDaniel Fojt
3686856ea928SPeter Avalos if (ca_key_path != NULL) {
368736e94dc5SPeter Avalos if (argc < 1 && !gen_krl) {
3688e9778795SPeter Avalos error("Too few arguments.");
3689856ea928SPeter Avalos usage();
3690856ea928SPeter Avalos }
36910cbfa66cSDaniel Fojt } else if (argc > 0 && !gen_krl && !check_krl &&
36920cbfa66cSDaniel Fojt !do_gen_candidates && !do_screen_candidates) {
3693e9778795SPeter Avalos error("Too many arguments.");
369418de8d7fSPeter Avalos usage();
369518de8d7fSPeter Avalos }
369618de8d7fSPeter Avalos if (change_passphrase && change_comment) {
3697e9778795SPeter Avalos error("Can only have one of -p and -c.");
369818de8d7fSPeter Avalos usage();
369918de8d7fSPeter Avalos }
370018de8d7fSPeter Avalos if (print_fingerprint && (delete_host || hash_hosts)) {
3701e9778795SPeter Avalos error("Cannot use -l with -H or -R.");
370218de8d7fSPeter Avalos usage();
370318de8d7fSPeter Avalos }
370436e94dc5SPeter Avalos if (gen_krl) {
3705664f4763Szrj do_gen_krl(pw, update_krl, ca_key_path,
3706664f4763Szrj cert_serial, identity_comment, argc, argv);
370736e94dc5SPeter Avalos return (0);
370836e94dc5SPeter Avalos }
370936e94dc5SPeter Avalos if (check_krl) {
37100cbfa66cSDaniel Fojt do_check_krl(pw, print_fingerprint, argc, argv);
371136e94dc5SPeter Avalos return (0);
371236e94dc5SPeter Avalos }
3713856ea928SPeter Avalos if (ca_key_path != NULL) {
3714856ea928SPeter Avalos if (cert_key_id == NULL)
3715856ea928SPeter Avalos fatal("Must specify key id (-I) when certifying");
37160cbfa66cSDaniel Fojt for (i = 0; i < nopts; i++)
37170cbfa66cSDaniel Fojt add_cert_option(opts[i]);
3718664f4763Szrj do_ca_sign(pw, ca_key_path, prefer_agent,
3719664f4763Szrj cert_serial, cert_serial_autoinc, argc, argv);
3720856ea928SPeter Avalos }
3721856ea928SPeter Avalos if (show_cert)
3722856ea928SPeter Avalos do_show_cert(pw);
3723664f4763Szrj if (delete_host || hash_hosts || find_host) {
3724664f4763Szrj do_known_hosts(pw, rr_hostname, find_host,
3725664f4763Szrj delete_host, hash_hosts);
3726664f4763Szrj }
372736e94dc5SPeter Avalos if (pkcs11provider != NULL)
372836e94dc5SPeter Avalos do_download(pw);
37290cbfa66cSDaniel Fojt if (download_sk) {
37300cbfa66cSDaniel Fojt for (i = 0; i < nopts; i++) {
37310cbfa66cSDaniel Fojt if (strncasecmp(opts[i], "device=", 7) == 0) {
37320cbfa66cSDaniel Fojt sk_device = xstrdup(opts[i] + 7);
37330cbfa66cSDaniel Fojt } else {
37340cbfa66cSDaniel Fojt fatal("Option \"%s\" is unsupported for "
37350cbfa66cSDaniel Fojt "FIDO authenticator download", opts[i]);
37360cbfa66cSDaniel Fojt }
37370cbfa66cSDaniel Fojt }
37380cbfa66cSDaniel Fojt return do_download_sk(sk_provider, sk_device);
37390cbfa66cSDaniel Fojt }
374018de8d7fSPeter Avalos if (print_fingerprint || print_bubblebabble)
374118de8d7fSPeter Avalos do_fingerprint(pw);
374218de8d7fSPeter Avalos if (change_passphrase)
374318de8d7fSPeter Avalos do_change_passphrase(pw);
374418de8d7fSPeter Avalos if (change_comment)
3745664f4763Szrj do_change_comment(pw, identity_comment);
374636e94dc5SPeter Avalos #ifdef WITH_OPENSSL
3747856ea928SPeter Avalos if (convert_to)
3748856ea928SPeter Avalos do_convert_to(pw);
3749856ea928SPeter Avalos if (convert_from)
3750856ea928SPeter Avalos do_convert_from(pw);
37510cbfa66cSDaniel Fojt #else /* WITH_OPENSSL */
37520cbfa66cSDaniel Fojt if (convert_to || convert_from)
37530cbfa66cSDaniel Fojt fatal("key conversion disabled at compile time");
37540cbfa66cSDaniel Fojt #endif /* WITH_OPENSSL */
375518de8d7fSPeter Avalos if (print_public)
375618de8d7fSPeter Avalos do_print_public(pw);
375718de8d7fSPeter Avalos if (rr_hostname != NULL) {
375818de8d7fSPeter Avalos unsigned int n = 0;
375918de8d7fSPeter Avalos
376018de8d7fSPeter Avalos if (have_identity) {
3761664f4763Szrj n = do_print_resource_record(pw, identity_file,
3762*ba1276acSMatthew Dillon rr_hostname, print_generic, opts, nopts);
3763e9778795SPeter Avalos if (n == 0)
3764e9778795SPeter Avalos fatal("%s: %s", identity_file, strerror(errno));
376518de8d7fSPeter Avalos exit(0);
376618de8d7fSPeter Avalos } else {
376718de8d7fSPeter Avalos
376818de8d7fSPeter Avalos n += do_print_resource_record(pw,
3769664f4763Szrj _PATH_HOST_RSA_KEY_FILE, rr_hostname,
3770*ba1276acSMatthew Dillon print_generic, opts, nopts);
3771*ba1276acSMatthew Dillon #ifdef WITH_DSA
377218de8d7fSPeter Avalos n += do_print_resource_record(pw,
3773664f4763Szrj _PATH_HOST_DSA_KEY_FILE, rr_hostname,
3774*ba1276acSMatthew Dillon print_generic, opts, nopts);
3775*ba1276acSMatthew Dillon #endif
377699e85e0dSPeter Avalos n += do_print_resource_record(pw,
3777664f4763Szrj _PATH_HOST_ECDSA_KEY_FILE, rr_hostname,
3778*ba1276acSMatthew Dillon print_generic, opts, nopts);
377936e94dc5SPeter Avalos n += do_print_resource_record(pw,
3780664f4763Szrj _PATH_HOST_ED25519_KEY_FILE, rr_hostname,
3781*ba1276acSMatthew Dillon print_generic, opts, nopts);
3782664f4763Szrj n += do_print_resource_record(pw,
3783664f4763Szrj _PATH_HOST_XMSS_KEY_FILE, rr_hostname,
3784*ba1276acSMatthew Dillon print_generic, opts, nopts);
378518de8d7fSPeter Avalos if (n == 0)
378618de8d7fSPeter Avalos fatal("no keys found.");
378718de8d7fSPeter Avalos exit(0);
378818de8d7fSPeter Avalos }
378918de8d7fSPeter Avalos }
379018de8d7fSPeter Avalos
37910cbfa66cSDaniel Fojt if (do_gen_candidates || do_screen_candidates) {
37920cbfa66cSDaniel Fojt if (argc <= 0)
37930cbfa66cSDaniel Fojt fatal("No output file specified");
37940cbfa66cSDaniel Fojt else if (argc > 1)
37950cbfa66cSDaniel Fojt fatal("Too many output files specified");
37960cbfa66cSDaniel Fojt }
379718de8d7fSPeter Avalos if (do_gen_candidates) {
37980cbfa66cSDaniel Fojt do_moduli_gen(argv[0], opts, nopts);
37990cbfa66cSDaniel Fojt return 0;
380018de8d7fSPeter Avalos }
380118de8d7fSPeter Avalos if (do_screen_candidates) {
38020cbfa66cSDaniel Fojt do_moduli_screen(argv[0], opts, nopts);
38030cbfa66cSDaniel Fojt return 0;
380418de8d7fSPeter Avalos }
380518de8d7fSPeter Avalos
38061c188a7fSPeter Avalos if (gen_all_hostkeys) {
38071c188a7fSPeter Avalos do_gen_all_hostkeys(pw);
38081c188a7fSPeter Avalos return (0);
38091c188a7fSPeter Avalos }
38101c188a7fSPeter Avalos
381118de8d7fSPeter Avalos if (key_type_name == NULL)
3812e9778795SPeter Avalos key_type_name = DEFAULT_KEY_TYPE_NAME;
381318de8d7fSPeter Avalos
3814e9778795SPeter Avalos type = sshkey_type_from_name(key_type_name);
3815e9778795SPeter Avalos type_bits_valid(type, key_type_name, &bits);
38161c188a7fSPeter Avalos
381718de8d7fSPeter Avalos if (!quiet)
3818e9778795SPeter Avalos printf("Generating public/private %s key pair.\n",
3819e9778795SPeter Avalos key_type_name);
38200cbfa66cSDaniel Fojt switch (type) {
38210cbfa66cSDaniel Fojt case KEY_ECDSA_SK:
38220cbfa66cSDaniel Fojt case KEY_ED25519_SK:
38230cbfa66cSDaniel Fojt for (i = 0; i < nopts; i++) {
38240cbfa66cSDaniel Fojt if (strcasecmp(opts[i], "no-touch-required") == 0) {
38250cbfa66cSDaniel Fojt sk_flags &= ~SSH_SK_USER_PRESENCE_REQD;
382650a69bb5SSascha Wildner } else if (strcasecmp(opts[i], "verify-required") == 0) {
382750a69bb5SSascha Wildner sk_flags |= SSH_SK_USER_VERIFICATION_REQD;
38280cbfa66cSDaniel Fojt } else if (strcasecmp(opts[i], "resident") == 0) {
38290cbfa66cSDaniel Fojt sk_flags |= SSH_SK_RESIDENT_KEY;
38300cbfa66cSDaniel Fojt } else if (strncasecmp(opts[i], "device=", 7) == 0) {
38310cbfa66cSDaniel Fojt sk_device = xstrdup(opts[i] + 7);
38320cbfa66cSDaniel Fojt } else if (strncasecmp(opts[i], "user=", 5) == 0) {
38330cbfa66cSDaniel Fojt sk_user = xstrdup(opts[i] + 5);
38340cbfa66cSDaniel Fojt } else if (strncasecmp(opts[i], "challenge=", 10) == 0) {
38350cbfa66cSDaniel Fojt if ((r = sshbuf_load_file(opts[i] + 10,
38360cbfa66cSDaniel Fojt &challenge)) != 0) {
383750a69bb5SSascha Wildner fatal_r(r, "Unable to load FIDO "
383850a69bb5SSascha Wildner "enrollment challenge \"%s\"",
383950a69bb5SSascha Wildner opts[i] + 10);
38400cbfa66cSDaniel Fojt }
38410cbfa66cSDaniel Fojt } else if (strncasecmp(opts[i],
38420cbfa66cSDaniel Fojt "write-attestation=", 18) == 0) {
384350a69bb5SSascha Wildner sk_attestation_path = opts[i] + 18;
38440cbfa66cSDaniel Fojt } else if (strncasecmp(opts[i],
38450cbfa66cSDaniel Fojt "application=", 12) == 0) {
38460cbfa66cSDaniel Fojt sk_application = xstrdup(opts[i] + 12);
38470cbfa66cSDaniel Fojt if (strncmp(sk_application, "ssh:", 4) != 0) {
38480cbfa66cSDaniel Fojt fatal("FIDO application string must "
38490cbfa66cSDaniel Fojt "begin with \"ssh:\"");
38500cbfa66cSDaniel Fojt }
38510cbfa66cSDaniel Fojt } else {
38520cbfa66cSDaniel Fojt fatal("Option \"%s\" is unsupported for "
38530cbfa66cSDaniel Fojt "FIDO authenticator enrollment", opts[i]);
38540cbfa66cSDaniel Fojt }
38550cbfa66cSDaniel Fojt }
38560cbfa66cSDaniel Fojt if ((attest = sshbuf_new()) == NULL)
38570cbfa66cSDaniel Fojt fatal("sshbuf_new failed");
3858ee116499SAntonio Huete Jimenez r = 0;
3859ee116499SAntonio Huete Jimenez for (i = 0 ;;) {
3860ee116499SAntonio Huete Jimenez if (!quiet) {
3861ee116499SAntonio Huete Jimenez printf("You may need to touch your "
3862ee116499SAntonio Huete Jimenez "authenticator%s to authorize key "
3863ee116499SAntonio Huete Jimenez "generation.\n",
3864ee116499SAntonio Huete Jimenez r == 0 ? "" : " again");
386550a69bb5SSascha Wildner }
38660cbfa66cSDaniel Fojt fflush(stdout);
38670cbfa66cSDaniel Fojt r = sshsk_enroll(type, sk_provider, sk_device,
38680cbfa66cSDaniel Fojt sk_application == NULL ? "ssh:" : sk_application,
38690cbfa66cSDaniel Fojt sk_user, sk_flags, passphrase, challenge,
38700cbfa66cSDaniel Fojt &private, attest);
38710cbfa66cSDaniel Fojt if (r == 0)
38720cbfa66cSDaniel Fojt break;
3873ee116499SAntonio Huete Jimenez if (r == SSH_ERR_KEY_BAD_PERMISSIONS &&
3874ee116499SAntonio Huete Jimenez (sk_flags & SSH_SK_RESIDENT_KEY) != 0 &&
3875ee116499SAntonio Huete Jimenez (sk_flags & SSH_SK_FORCE_OPERATION) == 0 &&
3876ee116499SAntonio Huete Jimenez confirm_sk_overwrite(sk_application, sk_user)) {
3877ee116499SAntonio Huete Jimenez sk_flags |= SSH_SK_FORCE_OPERATION;
3878ee116499SAntonio Huete Jimenez continue;
3879ee116499SAntonio Huete Jimenez }
38800cbfa66cSDaniel Fojt if (r != SSH_ERR_KEY_WRONG_PASSPHRASE)
388150a69bb5SSascha Wildner fatal_r(r, "Key enrollment failed");
388250a69bb5SSascha Wildner else if (passphrase != NULL) {
38830cbfa66cSDaniel Fojt error("PIN incorrect");
38840cbfa66cSDaniel Fojt freezero(passphrase, strlen(passphrase));
38850cbfa66cSDaniel Fojt passphrase = NULL;
38860cbfa66cSDaniel Fojt }
3887ee116499SAntonio Huete Jimenez if (++i >= 3)
38880cbfa66cSDaniel Fojt fatal("Too many incorrect PINs");
38890cbfa66cSDaniel Fojt passphrase = read_passphrase("Enter PIN for "
38900cbfa66cSDaniel Fojt "authenticator: ", RP_ALLOW_STDIN);
38910cbfa66cSDaniel Fojt }
38920cbfa66cSDaniel Fojt if (passphrase != NULL) {
38930cbfa66cSDaniel Fojt freezero(passphrase, strlen(passphrase));
38940cbfa66cSDaniel Fojt passphrase = NULL;
38950cbfa66cSDaniel Fojt }
38960cbfa66cSDaniel Fojt break;
38970cbfa66cSDaniel Fojt default:
3898e9778795SPeter Avalos if ((r = sshkey_generate(type, bits, &private)) != 0)
3899ce74bacaSMatthew Dillon fatal("sshkey_generate failed");
39000cbfa66cSDaniel Fojt break;
39010cbfa66cSDaniel Fojt }
3902e9778795SPeter Avalos if ((r = sshkey_from_private(private, &public)) != 0)
390350a69bb5SSascha Wildner fatal_r(r, "sshkey_from_private");
390418de8d7fSPeter Avalos
390518de8d7fSPeter Avalos if (!have_identity)
390618de8d7fSPeter Avalos ask_filename(pw, "Enter file in which to save the key");
390718de8d7fSPeter Avalos
390818de8d7fSPeter Avalos /* Create ~/.ssh directory if it doesn't already exist. */
390950a69bb5SSascha Wildner hostfile_create_user_ssh_dir(identity_file, !quiet);
391050a69bb5SSascha Wildner
391118de8d7fSPeter Avalos /* If the file already exists, ask the user to confirm. */
39120cbfa66cSDaniel Fojt if (!confirm_overwrite(identity_file))
391318de8d7fSPeter Avalos exit(1);
391418de8d7fSPeter Avalos
39150cbfa66cSDaniel Fojt /* Determine the passphrase for the private key */
39160cbfa66cSDaniel Fojt passphrase = private_key_passphrase();
391718de8d7fSPeter Avalos if (identity_comment) {
391818de8d7fSPeter Avalos strlcpy(comment, identity_comment, sizeof(comment));
391918de8d7fSPeter Avalos } else {
3920cb5eb4f1SPeter Avalos /* Create default comment field for the passphrase. */
392118de8d7fSPeter Avalos snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname);
392218de8d7fSPeter Avalos }
392318de8d7fSPeter Avalos
392418de8d7fSPeter Avalos /* Save the key with the given passphrase and comment. */
39250cbfa66cSDaniel Fojt if ((r = sshkey_save_private(private, identity_file, passphrase,
39260cbfa66cSDaniel Fojt comment, private_key_format, openssh_format_cipher, rounds)) != 0) {
392750a69bb5SSascha Wildner error_r(r, "Saving key \"%s\" failed", identity_file);
39280cbfa66cSDaniel Fojt freezero(passphrase, strlen(passphrase));
392918de8d7fSPeter Avalos exit(1);
393018de8d7fSPeter Avalos }
39310cbfa66cSDaniel Fojt freezero(passphrase, strlen(passphrase));
3932e9778795SPeter Avalos sshkey_free(private);
393318de8d7fSPeter Avalos
39340cbfa66cSDaniel Fojt if (!quiet) {
39350cbfa66cSDaniel Fojt printf("Your identification has been saved in %s\n",
39360cbfa66cSDaniel Fojt identity_file);
39370cbfa66cSDaniel Fojt }
393818de8d7fSPeter Avalos
393918de8d7fSPeter Avalos strlcat(identity_file, ".pub", sizeof(identity_file));
394050a69bb5SSascha Wildner if ((r = sshkey_save_public(public, identity_file, comment)) != 0)
394150a69bb5SSascha Wildner fatal_r(r, "Unable to save public key to %s", identity_file);
394218de8d7fSPeter Avalos
394318de8d7fSPeter Avalos if (!quiet) {
3944e9778795SPeter Avalos fp = sshkey_fingerprint(public, fingerprint_hash,
3945e9778795SPeter Avalos SSH_FP_DEFAULT);
3946e9778795SPeter Avalos ra = sshkey_fingerprint(public, fingerprint_hash,
394718de8d7fSPeter Avalos SSH_FP_RANDOMART);
3948e9778795SPeter Avalos if (fp == NULL || ra == NULL)
3949e9778795SPeter Avalos fatal("sshkey_fingerprint failed");
39500cbfa66cSDaniel Fojt printf("Your public key has been saved in %s\n",
395118de8d7fSPeter Avalos identity_file);
395218de8d7fSPeter Avalos printf("The key fingerprint is:\n");
395318de8d7fSPeter Avalos printf("%s %s\n", fp, comment);
395418de8d7fSPeter Avalos printf("The key's randomart image is:\n");
395518de8d7fSPeter Avalos printf("%s\n", ra);
395636e94dc5SPeter Avalos free(ra);
395736e94dc5SPeter Avalos free(fp);
395818de8d7fSPeter Avalos }
395918de8d7fSPeter Avalos
396050a69bb5SSascha Wildner if (sk_attestation_path != NULL)
396150a69bb5SSascha Wildner save_attestation(attest, sk_attestation_path);
396250a69bb5SSascha Wildner
39630cbfa66cSDaniel Fojt sshbuf_free(attest);
3964e9778795SPeter Avalos sshkey_free(public);
39650cbfa66cSDaniel Fojt
396618de8d7fSPeter Avalos exit(0);
396718de8d7fSPeter Avalos }
3968