예제 #1
0
/*
 * Match keys against a specified key, or look one up by key type.
 *
 * If looking for a keytype (key == NULL) and one is found then return
 * HOST_FOUND, otherwise HOST_NEW.
 *
 * If looking for a key (key != NULL):
 *  1. If the key is a cert and a matching CA is found, return HOST_OK
 *  2. If the key is not a cert and a matching key is found, return HOST_OK
 *  3. If no key matches but a key with a different type is found, then
 *     return HOST_CHANGED
 *  4. If no matching keys are found, then return HOST_NEW.
 *
 * Finally, check any found key is not revoked.
 */
static HostStatus
check_hostkeys_by_key_or_type(struct hostkeys *hostkeys,
    struct sshkey *k, int keytype, const struct hostkey_entry **found)
{
	u_int i;
	HostStatus end_return = HOST_NEW;
	int want_cert = sshkey_is_cert(k);
	HostkeyMarker want_marker = want_cert ? MRK_CA : MRK_NONE;
	int proto = (k ? k->type : keytype) == KEY_RSA1 ? 1 : 2;

	if (found != NULL)
		*found = NULL;

	for (i = 0; i < hostkeys->num_entries; i++) {
		if (proto == 1 && hostkeys->entries[i].key->type != KEY_RSA1)
			continue;
		if (proto == 2 && hostkeys->entries[i].key->type == KEY_RSA1)
			continue;
		if (hostkeys->entries[i].marker != want_marker)
			continue;
		if (k == NULL) {
			if (hostkeys->entries[i].key->type != keytype)
				continue;
			end_return = HOST_FOUND;
			if (found != NULL)
				*found = hostkeys->entries + i;
			k = hostkeys->entries[i].key;
			break;
		}
		if (want_cert) {
			if (sshkey_equal_public(k->cert->signature_key,
			    hostkeys->entries[i].key)) {
				/* A matching CA exists */
				end_return = HOST_OK;
				if (found != NULL)
					*found = hostkeys->entries + i;
				break;
			}
		} else {
			if (sshkey_equal(k, hostkeys->entries[i].key)) {
				end_return = HOST_OK;
				if (found != NULL)
					*found = hostkeys->entries + i;
				break;
			}
			/* A non-maching key exists */
			end_return = HOST_CHANGED;
			if (found != NULL)
				*found = hostkeys->entries + i;
		}
	}
	if (check_key_not_revoked(hostkeys, k) != 0) {
		end_return = HOST_REVOKED;
		if (found != NULL)
			*found = NULL;
	}
	return end_return;
}
예제 #2
0
static int
host_delete(struct hostkey_foreach_line *l, void *_ctx)
{
	struct host_delete_ctx *ctx = (struct host_delete_ctx *)_ctx;
	int loglevel = ctx->quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_VERBOSE;
	size_t i;

	if (l->status == HKF_STATUS_MATCHED) {
		if (l->marker != MRK_NONE) {
			/* Don't remove CA and revocation lines */
			fprintf(ctx->out, "%s\n", l->line);
			return 0;
		}

		/* XXX might need a knob for this later */
		/* Don't remove RSA1 keys */
		if (l->key->type == KEY_RSA1) {
			fprintf(ctx->out, "%s\n", l->line);
			return 0;
		}

		/*
		 * If this line contains one of the keys that we will be
		 * adding later, then don't change it and mark the key for
		 * skipping.
		 */
		for (i = 0; i < ctx->nkeys; i++) {
			if (sshkey_equal(ctx->keys[i], l->key)) {
				ctx->skip_keys[i] = 1;
				fprintf(ctx->out, "%s\n", l->line);
				debug3("%s: %s key already at %s:%ld", __func__,
				    sshkey_type(l->key), l->path, l->linenum);
				return 0;
			}
		}

		/*
		 * Hostname matches and has no CA/revoke marker, delete it
		 * by *not* writing the line to ctx->out.
		 */
		do_log2(loglevel, "%s%s%s:%ld: Removed %s key for host %s",
		    ctx->quiet ? __func__ : "", ctx->quiet ? ": " : "",
		    l->path, l->linenum, sshkey_type(l->key), ctx->host);
		ctx->modified = 1;
		return 0;
	}
	/* Retain non-matching hosts and invalid lines when deleting */
	if (l->status == HKF_STATUS_INVALID) {
		do_log2(loglevel, "%s%s%s:%ld: invalid known_hosts entry",
		    ctx->quiet ? __func__ : "", ctx->quiet ? ": " : "",
		    l->path, l->linenum);
	}
	fprintf(ctx->out, "%s\n", l->line);
	return 0;
}
예제 #3
0
/* returns 0 if key verifies or -1 if key does NOT verify */
int
verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key)
{
	int r = -1, flags = 0;
	char *fp = NULL;
	struct sshkey *plain = NULL;

	if ((fp = sshkey_fingerprint(host_key,
	    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
		error("%s: fingerprint host key: %s", __func__, ssh_err(r));
		r = -1;
		goto out;
	}

	debug("Server host key: %s %s",
	    compat20 ? sshkey_ssh_name(host_key) : sshkey_type(host_key), fp);

	if (sshkey_equal(previous_host_key, host_key)) {
		debug2("%s: server host key %s %s matches cached key",
		    __func__, sshkey_type(host_key), fp);
		r = 0;
		goto out;
	}

	/* Check in RevokedHostKeys file if specified */
	if (options.revoked_host_keys != NULL) {
		r = sshkey_check_revoked(host_key, options.revoked_host_keys);
		switch (r) {
		case 0:
			break; /* not revoked */
		case SSH_ERR_KEY_REVOKED:
			error("Host key %s %s revoked by file %s",
			    sshkey_type(host_key), fp,
			    options.revoked_host_keys);
			r = -1;
			goto out;
		default:
			error("Error checking host key %s %s in "
			    "revoked keys file %s: %s", sshkey_type(host_key),
			    fp, options.revoked_host_keys, ssh_err(r));
			r = -1;
			goto out;
		}
	}

	if (options.verify_host_key_dns) {
		/*
		 * XXX certs are not yet supported for DNS, so downgrade
		 * them and try the plain key.
		 */
		if ((r = sshkey_from_private(host_key, &plain)) != 0)
			goto out;
		if (sshkey_is_cert(plain))
			sshkey_drop_cert(plain);
		if (verify_host_key_dns(host, hostaddr, plain, &flags) == 0) {
			if (flags & DNS_VERIFY_FOUND) {
				if (options.verify_host_key_dns == 1 &&
				    flags & DNS_VERIFY_MATCH &&
				    flags & DNS_VERIFY_SECURE) {
					r = 0;
					goto out;
				}
				if (flags & DNS_VERIFY_MATCH) {
					matching_host_key_dns = 1;
				} else {
					warn_changed_key(plain);
					error("Update the SSHFP RR in DNS "
					    "with the new host key to get rid "
					    "of this message.");
				}
			}
		}
	}
	r = check_host_key(host, hostaddr, options.port, host_key, RDRW,
	    options.user_hostfiles, options.num_user_hostfiles,
	    options.system_hostfiles, options.num_system_hostfiles);

out:
	sshkey_free(plain);
	free(fp);
	if (r == 0 && host_key != NULL) {
		key_free(previous_host_key);
		previous_host_key = key_from_private(host_key);
	}

	return r;
}
예제 #4
0
void
sshkey_tests(void)
{
	struct sshkey *k1, *k2, *k3, *k4, *kr, *kd, *ke, *kf;
	struct sshbuf *b;

	TEST_START("new invalid");
	k1 = sshkey_new(-42);
	ASSERT_PTR_EQ(k1, NULL);
	TEST_DONE();

	TEST_START("new/free KEY_UNSPEC");
	k1 = sshkey_new(KEY_UNSPEC);
	ASSERT_PTR_NE(k1, NULL);
	sshkey_free(k1);
	TEST_DONE();

	TEST_START("new/free KEY_RSA1");
	k1 = sshkey_new(KEY_RSA1);
	ASSERT_PTR_NE(k1, NULL);
	ASSERT_PTR_NE(k1->rsa, NULL);
	ASSERT_PTR_NE(k1->rsa->n, NULL);
	ASSERT_PTR_NE(k1->rsa->e, NULL);
	ASSERT_PTR_EQ(k1->rsa->p, NULL);
	sshkey_free(k1);
	TEST_DONE();

	TEST_START("new/free KEY_RSA");
	k1 = sshkey_new(KEY_RSA);
	ASSERT_PTR_NE(k1, NULL);
	ASSERT_PTR_NE(k1->rsa, NULL);
	ASSERT_PTR_NE(k1->rsa->n, NULL);
	ASSERT_PTR_NE(k1->rsa->e, NULL);
	ASSERT_PTR_EQ(k1->rsa->p, NULL);
	sshkey_free(k1);
	TEST_DONE();

	TEST_START("new/free KEY_DSA");
	k1 = sshkey_new(KEY_DSA);
	ASSERT_PTR_NE(k1, NULL);
	ASSERT_PTR_NE(k1->dsa, NULL);
	ASSERT_PTR_NE(k1->dsa->g, NULL);
	ASSERT_PTR_EQ(k1->dsa->priv_key, NULL);
	sshkey_free(k1);
	TEST_DONE();

	TEST_START("new/free KEY_ECDSA");
	k1 = sshkey_new(KEY_ECDSA);
	ASSERT_PTR_NE(k1, NULL);
	ASSERT_PTR_EQ(k1->ecdsa, NULL);  /* Can't allocate without NID */
	sshkey_free(k1);
	TEST_DONE();

	TEST_START("new/free KEY_ED25519");
	k1 = sshkey_new(KEY_ED25519);
	ASSERT_PTR_NE(k1, NULL);
	/* These should be blank until key loaded or generated */
	ASSERT_PTR_EQ(k1->ed25519_sk, NULL);
	ASSERT_PTR_EQ(k1->ed25519_pk, NULL);
	sshkey_free(k1);
	TEST_DONE();

	TEST_START("new_private KEY_RSA");
	k1 = sshkey_new_private(KEY_RSA);
	ASSERT_PTR_NE(k1, NULL);
	ASSERT_PTR_NE(k1->rsa, NULL);
	ASSERT_PTR_NE(k1->rsa->n, NULL);
	ASSERT_PTR_NE(k1->rsa->e, NULL);
	ASSERT_PTR_NE(k1->rsa->p, NULL);
	ASSERT_INT_EQ(sshkey_add_private(k1), 0);
	sshkey_free(k1);
	TEST_DONE();

	TEST_START("new_private KEY_DSA");
	k1 = sshkey_new_private(KEY_DSA);
	ASSERT_PTR_NE(k1, NULL);
	ASSERT_PTR_NE(k1->dsa, NULL);
	ASSERT_PTR_NE(k1->dsa->g, NULL);
	ASSERT_PTR_NE(k1->dsa->priv_key, NULL);
	ASSERT_INT_EQ(sshkey_add_private(k1), 0);
	sshkey_free(k1);
	TEST_DONE();

	TEST_START("generate KEY_RSA too small modulus");
	ASSERT_INT_EQ(sshkey_generate(KEY_RSA, 128, &k1),
	    SSH_ERR_INVALID_ARGUMENT);
	ASSERT_PTR_EQ(k1, NULL);
	TEST_DONE();

	TEST_START("generate KEY_RSA too large modulus");
	ASSERT_INT_EQ(sshkey_generate(KEY_RSA, 1 << 20, &k1),
	    SSH_ERR_INVALID_ARGUMENT);
	ASSERT_PTR_EQ(k1, NULL);
	TEST_DONE();

	TEST_START("generate KEY_DSA wrong bits");
	ASSERT_INT_EQ(sshkey_generate(KEY_DSA, 2048, &k1),
	    SSH_ERR_INVALID_ARGUMENT);
	ASSERT_PTR_EQ(k1, NULL);
	sshkey_free(k1);
	TEST_DONE();

	TEST_START("generate KEY_ECDSA wrong bits");
	ASSERT_INT_EQ(sshkey_generate(KEY_ECDSA, 42, &k1),
	    SSH_ERR_INVALID_ARGUMENT);
	ASSERT_PTR_EQ(k1, NULL);
	sshkey_free(k1);
	TEST_DONE();

	TEST_START("generate KEY_RSA");
	ASSERT_INT_EQ(sshkey_generate(KEY_RSA, 768, &kr), 0);
	ASSERT_PTR_NE(kr, NULL);
	ASSERT_PTR_NE(kr->rsa, NULL);
	ASSERT_PTR_NE(kr->rsa->n, NULL);
	ASSERT_PTR_NE(kr->rsa->e, NULL);
	ASSERT_PTR_NE(kr->rsa->p, NULL);
	ASSERT_INT_EQ(BN_num_bits(kr->rsa->n), 768);
	TEST_DONE();

	TEST_START("generate KEY_DSA");
	ASSERT_INT_EQ(sshkey_generate(KEY_DSA, 1024, &kd), 0);
	ASSERT_PTR_NE(kd, NULL);
	ASSERT_PTR_NE(kd->dsa, NULL);
	ASSERT_PTR_NE(kd->dsa->g, NULL);
	ASSERT_PTR_NE(kd->dsa->priv_key, NULL);
	TEST_DONE();

	TEST_START("generate KEY_ECDSA");
	ASSERT_INT_EQ(sshkey_generate(KEY_ECDSA, 256, &ke), 0);
	ASSERT_PTR_NE(ke, NULL);
	ASSERT_PTR_NE(ke->ecdsa, NULL);
	ASSERT_PTR_NE(EC_KEY_get0_public_key(ke->ecdsa), NULL);
	ASSERT_PTR_NE(EC_KEY_get0_private_key(ke->ecdsa), NULL);
	TEST_DONE();

	TEST_START("generate KEY_ED25519");
	ASSERT_INT_EQ(sshkey_generate(KEY_ED25519, 256, &kf), 0);
	ASSERT_PTR_NE(kf, NULL);
	ASSERT_INT_EQ(kf->type, KEY_ED25519);
	ASSERT_PTR_NE(kf->ed25519_pk, NULL);
	ASSERT_PTR_NE(kf->ed25519_sk, NULL);
	TEST_DONE();

	TEST_START("demote KEY_RSA");
	ASSERT_INT_EQ(sshkey_demote(kr, &k1), 0);
	ASSERT_PTR_NE(k1, NULL);
	ASSERT_PTR_NE(kr, k1);
	ASSERT_INT_EQ(k1->type, KEY_RSA);
	ASSERT_PTR_NE(k1->rsa, NULL);
	ASSERT_PTR_NE(k1->rsa->n, NULL);
	ASSERT_PTR_NE(k1->rsa->e, NULL);
	ASSERT_PTR_EQ(k1->rsa->p, NULL);
	TEST_DONE();

	TEST_START("equal KEY_RSA/demoted KEY_RSA");
	ASSERT_INT_EQ(sshkey_equal(kr, k1), 1);
	sshkey_free(k1);
	TEST_DONE();

	TEST_START("demote KEY_DSA");
	ASSERT_INT_EQ(sshkey_demote(kd, &k1), 0);
	ASSERT_PTR_NE(k1, NULL);
	ASSERT_PTR_NE(kd, k1);
	ASSERT_INT_EQ(k1->type, KEY_DSA);
	ASSERT_PTR_NE(k1->dsa, NULL);
	ASSERT_PTR_NE(k1->dsa->g, NULL);
	ASSERT_PTR_EQ(k1->dsa->priv_key, NULL);
	TEST_DONE();

	TEST_START("equal KEY_DSA/demoted KEY_DSA");
	ASSERT_INT_EQ(sshkey_equal(kd, k1), 1);
	sshkey_free(k1);
	TEST_DONE();

	TEST_START("demote KEY_ECDSA");
	ASSERT_INT_EQ(sshkey_demote(ke, &k1), 0);
	ASSERT_PTR_NE(k1, NULL);
	ASSERT_PTR_NE(ke, k1);
	ASSERT_INT_EQ(k1->type, KEY_ECDSA);
	ASSERT_PTR_NE(k1->ecdsa, NULL);
	ASSERT_INT_EQ(k1->ecdsa_nid, ke->ecdsa_nid);
	ASSERT_PTR_NE(EC_KEY_get0_public_key(ke->ecdsa), NULL);
	ASSERT_PTR_EQ(EC_KEY_get0_private_key(k1->ecdsa), NULL);
	TEST_DONE();

	TEST_START("equal KEY_ECDSA/demoted KEY_ECDSA");
	ASSERT_INT_EQ(sshkey_equal(ke, k1), 1);
	sshkey_free(k1);
	TEST_DONE();

	TEST_START("demote KEY_ED25519");
	ASSERT_INT_EQ(sshkey_demote(kf, &k1), 0);
	ASSERT_PTR_NE(k1, NULL);
	ASSERT_PTR_NE(kf, k1);
	ASSERT_INT_EQ(k1->type, KEY_ED25519);
	ASSERT_PTR_NE(k1->ed25519_pk, NULL);
	ASSERT_PTR_EQ(k1->ed25519_sk, NULL);
	TEST_DONE();

	TEST_START("equal KEY_ED25519/demoted KEY_ED25519");
	ASSERT_INT_EQ(sshkey_equal(kf, k1), 1);
	sshkey_free(k1);
	TEST_DONE();

	TEST_START("equal mismatched key types");
	ASSERT_INT_EQ(sshkey_equal(kd, kr), 0);
	ASSERT_INT_EQ(sshkey_equal(kd, ke), 0);
	ASSERT_INT_EQ(sshkey_equal(kr, ke), 0);
	ASSERT_INT_EQ(sshkey_equal(ke, kf), 0);
	ASSERT_INT_EQ(sshkey_equal(kd, kf), 0);
	TEST_DONE();

	TEST_START("equal different keys");
	ASSERT_INT_EQ(sshkey_generate(KEY_RSA, 768, &k1), 0);
	ASSERT_INT_EQ(sshkey_equal(kr, k1), 0);
	sshkey_free(k1);
	ASSERT_INT_EQ(sshkey_generate(KEY_DSA, 1024, &k1), 0);
	ASSERT_INT_EQ(sshkey_equal(kd, k1), 0);
	sshkey_free(k1);
	ASSERT_INT_EQ(sshkey_generate(KEY_ECDSA, 256, &k1), 0);
	ASSERT_INT_EQ(sshkey_equal(ke, k1), 0);
	sshkey_free(k1);
	ASSERT_INT_EQ(sshkey_generate(KEY_ED25519, 256, &k1), 0);
	ASSERT_INT_EQ(sshkey_equal(kf, k1), 0);
	sshkey_free(k1);
	TEST_DONE();

	sshkey_free(kr);
	sshkey_free(kd);
	sshkey_free(ke);
	sshkey_free(kf);

/* XXX certify test */
/* XXX sign test */
/* XXX verify test */

	TEST_START("nested certificate");
	ASSERT_INT_EQ(sshkey_load_cert(test_data_file("rsa_1"), &k1), 0);
	ASSERT_INT_EQ(sshkey_load_public(test_data_file("rsa_1.pub"), &k2,
	    NULL), 0);
	b = load_file("rsa_2");
	ASSERT_INT_EQ(sshkey_parse_private_fileblob(b, "", "rsa_1",
	    &k3, NULL), 0);
	sshbuf_reset(b);
	build_cert(b, k2, "*****@*****.**", k3, k1);
	ASSERT_INT_EQ(sshkey_from_blob(sshbuf_ptr(b), sshbuf_len(b), &k4),
	    SSH_ERR_KEY_CERT_INVALID_SIGN_KEY);
	ASSERT_PTR_EQ(k4, NULL);
	sshbuf_free(b);
	sshkey_free(k1);
	sshkey_free(k2);
	sshkey_free(k3);
	TEST_DONE();

}
예제 #5
0
void
sshkey_file_tests(void)
{
	struct sshkey *k1, *k2;
	struct sshbuf *buf, *pw;
	BIGNUM *a, *b, *c;
	char *cp;

	TEST_START("load passphrase");
	pw = load_text_file("pw");
	TEST_DONE();

#ifdef WITH_SSH1
	TEST_START("parse RSA1 from private");
	buf = load_file("rsa1_1");
	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", "rsa1_1",
	    &k1, NULL), 0);
	sshbuf_free(buf);
	ASSERT_PTR_NE(k1, NULL);
	a = load_bignum("rsa1_1.param.n");
	ASSERT_BIGNUM_EQ(k1->rsa->n, a);
	BN_free(a);
	TEST_DONE();

	TEST_START("parse RSA1 from private w/ passphrase");
	buf = load_file("rsa1_1_pw");
	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf,
	    (const char *)sshbuf_ptr(pw), "rsa1_1_pw", &k2, NULL), 0);
	sshbuf_free(buf);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("load RSA1 from public");
	ASSERT_INT_EQ(sshkey_load_public(test_data_file("rsa1_1.pub"), &k2,
	    NULL), 0);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("RSA1 key hex fingerprint");
	buf = load_text_file("rsa1_1.fp");
	cp = sshkey_fingerprint(k1, SSH_DIGEST_MD5, SSH_FP_HEX);
	ASSERT_PTR_NE(cp, NULL);
	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
	sshbuf_free(buf);
	free(cp);
	TEST_DONE();

	TEST_START("RSA1 key bubblebabble fingerprint");
	buf = load_text_file("rsa1_1.fp.bb");
	cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA1, SSH_FP_BUBBLEBABBLE);
	ASSERT_PTR_NE(cp, NULL);
	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
	sshbuf_free(buf);
	free(cp);
	TEST_DONE();

	sshkey_free(k1);
