1323124Sdes/* $OpenBSD: ssh-keygen.c,v 1.290 2016/05/02 09:36:42 djm Exp $ */
257429Smarkm/*
357429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi>
457429Smarkm * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
557429Smarkm *                    All rights reserved
657429Smarkm * Identity and host key generation and maintenance.
765668Skris *
865668Skris * As far as I am concerned, the code I have written for this software
965668Skris * can be used freely for any purpose.  Any derived versions of this
1065668Skris * software must be clearly marked as such, and if the derived work is
1165668Skris * incompatible with the protocol description in the RFC file, it must be
1265668Skris * called by a name other than "ssh" or "Secure Shell".
1357429Smarkm */
1457429Smarkm
1557429Smarkm#include "includes.h"
1657429Smarkm
17162852Sdes#include <sys/types.h>
18162852Sdes#include <sys/socket.h>
19162852Sdes#include <sys/stat.h>
20162852Sdes
21295367Sdes#ifdef WITH_OPENSSL
2260573Skris#include <openssl/evp.h>
2360573Skris#include <openssl/pem.h>
24181111Sdes#include "openbsd-compat/openssl-compat.h"
25295367Sdes#endif
2660573Skris
27162852Sdes#include <errno.h>
28162852Sdes#include <fcntl.h>
29162852Sdes#include <netdb.h>
30162852Sdes#ifdef HAVE_PATHS_H
31162852Sdes# include <paths.h>
32162852Sdes#endif
33162852Sdes#include <pwd.h>
34162852Sdes#include <stdarg.h>
35162852Sdes#include <stdio.h>
36162852Sdes#include <stdlib.h>
37162852Sdes#include <string.h>
38162852Sdes#include <unistd.h>
39295367Sdes#include <limits.h>
40162852Sdes
4157429Smarkm#include "xmalloc.h"
42295367Sdes#include "sshkey.h"
4360573Skris#include "rsa.h"
4460573Skris#include "authfile.h"
4560573Skris#include "uuencode.h"
46295367Sdes#include "sshbuf.h"
4776259Sgreen#include "pathnames.h"
4876259Sgreen#include "log.h"
49137015Sdes#include "misc.h"
50146998Sdes#include "match.h"
51146998Sdes#include "hostfile.h"
52162852Sdes#include "dns.h"
53248619Sdes#include "ssh.h"
54204917Sdes#include "ssh2.h"
55295367Sdes#include "ssherr.h"
56204917Sdes#include "ssh-pkcs11.h"
57248619Sdes#include "atomicio.h"
58248619Sdes#include "krl.h"
59295367Sdes#include "digest.h"
6092555Sdes
61295367Sdes#ifdef WITH_OPENSSL
62295367Sdes# define DEFAULT_KEY_TYPE_NAME "rsa"
63295367Sdes#else
64295367Sdes# define DEFAULT_KEY_TYPE_NAME "ed25519"
65295367Sdes#endif
66295367Sdes
67157016Sdes/* Number of bits in the RSA/DSA key.  This value can be set on the command line. */
68157016Sdes#define DEFAULT_BITS		2048
69157016Sdes#define DEFAULT_BITS_DSA	1024
70221420Sdes#define DEFAULT_BITS_ECDSA	256
71157016Sdesu_int32_t bits = 0;
7257429Smarkm
7357429Smarkm/*
7457429Smarkm * Flag indicating that we just want to change the passphrase.  This can be
7557429Smarkm * set on the command line.
7657429Smarkm */
7757429Smarkmint change_passphrase = 0;
7857429Smarkm
7957429Smarkm/*
8057429Smarkm * Flag indicating that we just want to change the comment.  This can be set
8157429Smarkm * on the command line.
8257429Smarkm */
8357429Smarkmint change_comment = 0;
8457429Smarkm
8557429Smarkmint quiet = 0;
8657429Smarkm
87181111Sdesint log_level = SYSLOG_LEVEL_INFO;
88181111Sdes
89146998Sdes/* Flag indicating that we want to hash a known_hosts file */
90146998Sdesint hash_hosts = 0;
91146998Sdes/* Flag indicating that we want lookup a host in known_hosts file */
92146998Sdesint find_host = 0;
93146998Sdes/* Flag indicating that we want to delete a host from a known_hosts file */
94146998Sdesint delete_host = 0;
95146998Sdes
96204917Sdes/* Flag indicating that we want to show the contents of a certificate */
97204917Sdesint show_cert = 0;
98204917Sdes
9957429Smarkm/* Flag indicating that we just want to see the key fingerprint */
10057429Smarkmint print_fingerprint = 0;
10176259Sgreenint print_bubblebabble = 0;
10257429Smarkm
103295367Sdes/* Hash algorithm to use for fingerprints. */
104295367Sdesint fingerprint_hash = SSH_FP_HASH_DEFAULT;
105295367Sdes
10657429Smarkm/* The identity file name, given on the command line or entered by the user. */
10757429Smarkmchar identity_file[1024];
10857429Smarkmint have_identity = 0;
10957429Smarkm
11057429Smarkm/* This is set to the passphrase if given on the command line. */
11157429Smarkmchar *identity_passphrase = NULL;
11257429Smarkm
11357429Smarkm/* This is set to the new passphrase if given on the command line. */
11457429Smarkmchar *identity_new_passphrase = NULL;
11557429Smarkm
11657429Smarkm/* This is set to the new comment if given on the command line. */
11757429Smarkmchar *identity_comment = NULL;
11857429Smarkm
119204917Sdes/* Path to CA key when certifying keys. */
120204917Sdeschar *ca_key_path = NULL;
121204917Sdes
122215116Sdes/* Certificate serial number */
123248619Sdesunsigned long long cert_serial = 0;
124215116Sdes
125204917Sdes/* Key type when certifying */
126204917Sdesu_int cert_key_type = SSH2_CERT_TYPE_USER;
127204917Sdes
128204917Sdes/* "key ID" of signed key */
129204917Sdeschar *cert_key_id = NULL;
130204917Sdes
131204917Sdes/* Comma-separated list of principal names for certifying keys */
132204917Sdeschar *cert_principals = NULL;
133204917Sdes
134204917Sdes/* Validity period for certificates */
135204917Sdesu_int64_t cert_valid_from = 0;
136204917Sdesu_int64_t cert_valid_to = ~0ULL;
137204917Sdes
138215116Sdes/* Certificate options */
139215116Sdes#define CERTOPT_X_FWD	(1)
140215116Sdes#define CERTOPT_AGENT_FWD	(1<<1)
141215116Sdes#define CERTOPT_PORT_FWD	(1<<2)
142215116Sdes#define CERTOPT_PTY		(1<<3)
143215116Sdes#define CERTOPT_USER_RC	(1<<4)
144215116Sdes#define CERTOPT_DEFAULT	(CERTOPT_X_FWD|CERTOPT_AGENT_FWD| \
145215116Sdes			 CERTOPT_PORT_FWD|CERTOPT_PTY|CERTOPT_USER_RC)
146215116Sdesu_int32_t certflags_flags = CERTOPT_DEFAULT;
147215116Sdeschar *certflags_command = NULL;
148215116Sdeschar *certflags_src_addr = NULL;
149204917Sdes
150215116Sdes/* Conversion to/from various formats */
151215116Sdesint convert_to = 0;
152215116Sdesint convert_from = 0;
153215116Sdesenum {
154215116Sdes	FMT_RFC4716,
155215116Sdes	FMT_PKCS8,
156215116Sdes	FMT_PEM
157215116Sdes} convert_format = FMT_RFC4716;
15860573Skrisint print_public = 0;
159124208Sdesint print_generic = 0;
16060573Skris
16192555Sdeschar *key_type_name = NULL;
16276259Sgreen
163215116Sdes/* Load key from this PKCS#11 provider */
164215116Sdeschar *pkcs11provider = NULL;
165215116Sdes
166262566Sdes/* Use new OpenSSH private key format when writing SSH2 keys instead of PEM */
167262566Sdesint use_new_format = 0;
168262566Sdes
169262566Sdes/* Cipher for new-format private keys */
170262566Sdeschar *new_format_cipher = NULL;
171262566Sdes
172262566Sdes/*
173262566Sdes * Number of KDF rounds to derive new format keys /
174262566Sdes * number of primality trials when screening moduli.
175262566Sdes */
176262566Sdesint rounds = 0;
177262566Sdes
17857429Smarkm/* argv0 */
17957429Smarkmextern char *__progname;
18057429Smarkm
181295367Sdeschar hostname[NI_MAXHOST];
18260573Skris
183295367Sdes#ifdef WITH_OPENSSL
184137015Sdes/* moduli.c */
185149749Sdesint gen_candidates(FILE *, u_int32_t, u_int32_t, BIGNUM *);
186240075Sdesint prime_test(FILE *, FILE *, u_int32_t, u_int32_t, char *, unsigned long,
187240075Sdes    unsigned long);
188295367Sdes#endif
189137015Sdes
19092555Sdesstatic void
191295367Sdestype_bits_valid(int type, const char *name, u_int32_t *bitsp)
192226046Sdes{
193295367Sdes#ifdef WITH_OPENSSL
194295367Sdes	u_int maxbits, nid;
195295367Sdes#endif
196226046Sdes
197295367Sdes	if (type == KEY_UNSPEC)
198295367Sdes		fatal("unknown key type %s", key_type_name);
199226046Sdes	if (*bitsp == 0) {
200295367Sdes#ifdef WITH_OPENSSL
201226046Sdes		if (type == KEY_DSA)
202226046Sdes			*bitsp = DEFAULT_BITS_DSA;
203295367Sdes		else if (type == KEY_ECDSA) {
204295367Sdes			if (name != NULL &&
205295367Sdes			    (nid = sshkey_ecdsa_nid_from_name(name)) > 0)
206295367Sdes				*bitsp = sshkey_curve_nid_to_bits(nid);
207295367Sdes			if (*bitsp == 0)
208295367Sdes				*bitsp = DEFAULT_BITS_ECDSA;
209295367Sdes		} else
210295367Sdes#endif
211226046Sdes			*bitsp = DEFAULT_BITS;
212226046Sdes	}
213295367Sdes#ifdef WITH_OPENSSL
214226046Sdes	maxbits = (type == KEY_DSA) ?
215226046Sdes	    OPENSSL_DSA_MAX_MODULUS_BITS : OPENSSL_RSA_MAX_MODULUS_BITS;
216295367Sdes	if (*bitsp > maxbits)
217295367Sdes		fatal("key bits exceeds maximum %d", maxbits);
218226046Sdes	if (type == KEY_DSA && *bitsp != 1024)
219226046Sdes		fatal("DSA keys must be 1024 bits");
220295367Sdes	else if (type != KEY_ECDSA && type != KEY_ED25519 && *bitsp < 1024)
221295367Sdes		fatal("Key must at least be 1024 bits");
222295367Sdes	else if (type == KEY_ECDSA && sshkey_ecdsa_bits_to_nid(*bitsp) == -1)
223226046Sdes		fatal("Invalid ECDSA key length - valid lengths are "
224226046Sdes		    "256, 384 or 521 bits");
225295367Sdes#endif
226226046Sdes}
227226046Sdes
228226046Sdesstatic void
22957429Smarkmask_filename(struct passwd *pw, const char *prompt)
23057429Smarkm{
23157429Smarkm	char buf[1024];
23276259Sgreen	char *name = NULL;
23376259Sgreen
23492555Sdes	if (key_type_name == NULL)
23576259Sgreen		name = _PATH_SSH_CLIENT_ID_RSA;
236162852Sdes	else {
237295367Sdes		switch (sshkey_type_from_name(key_type_name)) {
23892555Sdes		case KEY_RSA1:
23992555Sdes			name = _PATH_SSH_CLIENT_IDENTITY;
24092555Sdes			break;
241215116Sdes		case KEY_DSA_CERT:
24292555Sdes		case KEY_DSA:
24392555Sdes			name = _PATH_SSH_CLIENT_ID_DSA;
24492555Sdes			break;
245221420Sdes#ifdef OPENSSL_HAS_ECC
246221420Sdes		case KEY_ECDSA_CERT:
247221420Sdes		case KEY_ECDSA:
248221420Sdes			name = _PATH_SSH_CLIENT_ID_ECDSA;
249221420Sdes			break;
250221420Sdes#endif
251215116Sdes		case KEY_RSA_CERT:
25292555Sdes		case KEY_RSA:
25392555Sdes			name = _PATH_SSH_CLIENT_ID_RSA;
25492555Sdes			break;
255262566Sdes		case KEY_ED25519:
256262566Sdes		case KEY_ED25519_CERT:
257262566Sdes			name = _PATH_SSH_CLIENT_ID_ED25519;
258262566Sdes			break;
25992555Sdes		default:
260295367Sdes			fatal("bad key type");
26192555Sdes		}
262162852Sdes	}
263295367Sdes	snprintf(identity_file, sizeof(identity_file),
264295367Sdes	    "%s/%s", pw->pw_dir, name);
265295367Sdes	printf("%s (%s): ", prompt, identity_file);
266295367Sdes	fflush(stdout);
26757429Smarkm	if (fgets(buf, sizeof(buf), stdin) == NULL)
26857429Smarkm		exit(1);
269181111Sdes	buf[strcspn(buf, "\n")] = '\0';
27057429Smarkm	if (strcmp(buf, "") != 0)
27157429Smarkm		strlcpy(identity_file, buf, sizeof(identity_file));
27257429Smarkm	have_identity = 1;
27357429Smarkm}
27457429Smarkm
275295367Sdesstatic struct sshkey *
27692555Sdesload_identity(char *filename)
27760573Skris{
27876259Sgreen	char *pass;
279295367Sdes	struct sshkey *prv;
280295367Sdes	int r;
28176259Sgreen
282295367Sdes	if ((r = sshkey_load_private(filename, "", &prv, NULL)) == 0)
283295367Sdes		return prv;
284295367Sdes	if (r != SSH_ERR_KEY_WRONG_PASSPHRASE)
285295367Sdes		fatal("Load key \"%s\": %s", filename, ssh_err(r));
286295367Sdes	if (identity_passphrase)
287295367Sdes		pass = xstrdup(identity_passphrase);
288295367Sdes	else
289295367Sdes		pass = read_passphrase("Enter passphrase: ", RP_ALLOW_STDIN);
290295367Sdes	r = sshkey_load_private(filename, pass, &prv, NULL);
291295367Sdes	explicit_bzero(pass, strlen(pass));
292295367Sdes	free(pass);
293295367Sdes	if (r != 0)
294295367Sdes		fatal("Load key \"%s\": %s", filename, ssh_err(r));
29576259Sgreen	return prv;
29660573Skris}
29760573Skris
29869587Sgreen#define SSH_COM_PUBLIC_BEGIN		"---- BEGIN SSH2 PUBLIC KEY ----"
29998675Sdes#define SSH_COM_PUBLIC_END		"---- END SSH2 PUBLIC KEY ----"
30069587Sgreen#define SSH_COM_PRIVATE_BEGIN		"---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----"
30176259Sgreen#define	SSH_COM_PRIVATE_KEY_MAGIC	0x3f6ff9eb
30260573Skris
303295367Sdes#ifdef WITH_OPENSSL
30492555Sdesstatic void
305295367Sdesdo_convert_to_ssh2(struct passwd *pw, struct sshkey *k)
30660573Skris{
307295367Sdes	size_t len;
30876259Sgreen	u_char *blob;
309204917Sdes	char comment[61];
310295367Sdes	int r;
31160573Skris
312295367Sdes	if (k->type == KEY_RSA1)
313295367Sdes		fatal("version 1 keys are not supported");
314295367Sdes	if ((r = sshkey_to_blob(k, &blob, &len)) != 0)
315295367Sdes		fatal("key_to_blob failed: %s", ssh_err(r));
316204917Sdes	/* Comment + surrounds must fit into 72 chars (RFC 4716 sec 3.3) */
317204917Sdes	snprintf(comment, sizeof(comment),
318204917Sdes	    "%u-bit %s, converted by %s@%s from OpenSSH",
319295367Sdes	    sshkey_size(k), sshkey_type(k),
32060573Skris	    pw->pw_name, hostname);
321204917Sdes
322204917Sdes	fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN);
323204917Sdes	fprintf(stdout, "Comment: \"%s\"\n", comment);
32460573Skris	dump_base64(stdout, blob, len);
32569587Sgreen	fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END);
326295367Sdes	sshkey_free(k);
327255767Sdes	free(blob);
32860573Skris	exit(0);
32960573Skris}
33060573Skris
33192555Sdesstatic void
332295367Sdesdo_convert_to_pkcs8(struct sshkey *k)
333215116Sdes{
334295367Sdes	switch (sshkey_type_plain(k->type)) {
335240075Sdes	case KEY_RSA1:
336215116Sdes	case KEY_RSA:
337215116Sdes		if (!PEM_write_RSA_PUBKEY(stdout, k->rsa))
338215116Sdes			fatal("PEM_write_RSA_PUBKEY failed");
339215116Sdes		break;
340215116Sdes	case KEY_DSA:
341215116Sdes		if (!PEM_write_DSA_PUBKEY(stdout, k->dsa))
342215116Sdes			fatal("PEM_write_DSA_PUBKEY failed");
343215116Sdes		break;
344221420Sdes#ifdef OPENSSL_HAS_ECC
345221420Sdes	case KEY_ECDSA:
346221420Sdes		if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa))
347221420Sdes			fatal("PEM_write_EC_PUBKEY failed");
348221420Sdes		break;
349221420Sdes#endif
350215116Sdes	default:
351295367Sdes		fatal("%s: unsupported key type %s", __func__, sshkey_type(k));
352215116Sdes	}
353215116Sdes	exit(0);
354215116Sdes}
355215116Sdes
356215116Sdesstatic void
357295367Sdesdo_convert_to_pem(struct sshkey *k)
358215116Sdes{
359295367Sdes	switch (sshkey_type_plain(k->type)) {
360240075Sdes	case KEY_RSA1:
361215116Sdes	case KEY_RSA:
362215116Sdes		if (!PEM_write_RSAPublicKey(stdout, k->rsa))
363215116Sdes			fatal("PEM_write_RSAPublicKey failed");
364215116Sdes		break;
365215116Sdes#if notyet /* OpenSSH 0.9.8 lacks this function */
366215116Sdes	case KEY_DSA:
367215116Sdes		if (!PEM_write_DSAPublicKey(stdout, k->dsa))
368215116Sdes			fatal("PEM_write_DSAPublicKey failed");
369215116Sdes		break;
370215116Sdes#endif
371221420Sdes	/* XXX ECDSA? */
372215116Sdes	default:
373295367Sdes		fatal("%s: unsupported key type %s", __func__, sshkey_type(k));
374215116Sdes	}
375215116Sdes	exit(0);
376215116Sdes}
377215116Sdes
378215116Sdesstatic void
379215116Sdesdo_convert_to(struct passwd *pw)
380215116Sdes{
381295367Sdes	struct sshkey *k;
382215116Sdes	struct stat st;
383295367Sdes	int r;
384215116Sdes
385215116Sdes	if (!have_identity)
386215116Sdes		ask_filename(pw, "Enter file in which the key is");
387215116Sdes	if (stat(identity_file, &st) < 0)
388215116Sdes		fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
389295367Sdes	if ((r = sshkey_load_public(identity_file, &k, NULL)) != 0)
390295367Sdes		k = load_identity(identity_file);
391215116Sdes	switch (convert_format) {
392215116Sdes	case FMT_RFC4716:
393215116Sdes		do_convert_to_ssh2(pw, k);
394215116Sdes		break;
395215116Sdes	case FMT_PKCS8:
396215116Sdes		do_convert_to_pkcs8(k);
397215116Sdes		break;
398215116Sdes	case FMT_PEM:
399215116Sdes		do_convert_to_pem(k);
400215116Sdes		break;
401215116Sdes	default:
402215116Sdes		fatal("%s: unknown key format %d", __func__, convert_format);
403215116Sdes	}
404215116Sdes	exit(0);
405215116Sdes}
406215116Sdes
407295367Sdes/*
408295367Sdes * This is almost exactly the bignum1 encoding, but with 32 bit for length
409295367Sdes * instead of 16.
410295367Sdes */
411215116Sdesstatic void
412295367Sdesbuffer_get_bignum_bits(struct sshbuf *b, BIGNUM *value)
41369587Sgreen{
414295367Sdes	u_int bytes, bignum_bits;
415295367Sdes	int r;
41676259Sgreen
417295367Sdes	if ((r = sshbuf_get_u32(b, &bignum_bits)) != 0)
418295367Sdes		fatal("%s: buffer error: %s", __func__, ssh_err(r));
419295367Sdes	bytes = (bignum_bits + 7) / 8;
420295367Sdes	if (sshbuf_len(b) < bytes)
421295367Sdes		fatal("%s: input buffer too small: need %d have %zu",
422295367Sdes		    __func__, bytes, sshbuf_len(b));
423295367Sdes	if (BN_bin2bn(sshbuf_ptr(b), bytes, value) == NULL)
424295367Sdes		fatal("%s: BN_bin2bn failed", __func__);
425295367Sdes	if ((r = sshbuf_consume(b, bytes)) != 0)
426295367Sdes		fatal("%s: buffer error: %s", __func__, ssh_err(r));
42769587Sgreen}
42869587Sgreen
429295367Sdesstatic struct sshkey *
43092555Sdesdo_convert_private_ssh2_from_blob(u_char *blob, u_int blen)
43169587Sgreen{
432295367Sdes	struct sshbuf *b;
433295367Sdes	struct sshkey *key = NULL;
43469587Sgreen	char *type, *cipher;
435295367Sdes	u_char e1, e2, e3, *sig = NULL, data[] = "abcde12345";
436295367Sdes	int r, rlen, ktype;
437295367Sdes	u_int magic, i1, i2, i3, i4;
438295367Sdes	size_t slen;
43992555Sdes	u_long e;
44069587Sgreen
441295367Sdes	if ((b = sshbuf_from(blob, blen)) == NULL)
442295367Sdes		fatal("%s: sshbuf_from failed", __func__);
443295367Sdes	if ((r = sshbuf_get_u32(b, &magic)) != 0)
444295367Sdes		fatal("%s: buffer error: %s", __func__, ssh_err(r));
44569587Sgreen
44669587Sgreen	if (magic != SSH_COM_PRIVATE_KEY_MAGIC) {
447295367Sdes		error("bad magic 0x%x != 0x%x", magic,
448295367Sdes		    SSH_COM_PRIVATE_KEY_MAGIC);
449295367Sdes		sshbuf_free(b);
45069587Sgreen		return NULL;
45169587Sgreen	}
452295367Sdes	if ((r = sshbuf_get_u32(b, &i1)) != 0 ||
453295367Sdes	    (r = sshbuf_get_cstring(b, &type, NULL)) != 0 ||
454295367Sdes	    (r = sshbuf_get_cstring(b, &cipher, NULL)) != 0 ||
455295367Sdes	    (r = sshbuf_get_u32(b, &i2)) != 0 ||
456295367Sdes	    (r = sshbuf_get_u32(b, &i3)) != 0 ||
457295367Sdes	    (r = sshbuf_get_u32(b, &i4)) != 0)
458295367Sdes		fatal("%s: buffer error: %s", __func__, ssh_err(r));
459181111Sdes	debug("ignore (%d %d %d %d)", i1, i2, i3, i4);
46069587Sgreen	if (strcmp(cipher, "none") != 0) {
46169587Sgreen		error("unsupported cipher %s", cipher);
462255767Sdes		free(cipher);
463295367Sdes		sshbuf_free(b);
464255767Sdes		free(type);
46569587Sgreen		return NULL;
46669587Sgreen	}
467255767Sdes	free(cipher);
46869587Sgreen
46976259Sgreen	if (strstr(type, "dsa")) {
47076259Sgreen		ktype = KEY_DSA;
47176259Sgreen	} else if (strstr(type, "rsa")) {
47276259Sgreen		ktype = KEY_RSA;
47376259Sgreen	} else {
474295367Sdes		sshbuf_free(b);
475255767Sdes		free(type);
47669587Sgreen		return NULL;
47769587Sgreen	}
478295367Sdes	if ((key = sshkey_new_private(ktype)) == NULL)
479295367Sdes		fatal("key_new_private failed");
480255767Sdes	free(type);
48176259Sgreen
48276259Sgreen	switch (key->type) {
48376259Sgreen	case KEY_DSA:
484295367Sdes		buffer_get_bignum_bits(b, key->dsa->p);
485295367Sdes		buffer_get_bignum_bits(b, key->dsa->g);
486295367Sdes		buffer_get_bignum_bits(b, key->dsa->q);
487295367Sdes		buffer_get_bignum_bits(b, key->dsa->pub_key);
488295367Sdes		buffer_get_bignum_bits(b, key->dsa->priv_key);
48976259Sgreen		break;
49076259Sgreen	case KEY_RSA:
491295367Sdes		if ((r = sshbuf_get_u8(b, &e1)) != 0 ||
492295367Sdes		    (e1 < 30 && (r = sshbuf_get_u8(b, &e2)) != 0) ||
493295367Sdes		    (e1 < 30 && (r = sshbuf_get_u8(b, &e3)) != 0))
494295367Sdes			fatal("%s: buffer error: %s", __func__, ssh_err(r));
495295367Sdes		e = e1;
49692555Sdes		debug("e %lx", e);
49792555Sdes		if (e < 30) {
49892555Sdes			e <<= 8;
499295367Sdes			e += e2;
50092555Sdes			debug("e %lx", e);
50192555Sdes			e <<= 8;
502295367Sdes			e += e3;
50392555Sdes			debug("e %lx", e);
50492555Sdes		}
50592555Sdes		if (!BN_set_word(key->rsa->e, e)) {
506295367Sdes			sshbuf_free(b);
507295367Sdes			sshkey_free(key);
50876259Sgreen			return NULL;
50976259Sgreen		}
510295367Sdes		buffer_get_bignum_bits(b, key->rsa->d);
511295367Sdes		buffer_get_bignum_bits(b, key->rsa->n);
512295367Sdes		buffer_get_bignum_bits(b, key->rsa->iqmp);
513295367Sdes		buffer_get_bignum_bits(b, key->rsa->q);
514295367Sdes		buffer_get_bignum_bits(b, key->rsa->p);
515295367Sdes		if ((r = rsa_generate_additional_parameters(key->rsa)) != 0)
516295367Sdes			fatal("generate RSA parameters failed: %s", ssh_err(r));
51776259Sgreen		break;
51876259Sgreen	}
519295367Sdes	rlen = sshbuf_len(b);
52092555Sdes	if (rlen != 0)
52176259Sgreen		error("do_convert_private_ssh2_from_blob: "
52276259Sgreen		    "remaining bytes in key blob %d", rlen);
523295367Sdes	sshbuf_free(b);
52476259Sgreen
52592555Sdes	/* try the key */
526296781Sdes	if (sshkey_sign(key, &sig, &slen, data, sizeof(data), NULL, 0) != 0 ||
527295367Sdes	    sshkey_verify(key, sig, slen, data, sizeof(data), 0) != 0) {
528295367Sdes		sshkey_free(key);
529295367Sdes		free(sig);
530295367Sdes		return NULL;
531295367Sdes	}
532255767Sdes	free(sig);
53369587Sgreen	return key;
53469587Sgreen}
53569587Sgreen
536162852Sdesstatic int
537162852Sdesget_line(FILE *fp, char *line, size_t len)
538162852Sdes{
539162852Sdes	int c;
540162852Sdes	size_t pos = 0;
541162852Sdes
542162852Sdes	line[0] = '\0';
543162852Sdes	while ((c = fgetc(fp)) != EOF) {
544295367Sdes		if (pos >= len - 1)
545295367Sdes			fatal("input line too long.");
546162852Sdes		switch (c) {
547162852Sdes		case '\r':
548162852Sdes			c = fgetc(fp);
549295367Sdes			if (c != EOF && c != '\n' && ungetc(c, fp) == EOF)
550295367Sdes				fatal("unget: %s", strerror(errno));
551162852Sdes			return pos;
552162852Sdes		case '\n':
553162852Sdes			return pos;
554162852Sdes		}
555162852Sdes		line[pos++] = c;
556162852Sdes		line[pos] = '\0';
557162852Sdes	}
558181111Sdes	/* We reached EOF */
559181111Sdes	return -1;
560162852Sdes}
561162852Sdes
56292555Sdesstatic void
563295367Sdesdo_convert_from_ssh2(struct passwd *pw, struct sshkey **k, int *private)
56460573Skris{
565295367Sdes	int r, blen, escaped = 0;
56698675Sdes	u_int len;
567162852Sdes	char line[1024];
56892555Sdes	u_char blob[8096];
56960573Skris	char encoded[8096];
57060573Skris	FILE *fp;
57160573Skris
572215116Sdes	if ((fp = fopen(identity_file, "r")) == NULL)
573215116Sdes		fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
57460573Skris	encoded[0] = '\0';
575162852Sdes	while ((blen = get_line(fp, line, sizeof(line))) != -1) {
576255767Sdes		if (blen > 0 && line[blen - 1] == '\\')
57760573Skris			escaped++;
57860573Skris		if (strncmp(line, "----", 4) == 0 ||
57960573Skris		    strstr(line, ": ") != NULL) {
58069587Sgreen			if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL)
581215116Sdes				*private = 1;
58292555Sdes			if (strstr(line, " END ") != NULL) {
58392555Sdes				break;
58492555Sdes			}
58576259Sgreen			/* fprintf(stderr, "ignore: %s", line); */
58660573Skris			continue;
58760573Skris		}
58860573Skris		if (escaped) {
58960573Skris			escaped--;
59076259Sgreen			/* fprintf(stderr, "escaped: %s", line); */
59160573Skris			continue;
59260573Skris		}
59360573Skris		strlcat(encoded, line, sizeof(encoded));
59460573Skris	}
59598675Sdes	len = strlen(encoded);
59698675Sdes	if (((len % 4) == 3) &&
59798675Sdes	    (encoded[len-1] == '=') &&
59898675Sdes	    (encoded[len-2] == '=') &&
59998675Sdes	    (encoded[len-3] == '='))
60098675Sdes		encoded[len-3] = '\0';
60192555Sdes	blen = uudecode(encoded, blob, sizeof(blob));
602295367Sdes	if (blen < 0)
603295367Sdes		fatal("uudecode failed.");
604295367Sdes	if (*private)
605295367Sdes		*k = do_convert_private_ssh2_from_blob(blob, blen);
606295367Sdes	else if ((r = sshkey_from_blob(blob, blen, k)) != 0)
607295367Sdes		fatal("decode blob failed: %s", ssh_err(r));
608215116Sdes	fclose(fp);
609215116Sdes}
610215116Sdes
611215116Sdesstatic void
612295367Sdesdo_convert_from_pkcs8(struct sshkey **k, int *private)
613215116Sdes{
614215116Sdes	EVP_PKEY *pubkey;
615215116Sdes	FILE *fp;
616215116Sdes
617215116Sdes	if ((fp = fopen(identity_file, "r")) == NULL)
618215116Sdes		fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
619215116Sdes	if ((pubkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
620215116Sdes		fatal("%s: %s is not a recognised public key format", __func__,
621215116Sdes		    identity_file);
622215116Sdes	}
623215116Sdes	fclose(fp);
624215116Sdes	switch (EVP_PKEY_type(pubkey->type)) {
625215116Sdes	case EVP_PKEY_RSA:
626295367Sdes		if ((*k = sshkey_new(KEY_UNSPEC)) == NULL)
627295367Sdes			fatal("sshkey_new failed");
628215116Sdes		(*k)->type = KEY_RSA;
629215116Sdes		(*k)->rsa = EVP_PKEY_get1_RSA(pubkey);
630215116Sdes		break;
631215116Sdes	case EVP_PKEY_DSA:
632295367Sdes		if ((*k = sshkey_new(KEY_UNSPEC)) == NULL)
633295367Sdes			fatal("sshkey_new failed");
634215116Sdes		(*k)->type = KEY_DSA;
635215116Sdes		(*k)->dsa = EVP_PKEY_get1_DSA(pubkey);
636215116Sdes		break;
637221420Sdes#ifdef OPENSSL_HAS_ECC
638221420Sdes	case EVP_PKEY_EC:
639295367Sdes		if ((*k = sshkey_new(KEY_UNSPEC)) == NULL)
640295367Sdes			fatal("sshkey_new failed");
641221420Sdes		(*k)->type = KEY_ECDSA;
642221420Sdes		(*k)->ecdsa = EVP_PKEY_get1_EC_KEY(pubkey);
643295367Sdes		(*k)->ecdsa_nid = sshkey_ecdsa_key_to_nid((*k)->ecdsa);
644221420Sdes		break;
645221420Sdes#endif
646215116Sdes	default:
647215116Sdes		fatal("%s: unsupported pubkey type %d", __func__,
648215116Sdes		    EVP_PKEY_type(pubkey->type));
649215116Sdes	}
650215116Sdes	EVP_PKEY_free(pubkey);
651215116Sdes	return;
652215116Sdes}
653215116Sdes
654215116Sdesstatic void
655295367Sdesdo_convert_from_pem(struct sshkey **k, int *private)
656215116Sdes{
657215116Sdes	FILE *fp;
658215116Sdes	RSA *rsa;
659215116Sdes#ifdef notyet
660215116Sdes	DSA *dsa;
661215116Sdes#endif
662215116Sdes
663215116Sdes	if ((fp = fopen(identity_file, "r")) == NULL)
664215116Sdes		fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
665215116Sdes	if ((rsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL)) != NULL) {
666295367Sdes		if ((*k = sshkey_new(KEY_UNSPEC)) == NULL)
667295367Sdes			fatal("sshkey_new failed");
668215116Sdes		(*k)->type = KEY_RSA;
669215116Sdes		(*k)->rsa = rsa;
670215116Sdes		fclose(fp);
671215116Sdes		return;
672215116Sdes	}
673215116Sdes#if notyet /* OpenSSH 0.9.8 lacks this function */
674215116Sdes	rewind(fp);
675215116Sdes	if ((dsa = PEM_read_DSAPublicKey(fp, NULL, NULL, NULL)) != NULL) {
676295367Sdes		if ((*k = sshkey_new(KEY_UNSPEC)) == NULL)
677295367Sdes			fatal("sshkey_new failed");
678215116Sdes		(*k)->type = KEY_DSA;
679215116Sdes		(*k)->dsa = dsa;
680215116Sdes		fclose(fp);
681215116Sdes		return;
682215116Sdes	}
683221420Sdes	/* XXX ECDSA */
684215116Sdes#endif
685215116Sdes	fatal("%s: unrecognised raw private key format", __func__);
686215116Sdes}
687215116Sdes
688215116Sdesstatic void
689215116Sdesdo_convert_from(struct passwd *pw)
690215116Sdes{
691295367Sdes	struct sshkey *k = NULL;
692295367Sdes	int r, private = 0, ok = 0;
693215116Sdes	struct stat st;
694215116Sdes
695215116Sdes	if (!have_identity)
696215116Sdes		ask_filename(pw, "Enter file in which the key is");
697215116Sdes	if (stat(identity_file, &st) < 0)
698215116Sdes		fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
699215116Sdes
700215116Sdes	switch (convert_format) {
701215116Sdes	case FMT_RFC4716:
702215116Sdes		do_convert_from_ssh2(pw, &k, &private);
703215116Sdes		break;
704215116Sdes	case FMT_PKCS8:
705215116Sdes		do_convert_from_pkcs8(&k, &private);
706215116Sdes		break;
707215116Sdes	case FMT_PEM:
708215116Sdes		do_convert_from_pem(&k, &private);
709215116Sdes		break;
710215116Sdes	default:
711215116Sdes		fatal("%s: unknown key format %d", __func__, convert_format);
712215116Sdes	}
713215116Sdes
714295367Sdes	if (!private) {
715295367Sdes		if ((r = sshkey_write(k, stdout)) == 0)
716295367Sdes			ok = 1;
717215116Sdes		if (ok)
718215116Sdes			fprintf(stdout, "\n");
719295367Sdes	} else {
720215116Sdes		switch (k->type) {
721215116Sdes		case KEY_DSA:
722215116Sdes			ok = PEM_write_DSAPrivateKey(stdout, k->dsa, NULL,
723215116Sdes			    NULL, 0, NULL, NULL);
724215116Sdes			break;
725221420Sdes#ifdef OPENSSL_HAS_ECC
726221420Sdes		case KEY_ECDSA:
727221420Sdes			ok = PEM_write_ECPrivateKey(stdout, k->ecdsa, NULL,
728221420Sdes			    NULL, 0, NULL, NULL);
729221420Sdes			break;
730221420Sdes#endif
731215116Sdes		case KEY_RSA:
732215116Sdes			ok = PEM_write_RSAPrivateKey(stdout, k->rsa, NULL,
733215116Sdes			    NULL, 0, NULL, NULL);
734215116Sdes			break;
735215116Sdes		default:
736215116Sdes			fatal("%s: unsupported key type %s", __func__,
737295367Sdes			    sshkey_type(k));
738215116Sdes		}
739215116Sdes	}
740215116Sdes
741295367Sdes	if (!ok)
742295367Sdes		fatal("key write failed");
743295367Sdes	sshkey_free(k);
74460573Skris	exit(0);
74560573Skris}
746295367Sdes#endif
74760573Skris
74892555Sdesstatic void
74960573Skrisdo_print_public(struct passwd *pw)
75060573Skris{
751295367Sdes	struct sshkey *prv;
75260573Skris	struct stat st;
753295367Sdes	int r;
75460573Skris
75560573Skris	if (!have_identity)
75660573Skris		ask_filename(pw, "Enter file in which the key is");
757295367Sdes	if (stat(identity_file, &st) < 0)
758295367Sdes		fatal("%s: %s", identity_file, strerror(errno));
75992555Sdes	prv = load_identity(identity_file);
760295367Sdes	if ((r = sshkey_write(prv, stdout)) != 0)
761295367Sdes		error("key_write failed: %s", ssh_err(r));
762295367Sdes	sshkey_free(prv);
76360573Skris	fprintf(stdout, "\n");
76460573Skris	exit(0);
76560573Skris}
76660573Skris
76792555Sdesstatic void
768215116Sdesdo_download(struct passwd *pw)
76992555Sdes{
770204917Sdes#ifdef ENABLE_PKCS11
771295367Sdes	struct sshkey **keys = NULL;
772204917Sdes	int i, nkeys;
773295367Sdes	enum sshkey_fp_rep rep;
774295367Sdes	int fptype;
775248619Sdes	char *fp, *ra;
77692555Sdes
777295367Sdes	fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash;
778295367Sdes	rep =    print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT;
779248619Sdes
780204917Sdes	pkcs11_init(0);
781204917Sdes	nkeys = pkcs11_add_provider(pkcs11provider, NULL, &keys);
782204917Sdes	if (nkeys <= 0)
783204917Sdes		fatal("cannot read public key from pkcs11");
784204917Sdes	for (i = 0; i < nkeys; i++) {
785248619Sdes		if (print_fingerprint) {
786295367Sdes			fp = sshkey_fingerprint(keys[i], fptype, rep);
787295367Sdes			ra = sshkey_fingerprint(keys[i], fingerprint_hash,
788248619Sdes			    SSH_FP_RANDOMART);
789295367Sdes			if (fp == NULL || ra == NULL)
790295367Sdes				fatal("%s: sshkey_fingerprint fail", __func__);
791295367Sdes			printf("%u %s %s (PKCS11 key)\n", sshkey_size(keys[i]),
792295367Sdes			    fp, sshkey_type(keys[i]));
793248619Sdes			if (log_level >= SYSLOG_LEVEL_VERBOSE)
794248619Sdes				printf("%s\n", ra);
795255767Sdes			free(ra);
796255767Sdes			free(fp);
797248619Sdes		} else {
798295367Sdes			(void) sshkey_write(keys[i], stdout); /* XXX check */
799248619Sdes			fprintf(stdout, "\n");
800248619Sdes		}
801295367Sdes		sshkey_free(keys[i]);
80298675Sdes	}
803255767Sdes	free(keys);
804204917Sdes	pkcs11_terminate();
80592555Sdes	exit(0);
806204917Sdes#else
807204917Sdes	fatal("no pkcs11 support");
808204917Sdes#endif /* ENABLE_PKCS11 */
80992555Sdes}
81092555Sdes
811296781Sdesstatic struct sshkey *
812296781Sdestry_read_key(char **cpp)
813296781Sdes{
814296781Sdes	struct sshkey *ret;
815296781Sdes	int r;
816296781Sdes
817296781Sdes	if ((ret = sshkey_new(KEY_RSA1)) == NULL)
818296781Sdes		fatal("sshkey_new failed");
819296781Sdes	/* Try RSA1 */
820296781Sdes	if ((r = sshkey_read(ret, cpp)) == 0)
821296781Sdes		return ret;
822296781Sdes	/* Try modern */
823296781Sdes	sshkey_free(ret);
824296781Sdes	if ((ret = sshkey_new(KEY_UNSPEC)) == NULL)
825296781Sdes		fatal("sshkey_new failed");
826296781Sdes	if ((r = sshkey_read(ret, cpp)) == 0)
827296781Sdes		return ret;
828296781Sdes	/* Not a key */
829296781Sdes	sshkey_free(ret);
830296781Sdes	return NULL;
831296781Sdes}
832296781Sdes
83392555Sdesstatic void
834296781Sdesfingerprint_one_key(const struct sshkey *public, const char *comment)
83557429Smarkm{
836296781Sdes	char *fp = NULL, *ra = NULL;
837295367Sdes	enum sshkey_fp_rep rep;
838295367Sdes	int fptype;
83957429Smarkm
840295367Sdes	fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash;
841295367Sdes	rep =    print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT;
842296781Sdes	fp = sshkey_fingerprint(public, fptype, rep);
843296781Sdes	ra = sshkey_fingerprint(public, fingerprint_hash, SSH_FP_RANDOMART);
844296781Sdes	if (fp == NULL || ra == NULL)
845296781Sdes		fatal("%s: sshkey_fingerprint failed", __func__);
846296781Sdes	printf("%u %s %s (%s)\n", sshkey_size(public), fp,
847296781Sdes	    comment ? comment : "no comment", sshkey_type(public));
848296781Sdes	if (log_level >= SYSLOG_LEVEL_VERBOSE)
849296781Sdes		printf("%s\n", ra);
850296781Sdes	free(ra);
851296781Sdes	free(fp);
852296781Sdes}
853296781Sdes
854296781Sdesstatic void
855296781Sdesfingerprint_private(const char *path)
856296781Sdes{
857296781Sdes	struct stat st;
858296781Sdes	char *comment = NULL;
859296781Sdes	struct sshkey *public = NULL;
860296781Sdes	int r;
861296781Sdes
862295367Sdes	if (stat(identity_file, &st) < 0)
863296781Sdes		fatal("%s: %s", path, strerror(errno));
864296781Sdes	if ((r = sshkey_load_public(path, &public, &comment)) != 0) {
865296781Sdes		debug("load public \"%s\": %s", path, ssh_err(r));
866296781Sdes		if ((r = sshkey_load_private(path, NULL,
867296781Sdes		    &public, &comment)) != 0) {
868296781Sdes			debug("load private \"%s\": %s", path, ssh_err(r));
869296781Sdes			fatal("%s is not a key file.", path);
870296781Sdes		}
87157429Smarkm	}
87257429Smarkm
873296781Sdes	fingerprint_one_key(public, comment);
874296781Sdes	sshkey_free(public);
875296781Sdes	free(comment);
876296781Sdes}
877215116Sdes
878296781Sdesstatic void
879296781Sdesdo_fingerprint(struct passwd *pw)
880296781Sdes{
881296781Sdes	FILE *f;
882296781Sdes	struct sshkey *public = NULL;
883296781Sdes	char *comment = NULL, *cp, *ep, line[SSH_MAX_PUBKEY_BYTES];
884296781Sdes	int i, invalid = 1;
885296781Sdes	const char *path;
886323124Sdes	u_long lnum = 0;
887296781Sdes
888296781Sdes	if (!have_identity)
889296781Sdes		ask_filename(pw, "Enter file in which the key is");
890296781Sdes	path = identity_file;
891296781Sdes
892296781Sdes	if (strcmp(identity_file, "-") == 0) {
893296781Sdes		f = stdin;
894296781Sdes		path = "(stdin)";
895296781Sdes	} else if ((f = fopen(path, "r")) == NULL)
896296781Sdes		fatal("%s: %s: %s", __progname, path, strerror(errno));
897296781Sdes
898296781Sdes	while (read_keyfile_line(f, path, line, sizeof(line), &lnum) == 0) {
899296781Sdes		cp = line;
900296781Sdes		cp[strcspn(cp, "\n")] = '\0';
901296781Sdes		/* Trim leading space and comments */
902296781Sdes		cp = line + strspn(line, " \t");
903296781Sdes		if (*cp == '#' || *cp == '\0')
904215116Sdes			continue;
905296781Sdes
906296781Sdes		/*
907296781Sdes		 * Input may be plain keys, private keys, authorized_keys
908296781Sdes		 * or known_hosts.
909296781Sdes		 */
910296781Sdes
911296781Sdes		/*
912296781Sdes		 * Try private keys first. Assume a key is private if
913296781Sdes		 * "SSH PRIVATE KEY" appears on the first line and we're
914296781Sdes		 * not reading from stdin (XXX support private keys on stdin).
915296781Sdes		 */
916296781Sdes		if (lnum == 1 && strcmp(identity_file, "-") != 0 &&
917296781Sdes		    strstr(cp, "PRIVATE KEY") != NULL) {
918296781Sdes			fclose(f);
919296781Sdes			fingerprint_private(path);
920296781Sdes			exit(0);
921215116Sdes		}
922296781Sdes
923296781Sdes		/*
924296781Sdes		 * If it's not a private key, then this must be prepared to
925296781Sdes		 * accept a public key prefixed with a hostname or options.
926296781Sdes		 * Try a bare key first, otherwise skip the leading stuff.
927296781Sdes		 */
928296781Sdes		if ((public = try_read_key(&cp)) == NULL) {
929296781Sdes			i = strtol(cp, &ep, 10);
930296781Sdes			if (i == 0 || ep == NULL ||
931296781Sdes			    (*ep != ' ' && *ep != '\t')) {
932296781Sdes				int quoted = 0;
933296781Sdes
934296781Sdes				comment = cp;
935296781Sdes				for (; *cp && (quoted || (*cp != ' ' &&
936296781Sdes				    *cp != '\t')); cp++) {
937296781Sdes					if (*cp == '\\' && cp[1] == '"')
938296781Sdes						cp++;	/* Skip both */
939296781Sdes					else if (*cp == '"')
940296781Sdes						quoted = !quoted;
941296781Sdes				}
942296781Sdes				if (!*cp)
943296781Sdes					continue;
944296781Sdes				*cp++ = '\0';
945296781Sdes			}
946296781Sdes		}
947296781Sdes		/* Retry after parsing leading hostname/key options */
948296781Sdes		if (public == NULL && (public = try_read_key(&cp)) == NULL) {
949323124Sdes			debug("%s:%lu: not a public key", path, lnum);
950215116Sdes			continue;
951215116Sdes		}
952215116Sdes
953296781Sdes		/* Find trailing comment, if any */
954296781Sdes		for (; *cp == ' ' || *cp == '\t'; cp++)
955215116Sdes			;
956296781Sdes		if (*cp != '\0' && *cp != '#')
957215116Sdes			comment = cp;
958296781Sdes
959296781Sdes		fingerprint_one_key(public, comment);
960295367Sdes		sshkey_free(public);
961296781Sdes		invalid = 0; /* One good key in the file is sufficient */
96257429Smarkm	}
963215116Sdes	fclose(f);
964215116Sdes
965295367Sdes	if (invalid)
966296781Sdes		fatal("%s is not a public key file.", path);
96757429Smarkm	exit(0);
96857429Smarkm}
96957429Smarkm
970146998Sdesstatic void
971226046Sdesdo_gen_all_hostkeys(struct passwd *pw)
972226046Sdes{
973226046Sdes	struct {
974226046Sdes		char *key_type;
975226046Sdes		char *key_type_display;
976226046Sdes		char *path;
977226046Sdes	} key_types[] = {
978295367Sdes#ifdef WITH_OPENSSL
979295367Sdes#ifdef WITH_SSH1
980226046Sdes		{ "rsa1", "RSA1", _PATH_HOST_KEY_FILE },
981295367Sdes#endif /* WITH_SSH1 */
982226046Sdes		{ "rsa", "RSA" ,_PATH_HOST_RSA_KEY_FILE },
983226046Sdes		{ "dsa", "DSA", _PATH_HOST_DSA_KEY_FILE },
984240075Sdes#ifdef OPENSSL_HAS_ECC
985226046Sdes		{ "ecdsa", "ECDSA",_PATH_HOST_ECDSA_KEY_FILE },
986295367Sdes#endif /* OPENSSL_HAS_ECC */
987295367Sdes#endif /* WITH_OPENSSL */
988262566Sdes		{ "ed25519", "ED25519",_PATH_HOST_ED25519_KEY_FILE },
989226046Sdes		{ NULL, NULL, NULL }
990226046Sdes	};
991226046Sdes
992226046Sdes	int first = 0;
993226046Sdes	struct stat st;
994295367Sdes	struct sshkey *private, *public;
995226046Sdes	char comment[1024];
996295367Sdes	int i, type, fd, r;
997226046Sdes	FILE *f;
998226046Sdes
999226046Sdes	for (i = 0; key_types[i].key_type; i++) {
1000226046Sdes		if (stat(key_types[i].path, &st) == 0)
1001226046Sdes			continue;
1002226046Sdes		if (errno != ENOENT) {
1003295367Sdes			error("Could not stat %s: %s", key_types[i].path,
1004226046Sdes			    strerror(errno));
1005226046Sdes			first = 0;
1006226046Sdes			continue;
1007226046Sdes		}
1008226046Sdes
1009226046Sdes		if (first == 0) {
1010226046Sdes			first = 1;
1011226046Sdes			printf("%s: generating new host keys: ", __progname);
1012226046Sdes		}
1013226046Sdes		printf("%s ", key_types[i].key_type_display);
1014226046Sdes		fflush(stdout);
1015295367Sdes		type = sshkey_type_from_name(key_types[i].key_type);
1016226046Sdes		strlcpy(identity_file, key_types[i].path, sizeof(identity_file));
1017226046Sdes		bits = 0;
1018295367Sdes		type_bits_valid(type, NULL, &bits);
1019295367Sdes		if ((r = sshkey_generate(type, bits, &private)) != 0) {
1020295367Sdes			error("key_generate failed: %s", ssh_err(r));
1021226046Sdes			first = 0;
1022226046Sdes			continue;
1023226046Sdes		}
1024295367Sdes		if ((r = sshkey_from_private(private, &public)) != 0)
1025295367Sdes			fatal("sshkey_from_private failed: %s", ssh_err(r));
1026226046Sdes		snprintf(comment, sizeof comment, "%s@%s", pw->pw_name,
1027226046Sdes		    hostname);
1028295367Sdes		if ((r = sshkey_save_private(private, identity_file, "",
1029295367Sdes		    comment, use_new_format, new_format_cipher, rounds)) != 0) {
1030295367Sdes			error("Saving key \"%s\" failed: %s",
1031295367Sdes			    identity_file, ssh_err(r));
1032295367Sdes			sshkey_free(private);
1033295367Sdes			sshkey_free(public);
1034226046Sdes			first = 0;
1035226046Sdes			continue;
1036226046Sdes		}
1037295367Sdes		sshkey_free(private);
1038226046Sdes		strlcat(identity_file, ".pub", sizeof(identity_file));
1039226046Sdes		fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
1040226046Sdes		if (fd == -1) {
1041295367Sdes			error("Could not save your public key in %s",
1042226046Sdes			    identity_file);
1043295367Sdes			sshkey_free(public);
1044226046Sdes			first = 0;
1045226046Sdes			continue;
1046226046Sdes		}
1047226046Sdes		f = fdopen(fd, "w");
1048226046Sdes		if (f == NULL) {
1049295367Sdes			error("fdopen %s failed", identity_file);
1050295367Sdes			close(fd);
1051295367Sdes			sshkey_free(public);
1052226046Sdes			first = 0;
1053226046Sdes			continue;
1054226046Sdes		}
1055295367Sdes		if ((r = sshkey_write(public, f)) != 0) {
1056295367Sdes			error("write key failed: %s", ssh_err(r));
1057295367Sdes			fclose(f);
1058295367Sdes			sshkey_free(public);
1059226046Sdes			first = 0;
1060226046Sdes			continue;
1061226046Sdes		}
1062226046Sdes		fprintf(f, " %s\n", comment);
1063226046Sdes		fclose(f);
1064295367Sdes		sshkey_free(public);
1065226046Sdes
1066226046Sdes	}
1067226046Sdes	if (first != 0)
1068226046Sdes		printf("\n");
1069226046Sdes}
1070226046Sdes
1071295367Sdesstruct known_hosts_ctx {
1072295367Sdes	const char *host;	/* Hostname searched for in find/delete case */
1073295367Sdes	FILE *out;		/* Output file, stdout for find_hosts case */
1074295367Sdes	int has_unhashed;	/* When hashing, original had unhashed hosts */
1075295367Sdes	int found_key;		/* For find/delete, host was found */
1076295367Sdes	int invalid;		/* File contained invalid items; don't delete */
1077295367Sdes};
1078295367Sdes
1079295367Sdesstatic int
1080295367Sdesknown_hosts_hash(struct hostkey_foreach_line *l, void *_ctx)
1081146998Sdes{
1082295367Sdes	struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx;
1083295367Sdes	char *hashed, *cp, *hosts, *ohosts;
1084295367Sdes	int has_wild = l->hosts && strcspn(l->hosts, "*?!") != strlen(l->hosts);
1085181111Sdes
1086295367Sdes	switch (l->status) {
1087295367Sdes	case HKF_STATUS_OK:
1088295367Sdes	case HKF_STATUS_MATCHED:
1089295367Sdes		/*
1090295367Sdes		 * Don't hash hosts already already hashed, with wildcard
1091295367Sdes		 * characters or a CA/revocation marker.
1092295367Sdes		 */
1093295367Sdes		if ((l->match & HKF_MATCH_HOST_HASHED) != 0 ||
1094295367Sdes		    has_wild || l->marker != MRK_NONE) {
1095295367Sdes			fprintf(ctx->out, "%s\n", l->line);
1096295367Sdes			if (has_wild && !find_host) {
1097295367Sdes				logit("%s:%ld: ignoring host name "
1098295367Sdes				    "with wildcard: %.64s", l->path,
1099295367Sdes				    l->linenum, l->hosts);
1100295367Sdes			}
1101295367Sdes			return 0;
1102295367Sdes		}
1103295367Sdes		/*
1104295367Sdes		 * Split any comma-separated hostnames from the host list,
1105295367Sdes		 * hash and store separately.
1106295367Sdes		 */
1107295367Sdes		ohosts = hosts = xstrdup(l->hosts);
1108295367Sdes		while ((cp = strsep(&hosts, ",")) != NULL && *cp != '\0') {
1109295367Sdes			if ((hashed = host_hash(cp, NULL, 0)) == NULL)
1110295367Sdes				fatal("hash_host failed");
1111295367Sdes			fprintf(ctx->out, "%s %s\n", hashed, l->rawkey);
1112295367Sdes			ctx->has_unhashed = 1;
1113295367Sdes		}
1114295367Sdes		free(ohosts);
1115295367Sdes		return 0;
1116295367Sdes	case HKF_STATUS_INVALID:
1117295367Sdes		/* Retain invalid lines, but mark file as invalid. */
1118295367Sdes		ctx->invalid = 1;
1119295367Sdes		logit("%s:%ld: invalid line", l->path, l->linenum);
1120295367Sdes		/* FALLTHROUGH */
1121295367Sdes	default:
1122295367Sdes		fprintf(ctx->out, "%s\n", l->line);
1123295367Sdes		return 0;
1124181111Sdes	}
1125295367Sdes	/* NOTREACHED */
1126295367Sdes	return -1;
1127146998Sdes}
1128146998Sdes
1129295367Sdesstatic int
1130295367Sdesknown_hosts_find_delete(struct hostkey_foreach_line *l, void *_ctx)
1131295367Sdes{
1132295367Sdes	struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx;
1133295367Sdes	enum sshkey_fp_rep rep;
1134295367Sdes	int fptype;
1135295367Sdes	char *fp;
1136295367Sdes
1137295367Sdes	fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash;
1138295367Sdes	rep =    print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT;
1139295367Sdes
1140295367Sdes	if (l->status == HKF_STATUS_MATCHED) {
1141295367Sdes		if (delete_host) {
1142295367Sdes			if (l->marker != MRK_NONE) {
1143295367Sdes				/* Don't remove CA and revocation lines */
1144295367Sdes				fprintf(ctx->out, "%s\n", l->line);
1145295367Sdes			} else {
1146295367Sdes				/*
1147295367Sdes				 * Hostname matches and has no CA/revoke
1148295367Sdes				 * marker, delete it by *not* writing the
1149295367Sdes				 * line to ctx->out.
1150295367Sdes				 */
1151295367Sdes				ctx->found_key = 1;
1152295367Sdes				if (!quiet)
1153295367Sdes					printf("# Host %s found: line %ld\n",
1154295367Sdes					    ctx->host, l->linenum);
1155295367Sdes			}
1156295367Sdes			return 0;
1157295367Sdes		} else if (find_host) {
1158295367Sdes			ctx->found_key = 1;
1159295367Sdes			if (!quiet) {
1160295367Sdes				printf("# Host %s found: line %ld %s\n",
1161295367Sdes				    ctx->host,
1162295367Sdes				    l->linenum, l->marker == MRK_CA ? "CA" :
1163295367Sdes				    (l->marker == MRK_REVOKE ? "REVOKED" : ""));
1164295367Sdes			}
1165295367Sdes			if (hash_hosts)
1166295367Sdes				known_hosts_hash(l, ctx);
1167295367Sdes			else if (print_fingerprint) {
1168295367Sdes				fp = sshkey_fingerprint(l->key, fptype, rep);
1169295367Sdes				printf("%s %s %s %s\n", ctx->host,
1170295367Sdes				    sshkey_type(l->key), fp, l->comment);
1171295367Sdes				free(fp);
1172295367Sdes			} else
1173295367Sdes				fprintf(ctx->out, "%s\n", l->line);
1174295367Sdes			return 0;
1175295367Sdes		}
1176295367Sdes	} else if (delete_host) {
1177295367Sdes		/* Retain non-matching hosts when deleting */
1178295367Sdes		if (l->status == HKF_STATUS_INVALID) {
1179295367Sdes			ctx->invalid = 1;
1180295367Sdes			logit("%s:%ld: invalid line", l->path, l->linenum);
1181295367Sdes		}
1182295367Sdes		fprintf(ctx->out, "%s\n", l->line);
1183295367Sdes	}
1184295367Sdes	return 0;
1185295367Sdes}
1186295367Sdes
1187146998Sdesstatic void
1188146998Sdesdo_known_hosts(struct passwd *pw, const char *name)
1189146998Sdes{
1190295367Sdes	char *cp, tmp[PATH_MAX], old[PATH_MAX];
1191295367Sdes	int r, fd, oerrno, inplace = 0;
1192295367Sdes	struct known_hosts_ctx ctx;
1193295367Sdes	u_int foreach_options;
1194146998Sdes
1195146998Sdes	if (!have_identity) {
1196146998Sdes		cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid);
1197146998Sdes		if (strlcpy(identity_file, cp, sizeof(identity_file)) >=
1198146998Sdes		    sizeof(identity_file))
1199146998Sdes			fatal("Specified known hosts path too long");
1200255767Sdes		free(cp);
1201146998Sdes		have_identity = 1;
1202146998Sdes	}
1203146998Sdes
1204295367Sdes	memset(&ctx, 0, sizeof(ctx));
1205295367Sdes	ctx.out = stdout;
1206295367Sdes	ctx.host = name;
1207295367Sdes
1208146998Sdes	/*
1209146998Sdes	 * Find hosts goes to stdout, hash and deletions happen in-place
1210146998Sdes	 * A corner case is ssh-keygen -HF foo, which should go to stdout
1211146998Sdes	 */
1212146998Sdes	if (!find_host && (hash_hosts || delete_host)) {
1213146998Sdes		if (strlcpy(tmp, identity_file, sizeof(tmp)) >= sizeof(tmp) ||
1214146998Sdes		    strlcat(tmp, ".XXXXXXXXXX", sizeof(tmp)) >= sizeof(tmp) ||
1215146998Sdes		    strlcpy(old, identity_file, sizeof(old)) >= sizeof(old) ||
1216146998Sdes		    strlcat(old, ".old", sizeof(old)) >= sizeof(old))
1217146998Sdes			fatal("known_hosts path too long");
1218146998Sdes		umask(077);
1219295367Sdes		if ((fd = mkstemp(tmp)) == -1)
1220146998Sdes			fatal("mkstemp: %s", strerror(errno));
1221295367Sdes		if ((ctx.out = fdopen(fd, "w")) == NULL) {
1222295367Sdes			oerrno = errno;
1223146998Sdes			unlink(tmp);
1224295367Sdes			fatal("fdopen: %s", strerror(oerrno));
1225146998Sdes		}
1226146998Sdes		inplace = 1;
1227146998Sdes	}
1228146998Sdes
1229295367Sdes	/* XXX support identity_file == "-" for stdin */
1230295367Sdes	foreach_options = find_host ? HKF_WANT_MATCH : 0;
1231295367Sdes	foreach_options |= print_fingerprint ? HKF_WANT_PARSE_KEY : 0;
1232295367Sdes	if ((r = hostkeys_foreach(identity_file,
1233295367Sdes	    hash_hosts ? known_hosts_hash : known_hosts_find_delete, &ctx,
1234296781Sdes	    name, NULL, foreach_options)) != 0) {
1235296781Sdes		if (inplace)
1236296781Sdes			unlink(tmp);
1237295367Sdes		fatal("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r));
1238296781Sdes	}
1239146998Sdes
1240295367Sdes	if (inplace)
1241295367Sdes		fclose(ctx.out);
1242204917Sdes
1243295367Sdes	if (ctx.invalid) {
1244295367Sdes		error("%s is not a valid known_hosts file.", identity_file);
1245146998Sdes		if (inplace) {
1246295367Sdes			error("Not replacing existing known_hosts "
1247295367Sdes			    "file because of errors");
1248146998Sdes			unlink(tmp);
1249146998Sdes		}
1250146998Sdes		exit(1);
1251295367Sdes	} else if (delete_host && !ctx.found_key) {
1252295367Sdes		logit("Host %s not found in %s", name, identity_file);
1253295367Sdes		if (inplace)
1254295367Sdes			unlink(tmp);
1255295367Sdes	} else if (inplace) {
1256146998Sdes		/* Backup existing file */
1257146998Sdes		if (unlink(old) == -1 && errno != ENOENT)
1258146998Sdes			fatal("unlink %.100s: %s", old, strerror(errno));
1259146998Sdes		if (link(identity_file, old) == -1)
1260146998Sdes			fatal("link %.100s to %.100s: %s", identity_file, old,
1261146998Sdes			    strerror(errno));
1262146998Sdes		/* Move new one into place */
1263146998Sdes		if (rename(tmp, identity_file) == -1) {
1264146998Sdes			error("rename\"%s\" to \"%s\": %s", tmp, identity_file,
1265146998Sdes			    strerror(errno));
1266146998Sdes			unlink(tmp);
1267146998Sdes			unlink(old);
1268146998Sdes			exit(1);
1269146998Sdes		}
1270146998Sdes
1271295367Sdes		printf("%s updated.\n", identity_file);
1272295367Sdes		printf("Original contents retained as %s\n", old);
1273295367Sdes		if (ctx.has_unhashed) {
1274295367Sdes			logit("WARNING: %s contains unhashed entries", old);
1275295367Sdes			logit("Delete this file to ensure privacy "
1276295367Sdes			    "of hostnames");
1277146998Sdes		}
1278146998Sdes	}
1279146998Sdes
1280295367Sdes	exit (find_host && !ctx.found_key);
1281146998Sdes}
1282146998Sdes
128357429Smarkm/*
128457429Smarkm * Perform changing a passphrase.  The argument is the passwd structure
128557429Smarkm * for the current user.
128657429Smarkm */
128792555Sdesstatic void
128857429Smarkmdo_change_passphrase(struct passwd *pw)
128957429Smarkm{
129057429Smarkm	char *comment;
129157429Smarkm	char *old_passphrase, *passphrase1, *passphrase2;
129257429Smarkm	struct stat st;
1293295367Sdes	struct sshkey *private;
1294295367Sdes	int r;
129557429Smarkm
129657429Smarkm	if (!have_identity)
129757429Smarkm		ask_filename(pw, "Enter file in which the key is");
1298295367Sdes	if (stat(identity_file, &st) < 0)
1299295367Sdes		fatal("%s: %s", identity_file, strerror(errno));
130057429Smarkm	/* Try to load the file with empty passphrase. */
1301295367Sdes	r = sshkey_load_private(identity_file, "", &private, &comment);
1302295367Sdes	if (r == SSH_ERR_KEY_WRONG_PASSPHRASE) {
130357429Smarkm		if (identity_passphrase)
130457429Smarkm			old_passphrase = xstrdup(identity_passphrase);
130557429Smarkm		else
130692555Sdes			old_passphrase =
130792555Sdes			    read_passphrase("Enter old passphrase: ",
130892555Sdes			    RP_ALLOW_STDIN);
1309295367Sdes		r = sshkey_load_private(identity_file, old_passphrase,
1310295367Sdes		    &private, &comment);
1311264377Sdes		explicit_bzero(old_passphrase, strlen(old_passphrase));
1312255767Sdes		free(old_passphrase);
1313295367Sdes		if (r != 0)
1314295367Sdes			goto badkey;
1315295367Sdes	} else if (r != 0) {
1316295367Sdes badkey:
1317295367Sdes		fatal("Failed to load key %s: %s", identity_file, ssh_err(r));
131857429Smarkm	}
1319295367Sdes	if (comment)
1320295367Sdes		printf("Key has comment '%s'\n", comment);
132157429Smarkm
132257429Smarkm	/* Ask the new passphrase (twice). */
132357429Smarkm	if (identity_new_passphrase) {
132457429Smarkm		passphrase1 = xstrdup(identity_new_passphrase);
132557429Smarkm		passphrase2 = NULL;
132657429Smarkm	} else {
132757429Smarkm		passphrase1 =
132892555Sdes			read_passphrase("Enter new passphrase (empty for no "
132992555Sdes			    "passphrase): ", RP_ALLOW_STDIN);
133092555Sdes		passphrase2 = read_passphrase("Enter same passphrase again: ",
133192555Sdes		    RP_ALLOW_STDIN);
133257429Smarkm
133357429Smarkm		/* Verify that they are the same. */
133457429Smarkm		if (strcmp(passphrase1, passphrase2) != 0) {
1335264377Sdes			explicit_bzero(passphrase1, strlen(passphrase1));
1336264377Sdes			explicit_bzero(passphrase2, strlen(passphrase2));
1337255767Sdes			free(passphrase1);
1338255767Sdes			free(passphrase2);
133957429Smarkm			printf("Pass phrases do not match.  Try again.\n");
134057429Smarkm			exit(1);
134157429Smarkm		}
134257429Smarkm		/* Destroy the other copy. */
1343264377Sdes		explicit_bzero(passphrase2, strlen(passphrase2));
1344255767Sdes		free(passphrase2);
134557429Smarkm	}
134657429Smarkm
134757429Smarkm	/* Save the file using the new passphrase. */
1348295367Sdes	if ((r = sshkey_save_private(private, identity_file, passphrase1,
1349295367Sdes	    comment, use_new_format, new_format_cipher, rounds)) != 0) {
1350295367Sdes		error("Saving key \"%s\" failed: %s.",
1351295367Sdes		    identity_file, ssh_err(r));
1352264377Sdes		explicit_bzero(passphrase1, strlen(passphrase1));
1353255767Sdes		free(passphrase1);
1354295367Sdes		sshkey_free(private);
1355255767Sdes		free(comment);
135657429Smarkm		exit(1);
135757429Smarkm	}
135857429Smarkm	/* Destroy the passphrase and the copy of the key in memory. */
1359264377Sdes	explicit_bzero(passphrase1, strlen(passphrase1));
1360255767Sdes	free(passphrase1);
1361295367Sdes	sshkey_free(private);		 /* Destroys contents */
1362255767Sdes	free(comment);
136357429Smarkm
136457429Smarkm	printf("Your identification has been saved with the new passphrase.\n");
136557429Smarkm	exit(0);
136657429Smarkm}
136757429Smarkm
136857429Smarkm/*
1369124208Sdes * Print the SSHFP RR.
1370124208Sdes */
1371162852Sdesstatic int
1372162852Sdesdo_print_resource_record(struct passwd *pw, char *fname, char *hname)
1373124208Sdes{
1374295367Sdes	struct sshkey *public;
1375124208Sdes	char *comment = NULL;
1376124208Sdes	struct stat st;
1377295367Sdes	int r;
1378124208Sdes
1379162852Sdes	if (fname == NULL)
1380255767Sdes		fatal("%s: no filename", __func__);
1381162852Sdes	if (stat(fname, &st) < 0) {
1382162852Sdes		if (errno == ENOENT)
1383162852Sdes			return 0;
1384295367Sdes		fatal("%s: %s", fname, strerror(errno));
1385124208Sdes	}
1386295367Sdes	if ((r = sshkey_load_public(fname, &public, &comment)) != 0)
1387295367Sdes		fatal("Failed to read v2 public key from \"%s\": %s.",
1388295367Sdes		    fname, ssh_err(r));
1389295367Sdes	export_dns_rr(hname, public, stdout, print_generic);
1390295367Sdes	sshkey_free(public);
1391295367Sdes	free(comment);
1392295367Sdes	return 1;
1393124208Sdes}
1394124208Sdes
1395124208Sdes/*
139657429Smarkm * Change the comment of a private key file.
139757429Smarkm */
139892555Sdesstatic void
139957429Smarkmdo_change_comment(struct passwd *pw)
140057429Smarkm{
140176259Sgreen	char new_comment[1024], *comment, *passphrase;
1402295367Sdes	struct sshkey *private;
1403295367Sdes	struct sshkey *public;
140457429Smarkm	struct stat st;
140557429Smarkm	FILE *f;
1406295367Sdes	int r, fd;
140757429Smarkm
140857429Smarkm	if (!have_identity)
140957429Smarkm		ask_filename(pw, "Enter file in which the key is");
1410295367Sdes	if (stat(identity_file, &st) < 0)
1411295367Sdes		fatal("%s: %s", identity_file, strerror(errno));
1412295367Sdes	if ((r = sshkey_load_private(identity_file, "",
1413295367Sdes	    &private, &comment)) == 0)
1414295367Sdes		passphrase = xstrdup("");
1415295367Sdes	else if (r != SSH_ERR_KEY_WRONG_PASSPHRASE)
1416295367Sdes		fatal("Cannot load private key \"%s\": %s.",
1417295367Sdes		    identity_file, ssh_err(r));
1418295367Sdes	else {
141957429Smarkm		if (identity_passphrase)
142057429Smarkm			passphrase = xstrdup(identity_passphrase);
142157429Smarkm		else if (identity_new_passphrase)
142257429Smarkm			passphrase = xstrdup(identity_new_passphrase);
142357429Smarkm		else
142492555Sdes			passphrase = read_passphrase("Enter passphrase: ",
142592555Sdes			    RP_ALLOW_STDIN);
142657429Smarkm		/* Try to load using the passphrase. */
1427295367Sdes		if ((r = sshkey_load_private(identity_file, passphrase,
1428295367Sdes		    &private, &comment)) != 0) {
1429264377Sdes			explicit_bzero(passphrase, strlen(passphrase));
1430255767Sdes			free(passphrase);
1431295367Sdes			fatal("Cannot load private key \"%s\": %s.",
1432295367Sdes			    identity_file, ssh_err(r));
143357429Smarkm		}
143457429Smarkm	}
1435296781Sdes
1436296781Sdes	if (private->type != KEY_RSA1 && private->type != KEY_ED25519 &&
1437296781Sdes	    !use_new_format) {
1438296781Sdes		error("Comments are only supported for RSA1 or keys stored in "
1439296781Sdes		    "the new format (-o).");
1440295367Sdes		explicit_bzero(passphrase, strlen(passphrase));
1441295367Sdes		sshkey_free(private);
144276259Sgreen		exit(1);
144392555Sdes	}
144457429Smarkm	printf("Key now has comment '%s'\n", comment);
144557429Smarkm
144657429Smarkm	if (identity_comment) {
144757429Smarkm		strlcpy(new_comment, identity_comment, sizeof(new_comment));
144857429Smarkm	} else {
144957429Smarkm		printf("Enter new comment: ");
145057429Smarkm		fflush(stdout);
145157429Smarkm		if (!fgets(new_comment, sizeof(new_comment), stdin)) {
1452264377Sdes			explicit_bzero(passphrase, strlen(passphrase));
1453295367Sdes			sshkey_free(private);
145457429Smarkm			exit(1);
145557429Smarkm		}
1456181111Sdes		new_comment[strcspn(new_comment, "\n")] = '\0';
145757429Smarkm	}
145857429Smarkm
145957429Smarkm	/* Save the file using the new passphrase. */
1460295367Sdes	if ((r = sshkey_save_private(private, identity_file, passphrase,
1461295367Sdes	    new_comment, use_new_format, new_format_cipher, rounds)) != 0) {
1462295367Sdes		error("Saving key \"%s\" failed: %s",
1463295367Sdes		    identity_file, ssh_err(r));
1464264377Sdes		explicit_bzero(passphrase, strlen(passphrase));
1465255767Sdes		free(passphrase);
1466295367Sdes		sshkey_free(private);
1467255767Sdes		free(comment);
146857429Smarkm		exit(1);
146957429Smarkm	}
1470264377Sdes	explicit_bzero(passphrase, strlen(passphrase));
1471255767Sdes	free(passphrase);
1472295367Sdes	if ((r = sshkey_from_private(private, &public)) != 0)
1473295367Sdes		fatal("key_from_private failed: %s", ssh_err(r));
1474295367Sdes	sshkey_free(private);
147557429Smarkm
147657429Smarkm	strlcat(identity_file, ".pub", sizeof(identity_file));
147776259Sgreen	fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
1478295367Sdes	if (fd == -1)
1479295367Sdes		fatal("Could not save your public key in %s", identity_file);
148076259Sgreen	f = fdopen(fd, "w");
1481295367Sdes	if (f == NULL)
1482295367Sdes		fatal("fdopen %s failed: %s", identity_file, strerror(errno));
1483295367Sdes	if ((r = sshkey_write(public, f)) != 0)
1484295367Sdes		fatal("write key failed: %s", ssh_err(r));
1485295367Sdes	sshkey_free(public);
148660573Skris	fprintf(f, " %s\n", new_comment);
148757429Smarkm	fclose(f);
148857429Smarkm
1489255767Sdes	free(comment);
149057429Smarkm
149157429Smarkm	printf("The comment in your key file has been changed.\n");
149257429Smarkm	exit(0);
149357429Smarkm}
149457429Smarkm
149592555Sdesstatic void
1496295367Sdesadd_flag_option(struct sshbuf *c, const char *name)
1497204917Sdes{
1498295367Sdes	int r;
1499295367Sdes
1500204917Sdes	debug3("%s: %s", __func__, name);
1501295367Sdes	if ((r = sshbuf_put_cstring(c, name)) != 0 ||
1502295367Sdes	    (r = sshbuf_put_string(c, NULL, 0)) != 0)
1503295367Sdes		fatal("%s: buffer error: %s", __func__, ssh_err(r));
1504204917Sdes}
1505204917Sdes
1506204917Sdesstatic void
1507295367Sdesadd_string_option(struct sshbuf *c, const char *name, const char *value)
1508204917Sdes{
1509295367Sdes	struct sshbuf *b;
1510295367Sdes	int r;
1511204917Sdes
1512204917Sdes	debug3("%s: %s=%s", __func__, name, value);
1513295367Sdes	if ((b = sshbuf_new()) == NULL)
1514295367Sdes		fatal("%s: sshbuf_new failed", __func__);
1515295367Sdes	if ((r = sshbuf_put_cstring(b, value)) != 0 ||
1516295367Sdes	    (r = sshbuf_put_cstring(c, name)) != 0 ||
1517295367Sdes	    (r = sshbuf_put_stringb(c, b)) != 0)
1518295367Sdes		fatal("%s: buffer error: %s", __func__, ssh_err(r));
1519204917Sdes
1520295367Sdes	sshbuf_free(b);
1521204917Sdes}
1522204917Sdes
1523215116Sdes#define OPTIONS_CRITICAL	1
1524215116Sdes#define OPTIONS_EXTENSIONS	2
1525204917Sdesstatic void
1526295367Sdesprepare_options_buf(struct sshbuf *c, int which)
1527204917Sdes{
1528295367Sdes	sshbuf_reset(c);
1529215116Sdes	if ((which & OPTIONS_CRITICAL) != 0 &&
1530215116Sdes	    certflags_command != NULL)
1531215116Sdes		add_string_option(c, "force-command", certflags_command);
1532215116Sdes	if ((which & OPTIONS_EXTENSIONS) != 0 &&
1533226046Sdes	    (certflags_flags & CERTOPT_X_FWD) != 0)
1534226046Sdes		add_flag_option(c, "permit-X11-forwarding");
1535226046Sdes	if ((which & OPTIONS_EXTENSIONS) != 0 &&
1536215116Sdes	    (certflags_flags & CERTOPT_AGENT_FWD) != 0)
1537215116Sdes		add_flag_option(c, "permit-agent-forwarding");
1538215116Sdes	if ((which & OPTIONS_EXTENSIONS) != 0 &&
1539215116Sdes	    (certflags_flags & CERTOPT_PORT_FWD) != 0)
1540215116Sdes		add_flag_option(c, "permit-port-forwarding");
1541215116Sdes	if ((which & OPTIONS_EXTENSIONS) != 0 &&
1542215116Sdes	    (certflags_flags & CERTOPT_PTY) != 0)
1543215116Sdes		add_flag_option(c, "permit-pty");
1544215116Sdes	if ((which & OPTIONS_EXTENSIONS) != 0 &&
1545215116Sdes	    (certflags_flags & CERTOPT_USER_RC) != 0)
1546215116Sdes		add_flag_option(c, "permit-user-rc");
1547215116Sdes	if ((which & OPTIONS_CRITICAL) != 0 &&
1548215116Sdes	    certflags_src_addr != NULL)
1549215116Sdes		add_string_option(c, "source-address", certflags_src_addr);
1550204917Sdes}
1551204917Sdes
1552295367Sdesstatic struct sshkey *
1553215116Sdesload_pkcs11_key(char *path)
1554215116Sdes{
1555215116Sdes#ifdef ENABLE_PKCS11
1556295367Sdes	struct sshkey **keys = NULL, *public, *private = NULL;
1557295367Sdes	int r, i, nkeys;
1558215116Sdes
1559295367Sdes	if ((r = sshkey_load_public(path, &public, NULL)) != 0)
1560295367Sdes		fatal("Couldn't load CA public key \"%s\": %s",
1561295367Sdes		    path, ssh_err(r));
1562215116Sdes
1563215116Sdes	nkeys = pkcs11_add_provider(pkcs11provider, identity_passphrase, &keys);
1564215116Sdes	debug3("%s: %d keys", __func__, nkeys);
1565215116Sdes	if (nkeys <= 0)
1566215116Sdes		fatal("cannot read public key from pkcs11");
1567215116Sdes	for (i = 0; i < nkeys; i++) {
1568295367Sdes		if (sshkey_equal_public(public, keys[i])) {
1569215116Sdes			private = keys[i];
1570215116Sdes			continue;
1571215116Sdes		}
1572295367Sdes		sshkey_free(keys[i]);
1573215116Sdes	}
1574255767Sdes	free(keys);
1575295367Sdes	sshkey_free(public);
1576215116Sdes	return private;
1577215116Sdes#else
1578215116Sdes	fatal("no pkcs11 support");
1579215116Sdes#endif /* ENABLE_PKCS11 */
1580215116Sdes}
1581215116Sdes
1582204917Sdesstatic void
1583204917Sdesdo_ca_sign(struct passwd *pw, int argc, char **argv)
1584204917Sdes{
1585295367Sdes	int r, i, fd;
1586204917Sdes	u_int n;
1587295367Sdes	struct sshkey *ca, *public;
1588296781Sdes	char valid[64], *otmp, *tmp, *cp, *out, *comment, **plist = NULL;
1589204917Sdes	FILE *f;
1590204917Sdes
1591295367Sdes#ifdef ENABLE_PKCS11
1592215116Sdes	pkcs11_init(1);
1593295367Sdes#endif
1594204917Sdes	tmp = tilde_expand_filename(ca_key_path, pw->pw_uid);
1595215116Sdes	if (pkcs11provider != NULL) {
1596215116Sdes		if ((ca = load_pkcs11_key(tmp)) == NULL)
1597215116Sdes			fatal("No PKCS#11 key matching %s found", ca_key_path);
1598295367Sdes	} else
1599295367Sdes		ca = load_identity(tmp);
1600255767Sdes	free(tmp);
1601204917Sdes
1602323124Sdes	if (key_type_name != NULL &&
1603323124Sdes	    sshkey_type_from_name(key_type_name) != ca->type)  {
1604323124Sdes		fatal("CA key type %s doesn't match specified %s",
1605323124Sdes		    sshkey_ssh_name(ca), key_type_name);
1606323124Sdes	}
1607323124Sdes
1608204917Sdes	for (i = 0; i < argc; i++) {
1609204917Sdes		/* Split list of principals */
1610204917Sdes		n = 0;
1611204917Sdes		if (cert_principals != NULL) {
1612204917Sdes			otmp = tmp = xstrdup(cert_principals);
1613204917Sdes			plist = NULL;
1614204917Sdes			for (; (cp = strsep(&tmp, ",")) != NULL; n++) {
1615295367Sdes				plist = xreallocarray(plist, n + 1, sizeof(*plist));
1616204917Sdes				if (*(plist[n] = xstrdup(cp)) == '\0')
1617204917Sdes					fatal("Empty principal name");
1618204917Sdes			}
1619255767Sdes			free(otmp);
1620204917Sdes		}
1621204917Sdes
1622204917Sdes		tmp = tilde_expand_filename(argv[i], pw->pw_uid);
1623295367Sdes		if ((r = sshkey_load_public(tmp, &public, &comment)) != 0)
1624295367Sdes			fatal("%s: unable to open \"%s\": %s",
1625295367Sdes			    __func__, tmp, ssh_err(r));
1626221420Sdes		if (public->type != KEY_RSA && public->type != KEY_DSA &&
1627262566Sdes		    public->type != KEY_ECDSA && public->type != KEY_ED25519)
1628204917Sdes			fatal("%s: key \"%s\" type %s cannot be certified",
1629295367Sdes			    __func__, tmp, sshkey_type(public));
1630204917Sdes
1631204917Sdes		/* Prepare certificate to sign */
1632295367Sdes		if ((r = sshkey_to_certified(public)) != 0)
1633295367Sdes			fatal("Could not upgrade key %s to certificate: %s",
1634295367Sdes			    tmp, ssh_err(r));
1635204917Sdes		public->cert->type = cert_key_type;
1636215116Sdes		public->cert->serial = (u_int64_t)cert_serial;
1637204917Sdes		public->cert->key_id = xstrdup(cert_key_id);
1638204917Sdes		public->cert->nprincipals = n;
1639204917Sdes		public->cert->principals = plist;
1640204917Sdes		public->cert->valid_after = cert_valid_from;
1641204917Sdes		public->cert->valid_before = cert_valid_to;
1642295367Sdes		prepare_options_buf(public->cert->critical, OPTIONS_CRITICAL);
1643295367Sdes		prepare_options_buf(public->cert->extensions,
1644295367Sdes		    OPTIONS_EXTENSIONS);
1645295367Sdes		if ((r = sshkey_from_private(ca,
1646295367Sdes		    &public->cert->signature_key)) != 0)
1647295367Sdes			fatal("key_from_private (ca key): %s", ssh_err(r));
1648204917Sdes
1649323124Sdes		if ((r = sshkey_certify(public, ca, key_type_name)) != 0)
1650323124Sdes			fatal("Couldn't certify key %s: %s", tmp, ssh_err(r));
1651204917Sdes
1652204917Sdes		if ((cp = strrchr(tmp, '.')) != NULL && strcmp(cp, ".pub") == 0)
1653204917Sdes			*cp = '\0';
1654204917Sdes		xasprintf(&out, "%s-cert.pub", tmp);
1655255767Sdes		free(tmp);
1656204917Sdes
1657204917Sdes		if ((fd = open(out, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
1658204917Sdes			fatal("Could not open \"%s\" for writing: %s", out,
1659204917Sdes			    strerror(errno));
1660204917Sdes		if ((f = fdopen(fd, "w")) == NULL)
1661204917Sdes			fatal("%s: fdopen: %s", __func__, strerror(errno));
1662295367Sdes		if ((r = sshkey_write(public, f)) != 0)
1663295367Sdes			fatal("Could not write certified key to %s: %s",
1664295367Sdes			    out, ssh_err(r));
1665204917Sdes		fprintf(f, " %s\n", comment);
1666204917Sdes		fclose(f);
1667204917Sdes
1668215116Sdes		if (!quiet) {
1669296781Sdes			sshkey_format_cert_validity(public->cert,
1670296781Sdes			    valid, sizeof(valid));
1671215116Sdes			logit("Signed %s key %s: id \"%s\" serial %llu%s%s "
1672296781Sdes			    "valid %s", sshkey_cert_type(public),
1673221420Sdes			    out, public->cert->key_id,
1674221420Sdes			    (unsigned long long)public->cert->serial,
1675204917Sdes			    cert_principals != NULL ? " for " : "",
1676204917Sdes			    cert_principals != NULL ? cert_principals : "",
1677296781Sdes			    valid);
1678215116Sdes		}
1679204917Sdes
1680295367Sdes		sshkey_free(public);
1681255767Sdes		free(out);
1682204917Sdes	}
1683295367Sdes#ifdef ENABLE_PKCS11
1684215116Sdes	pkcs11_terminate();
1685295367Sdes#endif
1686204917Sdes	exit(0);
1687204917Sdes}
1688204917Sdes
1689204917Sdesstatic u_int64_t
1690204917Sdesparse_relative_time(const char *s, time_t now)
1691204917Sdes{
1692204917Sdes	int64_t mul, secs;
1693204917Sdes
1694204917Sdes	mul = *s == '-' ? -1 : 1;
1695204917Sdes
1696204917Sdes	if ((secs = convtime(s + 1)) == -1)
1697204917Sdes		fatal("Invalid relative certificate time %s", s);
1698204917Sdes	if (mul == -1 && secs > now)
1699204917Sdes		fatal("Certificate time %s cannot be represented", s);
1700204917Sdes	return now + (u_int64_t)(secs * mul);
1701204917Sdes}
1702204917Sdes
1703204917Sdesstatic u_int64_t
1704204917Sdesparse_absolute_time(const char *s)
1705204917Sdes{
1706204917Sdes	struct tm tm;
1707204917Sdes	time_t tt;
1708204917Sdes	char buf[32], *fmt;
1709204917Sdes
1710204917Sdes	/*
1711296781Sdes	 * POSIX strptime says "The application shall ensure that there
1712204917Sdes	 * is white-space or other non-alphanumeric characters between
1713204917Sdes	 * any two conversion specifications" so arrange things this way.
1714204917Sdes	 */
1715204917Sdes	switch (strlen(s)) {
1716204917Sdes	case 8:
1717204917Sdes		fmt = "%Y-%m-%d";
1718204917Sdes		snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2s", s, s + 4, s + 6);
1719204917Sdes		break;
1720204917Sdes	case 14:
1721204917Sdes		fmt = "%Y-%m-%dT%H:%M:%S";
1722204917Sdes		snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2sT%.2s:%.2s:%.2s",
1723204917Sdes		    s, s + 4, s + 6, s + 8, s + 10, s + 12);
1724204917Sdes		break;
1725204917Sdes	default:
1726204917Sdes		fatal("Invalid certificate time format %s", s);
1727204917Sdes	}
1728204917Sdes
1729264377Sdes	memset(&tm, 0, sizeof(tm));
1730204917Sdes	if (strptime(buf, fmt, &tm) == NULL)
1731204917Sdes		fatal("Invalid certificate time %s", s);
1732204917Sdes	if ((tt = mktime(&tm)) < 0)
1733204917Sdes		fatal("Certificate time %s cannot be represented", s);
1734204917Sdes	return (u_int64_t)tt;
1735204917Sdes}
1736204917Sdes
1737204917Sdesstatic void
1738204917Sdesparse_cert_times(char *timespec)
1739204917Sdes{
1740204917Sdes	char *from, *to;
1741204917Sdes	time_t now = time(NULL);
1742204917Sdes	int64_t secs;
1743204917Sdes
1744204917Sdes	/* +timespec relative to now */
1745204917Sdes	if (*timespec == '+' && strchr(timespec, ':') == NULL) {
1746204917Sdes		if ((secs = convtime(timespec + 1)) == -1)
1747204917Sdes			fatal("Invalid relative certificate life %s", timespec);
1748204917Sdes		cert_valid_to = now + secs;
1749204917Sdes		/*
1750204917Sdes		 * Backdate certificate one minute to avoid problems on hosts
1751204917Sdes		 * with poorly-synchronised clocks.
1752204917Sdes		 */
1753204917Sdes		cert_valid_from = ((now - 59)/ 60) * 60;
1754204917Sdes		return;
1755204917Sdes	}
1756204917Sdes
1757204917Sdes	/*
1758204917Sdes	 * from:to, where
1759204917Sdes	 * from := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS
1760204917Sdes	 *   to := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS
1761204917Sdes	 */
1762204917Sdes	from = xstrdup(timespec);
1763204917Sdes	to = strchr(from, ':');
1764204917Sdes	if (to == NULL || from == to || *(to + 1) == '\0')
1765204917Sdes		fatal("Invalid certificate life specification %s", timespec);
1766204917Sdes	*to++ = '\0';
1767204917Sdes
1768204917Sdes	if (*from == '-' || *from == '+')
1769204917Sdes		cert_valid_from = parse_relative_time(from, now);
1770204917Sdes	else
1771204917Sdes		cert_valid_from = parse_absolute_time(from);
1772204917Sdes
1773204917Sdes	if (*to == '-' || *to == '+')
1774262566Sdes		cert_valid_to = parse_relative_time(to, now);
1775204917Sdes	else
1776204917Sdes		cert_valid_to = parse_absolute_time(to);
1777204917Sdes
1778204917Sdes	if (cert_valid_to <= cert_valid_from)
1779204917Sdes		fatal("Empty certificate validity interval");
1780255767Sdes	free(from);
1781204917Sdes}
1782204917Sdes
1783204917Sdesstatic void
1784215116Sdesadd_cert_option(char *opt)
1785204917Sdes{
1786204917Sdes	char *val;
1787204917Sdes
1788226046Sdes	if (strcasecmp(opt, "clear") == 0)
1789215116Sdes		certflags_flags = 0;
1790204917Sdes	else if (strcasecmp(opt, "no-x11-forwarding") == 0)
1791215116Sdes		certflags_flags &= ~CERTOPT_X_FWD;
1792204917Sdes	else if (strcasecmp(opt, "permit-x11-forwarding") == 0)
1793215116Sdes		certflags_flags |= CERTOPT_X_FWD;
1794204917Sdes	else if (strcasecmp(opt, "no-agent-forwarding") == 0)
1795215116Sdes		certflags_flags &= ~CERTOPT_AGENT_FWD;
1796204917Sdes	else if (strcasecmp(opt, "permit-agent-forwarding") == 0)
1797215116Sdes		certflags_flags |= CERTOPT_AGENT_FWD;
1798204917Sdes	else if (strcasecmp(opt, "no-port-forwarding") == 0)
1799215116Sdes		certflags_flags &= ~CERTOPT_PORT_FWD;
1800204917Sdes	else if (strcasecmp(opt, "permit-port-forwarding") == 0)
1801215116Sdes		certflags_flags |= CERTOPT_PORT_FWD;
1802204917Sdes	else if (strcasecmp(opt, "no-pty") == 0)
1803215116Sdes		certflags_flags &= ~CERTOPT_PTY;
1804204917Sdes	else if (strcasecmp(opt, "permit-pty") == 0)
1805215116Sdes		certflags_flags |= CERTOPT_PTY;
1806204917Sdes	else if (strcasecmp(opt, "no-user-rc") == 0)
1807215116Sdes		certflags_flags &= ~CERTOPT_USER_RC;
1808204917Sdes	else if (strcasecmp(opt, "permit-user-rc") == 0)
1809215116Sdes		certflags_flags |= CERTOPT_USER_RC;
1810204917Sdes	else if (strncasecmp(opt, "force-command=", 14) == 0) {
1811204917Sdes		val = opt + 14;
1812204917Sdes		if (*val == '\0')
1813215116Sdes			fatal("Empty force-command option");
1814215116Sdes		if (certflags_command != NULL)
1815204917Sdes			fatal("force-command already specified");
1816215116Sdes		certflags_command = xstrdup(val);
1817204917Sdes	} else if (strncasecmp(opt, "source-address=", 15) == 0) {
1818204917Sdes		val = opt + 15;
1819204917Sdes		if (*val == '\0')
1820215116Sdes			fatal("Empty source-address option");
1821215116Sdes		if (certflags_src_addr != NULL)
1822204917Sdes			fatal("source-address already specified");
1823204917Sdes		if (addr_match_cidr_list(NULL, val) != 0)
1824204917Sdes			fatal("Invalid source-address list");
1825215116Sdes		certflags_src_addr = xstrdup(val);
1826204917Sdes	} else
1827215116Sdes		fatal("Unsupported certificate option \"%s\"", opt);
1828204917Sdes}
1829204917Sdes
1830204917Sdesstatic void
1831295367Sdesshow_options(struct sshbuf *optbuf, int in_critical)
1832215116Sdes{
1833295367Sdes	char *name, *arg;
1834295367Sdes	struct sshbuf *options, *option = NULL;
1835295367Sdes	int r;
1836215116Sdes
1837295367Sdes	if ((options = sshbuf_fromb(optbuf)) == NULL)
1838295367Sdes		fatal("%s: sshbuf_fromb failed", __func__);
1839295367Sdes	while (sshbuf_len(options) != 0) {
1840295367Sdes		sshbuf_free(option);
1841295367Sdes		option = NULL;
1842295367Sdes		if ((r = sshbuf_get_cstring(options, &name, NULL)) != 0 ||
1843295367Sdes		    (r = sshbuf_froms(options, &option)) != 0)
1844295367Sdes			fatal("%s: buffer error: %s", __func__, ssh_err(r));
1845215116Sdes		printf("                %s", name);
1846295367Sdes		if (!in_critical &&
1847215116Sdes		    (strcmp(name, "permit-X11-forwarding") == 0 ||
1848215116Sdes		    strcmp(name, "permit-agent-forwarding") == 0 ||
1849215116Sdes		    strcmp(name, "permit-port-forwarding") == 0 ||
1850215116Sdes		    strcmp(name, "permit-pty") == 0 ||
1851215116Sdes		    strcmp(name, "permit-user-rc") == 0))
1852215116Sdes			printf("\n");
1853295367Sdes		else if (in_critical &&
1854215116Sdes		    (strcmp(name, "force-command") == 0 ||
1855215116Sdes		    strcmp(name, "source-address") == 0)) {
1856295367Sdes			if ((r = sshbuf_get_cstring(option, &arg, NULL)) != 0)
1857295367Sdes				fatal("%s: buffer error: %s",
1858295367Sdes				    __func__, ssh_err(r));
1859295367Sdes			printf(" %s\n", arg);
1860295367Sdes			free(arg);
1861215116Sdes		} else {
1862295367Sdes			printf(" UNKNOWN OPTION (len %zu)\n",
1863295367Sdes			    sshbuf_len(option));
1864295367Sdes			sshbuf_reset(option);
1865215116Sdes		}
1866255767Sdes		free(name);
1867295367Sdes		if (sshbuf_len(option) != 0)
1868215116Sdes			fatal("Option corrupt: extra data at end");
1869215116Sdes	}
1870295367Sdes	sshbuf_free(option);
1871295367Sdes	sshbuf_free(options);
1872215116Sdes}
1873215116Sdes
1874215116Sdesstatic void
1875296781Sdesprint_cert(struct sshkey *key)
1876204917Sdes{
1877296781Sdes	char valid[64], *key_fp, *ca_fp;
1878295367Sdes	u_int i;
1879204917Sdes
1880295367Sdes	key_fp = sshkey_fingerprint(key, fingerprint_hash, SSH_FP_DEFAULT);
1881295367Sdes	ca_fp = sshkey_fingerprint(key->cert->signature_key,
1882295367Sdes	    fingerprint_hash, SSH_FP_DEFAULT);
1883295367Sdes	if (key_fp == NULL || ca_fp == NULL)
1884295367Sdes		fatal("%s: sshkey_fingerprint fail", __func__);
1885296781Sdes	sshkey_format_cert_validity(key->cert, valid, sizeof(valid));
1886204917Sdes
1887295367Sdes	printf("        Type: %s %s certificate\n", sshkey_ssh_name(key),
1888295367Sdes	    sshkey_cert_type(key));
1889295367Sdes	printf("        Public key: %s %s\n", sshkey_type(key), key_fp);
1890215116Sdes	printf("        Signing CA: %s %s\n",
1891295367Sdes	    sshkey_type(key->cert->signature_key), ca_fp);
1892215116Sdes	printf("        Key ID: \"%s\"\n", key->cert->key_id);
1893295367Sdes	printf("        Serial: %llu\n", (unsigned long long)key->cert->serial);
1894296781Sdes	printf("        Valid: %s\n", valid);
1895204917Sdes	printf("        Principals: ");
1896204917Sdes	if (key->cert->nprincipals == 0)
1897204917Sdes		printf("(none)\n");
1898204917Sdes	else {
1899204917Sdes		for (i = 0; i < key->cert->nprincipals; i++)
1900204917Sdes			printf("\n                %s",
1901204917Sdes			    key->cert->principals[i]);
1902204917Sdes		printf("\n");
1903204917Sdes	}
1904215116Sdes	printf("        Critical Options: ");
1905295367Sdes	if (sshbuf_len(key->cert->critical) == 0)
1906204917Sdes		printf("(none)\n");
1907204917Sdes	else {
1908204917Sdes		printf("\n");
1909295367Sdes		show_options(key->cert->critical, 1);
1910215116Sdes	}
1911295367Sdes	printf("        Extensions: ");
1912295367Sdes	if (sshbuf_len(key->cert->extensions) == 0)
1913295367Sdes		printf("(none)\n");
1914295367Sdes	else {
1915295367Sdes		printf("\n");
1916295367Sdes		show_options(key->cert->extensions, 0);
1917204917Sdes	}
1918204917Sdes}
1919204917Sdes
1920204917Sdesstatic void
1921296781Sdesdo_show_cert(struct passwd *pw)
1922296781Sdes{
1923296781Sdes	struct sshkey *key = NULL;
1924296781Sdes	struct stat st;
1925296781Sdes	int r, is_stdin = 0, ok = 0;
1926296781Sdes	FILE *f;
1927296781Sdes	char *cp, line[SSH_MAX_PUBKEY_BYTES];
1928296781Sdes	const char *path;
1929323124Sdes	u_long lnum = 0;
1930296781Sdes
1931296781Sdes	if (!have_identity)
1932296781Sdes		ask_filename(pw, "Enter file in which the key is");
1933296781Sdes	if (strcmp(identity_file, "-") != 0 && stat(identity_file, &st) < 0)
1934296781Sdes		fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
1935296781Sdes
1936296781Sdes	path = identity_file;
1937296781Sdes	if (strcmp(path, "-") == 0) {
1938296781Sdes		f = stdin;
1939296781Sdes		path = "(stdin)";
1940296781Sdes		is_stdin = 1;
1941296781Sdes	} else if ((f = fopen(identity_file, "r")) == NULL)
1942296781Sdes		fatal("fopen %s: %s", identity_file, strerror(errno));
1943296781Sdes
1944296781Sdes	while (read_keyfile_line(f, path, line, sizeof(line), &lnum) == 0) {
1945296781Sdes		sshkey_free(key);
1946296781Sdes		key = NULL;
1947296781Sdes		/* Trim leading space and comments */
1948296781Sdes		cp = line + strspn(line, " \t");
1949296781Sdes		if (*cp == '#' || *cp == '\0')
1950296781Sdes			continue;
1951296781Sdes		if ((key = sshkey_new(KEY_UNSPEC)) == NULL)
1952296781Sdes			fatal("key_new");
1953296781Sdes		if ((r = sshkey_read(key, &cp)) != 0) {
1954296781Sdes			error("%s:%lu: invalid key: %s", path,
1955296781Sdes			    lnum, ssh_err(r));
1956296781Sdes			continue;
1957296781Sdes		}
1958296781Sdes		if (!sshkey_is_cert(key)) {
1959296781Sdes			error("%s:%lu is not a certificate", path, lnum);
1960296781Sdes			continue;
1961296781Sdes		}
1962296781Sdes		ok = 1;
1963296781Sdes		if (!is_stdin && lnum == 1)
1964296781Sdes			printf("%s:\n", path);
1965296781Sdes		else
1966296781Sdes			printf("%s:%lu:\n", path, lnum);
1967296781Sdes		print_cert(key);
1968296781Sdes	}
1969296781Sdes	sshkey_free(key);
1970296781Sdes	fclose(f);
1971296781Sdes	exit(ok ? 0 : 1);
1972296781Sdes}
1973296781Sdes
1974296781Sdesstatic void
1975248619Sdesload_krl(const char *path, struct ssh_krl **krlp)
1976248619Sdes{
1977295367Sdes	struct sshbuf *krlbuf;
1978295367Sdes	int r, fd;
1979248619Sdes
1980295367Sdes	if ((krlbuf = sshbuf_new()) == NULL)
1981295367Sdes		fatal("sshbuf_new failed");
1982248619Sdes	if ((fd = open(path, O_RDONLY)) == -1)
1983248619Sdes		fatal("open %s: %s", path, strerror(errno));
1984295367Sdes	if ((r = sshkey_load_file(fd, krlbuf)) != 0)
1985295367Sdes		fatal("Unable to load KRL: %s", ssh_err(r));
1986248619Sdes	close(fd);
1987248619Sdes	/* XXX check sigs */
1988295367Sdes	if ((r = ssh_krl_from_blob(krlbuf, krlp, NULL, 0)) != 0 ||
1989248619Sdes	    *krlp == NULL)
1990295367Sdes		fatal("Invalid KRL file: %s", ssh_err(r));
1991295367Sdes	sshbuf_free(krlbuf);
1992248619Sdes}
1993248619Sdes
1994248619Sdesstatic void
1995295367Sdesupdate_krl_from_file(struct passwd *pw, const char *file, int wild_ca,
1996295367Sdes    const struct sshkey *ca, struct ssh_krl *krl)
1997248619Sdes{
1998295367Sdes	struct sshkey *key = NULL;
1999248619Sdes	u_long lnum = 0;
2000248619Sdes	char *path, *cp, *ep, line[SSH_MAX_PUBKEY_BYTES];
2001248619Sdes	unsigned long long serial, serial2;
2002248619Sdes	int i, was_explicit_key, was_sha1, r;
2003248619Sdes	FILE *krl_spec;
2004248619Sdes
2005248619Sdes	path = tilde_expand_filename(file, pw->pw_uid);
2006248619Sdes	if (strcmp(path, "-") == 0) {
2007248619Sdes		krl_spec = stdin;
2008248619Sdes		free(path);
2009248619Sdes		path = xstrdup("(standard input)");
2010248619Sdes	} else if ((krl_spec = fopen(path, "r")) == NULL)
2011248619Sdes		fatal("fopen %s: %s", path, strerror(errno));
2012248619Sdes
2013248619Sdes	if (!quiet)
2014248619Sdes		printf("Revoking from %s\n", path);
2015248619Sdes	while (read_keyfile_line(krl_spec, path, line, sizeof(line),
2016248619Sdes	    &lnum) == 0) {
2017248619Sdes		was_explicit_key = was_sha1 = 0;
2018248619Sdes		cp = line + strspn(line, " \t");
2019248619Sdes		/* Trim trailing space, comments and strip \n */
2020248619Sdes		for (i = 0, r = -1; cp[i] != '\0'; i++) {
2021248619Sdes			if (cp[i] == '#' || cp[i] == '\n') {
2022248619Sdes				cp[i] = '\0';
2023248619Sdes				break;
2024248619Sdes			}
2025248619Sdes			if (cp[i] == ' ' || cp[i] == '\t') {
2026248619Sdes				/* Remember the start of a span of whitespace */
2027248619Sdes				if (r == -1)
2028248619Sdes					r = i;
2029248619Sdes			} else
2030248619Sdes				r = -1;
2031248619Sdes		}
2032248619Sdes		if (r != -1)
2033248619Sdes			cp[r] = '\0';
2034248619Sdes		if (*cp == '\0')
2035248619Sdes			continue;
2036248619Sdes		if (strncasecmp(cp, "serial:", 7) == 0) {
2037295367Sdes			if (ca == NULL && !wild_ca) {
2038262566Sdes				fatal("revoking certificates by serial number "
2039248619Sdes				    "requires specification of a CA key");
2040248619Sdes			}
2041248619Sdes			cp += 7;
2042248619Sdes			cp = cp + strspn(cp, " \t");
2043248619Sdes			errno = 0;
2044248619Sdes			serial = strtoull(cp, &ep, 0);
2045248619Sdes			if (*cp == '\0' || (*ep != '\0' && *ep != '-'))
2046248619Sdes				fatal("%s:%lu: invalid serial \"%s\"",
2047248619Sdes				    path, lnum, cp);
2048248619Sdes			if (errno == ERANGE && serial == ULLONG_MAX)
2049248619Sdes				fatal("%s:%lu: serial out of range",
2050248619Sdes				    path, lnum);
2051248619Sdes			serial2 = serial;
2052248619Sdes			if (*ep == '-') {
2053248619Sdes				cp = ep + 1;
2054248619Sdes				errno = 0;
2055248619Sdes				serial2 = strtoull(cp, &ep, 0);
2056248619Sdes				if (*cp == '\0' || *ep != '\0')
2057248619Sdes					fatal("%s:%lu: invalid serial \"%s\"",
2058248619Sdes					    path, lnum, cp);
2059248619Sdes				if (errno == ERANGE && serial2 == ULLONG_MAX)
2060248619Sdes					fatal("%s:%lu: serial out of range",
2061248619Sdes					    path, lnum);
2062248619Sdes				if (serial2 <= serial)
2063248619Sdes					fatal("%s:%lu: invalid serial range "
2064248619Sdes					    "%llu:%llu", path, lnum,
2065248619Sdes					    (unsigned long long)serial,
2066248619Sdes					    (unsigned long long)serial2);
2067248619Sdes			}
2068248619Sdes			if (ssh_krl_revoke_cert_by_serial_range(krl,
2069248619Sdes			    ca, serial, serial2) != 0) {
2070248619Sdes				fatal("%s: revoke serial failed",
2071248619Sdes				    __func__);
2072248619Sdes			}
2073248619Sdes		} else if (strncasecmp(cp, "id:", 3) == 0) {
2074295367Sdes			if (ca == NULL && !wild_ca) {
2075262566Sdes				fatal("revoking certificates by key ID "
2076248619Sdes				    "requires specification of a CA key");
2077248619Sdes			}
2078248619Sdes			cp += 3;
2079248619Sdes			cp = cp + strspn(cp, " \t");
2080248619Sdes			if (ssh_krl_revoke_cert_by_key_id(krl, ca, cp) != 0)
2081248619Sdes				fatal("%s: revoke key ID failed", __func__);
2082248619Sdes		} else {
2083248619Sdes			if (strncasecmp(cp, "key:", 4) == 0) {
2084248619Sdes				cp += 4;
2085248619Sdes				cp = cp + strspn(cp, " \t");
2086248619Sdes				was_explicit_key = 1;
2087248619Sdes			} else if (strncasecmp(cp, "sha1:", 5) == 0) {
2088248619Sdes				cp += 5;
2089248619Sdes				cp = cp + strspn(cp, " \t");
2090248619Sdes				was_sha1 = 1;
2091248619Sdes			} else {
2092248619Sdes				/*
2093248619Sdes				 * Just try to process the line as a key.
2094248619Sdes				 * Parsing will fail if it isn't.
2095248619Sdes				 */
2096248619Sdes			}
2097295367Sdes			if ((key = sshkey_new(KEY_UNSPEC)) == NULL)
2098248619Sdes				fatal("key_new");
2099295367Sdes			if ((r = sshkey_read(key, &cp)) != 0)
2100295367Sdes				fatal("%s:%lu: invalid key: %s",
2101295367Sdes				    path, lnum, ssh_err(r));
2102248619Sdes			if (was_explicit_key)
2103248619Sdes				r = ssh_krl_revoke_key_explicit(krl, key);
2104248619Sdes			else if (was_sha1)
2105248619Sdes				r = ssh_krl_revoke_key_sha1(krl, key);
2106248619Sdes			else
2107248619Sdes				r = ssh_krl_revoke_key(krl, key);
2108248619Sdes			if (r != 0)
2109295367Sdes				fatal("%s: revoke key failed: %s",
2110295367Sdes				    __func__, ssh_err(r));
2111295367Sdes			sshkey_free(key);
2112248619Sdes		}
2113248619Sdes	}
2114248619Sdes	if (strcmp(path, "-") != 0)
2115248619Sdes		fclose(krl_spec);
2116255767Sdes	free(path);
2117248619Sdes}
2118248619Sdes
2119248619Sdesstatic void
2120248619Sdesdo_gen_krl(struct passwd *pw, int updating, int argc, char **argv)
2121248619Sdes{
2122248619Sdes	struct ssh_krl *krl;
2123248619Sdes	struct stat sb;
2124295367Sdes	struct sshkey *ca = NULL;
2125295367Sdes	int fd, i, r, wild_ca = 0;
2126248619Sdes	char *tmp;
2127295367Sdes	struct sshbuf *kbuf;
2128248619Sdes
2129248619Sdes	if (*identity_file == '\0')
2130248619Sdes		fatal("KRL generation requires an output file");
2131248619Sdes	if (stat(identity_file, &sb) == -1) {
2132248619Sdes		if (errno != ENOENT)
2133248619Sdes			fatal("Cannot access KRL \"%s\": %s",
2134248619Sdes			    identity_file, strerror(errno));
2135248619Sdes		if (updating)
2136248619Sdes			fatal("KRL \"%s\" does not exist", identity_file);
2137248619Sdes	}
2138248619Sdes	if (ca_key_path != NULL) {
2139295367Sdes		if (strcasecmp(ca_key_path, "none") == 0)
2140295367Sdes			wild_ca = 1;
2141295367Sdes		else {
2142295367Sdes			tmp = tilde_expand_filename(ca_key_path, pw->pw_uid);
2143295367Sdes			if ((r = sshkey_load_public(tmp, &ca, NULL)) != 0)
2144295367Sdes				fatal("Cannot load CA public key %s: %s",
2145295367Sdes				    tmp, ssh_err(r));
2146295367Sdes			free(tmp);
2147295367Sdes		}
2148248619Sdes	}
2149248619Sdes
2150248619Sdes	if (updating)
2151248619Sdes		load_krl(identity_file, &krl);
2152248619Sdes	else if ((krl = ssh_krl_init()) == NULL)
2153248619Sdes		fatal("couldn't create KRL");
2154248619Sdes
2155248619Sdes	if (cert_serial != 0)
2156248619Sdes		ssh_krl_set_version(krl, cert_serial);
2157248619Sdes	if (identity_comment != NULL)
2158248619Sdes		ssh_krl_set_comment(krl, identity_comment);
2159248619Sdes
2160248619Sdes	for (i = 0; i < argc; i++)
2161295367Sdes		update_krl_from_file(pw, argv[i], wild_ca, ca, krl);
2162248619Sdes
2163295367Sdes	if ((kbuf = sshbuf_new()) == NULL)
2164295367Sdes		fatal("sshbuf_new failed");
2165295367Sdes	if (ssh_krl_to_blob(krl, kbuf, NULL, 0) != 0)
2166248619Sdes		fatal("Couldn't generate KRL");
2167248619Sdes	if ((fd = open(identity_file, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
2168248619Sdes		fatal("open %s: %s", identity_file, strerror(errno));
2169295367Sdes	if (atomicio(vwrite, fd, (void *)sshbuf_ptr(kbuf), sshbuf_len(kbuf)) !=
2170295367Sdes	    sshbuf_len(kbuf))
2171248619Sdes		fatal("write %s: %s", identity_file, strerror(errno));
2172248619Sdes	close(fd);
2173295367Sdes	sshbuf_free(kbuf);
2174248619Sdes	ssh_krl_free(krl);
2175296781Sdes	sshkey_free(ca);
2176248619Sdes}
2177248619Sdes
2178248619Sdesstatic void
2179248619Sdesdo_check_krl(struct passwd *pw, int argc, char **argv)
2180248619Sdes{
2181248619Sdes	int i, r, ret = 0;
2182248619Sdes	char *comment;
2183248619Sdes	struct ssh_krl *krl;
2184295367Sdes	struct sshkey *k;
2185248619Sdes
2186248619Sdes	if (*identity_file == '\0')
2187248619Sdes		fatal("KRL checking requires an input file");
2188248619Sdes	load_krl(identity_file, &krl);
2189248619Sdes	for (i = 0; i < argc; i++) {
2190295367Sdes		if ((r = sshkey_load_public(argv[i], &k, &comment)) != 0)
2191295367Sdes			fatal("Cannot load public key %s: %s",
2192295367Sdes			    argv[i], ssh_err(r));
2193248619Sdes		r = ssh_krl_check_key(krl, k);
2194248619Sdes		printf("%s%s%s%s: %s\n", argv[i],
2195248619Sdes		    *comment ? " (" : "", comment, *comment ? ")" : "",
2196248619Sdes		    r == 0 ? "ok" : "REVOKED");
2197248619Sdes		if (r != 0)
2198248619Sdes			ret = 1;
2199295367Sdes		sshkey_free(k);
2200248619Sdes		free(comment);
2201248619Sdes	}
2202248619Sdes	ssh_krl_free(krl);
2203248619Sdes	exit(ret);
2204248619Sdes}
2205248619Sdes
2206248619Sdesstatic void
220757429Smarkmusage(void)
220857429Smarkm{
2209295367Sdes	fprintf(stderr,
2210295367Sdes	    "usage: ssh-keygen [-q] [-b bits] [-t dsa | ecdsa | ed25519 | rsa | rsa1]\n"
2211295367Sdes	    "                  [-N new_passphrase] [-C comment] [-f output_keyfile]\n"
2212295367Sdes	    "       ssh-keygen -p [-P old_passphrase] [-N new_passphrase] [-f keyfile]\n"
2213295367Sdes	    "       ssh-keygen -i [-m key_format] [-f input_keyfile]\n"
2214295367Sdes	    "       ssh-keygen -e [-m key_format] [-f input_keyfile]\n"
2215295367Sdes	    "       ssh-keygen -y [-f input_keyfile]\n"
2216295367Sdes	    "       ssh-keygen -c [-P passphrase] [-C comment] [-f keyfile]\n"
2217295367Sdes	    "       ssh-keygen -l [-v] [-E fingerprint_hash] [-f input_keyfile]\n"
2218295367Sdes	    "       ssh-keygen -B [-f input_keyfile]\n");
2219204917Sdes#ifdef ENABLE_PKCS11
2220295367Sdes	fprintf(stderr,
2221295367Sdes	    "       ssh-keygen -D pkcs11\n");
2222204917Sdes#endif
2223295367Sdes	fprintf(stderr,
2224295367Sdes	    "       ssh-keygen -F hostname [-f known_hosts_file] [-l]\n"
2225295367Sdes	    "       ssh-keygen -H [-f known_hosts_file]\n"
2226295367Sdes	    "       ssh-keygen -R hostname [-f known_hosts_file]\n"
2227295367Sdes	    "       ssh-keygen -r hostname [-f input_keyfile] [-g]\n"
2228295367Sdes#ifdef WITH_OPENSSL
2229295367Sdes	    "       ssh-keygen -G output_file [-v] [-b bits] [-M memory] [-S start_point]\n"
2230295367Sdes	    "       ssh-keygen -T output_file -f input_file [-v] [-a rounds] [-J num_lines]\n"
2231295367Sdes	    "                  [-j start_line] [-K checkpt] [-W generator]\n"
2232295367Sdes#endif
2233295367Sdes	    "       ssh-keygen -s ca_key -I certificate_identity [-h] [-n principals]\n"
2234295367Sdes	    "                  [-O option] [-V validity_interval] [-z serial_number] file ...\n"
2235295367Sdes	    "       ssh-keygen -L [-f input_keyfile]\n"
2236295367Sdes	    "       ssh-keygen -A\n"
2237295367Sdes	    "       ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n"
2238295367Sdes	    "                  file ...\n"
2239295367Sdes	    "       ssh-keygen -Q -f krl_file file ...\n");
224057429Smarkm	exit(1);
224157429Smarkm}
224257429Smarkm
224357429Smarkm/*
224457429Smarkm * Main program for key management.
224557429Smarkm */
224657429Smarkmint
2247181111Sdesmain(int argc, char **argv)
224857429Smarkm{
2249295367Sdes	char dotsshdir[PATH_MAX], comment[1024], *passphrase1, *passphrase2;
2250295367Sdes	char *rr_hostname = NULL, *ep, *fp, *ra;
2251295367Sdes	struct sshkey *private, *public;
225257429Smarkm	struct passwd *pw;
225357429Smarkm	struct stat st;
2254295367Sdes	int r, opt, type, fd;
2255295367Sdes	int gen_all_hostkeys = 0, gen_krl = 0, update_krl = 0, check_krl = 0;
2256295367Sdes	FILE *f;
2257295367Sdes	const char *errstr;
2258295367Sdes#ifdef WITH_OPENSSL
2259295367Sdes	/* Moduli generation/screening */
2260295367Sdes	char out_file[PATH_MAX], *checkpoint = NULL;
2261262566Sdes	u_int32_t memory = 0, generator_wanted = 0;
2262124208Sdes	int do_gen_candidates = 0, do_screen_candidates = 0;
2263240075Sdes	unsigned long start_lineno = 0, lines_to_process = 0;
2264124208Sdes	BIGNUM *start = NULL;
2265295367Sdes#endif
226676259Sgreen
226757429Smarkm	extern int optind;
226857429Smarkm	extern char *optarg;
226957429Smarkm
2270296781Sdes	ssh_malloc_init();	/* must be called before any mallocs */
2271157016Sdes	/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
2272157016Sdes	sanitise_stdfd();
2273157016Sdes
2274181111Sdes	__progname = ssh_get_progname(argv[0]);
227598937Sdes
2276295367Sdes#ifdef WITH_OPENSSL
2277221420Sdes	OpenSSL_add_all_algorithms();
2278295367Sdes#endif
2279181111Sdes	log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1);
2280124208Sdes
2281106121Sdes	seed_rng();
228260573Skris
228357429Smarkm	/* we need this for the home * directory.  */
228457429Smarkm	pw = getpwuid(getuid());
2285295367Sdes	if (!pw)
2286295367Sdes		fatal("No user exists for uid %lu", (u_long)getuid());
2287295367Sdes	if (gethostname(hostname, sizeof(hostname)) < 0)
2288295367Sdes		fatal("gethostname: %s", strerror(errno));
228957429Smarkm
2290295367Sdes	/* Remaining characters: UYdw */
2291262566Sdes	while ((opt = getopt(argc, argv, "ABHLQXceghiklopquvxy"
2292295367Sdes	    "C:D:E:F:G:I:J:K:M:N:O:P:R:S:T:V:W:Z:"
2293295367Sdes	    "a:b:f:g:j:m:n:r:s:t:z:")) != -1) {
229457429Smarkm		switch (opt) {
2295226046Sdes		case 'A':
2296226046Sdes			gen_all_hostkeys = 1;
2297226046Sdes			break;
229857429Smarkm		case 'b':
2299221420Sdes			bits = (u_int32_t)strtonum(optarg, 256, 32768, &errstr);
2300149749Sdes			if (errstr)
2301149749Sdes				fatal("Bits has bad value %s (%s)",
2302149749Sdes					optarg, errstr);
230357429Smarkm			break;
2304295367Sdes		case 'E':
2305295367Sdes			fingerprint_hash = ssh_digest_alg_by_name(optarg);
2306295367Sdes			if (fingerprint_hash == -1)
2307295367Sdes				fatal("Invalid hash algorithm \"%s\"", optarg);
2308295367Sdes			break;
2309146998Sdes		case 'F':
2310146998Sdes			find_host = 1;
2311146998Sdes			rr_hostname = optarg;
2312146998Sdes			break;
2313146998Sdes		case 'H':
2314146998Sdes			hash_hosts = 1;
2315146998Sdes			break;
2316204917Sdes		case 'I':
2317204917Sdes			cert_key_id = optarg;
2318204917Sdes			break;
2319146998Sdes		case 'R':
2320146998Sdes			delete_host = 1;
2321146998Sdes			rr_hostname = optarg;
2322146998Sdes			break;
2323204917Sdes		case 'L':
2324204917Sdes			show_cert = 1;
2325204917Sdes			break;
232657429Smarkm		case 'l':
232757429Smarkm			print_fingerprint = 1;
232857429Smarkm			break;
232976259Sgreen		case 'B':
233076259Sgreen			print_bubblebabble = 1;
233176259Sgreen			break;
2332215116Sdes		case 'm':
2333215116Sdes			if (strcasecmp(optarg, "RFC4716") == 0 ||
2334215116Sdes			    strcasecmp(optarg, "ssh2") == 0) {
2335215116Sdes				convert_format = FMT_RFC4716;
2336215116Sdes				break;
2337215116Sdes			}
2338215116Sdes			if (strcasecmp(optarg, "PKCS8") == 0) {
2339215116Sdes				convert_format = FMT_PKCS8;
2340215116Sdes				break;
2341215116Sdes			}
2342215116Sdes			if (strcasecmp(optarg, "PEM") == 0) {
2343215116Sdes				convert_format = FMT_PEM;
2344215116Sdes				break;
2345215116Sdes			}
2346215116Sdes			fatal("Unsupported conversion format \"%s\"", optarg);
2347204917Sdes		case 'n':
2348204917Sdes			cert_principals = optarg;
2349204917Sdes			break;
2350262566Sdes		case 'o':
2351262566Sdes			use_new_format = 1;
2352262566Sdes			break;
235357429Smarkm		case 'p':
235457429Smarkm			change_passphrase = 1;
235557429Smarkm			break;
235657429Smarkm		case 'c':
235757429Smarkm			change_comment = 1;
235857429Smarkm			break;
235957429Smarkm		case 'f':
2360295367Sdes			if (strlcpy(identity_file, optarg,
2361295367Sdes			    sizeof(identity_file)) >= sizeof(identity_file))
2362149749Sdes				fatal("Identity filename too long");
236357429Smarkm			have_identity = 1;
236457429Smarkm			break;
2365124208Sdes		case 'g':
2366124208Sdes			print_generic = 1;
2367124208Sdes			break;
236857429Smarkm		case 'P':
236957429Smarkm			identity_passphrase = optarg;
237057429Smarkm			break;
237157429Smarkm		case 'N':
237257429Smarkm			identity_new_passphrase = optarg;
237357429Smarkm			break;
2374248619Sdes		case 'Q':
2375248619Sdes			check_krl = 1;
2376248619Sdes			break;
2377204917Sdes		case 'O':
2378215116Sdes			add_cert_option(optarg);
2379204917Sdes			break;
2380262566Sdes		case 'Z':
2381262566Sdes			new_format_cipher = optarg;
2382262566Sdes			break;
238357429Smarkm		case 'C':
238457429Smarkm			identity_comment = optarg;
238557429Smarkm			break;
238657429Smarkm		case 'q':
238757429Smarkm			quiet = 1;
238857429Smarkm			break;
238976259Sgreen		case 'e':
239060573Skris		case 'x':
239176259Sgreen			/* export key */
2392215116Sdes			convert_to = 1;
239360573Skris			break;
2394204917Sdes		case 'h':
2395204917Sdes			cert_key_type = SSH2_CERT_TYPE_HOST;
2396215116Sdes			certflags_flags = 0;
2397204917Sdes			break;
2398248619Sdes		case 'k':
2399248619Sdes			gen_krl = 1;
2400248619Sdes			break;
240176259Sgreen		case 'i':
240260573Skris		case 'X':
240376259Sgreen			/* import key */
2404215116Sdes			convert_from = 1;
240560573Skris			break;
240660573Skris		case 'y':
240760573Skris			print_public = 1;
240860573Skris			break;
2409204917Sdes		case 's':
2410204917Sdes			ca_key_path = optarg;
2411204917Sdes			break;
241276259Sgreen		case 't':
241376259Sgreen			key_type_name = optarg;
241476259Sgreen			break;
241592555Sdes		case 'D':
2416204917Sdes			pkcs11provider = optarg;
241792555Sdes			break;
2418248619Sdes		case 'u':
2419248619Sdes			update_krl = 1;
2420248619Sdes			break;
2421126274Sdes		case 'v':
2422126274Sdes			if (log_level == SYSLOG_LEVEL_INFO)
2423126274Sdes				log_level = SYSLOG_LEVEL_DEBUG1;
2424126274Sdes			else {
2425137015Sdes				if (log_level >= SYSLOG_LEVEL_DEBUG1 &&
2426126274Sdes				    log_level < SYSLOG_LEVEL_DEBUG3)
2427126274Sdes					log_level++;
2428126274Sdes			}
2429126274Sdes			break;
2430124208Sdes		case 'r':
2431146998Sdes			rr_hostname = optarg;
2432124208Sdes			break;
2433295367Sdes		case 'a':
2434295367Sdes			rounds = (int)strtonum(optarg, 1, INT_MAX, &errstr);
2435295367Sdes			if (errstr)
2436295367Sdes				fatal("Invalid number: %s (%s)",
2437295367Sdes					optarg, errstr);
2438295367Sdes			break;
2439295367Sdes		case 'V':
2440295367Sdes			parse_cert_times(optarg);
2441295367Sdes			break;
2442295367Sdes		case 'z':
2443295367Sdes			errno = 0;
2444295367Sdes			cert_serial = strtoull(optarg, &ep, 10);
2445295367Sdes			if (*optarg < '0' || *optarg > '9' || *ep != '\0' ||
2446295367Sdes			    (errno == ERANGE && cert_serial == ULLONG_MAX))
2447295367Sdes				fatal("Invalid serial number \"%s\"", optarg);
2448295367Sdes			break;
2449295367Sdes#ifdef WITH_OPENSSL
2450295367Sdes		/* Moduli generation/screening */
2451124208Sdes		case 'W':
2452162852Sdes			generator_wanted = (u_int32_t)strtonum(optarg, 1,
2453162852Sdes			    UINT_MAX, &errstr);
2454149749Sdes			if (errstr)
2455149749Sdes				fatal("Desired generator has bad value: %s (%s)",
2456149749Sdes					optarg, errstr);
2457124208Sdes			break;
2458124208Sdes		case 'M':
2459162852Sdes			memory = (u_int32_t)strtonum(optarg, 1, UINT_MAX, &errstr);
2460215116Sdes			if (errstr)
2461149749Sdes				fatal("Memory limit is %s: %s", errstr, optarg);
2462124208Sdes			break;
2463124208Sdes		case 'G':
2464124208Sdes			do_gen_candidates = 1;
2465149749Sdes			if (strlcpy(out_file, optarg, sizeof(out_file)) >=
2466149749Sdes			    sizeof(out_file))
2467149749Sdes				fatal("Output filename too long");
2468124208Sdes			break;
2469124208Sdes		case 'T':
2470124208Sdes			do_screen_candidates = 1;
2471149749Sdes			if (strlcpy(out_file, optarg, sizeof(out_file)) >=
2472149749Sdes			    sizeof(out_file))
2473149749Sdes				fatal("Output filename too long");
2474124208Sdes			break;
2475240075Sdes		case 'K':
2476295367Sdes			if (strlen(optarg) >= PATH_MAX)
2477240075Sdes				fatal("Checkpoint filename too long");
2478240075Sdes			checkpoint = xstrdup(optarg);
2479240075Sdes			break;
2480124208Sdes		case 'S':
2481124208Sdes			/* XXX - also compare length against bits */
2482124208Sdes			if (BN_hex2bn(&start, optarg) == 0)
2483124208Sdes				fatal("Invalid start point.");
2484124208Sdes			break;
2485295367Sdes#endif /* WITH_OPENSSL */
248657429Smarkm		case '?':
248757429Smarkm		default:
248857429Smarkm			usage();
248957429Smarkm		}
249057429Smarkm	}
2491126274Sdes
2492126274Sdes	/* reinit */
2493181111Sdes	log_init(argv[0], log_level, SYSLOG_FACILITY_USER, 1);
2494126274Sdes
2495204917Sdes	argv += optind;
2496204917Sdes	argc -= optind;
2497204917Sdes
2498204917Sdes	if (ca_key_path != NULL) {
2499248619Sdes		if (argc < 1 && !gen_krl) {
2500295367Sdes			error("Too few arguments.");
2501204917Sdes			usage();
2502204917Sdes		}
2503248619Sdes	} else if (argc > 0 && !gen_krl && !check_krl) {
2504295367Sdes		error("Too many arguments.");
250557429Smarkm		usage();
250657429Smarkm	}
250757429Smarkm	if (change_passphrase && change_comment) {
2508295367Sdes		error("Can only have one of -p and -c.");
250957429Smarkm		usage();
251057429Smarkm	}
2511181111Sdes	if (print_fingerprint && (delete_host || hash_hosts)) {
2512295367Sdes		error("Cannot use -l with -H or -R.");
2513181111Sdes		usage();
2514181111Sdes	}
2515248619Sdes	if (gen_krl) {
2516248619Sdes		do_gen_krl(pw, update_krl, argc, argv);
2517248619Sdes		return (0);
2518248619Sdes	}
2519248619Sdes	if (check_krl) {
2520248619Sdes		do_check_krl(pw, argc, argv);
2521248619Sdes		return (0);
2522248619Sdes	}
2523204917Sdes	if (ca_key_path != NULL) {
2524204917Sdes		if (cert_key_id == NULL)
2525204917Sdes			fatal("Must specify key id (-I) when certifying");
2526204917Sdes		do_ca_sign(pw, argc, argv);
2527204917Sdes	}
2528204917Sdes	if (show_cert)
2529204917Sdes		do_show_cert(pw);
2530146998Sdes	if (delete_host || hash_hosts || find_host)
2531146998Sdes		do_known_hosts(pw, rr_hostname);
2532248619Sdes	if (pkcs11provider != NULL)
2533248619Sdes		do_download(pw);
253476259Sgreen	if (print_fingerprint || print_bubblebabble)
253557429Smarkm		do_fingerprint(pw);
253657429Smarkm	if (change_passphrase)
253757429Smarkm		do_change_passphrase(pw);
2538106121Sdes	if (change_comment)
2539106121Sdes		do_change_comment(pw);
2540295367Sdes#ifdef WITH_OPENSSL
2541215116Sdes	if (convert_to)
2542215116Sdes		do_convert_to(pw);
2543215116Sdes	if (convert_from)
2544215116Sdes		do_convert_from(pw);
2545295367Sdes#endif
254660573Skris	if (print_public)
254760573Skris		do_print_public(pw);
2548146998Sdes	if (rr_hostname != NULL) {
2549162852Sdes		unsigned int n = 0;
2550162852Sdes
2551162852Sdes		if (have_identity) {
2552162852Sdes			n = do_print_resource_record(pw,
2553162852Sdes			    identity_file, rr_hostname);
2554295367Sdes			if (n == 0)
2555295367Sdes				fatal("%s: %s", identity_file, strerror(errno));
2556162852Sdes			exit(0);
2557162852Sdes		} else {
2558162852Sdes
2559162852Sdes			n += do_print_resource_record(pw,
2560162852Sdes			    _PATH_HOST_RSA_KEY_FILE, rr_hostname);
2561162852Sdes			n += do_print_resource_record(pw,
2562162852Sdes			    _PATH_HOST_DSA_KEY_FILE, rr_hostname);
2563240075Sdes			n += do_print_resource_record(pw,
2564240075Sdes			    _PATH_HOST_ECDSA_KEY_FILE, rr_hostname);
2565295367Sdes			n += do_print_resource_record(pw,
2566295367Sdes			    _PATH_HOST_ED25519_KEY_FILE, rr_hostname);
2567162852Sdes			if (n == 0)
2568162852Sdes				fatal("no keys found.");
2569162852Sdes			exit(0);
2570162852Sdes		}
2571124208Sdes	}
257257429Smarkm
2573295367Sdes#ifdef WITH_OPENSSL
2574124208Sdes	if (do_gen_candidates) {
2575124208Sdes		FILE *out = fopen(out_file, "w");
2576126274Sdes
2577124208Sdes		if (out == NULL) {
2578124208Sdes			error("Couldn't open modulus candidate file \"%s\": %s",
2579124208Sdes			    out_file, strerror(errno));
2580124208Sdes			return (1);
2581124208Sdes		}
2582157016Sdes		if (bits == 0)
2583157016Sdes			bits = DEFAULT_BITS;
2584124208Sdes		if (gen_candidates(out, memory, bits, start) != 0)
2585157016Sdes			fatal("modulus candidate generation failed");
2586124208Sdes
2587124208Sdes		return (0);
2588124208Sdes	}
2589124208Sdes
2590124208Sdes	if (do_screen_candidates) {
2591124208Sdes		FILE *in;
2592248619Sdes		FILE *out = fopen(out_file, "a");
2593124208Sdes
2594124208Sdes		if (have_identity && strcmp(identity_file, "-") != 0) {
2595124208Sdes			if ((in = fopen(identity_file, "r")) == NULL) {
2596124208Sdes				fatal("Couldn't open modulus candidate "
2597126274Sdes				    "file \"%s\": %s", identity_file,
2598124208Sdes				    strerror(errno));
2599124208Sdes			}
2600124208Sdes		} else
2601124208Sdes			in = stdin;
2602124208Sdes
2603124208Sdes		if (out == NULL) {
2604124208Sdes			fatal("Couldn't open moduli file \"%s\": %s",
2605124208Sdes			    out_file, strerror(errno));
2606124208Sdes		}
2607262566Sdes		if (prime_test(in, out, rounds == 0 ? 100 : rounds,
2608262566Sdes		    generator_wanted, checkpoint,
2609240075Sdes		    start_lineno, lines_to_process) != 0)
2610157016Sdes			fatal("modulus screening failed");
2611124208Sdes		return (0);
2612124208Sdes	}
2613295367Sdes#endif
2614124208Sdes
2615226046Sdes	if (gen_all_hostkeys) {
2616226046Sdes		do_gen_all_hostkeys(pw);
2617226046Sdes		return (0);
2618226046Sdes	}
2619226046Sdes
2620157016Sdes	if (key_type_name == NULL)
2621295367Sdes		key_type_name = DEFAULT_KEY_TYPE_NAME;
2622157016Sdes
2623295367Sdes	type = sshkey_type_from_name(key_type_name);
2624295367Sdes	type_bits_valid(type, key_type_name, &bits);
2625226046Sdes
262676259Sgreen	if (!quiet)
2627295367Sdes		printf("Generating public/private %s key pair.\n",
2628295367Sdes		    key_type_name);
2629295367Sdes	if ((r = sshkey_generate(type, bits, &private)) != 0)
2630295367Sdes		fatal("key_generate failed");
2631295367Sdes	if ((r = sshkey_from_private(private, &public)) != 0)
2632295367Sdes		fatal("key_from_private failed: %s\n", ssh_err(r));
263357429Smarkm
263457429Smarkm	if (!have_identity)
263557429Smarkm		ask_filename(pw, "Enter file in which to save the key");
263657429Smarkm
2637157016Sdes	/* Create ~/.ssh directory if it doesn't already exist. */
2638215116Sdes	snprintf(dotsshdir, sizeof dotsshdir, "%s/%s",
2639215116Sdes	    pw->pw_dir, _PATH_SSH_USER_DIR);
2640215116Sdes	if (strstr(identity_file, dotsshdir) != NULL) {
2641215116Sdes		if (stat(dotsshdir, &st) < 0) {
2642215116Sdes			if (errno != ENOENT) {
2643215116Sdes				error("Could not stat %s: %s", dotsshdir,
2644215116Sdes				    strerror(errno));
2645215116Sdes			} else if (mkdir(dotsshdir, 0700) < 0) {
2646215116Sdes				error("Could not create directory '%s': %s",
2647215116Sdes				    dotsshdir, strerror(errno));
2648215116Sdes			} else if (!quiet)
2649215116Sdes				printf("Created directory '%s'.\n", dotsshdir);
2650215116Sdes		}
265157429Smarkm	}
265257429Smarkm	/* If the file already exists, ask the user to confirm. */
265357429Smarkm	if (stat(identity_file, &st) >= 0) {
265457429Smarkm		char yesno[3];
265557429Smarkm		printf("%s already exists.\n", identity_file);
265657429Smarkm		printf("Overwrite (y/n)? ");
265757429Smarkm		fflush(stdout);
265857429Smarkm		if (fgets(yesno, sizeof(yesno), stdin) == NULL)
265957429Smarkm			exit(1);
266057429Smarkm		if (yesno[0] != 'y' && yesno[0] != 'Y')
266157429Smarkm			exit(1);
266257429Smarkm	}
266357429Smarkm	/* Ask for a passphrase (twice). */
266457429Smarkm	if (identity_passphrase)
266557429Smarkm		passphrase1 = xstrdup(identity_passphrase);
266657429Smarkm	else if (identity_new_passphrase)
266757429Smarkm		passphrase1 = xstrdup(identity_new_passphrase);
266857429Smarkm	else {
266957429Smarkmpassphrase_again:
267057429Smarkm		passphrase1 =
267192555Sdes			read_passphrase("Enter passphrase (empty for no "
267292555Sdes			    "passphrase): ", RP_ALLOW_STDIN);
267392555Sdes		passphrase2 = read_passphrase("Enter same passphrase again: ",
267492555Sdes		    RP_ALLOW_STDIN);
267557429Smarkm		if (strcmp(passphrase1, passphrase2) != 0) {
267692555Sdes			/*
267792555Sdes			 * The passphrases do not match.  Clear them and
267892555Sdes			 * retry.
267992555Sdes			 */
2680264377Sdes			explicit_bzero(passphrase1, strlen(passphrase1));
2681264377Sdes			explicit_bzero(passphrase2, strlen(passphrase2));
2682255767Sdes			free(passphrase1);
2683255767Sdes			free(passphrase2);
268457429Smarkm			printf("Passphrases do not match.  Try again.\n");
268557429Smarkm			goto passphrase_again;
268657429Smarkm		}
268757429Smarkm		/* Clear the other copy of the passphrase. */
2688264377Sdes		explicit_bzero(passphrase2, strlen(passphrase2));
2689255767Sdes		free(passphrase2);
269057429Smarkm	}
269157429Smarkm
269257429Smarkm	if (identity_comment) {
269357429Smarkm		strlcpy(comment, identity_comment, sizeof(comment));
269457429Smarkm	} else {
2695192595Sdes		/* Create default comment field for the passphrase. */
269657429Smarkm		snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname);
269757429Smarkm	}
269857429Smarkm
269957429Smarkm	/* Save the key with the given passphrase and comment. */
2700295367Sdes	if ((r = sshkey_save_private(private, identity_file, passphrase1,
2701295367Sdes	    comment, use_new_format, new_format_cipher, rounds)) != 0) {
2702295367Sdes		error("Saving key \"%s\" failed: %s",
2703295367Sdes		    identity_file, ssh_err(r));
2704264377Sdes		explicit_bzero(passphrase1, strlen(passphrase1));
2705255767Sdes		free(passphrase1);
270657429Smarkm		exit(1);
270757429Smarkm	}
270857429Smarkm	/* Clear the passphrase. */
2709264377Sdes	explicit_bzero(passphrase1, strlen(passphrase1));
2710255767Sdes	free(passphrase1);
271157429Smarkm
271257429Smarkm	/* Clear the private key and the random number generator. */
2713295367Sdes	sshkey_free(private);
271457429Smarkm
271557429Smarkm	if (!quiet)
271657429Smarkm		printf("Your identification has been saved in %s.\n", identity_file);
271757429Smarkm
271857429Smarkm	strlcat(identity_file, ".pub", sizeof(identity_file));
2719295367Sdes	if ((fd = open(identity_file, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
2720295367Sdes		fatal("Unable to save public key to %s: %s",
2721295367Sdes		    identity_file, strerror(errno));
2722295367Sdes	if ((f = fdopen(fd, "w")) == NULL)
2723295367Sdes		fatal("fdopen %s failed: %s", identity_file, strerror(errno));
2724295367Sdes	if ((r = sshkey_write(public, f)) != 0)
2725295367Sdes		error("write key failed: %s", ssh_err(r));
272660573Skris	fprintf(f, " %s\n", comment);
272757429Smarkm	fclose(f);
272857429Smarkm
272957429Smarkm	if (!quiet) {
2730295367Sdes		fp = sshkey_fingerprint(public, fingerprint_hash,
2731295367Sdes		    SSH_FP_DEFAULT);
2732295367Sdes		ra = sshkey_fingerprint(public, fingerprint_hash,
2733181111Sdes		    SSH_FP_RANDOMART);
2734295367Sdes		if (fp == NULL || ra == NULL)
2735295367Sdes			fatal("sshkey_fingerprint failed");
273660573Skris		printf("Your public key has been saved in %s.\n",
273760573Skris		    identity_file);
273857429Smarkm		printf("The key fingerprint is:\n");
273976259Sgreen		printf("%s %s\n", fp, comment);
2740181111Sdes		printf("The key's randomart image is:\n");
2741181111Sdes		printf("%s\n", ra);
2742255767Sdes		free(ra);
2743255767Sdes		free(fp);
274457429Smarkm	}
274560573Skris
2746295367Sdes	sshkey_free(public);
274757429Smarkm	exit(0);
274857429Smarkm}
2749