ssh-keygen.c 78.6 KB
Newer Older
1
/* $OpenBSD: ssh-keygen.c,v 1.322 2018/09/14 04:17:44 djm Exp $ */
Damien Miller's avatar
Damien Miller committed
2
/*
3 4 5 6
 * Author: Tatu Ylonen <ylo@cs.hut.fi>
 * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
 *                    All rights reserved
 * Identity and host key generation and maintenance.
7 8 9 10 11 12
 *
 * As far as I am concerned, the code I have written for this software
 * can be used freely for any purpose.  Any derived versions of this
 * software must be clearly marked as such, and if the derived work is
 * incompatible with the protocol description in the RFC file, it must be
 * called by a name other than "ssh" or "Secure Shell".
13
 */
Damien Miller's avatar
Damien Miller committed
14 15

#include "includes.h"
16 17

#include <sys/types.h>
18
#include <sys/socket.h>
19
#include <sys/stat.h>
20

21
#ifdef WITH_OPENSSL
22 23
#include <openssl/evp.h>
#include <openssl/pem.h>
24
#include "openbsd-compat/openssl-compat.h"
25
#endif
Damien Miller's avatar
Damien Miller committed
26

27
#include <errno.h>
28
#include <fcntl.h>
29
#include <netdb.h>
30 31 32
#ifdef HAVE_PATHS_H
# include <paths.h>
#endif
33
#include <pwd.h>
34
#include <stdarg.h>
35
#include <stdio.h>
36
#include <stdlib.h>
37
#include <string.h>
38
#include <unistd.h>
deraadt@openbsd.org's avatar
deraadt@openbsd.org committed
39
#include <limits.h>
djm@openbsd.org's avatar
djm@openbsd.org committed
40
#include <locale.h>
Damien Miller's avatar
Damien Miller committed
41
#include <time.h>
42

Damien Miller's avatar
Damien Miller committed
43
#include "xmalloc.h"
djm@openbsd.org's avatar
djm@openbsd.org committed
44
#include "sshkey.h"
45 46
#include "authfile.h"
#include "uuencode.h"
djm@openbsd.org's avatar
djm@openbsd.org committed
47
#include "sshbuf.h"
48 49
#include "pathnames.h"
#include "log.h"
50
#include "misc.h"
51 52
#include "match.h"
#include "hostfile.h"
53
#include "dns.h"
54
#include "ssh.h"
Damien Miller's avatar
Damien Miller committed
55
#include "ssh2.h"
djm@openbsd.org's avatar
djm@openbsd.org committed
56
#include "ssherr.h"
57
#include "ssh-pkcs11.h"
58 59
#include "atomicio.h"
#include "krl.h"
djm@openbsd.org's avatar
djm@openbsd.org committed
60
#include "digest.h"
djm@openbsd.org's avatar
djm@openbsd.org committed
61
#include "utf8.h"
djm@openbsd.org's avatar
djm@openbsd.org committed
62
#include "authfd.h"
63

djm@openbsd.org's avatar
djm@openbsd.org committed
64 65 66 67 68 69
#ifdef WITH_OPENSSL
# define DEFAULT_KEY_TYPE_NAME "rsa"
#else
# define DEFAULT_KEY_TYPE_NAME "ed25519"
#endif

70 71 72
/* Number of bits in the RSA/DSA key.  This value can be set on the command line. */
#define DEFAULT_BITS		2048
#define DEFAULT_BITS_DSA	1024
73
#define DEFAULT_BITS_ECDSA	256
74
u_int32_t bits = 0;
Damien Miller's avatar
Damien Miller committed
75

Damien Miller's avatar
Damien Miller committed
76 77 78 79
/*
 * Flag indicating that we just want to change the passphrase.  This can be
 * set on the command line.
 */
Damien Miller's avatar
Damien Miller committed
80 81
int change_passphrase = 0;

Damien Miller's avatar
Damien Miller committed
82 83 84 85
/*
 * Flag indicating that we just want to change the comment.  This can be set
 * on the command line.
 */
Damien Miller's avatar
Damien Miller committed
86 87 88 89
int change_comment = 0;

int quiet = 0;

90 91
int log_level = SYSLOG_LEVEL_INFO;

92 93 94 95 96 97 98
/* Flag indicating that we want to hash a known_hosts file */
int hash_hosts = 0;
/* Flag indicating that we want lookup a host in known_hosts file */
int find_host = 0;
/* Flag indicating that we want to delete a host from a known_hosts file */
int delete_host = 0;

99 100 101
/* Flag indicating that we want to show the contents of a certificate */
int show_cert = 0;

102 103
/* Flag indicating that we just want to see the key fingerprint */
int print_fingerprint = 0;
104
int print_bubblebabble = 0;
105

djm@openbsd.org's avatar
djm@openbsd.org committed
106 107 108
/* Hash algorithm to use for fingerprints. */
int fingerprint_hash = SSH_FP_HASH_DEFAULT;

Damien Miller's avatar
Damien Miller committed
109 110 111
/* The identity file name, given on the command line or entered by the user. */
char identity_file[1024];
int have_identity = 0;
Damien Miller's avatar
Damien Miller committed
112 113 114 115 116 117 118 119 120 121

/* This is set to the passphrase if given on the command line. */
char *identity_passphrase = NULL;

/* This is set to the new passphrase if given on the command line. */
char *identity_new_passphrase = NULL;

/* This is set to the new comment if given on the command line. */
char *identity_comment = NULL;

Damien Miller's avatar
Damien Miller committed
122 123 124
/* Path to CA key when certifying keys. */
char *ca_key_path = NULL;

djm@openbsd.org's avatar
djm@openbsd.org committed
125 126 127
/* Prefer to use agent keys for CA signing */
int prefer_agent = 0;

128
/* Certificate serial number */
129
unsigned long long cert_serial = 0;
130

Damien Miller's avatar
Damien Miller committed
131 132 133 134 135 136 137 138 139 140 141 142 143
/* Key type when certifying */
u_int cert_key_type = SSH2_CERT_TYPE_USER;

/* "key ID" of signed key */
char *cert_key_id = NULL;

/* Comma-separated list of principal names for certifying keys */
char *cert_principals = NULL;

/* Validity period for certificates */
u_int64_t cert_valid_from = 0;
u_int64_t cert_valid_to = ~0ULL;

144
/* Certificate options */
145 146 147 148 149 150 151 152 153 154
#define CERTOPT_X_FWD	(1)
#define CERTOPT_AGENT_FWD	(1<<1)
#define CERTOPT_PORT_FWD	(1<<2)
#define CERTOPT_PTY		(1<<3)
#define CERTOPT_USER_RC	(1<<4)
#define CERTOPT_DEFAULT	(CERTOPT_X_FWD|CERTOPT_AGENT_FWD| \
			 CERTOPT_PORT_FWD|CERTOPT_PTY|CERTOPT_USER_RC)
u_int32_t certflags_flags = CERTOPT_DEFAULT;
char *certflags_command = NULL;
char *certflags_src_addr = NULL;
Damien Miller's avatar
Damien Miller committed
155

djm@openbsd.org's avatar
djm@openbsd.org committed
156 157 158 159 160 161 162 163 164
/* Arbitrary extensions specified by user */
struct cert_userext {
	char *key;
	char *val;
	int crit;
};
struct cert_userext *cert_userext;
size_t ncert_userext;

165 166 167 168 169 170 171 172
/* Conversion to/from various formats */
int convert_to = 0;
int convert_from = 0;
enum {
	FMT_RFC4716,
	FMT_PKCS8,
	FMT_PEM
} convert_format = FMT_RFC4716;
173
int print_public = 0;
174
int print_generic = 0;
175

176
char *key_type_name = NULL;
177

178 179
/* Load key from this PKCS#11 provider */
char *pkcs11provider = NULL;
180

181
/* Use new OpenSSH private key format when writing SSH2 keys instead of PEM */
182
int use_new_format = 1;
183 184 185 186 187 188 189 190 191 192