#endif

	TEST_START("parse RSA from private");
	buf = load_file("rsa_1");
	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", "rsa_1",
	    &k1, NULL), 0);
	sshbuf_free(buf);
	ASSERT_PTR_NE(k1, NULL);
	a = load_bignum("rsa_1.param.n");
	b = load_bignum("rsa_1.param.p");
	c = load_bignum("rsa_1.param.q");
	ASSERT_BIGNUM_EQ(k1->rsa->n, a);
	ASSERT_BIGNUM_EQ(k1->rsa->p, b);
	ASSERT_BIGNUM_EQ(k1->rsa->q, c);
	BN_free(a);
	BN_free(b);
	BN_free(c);
	TEST_DONE();

	TEST_START("parse RSA from private w/ passphrase");
	buf = load_file("rsa_1_pw");
	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf,
	    (const char *)sshbuf_ptr(pw), "rsa_1_pw", &k2, NULL), 0);
	sshbuf_free(buf);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("parse RSA from new-format");
	buf = load_file("rsa_n");
	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf,
	    "", "rsa_n", &k2, NULL), 0);
	sshbuf_free(buf);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("parse RSA from new-format w/ passphrase");
	buf = load_file("rsa_n_pw");
	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf,
	    (const char *)sshbuf_ptr(pw), "rsa_n_pw", &k2, NULL), 0);
	sshbuf_free(buf);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("load RSA from public");
	ASSERT_INT_EQ(sshkey_load_public(test_data_file("rsa_1.pub"), &k2,
	    NULL), 0);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("load RSA cert");
	ASSERT_INT_EQ(sshkey_load_cert(test_data_file("rsa_1"), &k2), 0);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(k2->type, KEY_RSA_CERT);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 0);
	ASSERT_INT_EQ(sshkey_equal_public(k1, k2), 1);
	TEST_DONE();

	TEST_START("RSA key hex fingerprint");
	buf = load_text_file("rsa_1.fp");
	cp = sshkey_fingerprint(k1, SSH_DIGEST_MD5, SSH_FP_HEX);
	ASSERT_PTR_NE(cp, NULL);
	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
	sshbuf_free(buf);
	free(cp);
	TEST_DONE();

	TEST_START("RSA cert hex fingerprint");
	buf = load_text_file("rsa_1-cert.fp");
	cp = sshkey_fingerprint(k2, SSH_DIGEST_MD5, SSH_FP_HEX);
	ASSERT_PTR_NE(cp, NULL);
	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
	sshbuf_free(buf);
	free(cp);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("RSA key bubblebabble fingerprint");
	buf = load_text_file("rsa_1.fp.bb");
	cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA1, SSH_FP_BUBBLEBABBLE);
	ASSERT_PTR_NE(cp, NULL);
	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
	sshbuf_free(buf);
	free(cp);
	TEST_DONE();

	sshkey_free(k1);

	TEST_START("parse DSA from private");
	buf = load_file("dsa_1");
	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", "dsa_1",
	    &k1, NULL), 0);
	sshbuf_free(buf);
	ASSERT_PTR_NE(k1, NULL);
	a = load_bignum("dsa_1.param.g");
	b = load_bignum("dsa_1.param.priv");
	c = load_bignum("dsa_1.param.pub");
	ASSERT_BIGNUM_EQ(k1->dsa->g, a);
	ASSERT_BIGNUM_EQ(k1->dsa->priv_key, b);
	ASSERT_BIGNUM_EQ(k1->dsa->pub_key, c);
	BN_free(a);
	BN_free(b);
	BN_free(c);
	TEST_DONE();

	TEST_START("parse DSA from private w/ passphrase");
	buf = load_file("dsa_1_pw");
	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf,
	    (const char *)sshbuf_ptr(pw), "dsa_1_pw", &k2, NULL), 0);
	sshbuf_free(buf);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("parse DSA from new-format");
	buf = load_file("dsa_n");
	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf,
	    "", "dsa_n", &k2, NULL), 0);
	sshbuf_free(buf);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("parse DSA from new-format w/ passphrase");
	buf = load_file("dsa_n_pw");
	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf,
	    (const char *)sshbuf_ptr(pw), "dsa_n_pw", &k2, NULL), 0);
	sshbuf_free(buf);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("load DSA from public");
	ASSERT_INT_EQ(sshkey_load_public(test_data_file("dsa_1.pub"), &k2,
	    NULL), 0);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("load DSA cert");
	ASSERT_INT_EQ(sshkey_load_cert(test_data_file("dsa_1"), &k2), 0);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(k2->type, KEY_DSA_CERT);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 0);
	ASSERT_INT_EQ(sshkey_equal_public(k1, k2), 1);
	TEST_DONE();

	TEST_START("DSA key hex fingerprint");
	buf = load_text_file("dsa_1.fp");
	cp = sshkey_fingerprint(k1, SSH_DIGEST_MD5, SSH_FP_HEX);
	ASSERT_PTR_NE(cp, NULL);
	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
	sshbuf_free(buf);
	free(cp);
	TEST_DONE();

	TEST_START("DSA cert hex fingerprint");
	buf = load_text_file("dsa_1-cert.fp");
	cp = sshkey_fingerprint(k2, SSH_DIGEST_MD5, SSH_FP_HEX);
	ASSERT_PTR_NE(cp, NULL);
	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
	sshbuf_free(buf);
	free(cp);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("DSA key bubblebabble fingerprint");
	buf = load_text_file("dsa_1.fp.bb");
	cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA1, SSH_FP_BUBBLEBABBLE);
	ASSERT_PTR_NE(cp, NULL);
	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
	sshbuf_free(buf);
	free(cp);
	TEST_DONE();

	sshkey_free(k1);