/* Cipher for new-format private keys */
char *new_format_cipher = NULL;

/*
 * Number of KDF rounds to derive new format keys /
 * number of primality trials when screening moduli.
 */
int rounds = 0;

Damien Miller's avatar
Damien Miller committed
193 194
/* argv0 */
extern char *__progname;
Damien Miller's avatar
Damien Miller committed
195

196
char hostname[NI_MAXHOST];
197

djm@openbsd.org's avatar
djm@openbsd.org committed
198
#ifdef WITH_OPENSSL
199
/* moduli.c */
200
int gen_candidates(FILE *, u_int32_t, u_int32_t, BIGNUM *);
201 202
int prime_test(FILE *, FILE *, u_int32_t, u_int32_t, char *, unsigned long,
    unsigned long);
djm@openbsd.org's avatar
djm@openbsd.org committed
203
#endif
204

205
static void
djm@openbsd.org's avatar
djm@openbsd.org committed
206
type_bits_valid(int type, const char *name, u_int32_t *bitsp)
207
{
208
#ifdef WITH_OPENSSL
djm@openbsd.org's avatar
djm@openbsd.org committed
209
	u_int maxbits, nid;
210
#endif
211

djm@openbsd.org's avatar
djm@openbsd.org committed
212 213
	if (type == KEY_UNSPEC)
		fatal("unknown key type %s", key_type_name);
214
	if (*bitsp == 0) {
215
#ifdef WITH_OPENSSL
216
		if (type == KEY_DSA)
217
			*bitsp = DEFAULT_BITS_DSA;
djm@openbsd.org's avatar
djm@openbsd.org committed
218 219 220 221 222 223
		else if (type == KEY_ECDSA) {
			if (name != NULL &&
			    (nid = sshkey_ecdsa_nid_from_name(name)) > 0)
				*bitsp = sshkey_curve_nid_to_bits(nid);
			if (*bitsp == 0)
				*bitsp = DEFAULT_BITS_ECDSA;
224 225
		} else
#endif
226
			*bitsp = DEFAULT_BITS;
227
	}
228
#ifdef WITH_OPENSSL
229 230
	maxbits = (type == KEY_DSA) ?
	    OPENSSL_DSA_MAX_MODULUS_BITS : OPENSSL_RSA_MAX_MODULUS_BITS;
djm@openbsd.org's avatar
djm@openbsd.org committed
231 232
	if (*bitsp > maxbits)
		fatal("key bits exceeds maximum %d", maxbits);
djm@openbsd.org's avatar
djm@openbsd.org committed
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
	switch (type) {
	case KEY_DSA:
		if (*bitsp != 1024)
			fatal("Invalid DSA key length: must be 1024 bits");
		break;
	case KEY_RSA:
		if (*bitsp < SSH_RSA_MINIMUM_MODULUS_SIZE)
			fatal("Invalid RSA key length: minimum is %d bits",
			    SSH_RSA_MINIMUM_MODULUS_SIZE);
		break;
	case KEY_ECDSA:
		if (sshkey_ecdsa_bits_to_nid(*bitsp) == -1)
			fatal("Invalid ECDSA key length: valid lengths are "
			    "256, 384 or 521 bits");
	}
248
#endif
249 250
}

251
static void
Damien Miller's avatar
Damien Miller committed
252
ask_filename(struct passwd *pw, const char *prompt)
Damien Miller's avatar
Damien Miller committed
253
{
254
	char buf[1024];
255 256
	char *name = NULL;

257
	if (key_type_name == NULL)
258
		name = _PATH_SSH_CLIENT_ID_RSA;
259
	else {
djm@openbsd.org's avatar
djm@openbsd.org committed
260
		switch (sshkey_type_from_name(key_type_name)) {
261
		case KEY_DSA_CERT:
262 263 264
		case KEY_DSA:
			name = _PATH_SSH_CLIENT_ID_DSA;
			break;
265
#ifdef OPENSSL_HAS_ECC
266 267 268 269
		case KEY_ECDSA_CERT:
		case KEY_ECDSA:
			name = _PATH_SSH_CLIENT_ID_ECDSA;
			break;
270
#endif
271
		case KEY_RSA_CERT:
272 273 274
		case KEY_RSA:
			name = _PATH_SSH_CLIENT_ID_RSA;
			break;
275 276 277 278
		case KEY_ED25519:
		case KEY_ED25519_CERT:
			name = _PATH_SSH_CLIENT_ID_ED25519;
			break;
279 280 281 282
		case KEY_XMSS:
		case KEY_XMSS_CERT:
			name = _PATH_SSH_CLIENT_ID_XMSS;
			break;
283
		default:
djm@openbsd.org's avatar
djm@openbsd.org committed
284
			fatal("bad key type");
285
		}
286
	}
djm@openbsd.org's avatar
djm@openbsd.org committed
287 288 289 290
	snprintf(identity_file, sizeof(identity_file),
	    "%s/%s", pw->pw_dir, name);
	printf("%s (%s): ", prompt, identity_file);
	fflush(stdout);
291 292
	if (fgets(buf, sizeof(buf), stdin) == NULL)
		exit(1);
293
	buf[strcspn(buf, "\n")] = '\0';
294 295 296
	if (strcmp(buf, "") != 0)
		strlcpy(identity_file, buf, sizeof(identity_file));
	have_identity = 1;
297
}
Damien Miller's avatar
Damien Miller committed
298

djm@openbsd.org's avatar
djm@openbsd.org committed
299
static struct sshkey *
300
load_identity(char *filename)
301
{
302
	char *pass;
djm@openbsd.org's avatar
djm@openbsd.org committed
303 304
	struct sshkey *prv;
	int r;
305

djm@openbsd.org's avatar
djm@openbsd.org committed
306 307 308 309 310 311 312 313 314 315 316 317 318
	if ((r = sshkey_load_private(filename, "", &prv, NULL)) == 0)
		return prv;
	if (r != SSH_ERR_KEY_WRONG_PASSPHRASE)
		fatal("Load key \"%s\": %s", filename, ssh_err(r));
	if (identity_passphrase)
		pass = xstrdup(identity_passphrase);
	else
		pass = read_passphrase("Enter passphrase: ", RP_ALLOW_STDIN);
	r = sshkey_load_private(filename, pass, &prv, NULL);
	explicit_bzero(pass, strlen(pass));
	free(pass);
	if (r != 0)
		fatal("Load key \"%s\": %s", filename, ssh_err(r));
319
	return prv;
320 321
}

322
#define SSH_COM_PUBLIC_BEGIN		"---- BEGIN SSH2 PUBLIC KEY ----"
323
#define SSH_COM_PUBLIC_END		"---- END SSH2 PUBLIC KEY ----"
324
#define SSH_COM_PRIVATE_BEGIN		"---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----"
325
#define	SSH_COM_PRIVATE_KEY_MAGIC	0x3f6ff9eb
326

327
#ifdef WITH_OPENSSL
328
static void
djm@openbsd.org's avatar
djm@openbsd.org committed
329
do_convert_to_ssh2(struct passwd *pw, struct sshkey *k)
330
{
djm@openbsd.org's avatar
djm@openbsd.org committed
331
	size_t len;
332
	u_char *blob;
333
	char comment[61];
djm@openbsd.org's avatar
djm@openbsd.org committed
334
	int r;
335

djm@openbsd.org's avatar
djm@openbsd.org committed
336 337
	if ((r = sshkey_to_blob(k, &blob, &len)) != 0)
		fatal("key_to_blob failed: %s", ssh_err(r));
338 339 340
	/* Comment + surrounds must fit into 72 chars (RFC 4716 sec 3.3) */
	snprintf(comment, sizeof(comment),
	    "%u-bit %s, converted by %s@%s from OpenSSH",
djm@openbsd.org's avatar
djm@openbsd.org committed
341
	    sshkey_size(k), sshkey_type(k),
342
	    pw->pw_name, hostname);
343 344 345

	fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN);
	fprintf(stdout, "Comment: \"%s\"\n", comment);
346
	dump_base64(stdout, blob, len);
347
	fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END);
djm@openbsd.org's avatar
djm@openbsd.org committed
348
	sshkey_free(k);
349
	free(blob);
350 351 352
	exit(0);
}

353
static void
djm@openbsd.org's avatar
djm@openbsd.org committed
354
do_convert_to_pkcs8(struct sshkey *k)
355
{
djm@openbsd.org's avatar
djm@openbsd.org committed
356
	switch (sshkey_type_plain(k->type)) {
357 358 359 360 361 362 363 364
	case KEY_RSA:
		if (!PEM_write_RSA_PUBKEY(stdout, k->rsa))
			fatal("PEM_write_RSA_PUBKEY failed");
		break;
	case KEY_DSA:
		if (!PEM_write_DSA_PUBKEY(stdout, k->dsa))
			fatal("PEM_write_DSA_PUBKEY failed");
		break;
365
#ifdef OPENSSL_HAS_ECC
366 367 368 369
	case KEY_ECDSA:
		if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa))
			fatal("PEM_write_EC_PUBKEY failed");
		break;
370
#endif
371
	default:
djm@openbsd.org's avatar
djm@openbsd.org committed
372
		fatal("%s: unsupported key type %s", __func__, sshkey_type(k));
373 374 375 376 377
	}
	exit(0);
}

static void
djm@openbsd.org's avatar
djm@openbsd.org committed
378
do_convert_to_pem(struct sshkey *k)
379
{
djm@openbsd.org's avatar
djm@openbsd.org committed
380
	switch (sshkey_type_plain(k->type)) {
381 382 383 384 385
	case KEY_RSA:
		if (!PEM_write_RSAPublicKey(stdout, k->rsa))
			fatal("PEM_write_RSAPublicKey failed");
		break;
	default:
djm@openbsd.org's avatar
djm@openbsd.org committed
386
		fatal("%s: unsupported key type %s", __func__, sshkey_type(k));
387 388 389 390 391 392 393
	}
	exit(0);
}

static void
do_convert_to(struct passwd *pw)
{
djm@openbsd.org's avatar
djm@openbsd.org committed
394
	struct sshkey *k;
395
	struct stat st;
djm@openbsd.org's avatar
djm@openbsd.org committed
396
	int r;
397 398 399 400 401

	if (!have_identity)
		ask_filename(pw, "Enter file in which the key is");
	if (stat(identity_file, &st) < 0)
		fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
djm@openbsd.org's avatar
djm@openbsd.org committed
402 403
	if ((r = sshkey_load_public(identity_file, &k, NULL)) != 0)
		k = load_identity(identity_file);
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
	switch (convert_format) {
	case FMT_RFC4716:
		do_convert_to_ssh2(pw, k);
		break;
	case FMT_PKCS8:
		do_convert_to_pkcs8(k);
		break;
	case FMT_PEM:
		do_convert_to_pem(k);
		break;
	default:
		fatal("%s: unknown key format %d", __func__, convert_format);
	}
	exit(0);
}

djm@openbsd.org's avatar
djm@openbsd.org committed
420 421 422 423
/*
 * This is almost exactly the bignum1 encoding, but with 32 bit for length
 * instead of 16.
 */
424
static void
djm@openbsd.org's avatar
djm@openbsd.org committed
425
buffer_get_bignum_bits(struct sshbuf *b, BIGNUM *value)
426
{
djm@openbsd.org's avatar
djm@openbsd.org committed
427 428 429 430 431 432 433 434 435 436 437 438 439
	u_int bytes, bignum_bits;
	int r;

	if ((r = sshbuf_get_u32(b, &bignum_bits)) != 0)
		fatal("%s: buffer error: %s", __func__, ssh_err(r));
	bytes = (bignum_bits + 7) / 8;
	if (sshbuf_len(b) < bytes)
		fatal("%s: input buffer too small: need %d have %zu",
		    __func__, bytes, sshbuf_len(b));
	if (BN_bin2bn(sshbuf_ptr(b), bytes, value) == NULL)
		fatal("%s: BN_bin2bn failed", __func__);
	if ((r = sshbuf_consume(b, bytes)) != 0)
		fatal("%s: buffer error: %s", __func__, ssh_err(r));
440 441
}