#ifdef OPENSSL_HAS_ECC
	TEST_START("parse ECDSA from private");
	buf = load_file("ecdsa_1");
	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", "ecdsa_1",
	    &k1, NULL), 0);
	sshbuf_free(buf);
	ASSERT_PTR_NE(k1, NULL);
	buf = load_text_file("ecdsa_1.param.curve");
	ASSERT_STRING_EQ((const char *)sshbuf_ptr(buf),
	    OBJ_nid2sn(k1->ecdsa_nid));
	sshbuf_free(buf);
	a = load_bignum("ecdsa_1.param.priv");
	b = load_bignum("ecdsa_1.param.pub");
	c = EC_POINT_point2bn(EC_KEY_get0_group(k1->ecdsa),
	    EC_KEY_get0_public_key(k1->ecdsa), POINT_CONVERSION_UNCOMPRESSED,
	    NULL, NULL);
	ASSERT_PTR_NE(c, NULL);
	ASSERT_BIGNUM_EQ(EC_KEY_get0_private_key(k1->ecdsa), a);
	ASSERT_BIGNUM_EQ(b, c);
	BN_free(a);
	BN_free(b);
	BN_free(c);
	TEST_DONE();

	TEST_START("parse ECDSA from private w/ passphrase");
	buf = load_file("ecdsa_1_pw");
	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf,
	    (const char *)sshbuf_ptr(pw), "ecdsa_1_pw", &k2, NULL), 0);
	sshbuf_free(buf);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("parse ECDSA from new-format");
	buf = load_file("ecdsa_n");
	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf,
	    "", "ecdsa_n", &k2, NULL), 0);
	sshbuf_free(buf);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("parse ECDSA from new-format w/ passphrase");
	buf = load_file("ecdsa_n_pw");
	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf,
	    (const char *)sshbuf_ptr(pw), "ecdsa_n_pw", &k2, NULL), 0);
	sshbuf_free(buf);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("load ECDSA from public");
	ASSERT_INT_EQ(sshkey_load_public(test_data_file("ecdsa_1.pub"), &k2,
	    NULL), 0);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("load ECDSA cert");
	ASSERT_INT_EQ(sshkey_load_cert(test_data_file("ecdsa_1"), &k2), 0);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(k2->type, KEY_ECDSA_CERT);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 0);
	ASSERT_INT_EQ(sshkey_equal_public(k1, k2), 1);
	TEST_DONE();

	TEST_START("ECDSA key hex fingerprint");
	buf = load_text_file("ecdsa_1.fp");
	cp = sshkey_fingerprint(k1, SSH_DIGEST_MD5, SSH_FP_HEX);
	ASSERT_PTR_NE(cp, NULL);
	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
	sshbuf_free(buf);
	free(cp);
	TEST_DONE();

	TEST_START("ECDSA cert hex fingerprint");
	buf = load_text_file("ecdsa_1-cert.fp");
	cp = sshkey_fingerprint(k2, SSH_DIGEST_MD5, SSH_FP_HEX);
	ASSERT_PTR_NE(cp, NULL);
	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
	sshbuf_free(buf);
	free(cp);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("ECDSA key bubblebabble fingerprint");
	buf = load_text_file("ecdsa_1.fp.bb");
	cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA1, SSH_FP_BUBBLEBABBLE);
	ASSERT_PTR_NE(cp, NULL);
	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
	sshbuf_free(buf);
	free(cp);
	TEST_DONE();

	sshkey_free(k1);