djm@openbsd.org's avatar
djm@openbsd.org committed
442
static struct sshkey *
443
do_convert_private_ssh2_from_blob(u_char *blob, u_int blen)
444
{
djm@openbsd.org's avatar
djm@openbsd.org committed
445 446
	struct sshbuf *b;
	struct sshkey *key = NULL;
447
	char *type, *cipher;
djm@openbsd.org's avatar
djm@openbsd.org committed
448 449 450 451
	u_char e1, e2, e3, *sig = NULL, data[] = "abcde12345";
	int r, rlen, ktype;
	u_int magic, i1, i2, i3, i4;
	size_t slen;
452
	u_long e;
453 454 455 456
	BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL;
	BIGNUM *dsa_pub_key = NULL, *dsa_priv_key = NULL;
	BIGNUM *rsa_n = NULL, *rsa_e = NULL, *rsa_d = NULL;
	BIGNUM *rsa_p = NULL, *rsa_q = NULL, *rsa_iqmp = NULL;
djm@openbsd.org's avatar
djm@openbsd.org committed
457 458 459 460
	if ((b = sshbuf_from(blob, blen)) == NULL)
		fatal("%s: sshbuf_from failed", __func__);
	if ((r = sshbuf_get_u32(b, &magic)) != 0)
		fatal("%s: buffer error: %s", __func__, ssh_err(r));
461 462

	if (magic != SSH_COM_PRIVATE_KEY_MAGIC) {
djm@openbsd.org's avatar
djm@openbsd.org committed
463 464 465
		error("bad magic 0x%x != 0x%x", magic,
		    SSH_COM_PRIVATE_KEY_MAGIC);
		sshbuf_free(b);
466 467
		return NULL;
	}
djm@openbsd.org's avatar
djm@openbsd.org committed
468 469 470 471 472 473 474
	if ((r = sshbuf_get_u32(b, &i1)) != 0 ||
	    (r = sshbuf_get_cstring(b, &type, NULL)) != 0 ||
	    (r = sshbuf_get_cstring(b, &cipher, NULL)) != 0 ||
	    (r = sshbuf_get_u32(b, &i2)) != 0 ||
	    (r = sshbuf_get_u32(b, &i3)) != 0 ||
	    (r = sshbuf_get_u32(b, &i4)) != 0)
		fatal("%s: buffer error: %s", __func__, ssh_err(r));
475
	debug("ignore (%d %d %d %d)", i1, i2, i3, i4);
476 477
	if (strcmp(cipher, "none") != 0) {
		error("unsupported cipher %s", cipher);
478
		free(cipher);
djm@openbsd.org's avatar
djm@openbsd.org committed
479
		sshbuf_free(b);
480
		free(type);
481 482
		return NULL;
	}
483
	free(cipher);
484

485 486 487 488 489
	if (strstr(type, "dsa")) {
		ktype = KEY_DSA;
	} else if (strstr(type, "rsa")) {
		ktype = KEY_RSA;
	} else {
djm@openbsd.org's avatar
djm@openbsd.org committed
490
		sshbuf_free(b);
491
		free(type);
492 493
		return NULL;
	}
494 495
	if ((key = sshkey_new(ktype)) == NULL)
		fatal("sshkey_new failed");
496
	free(type);
497 498 499

	switch (key->type) {
	case KEY_DSA:
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516
		if ((dsa_p = BN_new()) == NULL ||
		    (dsa_q = BN_new()) == NULL ||
		    (dsa_g = BN_new()) == NULL ||
		    (dsa_pub_key = BN_new()) == NULL ||
		    (dsa_priv_key = BN_new()) == NULL)
			fatal("%s: BN_new", __func__);
		buffer_get_bignum_bits(b, dsa_p);
		buffer_get_bignum_bits(b, dsa_g);
		buffer_get_bignum_bits(b, dsa_q);
		buffer_get_bignum_bits(b, dsa_pub_key);
		buffer_get_bignum_bits(b, dsa_priv_key);
		if (!DSA_set0_pqg(key->dsa, dsa_p, dsa_q, dsa_g))
			fatal("%s: DSA_set0_pqg failed", __func__);
		dsa_p = dsa_q = dsa_g = NULL; /* transferred */
		if (!DSA_set0_key(key->dsa, dsa_pub_key, dsa_priv_key))
			fatal("%s: DSA_set0_key failed", __func__);
		dsa_pub_key = dsa_priv_key = NULL; /* transferred */
517 518
		break;
	case KEY_RSA:
djm@openbsd.org's avatar
djm@openbsd.org committed
519 520 521 522 523
		if ((r = sshbuf_get_u8(b, &e1)) != 0 ||
		    (e1 < 30 && (r = sshbuf_get_u8(b, &e2)) != 0) ||
		    (e1 < 30 && (r = sshbuf_get_u8(b, &e3)) != 0))
			fatal("%s: buffer error: %s", __func__, ssh_err(r));
		e = e1;
524 525 526
		debug("e %lx", e);
		if (e < 30) {
			e <<= 8;
djm@openbsd.org's avatar
djm@openbsd.org committed
527
			e += e2;
528 529
			debug("e %lx", e);
			e <<= 8;
djm@openbsd.org's avatar
djm@openbsd.org committed
530
			e += e3;
531 532
			debug("e %lx", e);
		}
533 534 535 536
		if ((rsa_e = BN_new()) == NULL)
			fatal("%s: BN_new", __func__);
		if (!BN_set_word(rsa_e, e)) {
			BN_clear_free(rsa_e);
djm@openbsd.org's avatar
djm@openbsd.org committed
537 538
			sshbuf_free(b);
			sshkey_free(key);
539 540
			return NULL;
		}
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558
		if ((rsa_n = BN_new()) == NULL ||
		    (rsa_d = BN_new()) == NULL ||
		    (rsa_p = BN_new()) == NULL ||
		    (rsa_q = BN_new()) == NULL ||
		    (rsa_iqmp = BN_new()) == NULL)
			fatal("%s: BN_new", __func__);
		buffer_get_bignum_bits(b, rsa_d);
		buffer_get_bignum_bits(b, rsa_n);
		buffer_get_bignum_bits(b, rsa_iqmp);
		buffer_get_bignum_bits(b, rsa_q);
		buffer_get_bignum_bits(b, rsa_p);
		if (!RSA_set0_key(key->rsa, rsa_n, rsa_e, rsa_d))
			fatal("%s: RSA_set0_key failed", __func__);
		rsa_n = rsa_e = rsa_d = NULL; /* transferred */
		if (!RSA_set0_factors(key->rsa, rsa_p, rsa_q))
			fatal("%s: RSA_set0_factors failed", __func__);
		rsa_p = rsa_q = NULL; /* transferred */
		if ((r = ssh_rsa_complete_crt_parameters(key, rsa_iqmp)) != 0)
djm@openbsd.org's avatar
djm@openbsd.org committed
559
			fatal("generate RSA parameters failed: %s", ssh_err(r));
560
		BN_clear_free(rsa_iqmp);
561 562
		break;
	}
djm@openbsd.org's avatar
djm@openbsd.org committed
563
	rlen = sshbuf_len(b);
564
	if (rlen != 0)
565 566
		error("do_convert_private_ssh2_from_blob: "
		    "remaining bytes in key blob %d", rlen);
djm@openbsd.org's avatar
djm@openbsd.org committed
567
	sshbuf_free(b);
568 569

	/* try the key */
markus@openbsd.org's avatar
markus@openbsd.org committed
570
	if (sshkey_sign(key, &sig, &slen, data, sizeof(data), NULL, 0) != 0 ||
djm@openbsd.org's avatar
djm@openbsd.org committed
571
	    sshkey_verify(key, sig, slen, data, sizeof(data), NULL, 0) != 0) {
djm@openbsd.org's avatar
djm@openbsd.org committed
572 573 574 575
		sshkey_free(key);
		free(sig);
		return NULL;
	}
576
	free(sig);
577 578 579
	return key;
}

580 581 582 583 584 585 586 587
static int
get_line(FILE *fp, char *line, size_t len)
{
	int c;
	size_t pos = 0;

	line[0] = '\0';
	while ((c = fgetc(fp)) != EOF) {
djm@openbsd.org's avatar
djm@openbsd.org committed
588 589
		if (pos >= len - 1)
			fatal("input line too long.");
590
		switch (c) {
591 592
		case '\r':
			c = fgetc(fp);
djm@openbsd.org's avatar
djm@openbsd.org committed
593 594
			if (c != EOF && c != '\n' && ungetc(c, fp) == EOF)
				fatal("unget: %s", strerror(errno));
595 596 597 598 599 600 601
			return pos;
		case '\n':
			return pos;
		}
		line[pos++] = c;
		line[pos] = '\0';
	}
602 603
	/* We reached EOF */
	return -1;
604 605
}

606
static void
djm@openbsd.org's avatar
djm@openbsd.org committed
607
do_convert_from_ssh2(struct passwd *pw, struct sshkey **k, int *private)
608
{
djm@openbsd.org's avatar
djm@openbsd.org committed
609
	int r, blen, escaped = 0;
610
	u_int len;
611
	char line[1024];
612
	u_char blob[8096];
613 614 615
	char encoded[8096];
	FILE *fp;

616 617
	if ((fp = fopen(identity_file, "r")) == NULL)
		fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
618
	encoded[0] = '\0';
619
	while ((blen = get_line(fp, line, sizeof(line))) != -1) {
620
		if (blen > 0 && line[blen - 1] == '\\')
Damien Miller's avatar
Damien Miller committed
621
			escaped++;
622 623
		if (strncmp(line, "----", 4) == 0 ||
		    strstr(line, ": ") != NULL) {
624
			if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL)
625
				*private = 1;
626 627 628
			if (strstr(line, " END ") != NULL) {
				break;
			}
629
			/* fprintf(stderr, "ignore: %s", line); */
630 631
			continue;
		}
Damien Miller's avatar
Damien Miller committed
632 633
		if (escaped) {
			escaped--;
634
			/* fprintf(stderr, "escaped: %s", line); */
Damien Miller's avatar
Damien Miller committed
635
			continue;
636 637 638
		}
		strlcat(encoded, line, sizeof(encoded));
	}
639 640 641 642 643 644
	len = strlen(encoded);
	if (((len % 4) == 3) &&
	    (encoded[len-1] == '=') &&
	    (encoded[len-2] == '=') &&
	    (encoded[len-3] == '='))
		encoded[len-3] = '\0';
645
	blen = uudecode(encoded, blob, sizeof(blob));
djm@openbsd.org's avatar
djm@openbsd.org committed
646 647
	if (blen < 0)
		fatal("uudecode failed.");
djm@openbsd.org's avatar
djm@openbsd.org committed
648 649
	if (*private)
		*k = do_convert_private_ssh2_from_blob(blob, blen);
djm@openbsd.org's avatar
djm@openbsd.org committed
650 651
	else if ((r = sshkey_from_blob(blob, blen, k)) != 0)
		fatal("decode blob failed: %s", ssh_err(r));
652 653 654 655
	fclose(fp);
}

static void
djm@openbsd.org's avatar
djm@openbsd.org committed
656
do_convert_from_pkcs8(struct sshkey **k, int *private)
657 658 659 660 661 662 663 664 665 666 667
{
	EVP_PKEY *pubkey;
	FILE *fp;

	if ((fp = fopen(identity_file, "r")) == NULL)
		fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
	if ((pubkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) {
		fatal("%s: %s is not a recognised public key format", __func__,
		    identity_file);
	}
	fclose(fp);
668
	switch (EVP_PKEY_base_id(pubkey)) {
669
	case EVP_PKEY_RSA:
djm@openbsd.org's avatar
djm@openbsd.org committed
670 671
		if ((*k = sshkey_new(KEY_UNSPEC)) == NULL)
			fatal("sshkey_new failed");
672 673 674 675
		(*k)->type = KEY_RSA;
		(*k)->rsa = EVP_PKEY_get1_RSA(pubkey);
		break;
	case EVP_PKEY_DSA:
djm@openbsd.org's avatar
djm@openbsd.org committed
676 677
		if ((*k = sshkey_new(KEY_UNSPEC)) == NULL)
			fatal("sshkey_new failed");
678 679 680
		(*k)->type = KEY_DSA;
		(*k)->dsa = EVP_PKEY_get1_DSA(pubkey);
		break;
681
#ifdef OPENSSL_HAS_ECC
682
	case EVP_PKEY_EC:
djm@openbsd.org's avatar
djm@openbsd.org committed
683 684
		if ((*k = sshkey_new(KEY_UNSPEC)) == NULL)
			fatal("sshkey_new failed");
685 686
		(*k)->type = KEY_ECDSA;
		(*k)->ecdsa = EVP_PKEY_get1_EC_KEY(pubkey);
djm@openbsd.org's avatar
djm@openbsd.org committed
687
		(*k)->ecdsa_nid = sshkey_ecdsa_key_to_nid((*k)->ecdsa);
688
		break;
689
#endif
690 691
	default:
		fatal("%s: unsupported pubkey type %d", __func__,
692
		    EVP_PKEY_base_id(pubkey));
693 694 695 696 697 698
	}
	EVP_PKEY_free(pubkey);
	return;
}

static void
djm@openbsd.org's avatar
djm@openbsd.org committed
699
do_convert_from_pem(struct sshkey **k, int *private)
700 701 702 703 704 705 706
{
	FILE *fp;
	RSA *rsa;

	if ((fp = fopen(identity_file, "r")) == NULL)
		fatal("%s: %s: %s", __progname, identity_file, strerror(errno));
	if ((rsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL)) != NULL) {
djm@openbsd.org's avatar
djm@openbsd.org committed
707 708
		if ((*k = sshkey_new(KEY_UNSPEC)) == NULL)
			fatal("sshkey_new failed");
709 710 711 712 713 714 715 716 717 718 719
		(*k)->type = KEY_RSA;
		(*k)->rsa = rsa;
		fclose(fp);
		return;
	}
	fatal("%s: unrecognised raw private key format", __func__);
}

static void
do_convert_from(struct passwd *pw)
{
djm@openbsd.org's avatar
djm@openbsd.org committed
720 721
	struct sshkey *k = NULL;
	int r, private = 0, ok = 0;
722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742
	struct stat st;

	if (!have_identity)
		ask_filename(pw, "Enter file in which the key is");
	if (stat(identity_file, &st) < 0)
		fatal("%s: %s: %s", __progname, identity_file, strerror(errno));

	switch (convert_format) {
	case FMT_RFC4716:
		do_convert_from_ssh2(pw, &k, &private);
		break;
	case FMT_PKCS8:
		do_convert_from_pkcs8(&k, &private);
		break;
	case FMT_PEM:
		do_convert_from_pem(&k, &private);
		break;
	default:
		fatal("%s: unknown key format %d", __func__, convert_format);
	}

djm@openbsd.org's avatar
djm@openbsd.org committed
743
	if (!private) {
djm@openbsd.org's avatar
djm@openbsd.org committed
744 745
		if ((r = sshkey_write(k, stdout)) == 0)
			ok = 1;
746 747
		if (ok)
			fprintf(stdout, "\n");
djm@openbsd.org's avatar
djm@openbsd.org committed
748
	} else {
749 750 751 752 753
		switch (k->type) {
		case KEY_DSA:
			ok = PEM_write_DSAPrivateKey(stdout, k->dsa, NULL,
			    NULL, 0, NULL, NULL);
			break;
754
#ifdef OPENSSL_HAS_ECC
755 756 757 758
		case KEY_ECDSA:
			ok = PEM_write_ECPrivateKey(stdout, k->ecdsa, NULL,
			    NULL, 0, NULL, NULL);
			break;
759
#endif
760 761 762 763 764 765
		case KEY_RSA:
			ok = PEM_write_RSAPrivateKey(stdout, k->rsa, NULL,
			    NULL, 0, NULL, NULL);
			break;
		default:
			fatal("%s: unsupported key type %s", __func__,
djm@openbsd.org's avatar
djm@openbsd.org committed
766
			    sshkey_type(k));
767 768 769
		}
	}

djm@openbsd.org's avatar
djm@openbsd.org committed
770 771
	if (!ok)
		fatal("key write failed");
djm@openbsd.org's avatar
djm@openbsd.org committed
772
	sshkey_free(k);
773 774
	exit(0);
}
775
#endif
776

777
static void
778 779
do_print_public(struct passwd *pw)
{
djm@openbsd.org's avatar
djm@openbsd.org committed
780
	struct sshkey *prv;
781
	struct stat st;
djm@openbsd.org's avatar
djm@openbsd.org committed
782
	int r;
783 784 785

	if (!have_identity)
		ask_filename(pw, "Enter file in which the key is");
djm@openbsd.org's avatar
djm@openbsd.org committed
786 787
	if (stat(identity_file, &st) < 0)
		fatal("%s: %s", identity_file, strerror(errno));
788
	prv = load_identity(identity_file);
djm@openbsd.org's avatar
djm@openbsd.org committed
789
	if ((r = sshkey_write(prv, stdout)) != 0)
markus@openbsd.org's avatar
markus@openbsd.org committed
790
		error("sshkey_write failed: %s", ssh_err(r));
djm@openbsd.org's avatar
djm@openbsd.org committed
791
	sshkey_free(prv);
792 793 794 795
	fprintf(stdout, "\n");
	exit(0);
}

796
static void
797
do_download(struct passwd *pw)
798
{
799
#ifdef ENABLE_PKCS11
djm@openbsd.org's avatar
djm@openbsd.org committed
800
	struct sshkey **keys = NULL;
801
	int i, nkeys;
djm@openbsd.org's avatar
djm@openbsd.org committed
802
	enum sshkey_fp_rep rep;
djm@openbsd.org's avatar
djm@openbsd.org committed
803
	int fptype;
804
	char *fp, *ra;
805

djm@openbsd.org's avatar
djm@openbsd.org committed
806 807
	fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash;
	rep =    print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT;
808

809 810 811 812 813
	pkcs11_init(0);
	nkeys = pkcs11_add_provider(pkcs11provider, NULL, &keys);
	if (nkeys <= 0)
		fatal("cannot read public key from pkcs11");
	for (i = 0; i < nkeys; i++) {
814
		if (print_fingerprint) {
djm@openbsd.org's avatar
djm@openbsd.org committed
815 816
			fp = sshkey_fingerprint(keys[i], fptype, rep);
			ra = sshkey_fingerprint(keys[i], fingerprint_hash,
817
			    SSH_FP_RANDOMART);
djm@openbsd.org's avatar
djm@openbsd.org committed
818 819
			if (fp == NULL || ra == NULL)
				fatal("%s: sshkey_fingerprint fail", __func__);
djm@openbsd.org's avatar
djm@openbsd.org committed
820 821
			printf("%u %s %s (PKCS11 key)\n", sshkey_size(keys[i]),
			    fp, sshkey_type(keys[i]));
822 823
			if (log_level >= SYSLOG_LEVEL_VERBOSE)
				printf("%s\n", ra);
824 825
			free(ra);
			free(fp);
826
		} else {
djm@openbsd.org's avatar
djm@openbsd.org committed
827
			(void) sshkey_write(keys[i], stdout); /* XXX check */
828 829
			fprintf(stdout, "\n");
		}
djm@openbsd.org's avatar
djm@openbsd.org committed
830
		sshkey_free(keys[i]);
831
	}
832
	free(keys);
833
	pkcs11_terminate();
834
	exit(0);
835 836 837
#else
	fatal("no pkcs11 support");
#endif /* ENABLE_PKCS11 */
838
}
839