#endif /* OPENSSL_HAS_ECC */

	TEST_START("parse Ed25519 from private");
	buf = load_file("ed25519_1");
	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf, "", "ed25519_1",
	    &k1, NULL), 0);
	sshbuf_free(buf);
	ASSERT_PTR_NE(k1, NULL);
	ASSERT_INT_EQ(k1->type, KEY_ED25519);
	/* XXX check key contents */
	TEST_DONE();

	TEST_START("parse Ed25519 from private w/ passphrase");
	buf = load_file("ed25519_1_pw");
	ASSERT_INT_EQ(sshkey_parse_private_fileblob(buf,
	    (const char *)sshbuf_ptr(pw), "ed25519_1_pw", &k2, NULL), 0);
	sshbuf_free(buf);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("load Ed25519 from public");
	ASSERT_INT_EQ(sshkey_load_public(test_data_file("ed25519_1.pub"), &k2,
	    NULL), 0);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("load Ed25519 cert");
	ASSERT_INT_EQ(sshkey_load_cert(test_data_file("ed25519_1"), &k2), 0);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(k2->type, KEY_ED25519_CERT);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 0);
	ASSERT_INT_EQ(sshkey_equal_public(k1, k2), 1);
	TEST_DONE();

	TEST_START("Ed25519 key hex fingerprint");
	buf = load_text_file("ed25519_1.fp");
	cp = sshkey_fingerprint(k1, SSH_DIGEST_MD5, SSH_FP_HEX);
	ASSERT_PTR_NE(cp, NULL);
	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
	sshbuf_free(buf);
	free(cp);
	TEST_DONE();

	TEST_START("Ed25519 cert hex fingerprint");
	buf = load_text_file("ed25519_1-cert.fp");
	cp = sshkey_fingerprint(k2, SSH_DIGEST_MD5, SSH_FP_HEX);
	ASSERT_PTR_NE(cp, NULL);
	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
	sshbuf_free(buf);
	free(cp);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("Ed25519 key bubblebabble fingerprint");
	buf = load_text_file("ed25519_1.fp.bb");
	cp = sshkey_fingerprint(k1, SSH_DIGEST_SHA1, SSH_FP_BUBBLEBABBLE);
	ASSERT_PTR_NE(cp, NULL);
	ASSERT_STRING_EQ(cp, (const char *)sshbuf_ptr(buf));
	sshbuf_free(buf);
	free(cp);
	TEST_DONE();

	sshkey_free(k1);

	sshbuf_free(pw);

}
예제 #6
0
static int
check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port,
    struct sshkey *host_key, int readonly,
    char **user_hostfiles, u_int num_user_hostfiles,
    char **system_hostfiles, u_int num_system_hostfiles)
{
	HostStatus host_status;
	HostStatus ip_status;
	struct sshkey *raw_key = NULL;
	char *ip = NULL, *host = NULL;
	char hostline[1000], *hostp, *fp, *ra;
	char msg[1024];
	const char *type;
	const struct hostkey_entry *host_found, *ip_found;
	int len, cancelled_forwarding = 0;
	int local = sockaddr_is_local(hostaddr);
	int r, want_cert = sshkey_is_cert(host_key), host_ip_differ = 0;
	struct hostkeys *host_hostkeys, *ip_hostkeys;
	u_int i;

	/*
	 * Force accepting of the host key for loopback/localhost. The
	 * problem is that if the home directory is NFS-mounted to multiple
	 * machines, localhost will refer to a different machine in each of
	 * them, and the user will get bogus HOST_CHANGED warnings.  This
	 * essentially disables host authentication for localhost; however,
	 * this is probably not a real problem.
	 */
	if (options.no_host_authentication_for_localhost == 1 && local &&
	    options.host_key_alias == NULL) {
		debug("Forcing accepting of host key for "
		    "loopback/localhost.");
		return 0;
	}

	/*
	 * Prepare the hostname and address strings used for hostkey lookup.
	 * In some cases, these will have a port number appended.
	 */
	get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip);

	/*
	 * Turn off check_host_ip if the connection is to localhost, via proxy
	 * command or if we don't have a hostname to compare with
	 */
	if (options.check_host_ip && (local ||
	    strcmp(hostname, ip) == 0 || options.proxy_command != NULL))
		options.check_host_ip = 0;

	host_hostkeys = init_hostkeys();
	for (i = 0; i < num_user_hostfiles; i++)
		load_hostkeys(host_hostkeys, host, user_hostfiles[i]);
	for (i = 0; i < num_system_hostfiles; i++)
		load_hostkeys(host_hostkeys, host, system_hostfiles[i]);

	ip_hostkeys = NULL;
	if (!want_cert && options.check_host_ip) {
		ip_hostkeys = init_hostkeys();
		for (i = 0; i < num_user_hostfiles; i++)
			load_hostkeys(ip_hostkeys, ip, user_hostfiles[i]);
		for (i = 0; i < num_system_hostfiles; i++)
			load_hostkeys(ip_hostkeys, ip, system_hostfiles[i]);
	}

 retry:
	/* Reload these as they may have changed on cert->key downgrade */
	want_cert = sshkey_is_cert(host_key);
	type = sshkey_type(host_key);

	/*
	 * Check if the host key is present in the user's list of known
	 * hosts or in the systemwide list.
	 */
	host_status = check_key_in_hostkeys(host_hostkeys, host_key,
	    &host_found);

	/*
	 * Also perform check for the ip address, skip the check if we are
	 * localhost, looking for a certificate, or the hostname was an ip
	 * address to begin with.
	 */
	if (!want_cert && ip_hostkeys != NULL) {
		ip_status = check_key_in_hostkeys(ip_hostkeys, host_key,
		    &ip_found);
		if (host_status == HOST_CHANGED &&
		    (ip_status != HOST_CHANGED || 
		    (ip_found != NULL &&
		    !sshkey_equal(ip_found->key, host_found->key))))
			host_ip_differ = 1;
	} else
		ip_status = host_status;

	switch (host_status) {
	case HOST_OK:
		/* The host is known and the key matches. */
		debug("Host '%.200s' is known and matches the %s host %s.",
		    host, type, want_cert ? "certificate" : "key");
		debug("Found %s in %s:%lu", want_cert ? "CA key" : "key",
		    host_found->file, host_found->line);
		if (want_cert && !check_host_cert(hostname, host_key))
			goto fail;
		if (options.check_host_ip && ip_status == HOST_NEW) {
			if (readonly || want_cert)
				logit("%s host key for IP address "
				    "'%.128s' not in list of known hosts.",
				    type, ip);
			else if (!add_host_to_hostfile(user_hostfiles[0], ip,
			    host_key, options.hash_known_hosts))
				logit("Failed to add the %s host key for IP "
				    "address '%.128s' to the list of known "
				    "hosts (%.30s).", type, ip,
				    user_hostfiles[0]);
			else
				logit("Warning: Permanently added the %s host "
				    "key for IP address '%.128s' to the list "
				    "of known hosts.", type, ip);
		} else if (options.visual_host_key) {
			fp = sshkey_fingerprint(host_key, SSH_FP_MD5,
			    SSH_FP_HEX);
			ra = sshkey_fingerprint(host_key, SSH_FP_MD5,
			    SSH_FP_RANDOMART);
			logit("Host key fingerprint is %s\n%s\n", fp, ra);
			xfree(ra);
			xfree(fp);
		}
		break;
	case HOST_NEW:
		if (options.host_key_alias == NULL && port != 0 &&
		    port != SSH_DEFAULT_PORT) {
			debug("checking without port identifier");
			if (check_host_key(hostname, hostaddr, 0, host_key,
			    ROQUIET, user_hostfiles, num_user_hostfiles,
			    system_hostfiles, num_system_hostfiles) == 0) {
				debug("found matching key w/out port");
				break;
			}
		}
		if (readonly || want_cert)
			goto fail;
		/* The host is new. */
		if (options.strict_host_key_checking == 1) {
			/*
			 * User has requested strict host key checking.  We
			 * will not add the host key automatically.  The only
			 * alternative left is to abort.
			 */
			error("No %s host key is known for %.200s and you "
			    "have requested strict checking.", type, host);
			goto fail;
		} else if (options.strict_host_key_checking == 2) {
			char msg1[1024], msg2[1024];

			if (show_other_keys(host_hostkeys, host_key))
				snprintf(msg1, sizeof(msg1),
				    "\nbut keys of different type are already"
				    " known for this host.");
			else
				snprintf(msg1, sizeof(msg1), ".");
			/* The default */
			fp = sshkey_fingerprint(host_key, SSH_FP_MD5,
			    SSH_FP_HEX);
			ra = sshkey_fingerprint(host_key, SSH_FP_MD5,
			    SSH_FP_RANDOMART);
			msg2[0] = '\0';
			if (options.verify_host_key_dns) {
				if (matching_host_key_dns)
					snprintf(msg2, sizeof(msg2),
					    "Matching host key fingerprint"
					    " found in DNS.\n");
				else
					snprintf(msg2, sizeof(msg2),
					    "No matching host key fingerprint"
					    " found in DNS.\n");
			}
			snprintf(msg, sizeof(msg),
			    "The authenticity of host '%.200s (%s)' can't be "
			    "established%s\n"
			    "%s key fingerprint is %s.%s%s\n%s"
			    "Are you sure you want to continue connecting "
			    "(yes/no)? ",
			    host, ip, msg1, type, fp,
			    options.visual_host_key ? "\n" : "",
			    options.visual_host_key ? ra : "",
			    msg2);
			xfree(ra);
			xfree(fp);
			if (!confirm(msg))
				goto fail;
		}
		/*
		 * If not in strict mode, add the key automatically to the
		 * local known_hosts file.
		 */
		if (options.check_host_ip && ip_status == HOST_NEW) {
			snprintf(hostline, sizeof(hostline), "%s,%s", host, ip);
			hostp = hostline;
			if (options.hash_known_hosts) {
				/* Add hash of host and IP separately */
				r = add_host_to_hostfile(user_hostfiles[0],
				    host, host_key, options.hash_known_hosts) &&
				    add_host_to_hostfile(user_hostfiles[0], ip,
				    host_key, options.hash_known_hosts);
			} else {
				/* Add unhashed "host,ip" */
				r = add_host_to_hostfile(user_hostfiles[0],
				    hostline, host_key,
				    options.hash_known_hosts);
			}
		} else {
			r = add_host_to_hostfile(user_hostfiles[0], host,
			    host_key, options.hash_known_hosts);
			hostp = host;
		}

		if (!r)
			logit("Failed to add the host to the list of known "
			    "hosts (%.500s).", user_hostfiles[0]);
		else
			logit("Warning: Permanently added '%.200s' (%s) to the "
			    "list of known hosts.", hostp, type);
		break;
	case HOST_REVOKED:
		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
		error("@       WARNING: REVOKED HOST KEY DETECTED!               @");
		error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
		error("The %s host key for %s is marked as revoked.", type, host);
		error("This could mean that a stolen key is being used to");
		error("impersonate this host.");

		/*
		 * If strict host key checking is in use, the user will have
		 * to edit the key manually and we can only abort.
		 */
		if (options.strict_host_key_checking) {
			error("%s host key for %.200s was revoked and you have "
			    "requested strict checking.", type, host);
			goto fail;
		}
		goto continue_unsafe;

	case HOST_CHANGED:
		if (want_cert) {
			/*
			 * This is only a debug() since it is valid to have
			 * CAs with wildcard DNS matches that don't match
			 * all hosts that one might visit.
			 */
			debug("Host certificate authority does not "
			    "match %s in %s:%lu", CA_MARKER,
			    host_found->file, host_found->line);
			goto fail;
		}
		if (readonly == ROQUIET)
			goto fail;
		if (options.check_host_ip && host_ip_differ) {
			char *key_msg;
			if (ip_status == HOST_NEW)
				key_msg = "is unknown";
			else if (ip_status == HOST_OK)
				key_msg = "is unchanged";
			else
				key_msg = "has a different value";
			error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
			error("@       WARNING: POSSIBLE DNS SPOOFING DETECTED!          @");
			error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
			error("The %s host key for %s has changed,", type, host);
			error("and the key for the corresponding IP address %s", ip);
			error("%s. This could either mean that", key_msg);
			error("DNS SPOOFING is happening or the IP address for the host");
			error("and its host key have changed at the same time.");
			if (ip_status != HOST_NEW)
				error("Offending key for IP in %s:%lu",
				    ip_found->file, ip_found->line);
		}
		/* The host key has changed. */
		warn_changed_key(host_key);
		error("Add correct host key in %.100s to get rid of this message.",
		    user_hostfiles[0]);
		error("Offending %s key in %s:%lu",
		    sshkey_type(host_found->key),
		    host_found->file, host_found->line);

		/*
		 * If strict host key checking is in use, the user will have
		 * to edit the key manually and we can only abort.
		 */
		if (options.strict_host_key_checking) {
			error("%s host key for %.200s has changed and you have "
			    "requested strict checking.", type, host);
			goto fail;
		}

 continue_unsafe:
		/*
		 * If strict host key checking has not been requested, allow
		 * the connection but without MITM-able authentication or
		 * forwarding.
		 */
		if (options.password_authentication) {
			error("Password authentication is disabled to avoid "
			    "man-in-the-middle attacks.");
			options.password_authentication = 0;
			cancelled_forwarding = 1;
		}
		if (options.kbd_interactive_authentication) {
			error("Keyboard-interactive authentication is disabled"
			    " to avoid man-in-the-middle attacks.");
			options.kbd_interactive_authentication = 0;
			options.challenge_response_authentication = 0;
			cancelled_forwarding = 1;
		}
		if (options.challenge_response_authentication) {
			error("Challenge/response authentication is disabled"
			    " to avoid man-in-the-middle attacks.");
			options.challenge_response_authentication = 0;
			cancelled_forwarding = 1;
		}
		if (options.forward_agent) {
			error("Agent forwarding is disabled to avoid "
			    "man-in-the-middle attacks.");
			options.forward_agent = 0;
			cancelled_forwarding = 1;
		}
		if (options.forward_x11) {
			error("X11 forwarding is disabled to avoid "
			    "man-in-the-middle attacks.");
			options.forward_x11 = 0;
			cancelled_forwarding = 1;
		}
		if (options.num_local_forwards > 0 ||
		    options.num_remote_forwards > 0) {
			error("Port forwarding is disabled to avoid "
			    "man-in-the-middle attacks.");
			options.num_local_forwards =
			    options.num_remote_forwards = 0;
			cancelled_forwarding = 1;
		}
		if (options.tun_open != SSH_TUNMODE_NO) {
			error("Tunnel forwarding is disabled to avoid "
			    "man-in-the-middle attacks.");
			options.tun_open = SSH_TUNMODE_NO;
			cancelled_forwarding = 1;
		}
		if (options.exit_on_forward_failure && cancelled_forwarding)
			fatal("Error: forwarding disabled due to host key "
			    "check failure");
		
		/*
		 * XXX Should permit the user to change to use the new id.
		 * This could be done by converting the host key to an
		 * identifying sentence, tell that the host identifies itself
		 * by that sentence, and ask the user if he/she wishes to
		 * accept the authentication.
		 */
		break;
	case HOST_FOUND:
		fatal("internal error");
		break;
	}

	if (options.check_host_ip && host_status != HOST_CHANGED &&
	    ip_status == HOST_CHANGED) {
		snprintf(msg, sizeof(msg),
		    "Warning: the %s host key for '%.200s' "
		    "differs from the key for the IP address '%.128s'"
		    "\nOffending key for IP in %s:%lu",
		    type, host, ip, ip_found->file, ip_found->line);
		if (host_status == HOST_OK) {
			len = strlen(msg);
			snprintf(msg + len, sizeof(msg) - len,
			    "\nMatching host key in %s:%lu",
			    host_found->file, host_found->line);
		}
		if (options.strict_host_key_checking == 1) {
			logit("%s", msg);
			error("Exiting, you have requested strict checking.");
			goto fail;
		} else if (options.strict_host_key_checking == 2) {
			strlcat(msg, "\nAre you sure you want "
			    "to continue connecting (yes/no)? ", sizeof(msg));
			if (!confirm(msg))
				goto fail;
		} else {
			logit("%s", msg);
		}
	}

	xfree(ip);
	xfree(host);
	if (host_hostkeys != NULL)
		free_hostkeys(host_hostkeys);
	if (ip_hostkeys != NULL)
		free_hostkeys(ip_hostkeys);
	return 0;