djm@openbsd.org's avatar
djm@openbsd.org committed
840 841 842 843 844 845 846 847 848 849 850 851 852 853 854
static struct sshkey *
try_read_key(char **cpp)
{
	struct sshkey *ret;
	int r;

	if ((ret = sshkey_new(KEY_UNSPEC)) == NULL)
		fatal("sshkey_new failed");
	if ((r = sshkey_read(ret, cpp)) == 0)
		return ret;
	/* Not a key */
	sshkey_free(ret);
	return NULL;
}

855
static void
djm@openbsd.org's avatar
djm@openbsd.org committed
856
fingerprint_one_key(const struct sshkey *public, const char *comment)
857
{
djm@openbsd.org's avatar
djm@openbsd.org committed
858
	char *fp = NULL, *ra = NULL;
djm@openbsd.org's avatar
djm@openbsd.org committed
859
	enum sshkey_fp_rep rep;
djm@openbsd.org's avatar
djm@openbsd.org committed
860
	int fptype;
861

djm@openbsd.org's avatar
djm@openbsd.org committed
862 863
	fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash;
	rep =    print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT;
djm@openbsd.org's avatar
djm@openbsd.org committed
864 865 866 867
	fp = sshkey_fingerprint(public, fptype, rep);
	ra = sshkey_fingerprint(public, fingerprint_hash, SSH_FP_RANDOMART);
	if (fp == NULL || ra == NULL)
		fatal("%s: sshkey_fingerprint failed", __func__);
djm@openbsd.org's avatar
djm@openbsd.org committed
868
	mprintf("%u %s %s (%s)\n", sshkey_size(public), fp,
djm@openbsd.org's avatar
djm@openbsd.org committed
869 870 871 872 873 874 875 876 877 878 879 880 881 882 883
	    comment ? comment : "no comment", sshkey_type(public));
	if (log_level >= SYSLOG_LEVEL_VERBOSE)
		printf("%s\n", ra);
	free(ra);
	free(fp);
}

static void
fingerprint_private(const char *path)
{
	struct stat st;
	char *comment = NULL;
	struct sshkey *public = NULL;
	int r;

djm@openbsd.org's avatar
djm@openbsd.org committed
884
	if (stat(identity_file, &st) < 0)
djm@openbsd.org's avatar
djm@openbsd.org committed
885
		fatal("%s: %s", path, strerror(errno));
djm@openbsd.org's avatar
djm@openbsd.org committed
886 887 888 889 890 891 892 893 894
	if ((r = sshkey_load_public(path, &public, &comment)) != 0) {
		debug("load public \"%s\": %s", path, ssh_err(r));
		if ((r = sshkey_load_private(path, NULL,
		    &public, &comment)) != 0) {
			debug("load private \"%s\": %s", path, ssh_err(r));
			fatal("%s is not a key file.", path);
		}
	}

djm@openbsd.org's avatar
djm@openbsd.org committed
895 896 897 898
	fingerprint_one_key(public, comment);
	sshkey_free(public);
	free(comment);
}
899

djm@openbsd.org's avatar
djm@openbsd.org committed
900 901 902 903 904
static void
do_fingerprint(struct passwd *pw)
{
	FILE *f;
	struct sshkey *public = NULL;
905 906
	char *comment = NULL, *cp, *ep, *line = NULL;
	size_t linesize = 0;
djm@openbsd.org's avatar
djm@openbsd.org committed
907 908
	int i, invalid = 1;
	const char *path;
djm@openbsd.org's avatar
djm@openbsd.org committed
909
	u_long lnum = 0;
910

djm@openbsd.org's avatar
djm@openbsd.org committed
911 912 913 914 915 916 917 918 919 920
	if (!have_identity)
		ask_filename(pw, "Enter file in which the key is");
	path = identity_file;

	if (strcmp(identity_file, "-") == 0) {
		f = stdin;
		path = "(stdin)";
	} else if ((f = fopen(path, "r")) == NULL)
		fatal("%s: %s: %s", __progname, path, strerror(errno));

921 922
	while (getline(&line, &linesize, f) != -1) {
		lnum++;
djm@openbsd.org's avatar
djm@openbsd.org committed
923 924 925 926 927
		cp = line;
		cp[strcspn(cp, "\n")] = '\0';
		/* Trim leading space and comments */
		cp = line + strspn(line, " \t");
		if (*cp == '#' || *cp == '\0')
928
			continue;
djm@openbsd.org's avatar
djm@openbsd.org committed
929 930 931 932 933 934 935 936 937 938 939 940

		/*
		 * Input may be plain keys, private keys, authorized_keys
		 * or known_hosts.
		 */

		/*
		 * Try private keys first. Assume a key is private if
		 * "SSH PRIVATE KEY" appears on the first line and we're
		 * not reading from stdin (XXX support private keys on stdin).
		 */
		if (lnum == 1 && strcmp(identity_file, "-") != 0 &&
djm@openbsd.org's avatar
djm@openbsd.org committed
941
		    strstr(cp, "PRIVATE KEY") != NULL) {
942
			free(line);
djm@openbsd.org's avatar
djm@openbsd.org committed
943 944 945
			fclose(f);
			fingerprint_private(path);
			exit(0);
946
		}
djm@openbsd.org's avatar
djm@openbsd.org committed
947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973

		/*
		 * If it's not a private key, then this must be prepared to
		 * accept a public key prefixed with a hostname or options.
		 * Try a bare key first, otherwise skip the leading stuff.
		 */
		if ((public = try_read_key(&cp)) == NULL) {
			i = strtol(cp, &ep, 10);
			if (i == 0 || ep == NULL ||
			    (*ep != ' ' && *ep != '\t')) {
				int quoted = 0;

				comment = cp;
				for (; *cp && (quoted || (*cp != ' ' &&
				    *cp != '\t')); cp++) {
					if (*cp == '\\' && cp[1] == '"')
						cp++;	/* Skip both */
					else if (*cp == '"')
						quoted = !quoted;
				}
				if (!*cp)
					continue;
				*cp++ = '\0';
			}
		}
		/* Retry after parsing leading hostname/key options */
		if (public == NULL && (public = try_read_key(&cp)) == NULL) {
djm@openbsd.org's avatar
djm@openbsd.org committed
974
			debug("%s:%lu: not a public key", path, lnum);
975 976 977
			continue;
		}

djm@openbsd.org's avatar
djm@openbsd.org committed
978 979
		/* Find trailing comment, if any */
		for (; *cp == ' ' || *cp == '\t'; cp++)
980
			;
djm@openbsd.org's avatar
djm@openbsd.org committed
981
		if (*cp != '\0' && *cp != '#')
982
			comment = cp;
djm@openbsd.org's avatar
djm@openbsd.org committed
983 984

		fingerprint_one_key(public, comment);
djm@openbsd.org's avatar
djm@openbsd.org committed
985
		sshkey_free(public);
djm@openbsd.org's avatar
djm@openbsd.org committed
986
		invalid = 0; /* One good key in the file is sufficient */
987
	}
988
	fclose(f);
989
	free(line);
990

djm@openbsd.org's avatar
djm@openbsd.org committed
991
	if (invalid)
djm@openbsd.org's avatar
djm@openbsd.org committed
992
		fatal("%s is not a public key file.", path);
993
	exit(0);
994 995
}

996 997 998 999 1000 1001 1002 1003
static void
do_gen_all_hostkeys(struct passwd *pw)
{
	struct {
		char *key_type;
		char *key_type_display;
		char *path;
	} key_types[] = {
djm@openbsd.org's avatar
djm@openbsd.org committed
1004
#ifdef WITH_OPENSSL
1005 1006
		{ "rsa", "RSA" ,_PATH_HOST_RSA_KEY_FILE },
		{ "dsa", "DSA", _PATH_HOST_DSA_KEY_FILE },
1007
#ifdef OPENSSL_HAS_ECC
1008
		{ "ecdsa", "ECDSA",_PATH_HOST_ECDSA_KEY_FILE },
djm@openbsd.org's avatar
djm@openbsd.org committed
1009 1010
#endif /* OPENSSL_HAS_ECC */
#endif /* WITH_OPENSSL */
1011
		{ "ed25519", "ED25519",_PATH_HOST_ED25519_KEY_FILE },
1012 1013 1014
#ifdef WITH_XMSS
		{ "xmss", "XMSS",_PATH_HOST_XMSS_KEY_FILE },
#endif /* WITH_XMSS */
1015 1016 1017 1018 1019
		{ NULL, NULL, NULL }
	};

	int first = 0;
	struct stat st;
djm@openbsd.org's avatar
djm@openbsd.org committed
1020
	struct sshkey *private, *public;
djm@openbsd.org's avatar
djm@openbsd.org committed
1021
	char comment[1024], *prv_tmp, *pub_tmp, *prv_file, *pub_file;
djm@openbsd.org's avatar
djm@openbsd.org committed
1022
	int i, type, fd, r;
1023 1024 1025
	FILE *f;

	for (i = 0; key_types[i].key_type; i++) {
djm@openbsd.org's avatar
djm@openbsd.org committed
1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036
		public = private = NULL;
		prv_tmp = pub_tmp = prv_file = pub_file = NULL;

		xasprintf(&prv_file, "%s%s",
		    identity_file, key_types[i].path);

		/* Check whether private key exists and is not zero-length */
		if (stat(prv_file, &st) == 0) {
			if (st.st_size != 0)
				goto next;
		} else if (errno != ENOENT) {
djm@openbsd.org's avatar
djm@openbsd.org committed
1037
			error("Could not stat %s: %s", key_types[i].path,
1038
			    strerror(errno));
djm@openbsd.org's avatar
djm@openbsd.org committed
1039
			goto failnext;
1040 1041
		}

djm@openbsd.org's avatar
djm@openbsd.org committed
1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052
		/*
		 * Private key doesn't exist or is invalid; proceed with
		 * key generation.
		 */
		xasprintf(&prv_tmp, "%s%s.XXXXXXXXXX",
		    identity_file, key_types[i].path);
		xasprintf(&pub_tmp, "%s%s.pub.XXXXXXXXXX",
		    identity_file, key_types[i].path);
		xasprintf(&pub_file, "%s%s.pub",
		    identity_file, key_types[i].path);

1053 1054 1055 1056 1057 1058
		if (first == 0) {
			first = 1;
			printf("%s: generating new host keys: ", __progname);
		}
		printf("%s ", key_types[i].key_type_display);
		fflush(stdout);
djm@openbsd.org's avatar
djm@openbsd.org committed
1059
		type = sshkey_type_from_name(key_types[i].key_type);
djm@openbsd.org's avatar
djm@openbsd.org committed
1060 1061 1062 1063 1064 1065
		if ((fd = mkstemp(prv_tmp)) == -1) {
			error("Could not save your public key in %s: %s",
			    prv_tmp, strerror(errno));
			goto failnext;
		}
		close(fd); /* just using mkstemp() to generate/reserve a name */
1066
		bits = 0;
djm@openbsd.org's avatar
djm@openbsd.org committed
1067
		type_bits_valid(type, NULL, &bits);
djm@openbsd.org's avatar
djm@openbsd.org committed
1068
		if ((r = sshkey_generate(type, bits, &private)) != 0) {
markus@openbsd.org's avatar
markus@openbsd.org committed
1069
			error("sshkey_generate failed: %s", ssh_err(r));
djm@openbsd.org's avatar
djm@openbsd.org committed
1070
			goto failnext;
1071
		}
djm@openbsd.org's avatar
djm@openbsd.org committed
1072 1073
		if ((r = sshkey_from_private(private, &public)) != 0)
			fatal("sshkey_from_private failed: %s", ssh_err(r));
1074 1075
		snprintf(comment, sizeof comment, "%s@%s", pw->pw_name,
		    hostname);
djm@openbsd.org's avatar
djm@openbsd.org committed
1076
		if ((r = sshkey_save_private(private, prv_tmp, "",
djm@openbsd.org's avatar
djm@openbsd.org committed
1077
		    comment, use_new_format, new_format_cipher, rounds)) != 0) {
djm@openbsd.org's avatar
djm@openbsd.org committed
1078
			error("Saving key \"%s\" failed: %s",
djm@openbsd.org's avatar
djm@openbsd.org committed
1079 1080
			    prv_tmp, ssh_err(r));
			goto failnext;
1081
		}
djm@openbsd.org's avatar
djm@openbsd.org committed
1082 1083 1084 1085
		if ((fd = mkstemp(pub_tmp)) == -1) {
			error("Could not save your public key in %s: %s",
			    pub_tmp, strerror(errno));
			goto failnext;
1086
		}
djm@openbsd.org's avatar
djm@openbsd.org committed
1087
		(void)fchmod(fd, 0644);
1088 1089
		f = fdopen(fd, "w");
		if (f == NULL) {
djm@openbsd.org's avatar
djm@openbsd.org committed
1090
			error("fdopen %s failed: %s", pub_tmp, strerror(errno));
doug@openbsd.org's avatar
doug@openbsd.org committed
1091
			close(fd);
djm@openbsd.org's avatar
djm@openbsd.org committed
1092
			goto failnext;
1093
		}
djm@openbsd.org's avatar
djm@openbsd.org committed
1094
		if ((r = sshkey_write(public, f)) != 0) {
djm@openbsd.org's avatar
djm@openbsd.org committed
1095
			error("write key failed: %s", ssh_err(r));
doug@openbsd.org's avatar
doug@openbsd.org committed
1096
			fclose(f);
djm@openbsd.org's avatar
djm@openbsd.org committed
1097
			goto failnext;
1098 1099
		}
		fprintf(f, " %s\n", comment);
djm@openbsd.org's avatar
djm@openbsd.org committed
1100 1101 1102 1103 1104 1105 1106 1107 1108
		if (ferror(f) != 0) {
			error("write key failed: %s", strerror(errno));
			fclose(f);
			goto failnext;
		}
		if (fclose(f) != 0) {
			error("key close failed: %s", strerror(errno));
			goto failnext;
		}
1109

djm@openbsd.org's avatar
djm@openbsd.org committed
1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129
		/* Rename temporary files to their permanent locations. */
		if (rename(pub_tmp, pub_file) != 0) {
			error("Unable to move %s into position: %s",
			    pub_file, strerror(errno));
			goto failnext;
		}
		if (rename(prv_tmp, prv_file) != 0) {
			error("Unable to move %s into position: %s",
			    key_types[i].path, strerror(errno));
 failnext:
			first = 0;
			goto next;
		}
 next:
		sshkey_free(private);
		sshkey_free(public);
		free(prv_tmp);
		free(pub_tmp);
		free(prv_file);
		free(pub_file);
1130 1131 1132 1133 1134
	}
	if (first != 0)
		printf("\n");
}

djm@openbsd.org's avatar
djm@openbsd.org committed
1135
struct known_hosts_ctx {
djm@openbsd.org's avatar
djm@openbsd.org committed
1136 1137 1138 1139 1140
	const char *host;	/* Hostname searched for in find/delete case */
	FILE *out;		/* Output file, stdout for find_hosts case */
	int has_unhashed;	/* When hashing, original had unhashed hosts */
	int found_key;		/* For find/delete, host was found */
	int invalid;		/* File contained invalid items; don't delete */
djm@openbsd.org's avatar
djm@openbsd.org committed
1141 1142 1143 1144
};