fail:
	if (want_cert && host_status != HOST_REVOKED) {
		/*
		 * No matching certificate. Downgrade cert to raw key and
		 * search normally.
		 */
		debug("No matching CA found. Retry with plain key");
		if ((r = sshkey_from_private(host_key, &raw_key)) != 0)
			fatal("%s: sshkey_from_private: %s",
			    __func__, ssh_err(r));
		if ((r = sshkey_drop_cert(raw_key)) != 0)
			fatal("Couldn't drop certificate: %s", ssh_err(r));
		host_key = raw_key;
		goto retry;
	}
	if (raw_key != NULL)
		sshkey_free(raw_key);
	xfree(ip);
	xfree(host);
	if (host_hostkeys != NULL)
		free_hostkeys(host_hostkeys);
	if (ip_hostkeys != NULL)
		free_hostkeys(ip_hostkeys);
	return -1;
}
예제 #7
0
void
sshkey_file_tests(void)
{
	struct sshkey *k1, *k2;
	struct sshbuf *buf, *pw;
	BIGNUM *a, *b, *c;
	char *cp;

	TEST_START("load passphrase");
	pw = load_text_file("pw");
	TEST_DONE();

	TEST_START("parse RSA1 from private");
	buf = load_file("rsa1_1");
	ASSERT_INT_EQ(sshkey_parse_private(buf, "", "rsa1_1", &k1, NULL), 0);
	sshbuf_free(buf);
	ASSERT_PTR_NE(k1, NULL);
	a = load_bignum("rsa1_1.param.n");
	ASSERT_BIGNUM_EQ(k1->rsa->n, a);
	BN_free(a);
	TEST_DONE();

	TEST_START("parse RSA from private w/ passphrase");
	buf = load_file("rsa1_1_pw");
	ASSERT_INT_EQ(sshkey_parse_private(buf, sshbuf_ptr(pw), "rsa1_1_pw",
	    &k2, NULL), 0);
	sshbuf_free(buf);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("load RSA from public");
	ASSERT_INT_EQ(sshkey_load_public(test_data_file("rsa1_1.pub"), &k2,
	    NULL), 0);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("RSA key hex fingerprint");
	buf = load_text_file("rsa1_1.fp");
	cp = sshkey_fingerprint(k1, SSH_FP_MD5, SSH_FP_HEX);
	ASSERT_PTR_NE(cp, NULL);
	ASSERT_STRING_EQ(cp, sshbuf_ptr(buf));
	sshbuf_free(buf);
	free(cp);
	TEST_DONE();

	TEST_START("RSA key bubblebabble fingerprint");
	buf = load_text_file("rsa1_1.fp.bb");
	cp = sshkey_fingerprint(k1, SSH_FP_SHA1, SSH_FP_BUBBLEBABBLE);
	ASSERT_PTR_NE(cp, NULL);
	ASSERT_STRING_EQ(cp, sshbuf_ptr(buf));
	sshbuf_free(buf);
	free(cp);
	TEST_DONE();

	sshkey_free(k1);

	TEST_START("parse RSA from private");
	buf = load_file("rsa_1");
	ASSERT_INT_EQ(sshkey_parse_private(buf, "", "rsa_1", &k1, NULL), 0);
	sshbuf_free(buf);
	ASSERT_PTR_NE(k1, NULL);
	a = load_bignum("rsa_1.param.n");
	b = load_bignum("rsa_1.param.p");
	c = load_bignum("rsa_1.param.q");
	ASSERT_BIGNUM_EQ(k1->rsa->n, a);
	ASSERT_BIGNUM_EQ(k1->rsa->p, b);
	ASSERT_BIGNUM_EQ(k1->rsa->q, c);
	BN_free(a);
	BN_free(b);
	BN_free(c);
	TEST_DONE();

	TEST_START("parse RSA from private w/ passphrase");
	buf = load_file("rsa_1_pw");
	ASSERT_INT_EQ(sshkey_parse_private(buf, sshbuf_ptr(pw), "rsa_1_pw",
	    &k2, NULL), 0);
	sshbuf_free(buf);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("load RSA from public");
	ASSERT_INT_EQ(sshkey_load_public(test_data_file("rsa_1.pub"), &k2,
	    NULL), 0);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("RSA key hex fingerprint");
	buf = load_text_file("rsa_1.fp");
	cp = sshkey_fingerprint(k1, SSH_FP_MD5, SSH_FP_HEX);
	ASSERT_PTR_NE(cp, NULL);
	ASSERT_STRING_EQ(cp, sshbuf_ptr(buf));
	sshbuf_free(buf);
	free(cp);
	TEST_DONE();

	TEST_START("RSA key bubblebabble fingerprint");
	buf = load_text_file("rsa_1.fp.bb");
	cp = sshkey_fingerprint(k1, SSH_FP_SHA1, SSH_FP_BUBBLEBABBLE);
	ASSERT_PTR_NE(cp, NULL);
	ASSERT_STRING_EQ(cp, sshbuf_ptr(buf));
	sshbuf_free(buf);
	free(cp);
	TEST_DONE();

	sshkey_free(k1);

	TEST_START("parse DSA from private");
	buf = load_file("dsa_1");
	ASSERT_INT_EQ(sshkey_parse_private(buf, "", "dsa_1", &k1, NULL), 0);
	sshbuf_free(buf);
	ASSERT_PTR_NE(k1, NULL);
	a = load_bignum("dsa_1.param.g");
	b = load_bignum("dsa_1.param.priv");
	c = load_bignum("dsa_1.param.pub");
	ASSERT_BIGNUM_EQ(k1->dsa->g, a);
	ASSERT_BIGNUM_EQ(k1->dsa->priv_key, b);
	ASSERT_BIGNUM_EQ(k1->dsa->pub_key, c);
	BN_free(a);
	BN_free(b);
	BN_free(c);
	TEST_DONE();

	TEST_START("parse DSA from private w/ passphrase");
	buf = load_file("dsa_1_pw");
	ASSERT_INT_EQ(sshkey_parse_private(buf, sshbuf_ptr(pw), "dsa_1_pw",
	    &k2, NULL), 0);
	sshbuf_free(buf);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("load DSA from public");
	ASSERT_INT_EQ(sshkey_load_public(test_data_file("dsa_1.pub"), &k2,
	    NULL), 0);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("DSA key hex fingerprint");
	buf = load_text_file("dsa_1.fp");
	cp = sshkey_fingerprint(k1, SSH_FP_MD5, SSH_FP_HEX);
	ASSERT_PTR_NE(cp, NULL);
	ASSERT_STRING_EQ(cp, sshbuf_ptr(buf));
	sshbuf_free(buf);
	free(cp);
	TEST_DONE();

	TEST_START("DSA key bubblebabble fingerprint");
	buf = load_text_file("dsa_1.fp.bb");
	cp = sshkey_fingerprint(k1, SSH_FP_SHA1, SSH_FP_BUBBLEBABBLE);
	ASSERT_PTR_NE(cp, NULL);
	ASSERT_STRING_EQ(cp, sshbuf_ptr(buf));
	sshbuf_free(buf);
	free(cp);
	TEST_DONE();

	sshkey_free(k1);

	TEST_START("parse ECDSA from private");
	buf = load_file("ecdsa_1");
	ASSERT_INT_EQ(sshkey_parse_private(buf, "", "ecdsa_1", &k1, NULL), 0);
	sshbuf_free(buf);
	ASSERT_PTR_NE(k1, NULL);
	buf = load_text_file("ecdsa_1.param.curve");
	ASSERT_STRING_EQ(sshbuf_ptr(buf), OBJ_nid2sn(k1->ecdsa_nid));
	sshbuf_free(buf);
	a = load_bignum("ecdsa_1.param.priv");
	b = load_bignum("ecdsa_1.param.pub");
	c = EC_POINT_point2bn(EC_KEY_get0_group(k1->ecdsa),
	    EC_KEY_get0_public_key(k1->ecdsa), POINT_CONVERSION_UNCOMPRESSED,
	    NULL, NULL);
	ASSERT_PTR_NE(c, NULL);
	ASSERT_BIGNUM_EQ(EC_KEY_get0_private_key(k1->ecdsa), a);
	ASSERT_BIGNUM_EQ(b, c);
	BN_free(a);
	BN_free(b);
	BN_free(c);
	TEST_DONE();

	TEST_START("parse ECDSA from private w/ passphrase");
	buf = load_file("ecdsa_1_pw");
	ASSERT_INT_EQ(sshkey_parse_private(buf, sshbuf_ptr(pw), "ecdsa_1_pw",
	    &k2, NULL), 0);
	sshbuf_free(buf);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("load ECDSA from public");
	ASSERT_INT_EQ(sshkey_load_public(test_data_file("ecdsa_1.pub"), &k2,
	    NULL), 0);
	ASSERT_PTR_NE(k2, NULL);
	ASSERT_INT_EQ(sshkey_equal(k1, k2), 1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("ECDSA key hex fingerprint");
	buf = load_text_file("ecdsa_1.fp");
	cp = sshkey_fingerprint(k1, SSH_FP_MD5, SSH_FP_HEX);
	ASSERT_PTR_NE(cp, NULL);
	ASSERT_STRING_EQ(cp, sshbuf_ptr(buf));
	sshbuf_free(buf);
	free(cp);
	TEST_DONE();

	TEST_START("ECDSA key bubblebabble fingerprint");
	buf = load_text_file("ecdsa_1.fp.bb");
	cp = sshkey_fingerprint(k1, SSH_FP_SHA1, SSH_FP_BUBBLEBABBLE);
	ASSERT_PTR_NE(cp, NULL);
	ASSERT_STRING_EQ(cp, sshbuf_ptr(buf));
	sshbuf_free(buf);
	free(cp);
	TEST_DONE();

	sshkey_free(k1);

	sshbuf_free(pw);

}
예제 #8
0
/* returns 0 if key verifies or -1 if key does NOT verify */
int
verify_host_key(char *host, struct sockaddr *hostaddr, struct sshkey *host_key)
{
	u_int i;
	int r = -1, flags = 0;
	char valid[64], *fp = NULL, *cafp = NULL;
	struct sshkey *plain = NULL;

	if ((fp = sshkey_fingerprint(host_key,
	    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
		error("%s: fingerprint host key: %s", __func__, ssh_err(r));
		r = -1;
		goto out;
	}

	if (sshkey_is_cert(host_key)) {
		if ((cafp = sshkey_fingerprint(host_key->cert->signature_key,
		    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) {
			error("%s: fingerprint CA key: %s",
			    __func__, ssh_err(r));
			r = -1;
			goto out;
		}
		sshkey_format_cert_validity(host_key->cert,
		    valid, sizeof(valid));
		debug("Server host certificate: %s %s, serial %llu "
		    "ID \"%s\" CA %s %s valid %s",
		    sshkey_ssh_name(host_key), fp,
		    (unsigned long long)host_key->cert->serial,
		    host_key->cert->key_id,
		    sshkey_ssh_name(host_key->cert->signature_key), cafp,
		    valid);
		for (i = 0; i < host_key->cert->nprincipals; i++) {
			debug2("Server host certificate hostname: %s",
			    host_key->cert->principals[i]);
		}
	} else {
		debug("Server host key: %s %s", sshkey_ssh_name(host_key), fp);
	}

	if (sshkey_equal(previous_host_key, host_key)) {
		debug2("%s: server host key %s %s matches cached key",
		    __func__, sshkey_type(host_key), fp);
		r = 0;
		goto out;
	}

	/* Check in RevokedHostKeys file if specified */
	if (options.revoked_host_keys != NULL) {
		r = sshkey_check_revoked(host_key, options.revoked_host_keys);
		switch (r) {
		case 0:
			break; /* not revoked */
		case SSH_ERR_KEY_REVOKED:
			error("Host key %s %s revoked by file %s",
			    sshkey_type(host_key), fp,
			    options.revoked_host_keys);
			r = -1;
			goto out;
		default:
			error("Error checking host key %s %s in "
			    "revoked keys file %s: %s", sshkey_type(host_key),
			    fp, options.revoked_host_keys, ssh_err(r));
			r = -1;
			goto out;
		}
	}

	if (options.verify_host_key_dns) {
		/*
		 * XXX certs are not yet supported for DNS, so downgrade
		 * them and try the plain key.
		 */
		if ((r = sshkey_from_private(host_key, &plain)) != 0)
			goto out;
		if (sshkey_is_cert(plain))
			sshkey_drop_cert(plain);
		if (verify_host_key_dns(host, hostaddr, plain, &flags) == 0) {
			if (flags & DNS_VERIFY_FOUND) {
				if (options.verify_host_key_dns == 1 &&
				    flags & DNS_VERIFY_MATCH &&
				    flags & DNS_VERIFY_SECURE) {
					r = 0;
					goto out;
				}
				if (flags & DNS_VERIFY_MATCH) {
					matching_host_key_dns = 1;
				} else {
					warn_changed_key(plain);
					error("Update the SSHFP RR in DNS "
					    "with the new host key to get rid "
					    "of this message.");
				}
			}
		}
	}
	r = check_host_key(host, hostaddr, options.port, host_key, RDRW,
	    options.user_hostfiles, options.num_user_hostfiles,
	    options.system_hostfiles, options.num_system_hostfiles);

out:
	sshkey_free(plain);
	free(fp);
	free(cafp);
	if (r == 0 && host_key != NULL) {
		sshkey_free(previous_host_key);
		r = sshkey_from_private(host_key, &previous_host_key);
	}

	return r;
}
예제 #9
0
void
sshkey_tests(void)
{
	struct sshkey *k1, *k2, *k3, *k4, *kr, *kd, *kf;
#ifdef OPENSSL_HAS_ECC
	struct sshkey *ke;
#endif
	struct sshbuf *b;

	TEST_START("new invalid");
	k1 = sshkey_new(-42);
	ASSERT_PTR_EQ(k1, NULL);
	TEST_DONE();

	TEST_START("new/free KEY_UNSPEC");
	k1 = sshkey_new(KEY_UNSPEC);
	ASSERT_PTR_NE(k1, NULL);
	sshkey_free(k1);
	TEST_DONE();

	TEST_START("new/free KEY_RSA1");
	k1 = sshkey_new(KEY_RSA1);
	ASSERT_PTR_NE(k1, NULL);
	ASSERT_PTR_NE(k1->rsa, NULL);
	ASSERT_PTR_NE(k1->rsa->n, NULL);
	ASSERT_PTR_NE(k1->rsa->e, NULL);
	ASSERT_PTR_EQ(k1->rsa->p, NULL);
	sshkey_free(k1);
	TEST_DONE();

	TEST_START("new/free KEY_RSA");
	k1 = sshkey_new(KEY_RSA);
	ASSERT_PTR_NE(k1, NULL);
	ASSERT_PTR_NE(k1->rsa, NULL);
	ASSERT_PTR_NE(k1->rsa->n, NULL);
	ASSERT_PTR_NE(k1->rsa->e, NULL);
	ASSERT_PTR_EQ(k1->rsa->p, NULL);
	sshkey_free(k1);
	TEST_DONE();

	TEST_START("new/free KEY_DSA");
	k1 = sshkey_new(KEY_DSA);
	ASSERT_PTR_NE(k1, NULL);
	ASSERT_PTR_NE(k1->dsa, NULL);
	ASSERT_PTR_NE(k1->dsa->g, NULL);
	ASSERT_PTR_EQ(k1->dsa->priv_key, NULL);
	sshkey_free(k1);
	TEST_DONE();

#ifdef OPENSSL_HAS_ECC
	TEST_START("new/free KEY_ECDSA");
	k1 = sshkey_new(KEY_ECDSA);
	ASSERT_PTR_NE(k1, NULL);
	ASSERT_PTR_EQ(k1->ecdsa, NULL);  /* Can't allocate without NID */
	sshkey_free(k1);
	TEST_DONE();
#endif

	TEST_START("new/free KEY_ED25519");
	k1 = sshkey_new(KEY_ED25519);
	ASSERT_PTR_NE(k1, NULL);
	/* These should be blank until key loaded or generated */
	ASSERT_PTR_EQ(k1->ed25519_sk, NULL);
	ASSERT_PTR_EQ(k1->ed25519_pk, NULL);
	sshkey_free(k1);
	TEST_DONE();

	TEST_START("new_private KEY_RSA");
	k1 = sshkey_new_private(KEY_RSA);
	ASSERT_PTR_NE(k1, NULL);
	ASSERT_PTR_NE(k1->rsa, NULL);
	ASSERT_PTR_NE(k1->rsa->n, NULL);
	ASSERT_PTR_NE(k1->rsa->e, NULL);
	ASSERT_PTR_NE(k1->rsa->p, NULL);
	ASSERT_INT_EQ(sshkey_add_private(k1), 0);
	sshkey_free(k1);
	TEST_DONE();

	TEST_START("new_private KEY_DSA");
	k1 = sshkey_new_private(KEY_DSA);
	ASSERT_PTR_NE(k1, NULL);
	ASSERT_PTR_NE(k1->dsa, NULL);
	ASSERT_PTR_NE(k1->dsa->g, NULL);
	ASSERT_PTR_NE(k1->dsa->priv_key, NULL);
	ASSERT_INT_EQ(sshkey_add_private(k1), 0);
	sshkey_free(k1);
	TEST_DONE();

	TEST_START("generate KEY_RSA too small modulus");
	ASSERT_INT_EQ(sshkey_generate(KEY_RSA, 128, &k1),
	    SSH_ERR_INVALID_ARGUMENT);
	ASSERT_PTR_EQ(k1, NULL);
	TEST_DONE();

	TEST_START("generate KEY_RSA too large modulus");
	ASSERT_INT_EQ(sshkey_generate(KEY_RSA, 1 << 20, &k1),
	    SSH_ERR_INVALID_ARGUMENT);
	ASSERT_PTR_EQ(k1, NULL);
	TEST_DONE();

	TEST_START("generate KEY_DSA wrong bits");
	ASSERT_INT_EQ(sshkey_generate(KEY_DSA, 2048, &k1),
	    SSH_ERR_INVALID_ARGUMENT);
	ASSERT_PTR_EQ(k1, NULL);
	sshkey_free(k1);
	TEST_DONE();

#ifdef OPENSSL_HAS_ECC
	TEST_START("generate KEY_ECDSA wrong bits");
	ASSERT_INT_EQ(sshkey_generate(KEY_ECDSA, 42, &k1),
	    SSH_ERR_INVALID_ARGUMENT);
	ASSERT_PTR_EQ(k1, NULL);
	sshkey_free(k1);
	TEST_DONE();
#endif

	TEST_START("generate KEY_RSA");
	ASSERT_INT_EQ(sshkey_generate(KEY_RSA, 767, &kr),
	    SSH_ERR_INVALID_ARGUMENT);
	ASSERT_INT_EQ(sshkey_generate(KEY_RSA, 1024, &kr), 0);
	ASSERT_PTR_NE(kr, NULL);
	ASSERT_PTR_NE(kr->rsa, NULL);
	ASSERT_PTR_NE(kr->rsa->n, NULL);
	ASSERT_PTR_NE(kr->rsa->e, NULL);
	ASSERT_PTR_NE(kr->rsa->p, NULL);
	ASSERT_INT_EQ(BN_num_bits(kr->rsa->n), 1024);
	TEST_DONE();

	TEST_START("generate KEY_DSA");
	ASSERT_INT_EQ(sshkey_generate(KEY_DSA, 1024, &kd), 0);
	ASSERT_PTR_NE(kd, NULL);
	ASSERT_PTR_NE(kd->dsa, NULL);
	ASSERT_PTR_NE(kd->dsa->g, NULL);
	ASSERT_PTR_NE(kd->dsa->priv_key, NULL);
	TEST_DONE();

#ifdef OPENSSL_HAS_ECC
	TEST_START("generate KEY_ECDSA");
	ASSERT_INT_EQ(sshkey_generate(KEY_ECDSA, 256, &ke), 0);
	ASSERT_PTR_NE(ke, NULL);
	ASSERT_PTR_NE(ke->ecdsa, NULL);
	ASSERT_PTR_NE(EC_KEY_get0_public_key(ke->ecdsa), NULL);
	ASSERT_PTR_NE(EC_KEY_get0_private_key(ke->ecdsa), NULL);
	TEST_DONE();
#endif

	TEST_START("generate KEY_ED25519");
	ASSERT_INT_EQ(sshkey_generate(KEY_ED25519, 256, &kf), 0);
	ASSERT_PTR_NE(kf, NULL);
	ASSERT_INT_EQ(kf->type, KEY_ED25519);
	ASSERT_PTR_NE(kf->ed25519_pk, NULL);
	ASSERT_PTR_NE(kf->ed25519_sk, NULL);
	TEST_DONE();

	TEST_START("demote KEY_RSA");
	ASSERT_INT_EQ(sshkey_demote(kr, &k1), 0);
	ASSERT_PTR_NE(k1, NULL);
	ASSERT_PTR_NE(kr, k1);
	ASSERT_INT_EQ(k1->type, KEY_RSA);
	ASSERT_PTR_NE(k1->rsa, NULL);
	ASSERT_PTR_NE(k1->rsa->n, NULL);
	ASSERT_PTR_NE(k1->rsa->e, NULL);
	ASSERT_PTR_EQ(k1->rsa->p, NULL);
	TEST_DONE();

	TEST_START("equal KEY_RSA/demoted KEY_RSA");
	ASSERT_INT_EQ(sshkey_equal(kr, k1), 1);
	sshkey_free(k1);
	TEST_DONE();

	TEST_START("demote KEY_DSA");
	ASSERT_INT_EQ(sshkey_demote(kd, &k1), 0);
	ASSERT_PTR_NE(k1, NULL);
	ASSERT_PTR_NE(kd, k1);
	ASSERT_INT_EQ(k1->type, KEY_DSA);
	ASSERT_PTR_NE(k1->dsa, NULL);
	ASSERT_PTR_NE(k1->dsa->g, NULL);
	ASSERT_PTR_EQ(k1->dsa->priv_key, NULL);
	TEST_DONE();

	TEST_START("equal KEY_DSA/demoted KEY_DSA");
	ASSERT_INT_EQ(sshkey_equal(kd, k1), 1);
	sshkey_free(k1);
	TEST_DONE();

#ifdef OPENSSL_HAS_ECC
	TEST_START("demote KEY_ECDSA");
	ASSERT_INT_EQ(sshkey_demote(ke, &k1), 0);
	ASSERT_PTR_NE(k1, NULL);
	ASSERT_PTR_NE(ke, k1);
	ASSERT_INT_EQ(k1->type, KEY_ECDSA);
	ASSERT_PTR_NE(k1->ecdsa, NULL);
	ASSERT_INT_EQ(k1->ecdsa_nid, ke->ecdsa_nid);
	ASSERT_PTR_NE(EC_KEY_get0_public_key(ke->ecdsa), NULL);
	ASSERT_PTR_EQ(EC_KEY_get0_private_key(k1->ecdsa), NULL);
	TEST_DONE();

	TEST_START("equal KEY_ECDSA/demoted KEY_ECDSA");
	ASSERT_INT_EQ(sshkey_equal(ke, k1), 1);
	sshkey_free(k1);
	TEST_DONE();
#endif

	TEST_START("demote KEY_ED25519");
	ASSERT_INT_EQ(sshkey_demote(kf, &k1), 0);
	ASSERT_PTR_NE(k1, NULL);
	ASSERT_PTR_NE(kf, k1);
	ASSERT_INT_EQ(k1->type, KEY_ED25519);
	ASSERT_PTR_NE(k1->ed25519_pk, NULL);
	ASSERT_PTR_EQ(k1->ed25519_sk, NULL);
	TEST_DONE();

	TEST_START("equal KEY_ED25519/demoted KEY_ED25519");
	ASSERT_INT_EQ(sshkey_equal(kf, k1), 1);
	sshkey_free(k1);
	TEST_DONE();

	TEST_START("equal mismatched key types");
	ASSERT_INT_EQ(sshkey_equal(kd, kr), 0);
#ifdef OPENSSL_HAS_ECC
	ASSERT_INT_EQ(sshkey_equal(kd, ke), 0);
	ASSERT_INT_EQ(sshkey_equal(kr, ke), 0);
	ASSERT_INT_EQ(sshkey_equal(ke, kf), 0);
#endif
	ASSERT_INT_EQ(sshkey_equal(kd, kf), 0);
	TEST_DONE();

	TEST_START("equal different keys");
	ASSERT_INT_EQ(sshkey_generate(KEY_RSA, 1024, &k1), 0);
	ASSERT_INT_EQ(sshkey_equal(kr, k1), 0);
	sshkey_free(k1);
	ASSERT_INT_EQ(sshkey_generate(KEY_DSA, 1024, &k1), 0);
	ASSERT_INT_EQ(sshkey_equal(kd, k1), 0);
	sshkey_free(k1);
#ifdef OPENSSL_HAS_ECC
	ASSERT_INT_EQ(sshkey_generate(KEY_ECDSA, 256, &k1), 0);
	ASSERT_INT_EQ(sshkey_equal(ke, k1), 0);
	sshkey_free(k1);
#endif
	ASSERT_INT_EQ(sshkey_generate(KEY_ED25519, 256, &k1), 0);
	ASSERT_INT_EQ(sshkey_equal(kf, k1), 0);
	sshkey_free(k1);
	TEST_DONE();

	sshkey_free(kr);
	sshkey_free(kd);
#ifdef OPENSSL_HAS_ECC
	sshkey_free(ke);
#endif
	sshkey_free(kf);

	TEST_START("certify key");
	ASSERT_INT_EQ(sshkey_load_public(test_data_file("ed25519_1.pub"),
	    &k1, NULL), 0);
	k2 = get_private("ed25519_2");
	ASSERT_INT_EQ(sshkey_to_certified(k1), 0);
	ASSERT_PTR_NE(k1->cert, NULL);
	k1->cert->type = SSH2_CERT_TYPE_USER;
	k1->cert->serial = 1234;
	k1->cert->key_id = strdup("estragon");
	ASSERT_PTR_NE(k1->cert->key_id, NULL);
	k1->cert->principals = calloc(4, sizeof(*k1->cert->principals));
	ASSERT_PTR_NE(k1->cert->principals, NULL);
	k1->cert->principals[0] = strdup("estragon");
	k1->cert->principals[1] = strdup("vladimir");
	k1->cert->principals[2] = strdup("pozzo");
	k1->cert->principals[3] = strdup("lucky");
	ASSERT_PTR_NE(k1->cert->principals[0], NULL);
	ASSERT_PTR_NE(k1->cert->principals[1], NULL);
	ASSERT_PTR_NE(k1->cert->principals[2], NULL);
	ASSERT_PTR_NE(k1->cert->principals[3], NULL);
	k1->cert->valid_after = 0;
	k1->cert->valid_before = (u_int64_t)-1;
	k1->cert->critical = sshbuf_new();
	ASSERT_PTR_NE(k1->cert->critical, NULL);
	k1->cert->extensions = sshbuf_new();
	ASSERT_PTR_NE(k1->cert->extensions, NULL);
	put_opt(k1->cert->critical, "force-command", "/usr/bin/true");
	put_opt(k1->cert->critical, "source-address", "127.0.0.1");
	put_opt(k1->cert->extensions, "permit-X11-forwarding", NULL);
	put_opt(k1->cert->extensions, "permit-agent-forwarding", NULL);
	ASSERT_INT_EQ(sshkey_from_private(k2, &k1->cert->signature_key), 0);
	ASSERT_INT_EQ(sshkey_certify(k1, k2), 0);
	b = sshbuf_new();
	ASSERT_PTR_NE(b, NULL);
	ASSERT_INT_EQ(sshkey_putb(k1, b), 0);
	ASSERT_INT_EQ(sshkey_from_blob(sshbuf_ptr(b), sshbuf_len(b), &k3), 0);

	sshkey_free(k1);
	sshkey_free(k2);
	sshkey_free(k3);
	sshbuf_reset(b);
	TEST_DONE();

	TEST_START("sign and verify RSA");
	k1 = get_private("rsa_1");
	ASSERT_INT_EQ(sshkey_load_public(test_data_file("rsa_2.pub"), &k2,
	    NULL), 0);
	signature_tests(k1, k2);
	sshkey_free(k1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("sign and verify DSA");
	k1 = get_private("dsa_1");
	ASSERT_INT_EQ(sshkey_load_public(test_data_file("dsa_2.pub"), &k2,
	    NULL), 0);
	signature_tests(k1, k2);
	sshkey_free(k1);
	sshkey_free(k2);
	TEST_DONE();

#ifdef OPENSSL_HAS_ECC
#ifndef WIN32_FIXME
	TEST_START("sign and verify ECDSA");
	k1 = get_private("ecdsa_1");
	ASSERT_INT_EQ(sshkey_load_public(test_data_file("ecdsa_2.pub"), &k2,
	    NULL), 0);
	signature_tests(k1, k2);
	sshkey_free(k1);
	sshkey_free(k2);
	TEST_DONE();
#endif
#endif

	TEST_START("sign and verify ED25519");
	k1 = get_private("ed25519_1");
	ASSERT_INT_EQ(sshkey_load_public(test_data_file("ed25519_2.pub"), &k2,
	    NULL), 0);
	signature_tests(k1, k2);
	sshkey_free(k1);
	sshkey_free(k2);
	TEST_DONE();

	TEST_START("nested certificate");
	ASSERT_INT_EQ(sshkey_load_cert(test_data_file("rsa_1"), &k1), 0);
	ASSERT_INT_EQ(sshkey_load_public(test_data_file("rsa_1.pub"), &k2,
	    NULL), 0);
	k3 = get_private("rsa_1");
	build_cert(b, k2, "*****@*****.**", k3, k1);
	ASSERT_INT_EQ(sshkey_from_blob(sshbuf_ptr(b), sshbuf_len(b), &k4),
	    SSH_ERR_KEY_CERT_INVALID_SIGN_KEY);
	ASSERT_PTR_EQ(k4, NULL);
	sshkey_free(k1);
	sshkey_free(k2);
	sshkey_free(k3);
	sshbuf_free(b);
	TEST_DONE();

}