static int
known_hosts_hash(struct hostkey_foreach_line *l, void *_ctx)
1145
{
djm@openbsd.org's avatar
djm@openbsd.org committed
1146 1147 1148
	struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx;
	char *hashed, *cp, *hosts, *ohosts;
	int has_wild = l->hosts && strcspn(l->hosts, "*?!") != strlen(l->hosts);
dtucker@openbsd.org's avatar
dtucker@openbsd.org committed
1149
	int was_hashed = l->hosts && l->hosts[0] == HASH_DELIM;
djm@openbsd.org's avatar
djm@openbsd.org committed
1150

djm@openbsd.org's avatar
djm@openbsd.org committed
1151 1152 1153 1154 1155 1156 1157
	switch (l->status) {
	case HKF_STATUS_OK:
	case HKF_STATUS_MATCHED:
		/*
		 * Don't hash hosts already already hashed, with wildcard
		 * characters or a CA/revocation marker.
		 */
djm@openbsd.org's avatar
djm@openbsd.org committed
1158
		if (was_hashed || has_wild || l->marker != MRK_NONE) {
djm@openbsd.org's avatar
djm@openbsd.org committed
1159 1160
			fprintf(ctx->out, "%s\n", l->line);
			if (has_wild && !find_host) {
dtucker@openbsd.org's avatar
dtucker@openbsd.org committed
1161
				logit("%s:%lu: ignoring host name "
djm@openbsd.org's avatar
djm@openbsd.org committed
1162
				    "with wildcard: %.64s", l->path,
djm@openbsd.org's avatar
djm@openbsd.org committed
1163 1164 1165 1166 1167 1168 1169 1170 1171 1172
				    l->linenum, l->hosts);
			}
			return 0;
		}
		/*
		 * Split any comma-separated hostnames from the host list,
		 * hash and store separately.
		 */
		ohosts = hosts = xstrdup(l->hosts);
		while ((cp = strsep(&hosts, ",")) != NULL && *cp != '\0') {
djm@openbsd.org's avatar
djm@openbsd.org committed
1173
			lowercase(cp);
djm@openbsd.org's avatar
djm@openbsd.org committed
1174 1175 1176 1177 1178 1179 1180 1181 1182
			if ((hashed = host_hash(cp, NULL, 0)) == NULL)
				fatal("hash_host failed");
			fprintf(ctx->out, "%s %s\n", hashed, l->rawkey);
			ctx->has_unhashed = 1;
		}
		free(ohosts);
		return 0;
	case HKF_STATUS_INVALID:
		/* Retain invalid lines, but mark file as invalid. */
djm@openbsd.org's avatar
djm@openbsd.org committed
1183
		ctx->invalid = 1;
dtucker@openbsd.org's avatar
dtucker@openbsd.org committed
1184
		logit("%s:%lu: invalid line", l->path, l->linenum);
djm@openbsd.org's avatar
djm@openbsd.org committed
1185 1186
		/* FALLTHROUGH */
	default:
djm@openbsd.org's avatar
djm@openbsd.org committed
1187 1188 1189
		fprintf(ctx->out, "%s\n", l->line);
		return 0;
	}
djm@openbsd.org's avatar
djm@openbsd.org committed
1190 1191
	/* NOTREACHED */
	return -1;
djm@openbsd.org's avatar
djm@openbsd.org committed
1192 1193 1194 1195 1196 1197
}

static int
known_hosts_find_delete(struct hostkey_foreach_line *l, void *_ctx)
{
	struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx;
djm@openbsd.org's avatar
djm@openbsd.org committed
1198 1199 1200 1201 1202 1203
	enum sshkey_fp_rep rep;
	int fptype;
	char *fp;

	fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash;
	rep =    print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT;
djm@openbsd.org's avatar
djm@openbsd.org committed
1204

djm@openbsd.org's avatar
djm@openbsd.org committed
1205
	if (l->status == HKF_STATUS_MATCHED) {
djm@openbsd.org's avatar
djm@openbsd.org committed
1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217
		if (delete_host) {
			if (l->marker != MRK_NONE) {
				/* Don't remove CA and revocation lines */
				fprintf(ctx->out, "%s\n", l->line);
			} else {
				/*
				 * Hostname matches and has no CA/revoke
				 * marker, delete it by *not* writing the
				 * line to ctx->out.
				 */
				ctx->found_key = 1;
				if (!quiet)
dtucker@openbsd.org's avatar
dtucker@openbsd.org committed
1218
					printf("# Host %s found: line %lu\n",
djm@openbsd.org's avatar
djm@openbsd.org committed
1219 1220 1221 1222 1223 1224
					    ctx->host, l->linenum);
			}
			return 0;
		} else if (find_host) {
			ctx->found_key = 1;
			if (!quiet) {
dtucker@openbsd.org's avatar
dtucker@openbsd.org committed
1225
				printf("# Host %s found: line %lu %s\n",
djm@openbsd.org's avatar
djm@openbsd.org committed
1226 1227 1228 1229 1230 1231
				    ctx->host,
				    l->linenum, l->marker == MRK_CA ? "CA" :
				    (l->marker == MRK_REVOKE ? "REVOKED" : ""));
			}
			if (hash_hosts)
				known_hosts_hash(l, ctx);
djm@openbsd.org's avatar
djm@openbsd.org committed
1232 1233
			else if (print_fingerprint) {
				fp = sshkey_fingerprint(l->key, fptype, rep);
djm@openbsd.org's avatar
djm@openbsd.org committed
1234
				mprintf("%s %s %s %s\n", ctx->host,
djm@openbsd.org's avatar
djm@openbsd.org committed
1235 1236 1237
				    sshkey_type(l->key), fp, l->comment);
				free(fp);
			} else
djm@openbsd.org's avatar
djm@openbsd.org committed
1238 1239 1240 1241 1242 1243 1244
				fprintf(ctx->out, "%s\n", l->line);
			return 0;
		}
	} else if (delete_host) {
		/* Retain non-matching hosts when deleting */
		if (l->status == HKF_STATUS_INVALID) {
			ctx->invalid = 1;
dtucker@openbsd.org's avatar
dtucker@openbsd.org committed
1245
			logit("%s:%lu: invalid line", l->path, l->linenum);
djm@openbsd.org's avatar
djm@openbsd.org committed
1246 1247
		}
		fprintf(ctx->out, "%s\n", l->line);
1248
	}
djm@openbsd.org's avatar
djm@openbsd.org committed
1249
	return 0;
1250 1251 1252 1253 1254
}

static void
do_known_hosts(struct passwd *pw, const char *name)
{
deraadt@openbsd.org's avatar
deraadt@openbsd.org committed
1255
	char *cp, tmp[PATH_MAX], old[PATH_MAX];
djm@openbsd.org's avatar
djm@openbsd.org committed
1256
	int r, fd, oerrno, inplace = 0;
djm@openbsd.org's avatar
djm@openbsd.org committed
1257
	struct known_hosts_ctx ctx;
djm@openbsd.org's avatar
djm@openbsd.org committed
1258
	u_int foreach_options;
1259 1260 1261 1262 1263 1264

	if (!have_identity) {
		cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid);
		if (strlcpy(identity_file, cp, sizeof(identity_file)) >=
		    sizeof(identity_file))
			fatal("Specified known hosts path too long");
1265
		free(cp);
1266 1267 1268
		have_identity = 1;
	}

djm@openbsd.org's avatar
djm@openbsd.org committed
1269 1270 1271 1272
	memset(&ctx, 0, sizeof(ctx));
	ctx.out = stdout;
	ctx.host = name;

1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283
	/*
	 * Find hosts goes to stdout, hash and deletions happen in-place
	 * A corner case is ssh-keygen -HF foo, which should go to stdout
	 */
	if (!find_host && (hash_hosts || delete_host)) {
		if (strlcpy(tmp, identity_file, sizeof(tmp)) >= sizeof(tmp) ||
		    strlcat(tmp, ".XXXXXXXXXX", sizeof(tmp)) >= sizeof(tmp) ||
		    strlcpy(old, identity_file, sizeof(old)) >= sizeof(old) ||
		    strlcat(old, ".old", sizeof(old)) >= sizeof(old))
			fatal("known_hosts path too long");
		umask(077);
djm@openbsd.org's avatar
djm@openbsd.org committed
1284
		if ((fd = mkstemp(tmp)) == -1)
1285
			fatal("mkstemp: %s", strerror(errno));
djm@openbsd.org's avatar
djm@openbsd.org committed
1286 1287
		if ((ctx.out = fdopen