Beispiel #1
0
static int
input_kex_dh_gex_reply(int type, u_int32_t seq, struct ssh *ssh)
{
	Kex *kex = ssh->kex;
	BIGNUM *dh_server_pub = NULL, *shared_secret = NULL;
	struct sshkey *server_host_key;
	u_char *kbuf = NULL, *hash, *signature = NULL, *server_host_key_blob = NULL;
	size_t klen = 0, slen, sbloblen, hashlen;
	int kout, r;

	debug("got SSH2_MSG_KEX_DH_GEX_REPLY");
	if (kex->verify_host_key == NULL) {
		r = SSH_ERR_INVALID_ARGUMENT;
		goto out;
	}
	/* key, cert */
	if ((r = sshpkt_get_string(ssh, &server_host_key_blob,
	    &sbloblen)) != 0 ||
	    (r = sshkey_from_blob(server_host_key_blob, sbloblen,
	    &server_host_key)) != 0)
		goto out;
	if (server_host_key->type != kex->hostkey_type) {
		r = SSH_ERR_KEY_TYPE_MISMATCH;
		goto out;
	}
	if (kex->verify_host_key(server_host_key, ssh) == -1) {
		r = SSH_ERR_SIGNATURE_INVALID;
		goto out;
	}
	/* DH parameter f, server public DH key */
	if ((dh_server_pub = BN_new()) == NULL) {
		r = SSH_ERR_ALLOC_FAIL;
		goto out;
	}
	/* signed H */
	if ((r = sshpkt_get_bignum2(ssh, dh_server_pub)) != 0 ||
	    (r = sshpkt_get_string(ssh, &signature, &slen)) != 0 ||
	    (r = sshpkt_get_end(ssh)) != 0)
		goto out;
#ifdef DEBUG_KEXDH
	fprintf(stderr, "dh_server_pub= ");
	BN_print_fp(stderr, dh_server_pub);
	fprintf(stderr, "\n");
	debug("bits %d", BN_num_bits(dh_server_pub));
#endif
	if (!dh_pub_is_valid(kex->dh, dh_server_pub)) {
		sshpkt_disconnect(ssh, "bad server public DH value");
		r = SSH_ERR_MESSAGE_INCOMPLETE;
		goto out;
	}

	klen = DH_size(kex->dh);
	if ((kbuf = malloc(klen)) == NULL ||
	    (shared_secret = BN_new()) == NULL) {
		r = SSH_ERR_ALLOC_FAIL;
		goto out;
	}
	if ((kout = DH_compute_key(kbuf, dh_server_pub, kex->dh)) < 0 ||
	    BN_bin2bn(kbuf, kout, shared_secret) == NULL) {
		r = SSH_ERR_LIBCRYPTO_ERROR;
		goto out;
	}
#ifdef DEBUG_KEXDH
	dump_digest("shared secret", kbuf, kout);
#endif
	if (ssh->compat & SSH_OLD_DHGEX)
		kex->min = kex->max = -1;

	/* calc and verify H */
	if ((r = kexgex_hash(
	    kex->evp_md,
	    kex->client_version_string,
	    kex->server_version_string,
	    sshbuf_ptr(kex->my), sshbuf_len(kex->my),
	    sshbuf_ptr(kex->peer), sshbuf_len(kex->peer),
	    server_host_key_blob, sbloblen,
	    kex->min, kex->nbits, kex->max,
	    kex->dh->p, kex->dh->g,
	    kex->dh->pub_key,
	    dh_server_pub,
	    shared_secret,
	    &hash, &hashlen)) != 0)
		goto out;

	if ((r = sshkey_verify(server_host_key, signature, slen, hash,
	    hashlen, ssh->compat)) != 0)
		goto out;

	/* save session id */
	if (kex->session_id == NULL) {
		kex->session_id_len = hashlen;
		kex->session_id = malloc(kex->session_id_len);
		if (kex->session_id == NULL) {
			r = SSH_ERR_ALLOC_FAIL;
			goto out;
		}
		memcpy(kex->session_id, hash, kex->session_id_len);
	}

	if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
		r = kex_send_newkeys(ssh);
 out:
	DH_free(kex->dh);
	kex->dh = NULL;
	if (server_host_key_blob)
		free(server_host_key_blob);
	if (server_host_key)
		sshkey_free(server_host_key);
	if (dh_server_pub)
		BN_clear_free(dh_server_pub);
	if (kbuf) {
		bzero(kbuf, klen);
		free(kbuf);
	}
	if (shared_secret)
		BN_clear_free(shared_secret);
	if (signature)
		free(signature);
	return r;
}
Beispiel #2
0
static int
input_kex_c25519_init(int type, u_int32_t seq, struct ssh *ssh)
{
	struct kex *kex = ssh->kex;
	struct sshkey *server_host_private, *server_host_public;
	struct sshbuf *shared_secret = NULL;
	u_char *server_host_key_blob = NULL, *signature = NULL;
	u_char server_key[CURVE25519_SIZE];
	u_char *client_pubkey = NULL;
	u_char server_pubkey[CURVE25519_SIZE];
	u_char hash[SSH_DIGEST_MAX_LENGTH];
	size_t slen, pklen, sbloblen, hashlen;
	int r;

	/* generate private key */
	kexc25519_keygen(server_key, server_pubkey);
#ifdef DEBUG_KEXECDH
	dump_digest("server private key:", server_key, sizeof(server_key));
#endif
	if (kex->load_host_public_key == NULL ||
	    kex->load_host_private_key == NULL) {
		r = SSH_ERR_INVALID_ARGUMENT;
		goto out;
	}
	server_host_public = kex->load_host_public_key(kex->hostkey_type,
	    kex->hostkey_nid, ssh);
	server_host_private = kex->load_host_private_key(kex->hostkey_type,
	    kex->hostkey_nid, ssh);
	if (server_host_public == NULL) {
		r = SSH_ERR_NO_HOSTKEY_LOADED;
		goto out;
	}

	if ((r = sshpkt_get_string(ssh, &client_pubkey, &pklen)) != 0 ||
	    (r = sshpkt_get_end(ssh)) != 0)
		goto out;
	if (pklen != CURVE25519_SIZE) {
		r = SSH_ERR_SIGNATURE_INVALID;
		goto out;
	}
#ifdef DEBUG_KEXECDH
	dump_digest("client public key:", client_pubkey, CURVE25519_SIZE);
#endif

	if ((shared_secret = sshbuf_new()) == NULL) {
		r = SSH_ERR_ALLOC_FAIL;
		goto out;
	}
	if ((r = kexc25519_shared_key(server_key, client_pubkey,
	    shared_secret)) < 0)
		goto out;

	/* calc H */
	if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob,
	    &sbloblen)) != 0)
		goto out;
	hashlen = sizeof(hash);
	if ((r = kex_c25519_hash(
	    kex->hash_alg,
	    kex->client_version_string,
	    kex->server_version_string,
	    sshbuf_ptr(kex->peer), sshbuf_len(kex->peer),
	    sshbuf_ptr(kex->my), sshbuf_len(kex->my),
	    server_host_key_blob, sbloblen,
	    client_pubkey,
	    server_pubkey,
	    sshbuf_ptr(shared_secret), sshbuf_len(shared_secret),
	    hash, &hashlen)) < 0)
		goto out;

	/* save session id := H */
	if (kex->session_id == NULL) {
		kex->session_id_len = hashlen;
		kex->session_id = malloc(kex->session_id_len);
		if (kex->session_id == NULL) {
			r = SSH_ERR_ALLOC_FAIL;
			goto out;
		}
		memcpy(kex->session_id, hash, kex->session_id_len);
	}

	/* sign H */
	if ((r = kex->sign(server_host_private, server_host_public, &signature,
	     &slen, hash, hashlen, kex->hostkey_alg, ssh->compat)) < 0)
		goto out;

	/* send server hostkey, ECDH pubkey 'Q_S' and signed H */
	if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_ECDH_REPLY)) != 0 ||
	    (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 ||
	    (r = sshpkt_put_string(ssh, server_pubkey, sizeof(server_pubkey))) != 0 ||
	    (r = sshpkt_put_string(ssh, signature, slen)) != 0 ||
	    (r = sshpkt_send(ssh)) != 0)
		goto out;

	if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
		r = kex_send_newkeys(ssh);
out:
	explicit_bzero(hash, sizeof(hash));
	explicit_bzero(server_key, sizeof(server_key));
	free(server_host_key_blob);
	free(signature);
	free(client_pubkey);
	sshbuf_free(shared_secret);
	return r;
}
Beispiel #3
0
static int
input_kex_ecdh_init(int type, u_int32_t seq, struct ssh *ssh)
{
	struct kex *kex = ssh->kex;
	EC_POINT *client_public;
	EC_KEY *server_key = NULL;
	const EC_GROUP *group;
	const EC_POINT *public_key;
	BIGNUM *shared_secret = NULL;
	struct sshkey *server_host_private, *server_host_public;
	u_char *server_host_key_blob = NULL, *signature = NULL;
	u_char *kbuf = NULL;
	u_char hash[SSH_DIGEST_MAX_LENGTH];
	size_t slen, sbloblen;
	size_t klen = 0, hashlen;
	int r;

	if ((server_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) {
		r = SSH_ERR_ALLOC_FAIL;
		goto out;
	}
	if (EC_KEY_generate_key(server_key) != 1) {
		r = SSH_ERR_LIBCRYPTO_ERROR;
		goto out;
	}
	group = EC_KEY_get0_group(server_key);

#ifdef DEBUG_KEXECDH
	fputs("server private key:\n", stderr);
	sshkey_dump_ec_key(server_key);
#endif

	if (kex->load_host_public_key == NULL ||
	    kex->load_host_private_key == NULL) {
		r = SSH_ERR_INVALID_ARGUMENT;
		goto out;
	}
	server_host_public = kex->load_host_public_key(kex->hostkey_type,
	    kex->hostkey_nid, ssh);
	server_host_private = kex->load_host_private_key(kex->hostkey_type,
	    kex->hostkey_nid, ssh);
	if (server_host_public == NULL) {
		r = SSH_ERR_NO_HOSTKEY_LOADED;
		goto out;
	}
	if ((client_public = EC_POINT_new(group)) == NULL) {
		r = SSH_ERR_ALLOC_FAIL;
		goto out;
	}
	if ((r = sshpkt_get_ec(ssh, client_public, group)) != 0 ||
	    (r = sshpkt_get_end(ssh)) != 0)
		goto out;

#ifdef DEBUG_KEXECDH
	fputs("client public key:\n", stderr);
	sshkey_dump_ec_point(group, client_public);
#endif
	if (sshkey_ec_validate_public(group, client_public) != 0) {
		sshpkt_disconnect(ssh, "invalid client public key");
		r = SSH_ERR_MESSAGE_INCOMPLETE;
		goto out;
	}

	/* Calculate shared_secret */
	klen = (EC_GROUP_get_degree(group) + 7) / 8;
	if ((kbuf = malloc(klen)) == NULL ||
	    (shared_secret = BN_new()) == NULL) {
		r = SSH_ERR_ALLOC_FAIL;
		goto out;
	}
	if (ECDH_compute_key(kbuf, klen, client_public,
	    server_key, NULL) != (int)klen ||
	    BN_bin2bn(kbuf, klen, shared_secret) == NULL) {
		r = SSH_ERR_LIBCRYPTO_ERROR;
		goto out;
	}

#ifdef DEBUG_KEXECDH
	dump_digest("shared secret", kbuf, klen);
#endif
	/* calc H */
	if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob,
	    &sbloblen)) != 0)
		goto out;
	hashlen = sizeof(hash);
	if ((r = kex_ecdh_hash(
	    kex->hash_alg,
	    group,
	    kex->client_version_string,
	    kex->server_version_string,
	    sshbuf_ptr(kex->peer), sshbuf_len(kex->peer),
	    sshbuf_ptr(kex->my), sshbuf_len(kex->my),
	    server_host_key_blob, sbloblen,
	    client_public,
	    EC_KEY_get0_public_key(server_key),
	    shared_secret,
	    hash, &hashlen)) != 0)
		goto out;

	/* save session id := H */
	if (kex->session_id == NULL) {
		kex->session_id_len = hashlen;
		kex->session_id = malloc(kex->session_id_len);
		if (kex->session_id == NULL) {
			r = SSH_ERR_ALLOC_FAIL;
			goto out;
		}
		memcpy(kex->session_id, hash, kex->session_id_len);
	}

	/* sign H */
	if ((r = kex->sign(server_host_private, server_host_public, &signature,
	     &slen, hash, hashlen, kex->hostkey_alg, ssh->compat)) < 0)
		goto out;

	/* destroy_sensitive_data(); */

	public_key = EC_KEY_get0_public_key(server_key);
	/* send server hostkey, ECDH pubkey 'Q_S' and signed H */
	if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_ECDH_REPLY)) != 0 ||
	    (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 ||
	    (r = sshpkt_put_ec(ssh, public_key, group)) != 0 ||
	    (r = sshpkt_put_string(ssh, signature, slen)) != 0 ||
	    (r = sshpkt_send(ssh)) != 0)
		goto out;

	if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0)
		r = kex_send_newkeys(ssh);
 out:
	explicit_bzero(hash, sizeof(hash));
	if (kex->ec_client_key) {
		EC_KEY_free(kex->ec_client_key);
		kex->ec_client_key = NULL;
	}
	if (server_key)
		EC_KEY_free(server_key);
	if (kbuf) {
		explicit_bzero(kbuf, klen);
		free(kbuf);
	}
	if (shared_secret)
		BN_clear_free(shared_secret);
	free(server_host_key_blob);
	free(signature);
	return r;
}
Beispiel #4
0
static int
input_kex_ecdh_reply(int type, u_int32_t seq, void *ctxt)
{
	struct ssh *ssh = ctxt;
	struct kex *kex = ssh->kex;
	const EC_GROUP *group;
	EC_POINT *server_public = NULL;
	EC_KEY *client_key;
	BIGNUM *shared_secret = NULL;
	struct sshkey *server_host_key = NULL;
	u_char *server_host_key_blob = NULL, *signature = NULL;
	u_char *kbuf = NULL;
	u_char hash[SSH_DIGEST_MAX_LENGTH];
	size_t slen, sbloblen;
	size_t klen = 0, hashlen;
	int r;

	if (kex->verify_host_key == NULL) {
		r = SSH_ERR_INVALID_ARGUMENT;
		goto out;
	}
	group = kex->ec_group;
	client_key = kex->ec_client_key;

	/* hostkey */
	if ((r = sshpkt_get_string(ssh, &server_host_key_blob,
	    &sbloblen)) != 0 ||
	    (r = sshkey_from_blob(server_host_key_blob, sbloblen,
	    &server_host_key)) != 0)
		goto out;
	if (server_host_key->type != kex->hostkey_type ||
	    (kex->hostkey_type == KEY_ECDSA &&
	    server_host_key->ecdsa_nid != kex->hostkey_nid)) {
		r = SSH_ERR_KEY_TYPE_MISMATCH;
		goto out;
	}
	if (kex->verify_host_key(server_host_key, ssh) == -1) {
		r = SSH_ERR_SIGNATURE_INVALID;
		goto out;
	}

	/* Q_S, server public key */
	/* signed H */
	if ((server_public = EC_POINT_new(group)) == NULL) {
		r = SSH_ERR_ALLOC_FAIL;
		goto out;
	}
	if ((r = sshpkt_get_ec(ssh, server_public, group)) != 0 ||
	    (r = sshpkt_get_string(ssh, &signature, &slen)) != 0 ||
	    (r = sshpkt_get_end(ssh)) != 0)
		goto out;

#ifdef DEBUG_KEXECDH
	fputs("server public key:\n", stderr);
	sshkey_dump_ec_point(group, server_public);
#endif
	if (sshkey_ec_validate_public(group, server_public) != 0) {
		sshpkt_disconnect(ssh, "invalid server public key");
		r = SSH_ERR_MESSAGE_INCOMPLETE;
		goto out;
	}

	klen = (EC_GROUP_get_degree(group) + 7) / 8;
	if ((kbuf = malloc(klen)) == NULL ||
	    (shared_secret = BN_new()) == NULL) {
		r = SSH_ERR_ALLOC_FAIL;
		goto out;
	}
	if (ECDH_compute_key(kbuf, klen, server_public,
	    client_key, NULL) != (int)klen ||
	    BN_bin2bn(kbuf, klen, shared_secret) == NULL) {
		r = SSH_ERR_LIBCRYPTO_ERROR;
		goto out;
	}

#ifdef DEBUG_KEXECDH
	dump_digest("shared secret", kbuf, klen);
#endif
	/* calc and verify H */
	hashlen = sizeof(hash);
	if ((r = kex_ecdh_hash(
	    kex->hash_alg,
	    group,
	    kex->client_version_string,
	    kex->server_version_string,
	    sshbuf_ptr(kex->my), sshbuf_len(kex->my),
	    sshbuf_ptr(kex->peer), sshbuf_len(kex->peer),
	    server_host_key_blob, sbloblen,
	    EC_KEY_get0_public_key(client_key),
	    server_public,
	    shared_secret,
	    hash, &hashlen)) != 0)
		goto out;

	if ((r = sshkey_verify(server_host_key, signature, slen, hash,
	    hashlen, ssh->compat)) != 0)
		goto out;

	/* save session id */
	if (kex->session_id == NULL) {
		kex->session_id_len = hashlen;
		kex->session_id = malloc(kex->session_id_len);
		if (kex->session_id == NULL) {
			r = SSH_ERR_ALLOC_FAIL;
			goto out;
		}
		memcpy(kex->session_id, hash, kex->session_id_len);
	}

	if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0)
		r = kex_send_newkeys(ssh);
 out:
	explicit_bzero(hash, sizeof(hash));
	if (kex->ec_client_key) {
		EC_KEY_free(kex->ec_client_key);
		kex->ec_client_key = NULL;
	}
	if (server_public)
		EC_POINT_clear_free(server_public);
	if (kbuf) {
		explicit_bzero(kbuf, klen);
		free(kbuf);
	}
	if (shared_secret)
		BN_clear_free(shared_secret);
	sshkey_free(server_host_key);
	free(server_host_key_blob);
	free(signature);
	return r;
}
Beispiel #5
0
int
input_kex_dh_init(int type, u_int32_t seq, void *ctxt)
{
	struct ssh *ssh = ctxt;
	struct kex *kex = ssh->kex;
	BIGNUM *shared_secret = NULL, *dh_client_pub = NULL;
	struct sshkey *server_host_public, *server_host_private;
	u_char *kbuf = NULL, *signature = NULL, *server_host_key_blob = NULL;
	u_char hash[SSH_DIGEST_MAX_LENGTH];
	size_t sbloblen, slen;
	size_t klen = 0, hashlen;
	int kout, r;

	if (kex->load_host_public_key == NULL ||
	    kex->load_host_private_key == NULL) {
		r = SSH_ERR_INVALID_ARGUMENT;
		goto out;
	}
	server_host_public = kex->load_host_public_key(kex->hostkey_type,
	    kex->hostkey_nid, ssh);
	server_host_private = kex->load_host_private_key(kex->hostkey_type,
	    kex->hostkey_nid, ssh);
	if (server_host_public == NULL) {
		r = SSH_ERR_NO_HOSTKEY_LOADED;
		goto out;
	}

	/* key, cert */
	if ((dh_client_pub = BN_new()) == NULL) {
		r = SSH_ERR_ALLOC_FAIL;
		goto out;
	}
	if ((r = sshpkt_get_bignum2(ssh, dh_client_pub)) != 0 ||
	    (r = sshpkt_get_end(ssh)) != 0)
		goto out;

#ifdef DEBUG_KEXDH
	fprintf(stderr, "dh_client_pub= ");
	BN_print_fp(stderr, dh_client_pub);
	fprintf(stderr, "\n");
	debug("bits %d", BN_num_bits(dh_client_pub));
#endif

#ifdef DEBUG_KEXDH
	DHparams_print_fp(stderr, kex->dh);
	fprintf(stderr, "pub= ");
	BN_print_fp(stderr, kex->dh->pub_key);
	fprintf(stderr, "\n");
#endif
	if (!dh_pub_is_valid(kex->dh, dh_client_pub)) {
		sshpkt_disconnect(ssh, "bad client public DH value");
		r = SSH_ERR_MESSAGE_INCOMPLETE;
		goto out;
	}

	klen = DH_size(kex->dh);
	if ((kbuf = malloc(klen)) == NULL ||
	    (shared_secret = BN_new()) == NULL) {
		r = SSH_ERR_ALLOC_FAIL;
		goto out;
	}
	if ((kout = DH_compute_key(kbuf, dh_client_pub, kex->dh)) < 0 ||
	    BN_bin2bn(kbuf, kout, shared_secret) == NULL) {
		r = SSH_ERR_LIBCRYPTO_ERROR;
		goto out;
	}
#ifdef DEBUG_KEXDH
	dump_digest("shared secret", kbuf, kout);
#endif
	if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob,
	    &sbloblen)) != 0)
		goto out;
	/* calc H */
	hashlen = sizeof(hash);
	if ((r = kex_dh_hash(
	    kex->client_version_string,
	    kex->server_version_string,
	    sshbuf_ptr(kex->peer), sshbuf_len(kex->peer),
	    sshbuf_ptr(kex->my), sshbuf_len(kex->my),
	    server_host_key_blob, sbloblen,
	    dh_client_pub,
	    kex->dh->pub_key,
	    shared_secret,
	    hash, &hashlen)) != 0)
		goto out;

	/* save session id := H */
	if (kex->session_id == NULL) {
		kex->session_id_len = hashlen;
		kex->session_id = malloc(kex->session_id_len);
		if (kex->session_id == NULL) {
			r = SSH_ERR_ALLOC_FAIL;
			goto out;
		}
		memcpy(kex->session_id, hash, kex->session_id_len);
	}

	/* sign H */
	if ((r = kex->sign(server_host_private, server_host_public, &signature,
	     &slen, hash, hashlen, kex->hostkey_alg, ssh->compat)) < 0)
		goto out;

	/* destroy_sensitive_data(); */

	/* send server hostkey, DH pubkey 'f' and singed H */
	if ((r = sshpkt_start(ssh, SSH2_MSG_KEXDH_REPLY)) != 0 ||
	    (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 ||
	    (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 ||	/* f */
	    (r = sshpkt_put_string(ssh, signature, slen)) != 0 ||
	    (r = sshpkt_send(ssh)) != 0)
		goto out;

	if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0)
		r = kex_send_newkeys(ssh);
 out:
	explicit_bzero(hash, sizeof(hash));
	DH_free(kex->dh);
	kex->dh = NULL;
	if (dh_client_pub)
		BN_clear_free(dh_client_pub);
	if (kbuf) {
		explicit_bzero(kbuf, klen);
		free(kbuf);
	}
	if (shared_secret)
		BN_clear_free(shared_secret);
	free(server_host_key_blob);
	free(signature);
	return r;
}
Beispiel #6
0
static int
input_kex_c25519_reply(int type, u_int32_t seq, struct ssh *ssh)
{
	struct kex *kex = ssh->kex;
	struct sshkey *server_host_key = NULL;
	struct sshbuf *shared_secret = NULL;
	u_char *server_pubkey = NULL;
	u_char *server_host_key_blob = NULL, *signature = NULL;
	u_char *hash;
	size_t slen, pklen, sbloblen, hashlen;
	int r;

	if (kex->verify_host_key == NULL) {
		r = SSH_ERR_INVALID_ARGUMENT;
		goto out;
	}

	/* hostkey */
	if ((r = sshpkt_get_string(ssh, &server_host_key_blob,
	    &sbloblen)) != 0 ||
	    (r = sshkey_from_blob(server_host_key_blob, sbloblen,
	    &server_host_key)) != 0)
		goto out;
	if (server_host_key->type != kex->hostkey_type) {
		r = SSH_ERR_KEY_TYPE_MISMATCH;
		goto out;
	}
	if (kex->verify_host_key(server_host_key, ssh) == -1) {
		r = SSH_ERR_SIGNATURE_INVALID;
		goto out;
	}

	/* Q_S, server public key */
	/* signed H */
	if ((r = sshpkt_get_string(ssh, &server_pubkey, &pklen)) != 0 ||
	    (r = sshpkt_get_string(ssh, &signature, &slen)) != 0 ||
	    (r = sshpkt_get_end(ssh)) != 0)
		goto out;
	if (pklen != CURVE25519_SIZE) {
		r = SSH_ERR_SIGNATURE_INVALID;
		goto out;
	}

#ifdef DEBUG_KEXECDH
	dump_digest("server public key:", server_pubkey, CURVE25519_SIZE);
#endif

	if ((shared_secret = sshbuf_new()) == NULL) {
		r = SSH_ERR_ALLOC_FAIL;
		goto out;
	}
	if ((r = kexc25519_shared_key(kex->c25519_client_key, server_pubkey,
	    shared_secret)) < 0)
		goto out;

	/* calc and verify H */
	if ((r = kex_c25519_hash(
	    kex->hash_alg,
	    kex->client_version_string,
	    kex->server_version_string,
	    sshbuf_ptr(kex->my), sshbuf_len(kex->my),
	    sshbuf_ptr(kex->peer), sshbuf_len(kex->peer),
	    server_host_key_blob, sbloblen,
	    kex->c25519_client_pubkey,
	    server_pubkey,
	    sshbuf_ptr(shared_secret), sshbuf_len(shared_secret),
	    &hash, &hashlen)) < 0)
		goto out;

	if ((r = sshkey_verify(server_host_key, signature, slen, hash, hashlen,
	    ssh->compat)) != 0)
		goto out;

	/* save session id */
	if (kex->session_id == NULL) {
		kex->session_id_len = hashlen;
		kex->session_id = malloc(kex->session_id_len);
		if (kex->session_id == NULL) {
			r = SSH_ERR_ALLOC_FAIL;
			goto out;
		}
		memcpy(kex->session_id, hash, kex->session_id_len);
	}

	if ((r = kex_derive_keys(ssh, hash, hashlen, sshbuf_ptr(shared_secret),
	    sshbuf_len(shared_secret))) == 0)
		r = kex_send_newkeys(ssh);

	r = 0;
out:
	explicit_bzero(kex->c25519_client_key, sizeof(kex->c25519_client_key));
	free(server_host_key_blob);
	free(server_pubkey);
	free(signature);
	sshkey_free(server_host_key);
	sshbuf_free(shared_secret);
	return r;
}
Beispiel #7
0
Datei: fsm.c Projekt: gpg/gsti
/* Handle an incoming packet for the context CTX on the client side.  */
static gsti_error_t
client_handle_packet (gsti_ctx_t ctx)
{
  gsti_error_t err = 0;

  _gsti_log_debug (ctx, "** FSM (client) state=%s\n",
		   state_to_string (ctx->state));

  switch (ctx->state)
    {
    case FSM_kex_start:
      err = _gsti_kex_proc_init_packet (ctx);
      if (!err && ctx->gex.used)
	{
	  err = _gsti_kex_send_gex_request (ctx);
	  if (!err)
	    ctx->state = FSM_kex_wait_gex;

	}
      else
	{
	  if (!err)
	    err = kex_send_kexdh_reply (ctx);
	  if (!err)
	    ctx->state = FSM_kex_wait;
	}
      break;

    case FSM_kex_wait_gex:
      switch (ctx->pkt.type)
	{
	case SSH_MSG_KEX_DH_GEX_GROUP:
	  err = _gsti_kex_proc_gex_group (ctx);
	  if (!err)
	    err = _gsti_kex_send_kexdh_init (ctx);
	  if (!err)
	    ctx->state = FSM_kex_wait;
	  break;

	default:
	  _gsti_log_err (ctx,  "client got wrong packet "
			 "(pkttype=%d)\n",
			 ctx->pkt.type);
	  err = gsti_error (GPG_ERR_PROTOCOL_VIOLATION);
	  break; 
	}
      break;

    case FSM_kex_wait:
      switch (ctx->pkt.type)
	{
	case SSH_MSG_KEXDH_INIT:
	  _gsti_log_err (ctx, "client got unexpected KEXDH_INIT\n");
	  err = gsti_error (GPG_ERR_PROTOCOL_VIOLATION);
	  break;

	case SSH_MSG_KEXDH_REPLY:
	  err = kex_proc_kexdh_reply (ctx);
	  if (!err)
	    err = kex_send_newkeys (ctx);
	  if (!err)
	    ctx->state = FSM_kex_wait_newkeys;
	  break;
	}
      break;

    case FSM_kex_wait_newkeys:
      switch (ctx->pkt.type)
	{
	case SSH_MSG_NEWKEYS:
	  err = kex_proc_newkeys (ctx);
	  if (!err)
	    {
	      _gsti_log_info (ctx, "is local service? (%d)\n",
			      ctx->local_services ? 1 : 0);
	      err = kex_send_service_request (ctx, ctx->local_services ?
					      ctx->local_services->d
					      : "ssh-userauth");
	      _gsti_log_cont (ctx, "\n");
	      if (!err)
		ctx->state = FSM_wait_service_accept;
	    }
	  break;
	  
	default:
	  log_error (ctx);
	  ctx->state = FSM_kex_failed;
	  err = gsti_error (GPG_ERR_INV_PACKET);
	}
      break;

    case FSM_wait_service_accept:
      switch (ctx->pkt.type)
	{
	case SSH_MSG_SERVICE_ACCEPT:
	  err = kex_proc_service_accept (ctx);
	  if (!err)
	    {
	      _gsti_log_info (ctx, "service `");
	      _gsti_print_string (ctx, gsti_bstr_data (ctx->service_name),
				  gsti_bstr_length (ctx->service_name));
	      _gsti_log_cont (ctx, "' has been started (client)\n");

	      err = _gsti_auth_send_request_packet (ctx);
	      if (!err)
		ctx->state = FSM_auth_wait_pkok;
	    }
	  break;

	default:
	  log_error (ctx);
	  ctx->state = FSM_kex_failed;
	  err = gsti_error (GPG_ERR_INV_PACKET);
	}
      break;

    case FSM_auth_wait_pkok:
      switch (ctx->pkt.type)
	{
	case SSH_MSG_USERAUTH_BANNER:
	  err = _gsti_auth_proc_banner_packet (ctx);
	  if (!err)
            err = _gsti_banner_run_auth_cb (ctx);
	  break;
                  
	case SSH_MSG_USERAUTH_PK_OK:
	  err = _gsti_auth_proc_pkok_packet (ctx);
	  if (!err)
	    {
	      err = _gsti_auth_send_request_packet (ctx);
	      if (!err)
		ctx->state = FSM_auth_wait_success;
	    }
	  break;

	case SSH_MSG_USERAUTH_FAILURE:
	  _gsti_log_err (ctx, "user authentication failure\n");
	  ctx->state = FSM_auth_failed;
	  err = gsti_error (GPG_ERR_INV_NAME);
	  break;

	default:
	  log_error (ctx);
	  ctx->state = FSM_auth_failed;
	  err = gsti_error (GPG_ERR_INV_PACKET);
	}
      break;

    case FSM_auth_wait_success:
      switch (ctx->pkt.type)
	{
	case SSH_MSG_USERAUTH_SUCCESS:
	  err = _gsti_auth_proc_success_packet (ctx);
	  if (!err)
	    {
	      /* Signal to the user that the connection can be used now.  */
	      if (ctx->control_cb)
		(*ctx->control_cb) (ctx, ctx->control_cb_value,
				    GSTI_CONTROL_FLAG_KEX,
				    ~GSTI_CONTROL_FLAG_KEX);

	      ctx->state = FSM_read;
	    }
	  break;

	default:
	  log_error (ctx);
	  ctx->state = FSM_auth_failed;
	  err = gsti_error (GPG_ERR_INV_PACKET);
	}
      break;

    case FSM_read:
      if (ctx->pkt.type >= SSH_MSG_CHANNEL_BEGIN
	  && ctx->pkt.type <= SSH_MSG_CHANNEL_END)
	err = _gsti_handle_channel_packet (ctx);
      else if (ctx->pkt.type < SSH_MSG_USER_BEGIN)
	err = gsti_error (GPG_ERR_INV_PACKET);
      else		  
	{
	  struct gsti_pktdesc_s pkt;
	  u32 seqno = ctx->recv_seqno - 1;

	  pkt.datalen = ctx->pkt.payload_len;
	  pkt.data = ctx->pkt.payload;
	  pkt.seqno = seqno;

	  if (ctx->user_pkt_handler_cb)
	    err = (*ctx->user_pkt_handler_cb) (ctx,
					       ctx->user_pkt_handler_cb_value,
					       &pkt);
	}
      break;

    default:
      log_state_error (ctx);
      err = gsti_error (GPG_ERR_BUG);
    }
  return err;
}
Beispiel #8
0
Datei: fsm.c Projekt: gpg/gsti
/* Handle an incoming packet for the context CTX on the server side.  */
static gsti_error_t
server_handle_packet (gsti_ctx_t ctx)
{
  gsti_error_t err = 0;

  _gsti_log_debug (ctx, "** FSM (server) state=%s\n",
		   state_to_string (ctx->state));

  switch (ctx->state)
    {
    case FSM_kex_start:
      switch (ctx->pkt.type)
	{
	case SSH_MSG_KEXINIT:
	  err = _gsti_kex_proc_init_packet (ctx);
	  if (!err)
	    ctx->state = FSM_kex_wait;
	  break;

	default:
	  log_error (ctx);
	  ctx->state = FSM_kex_failed;
	  err = gsti_error (GPG_ERR_INV_PACKET);
	}
      break;

    case FSM_kex_wait:
      switch (ctx->pkt.type)
	{
	case SSH_MSG_KEXDH_REPLY:
	  _gsti_log_err (ctx, "server got unexpected KEXDH_REPLY\n");
	  err = gsti_error (GPG_ERR_PROTOCOL_VIOLATION);
	  break;
                  
	case SSH_MSG_KEX_DH_GEX_REQUEST:
	  err = _gsti_kex_proc_gex_request (ctx);
	  if (!err)
	    err = _gsti_kex_send_gex_group (ctx);
	  if (!err)
	    {
	      _gsti_log_debug (ctx, "KEX: enable DH group exchange\n");
	      ctx->gex.used = 1;
	    }
	  break;

	case SSH_MSG_KEXDH_INIT:
	case SSH_MSG_KEX_DH_GEX_INIT:
	  err = kex_proc_kexdh_init (ctx);
	  if (!err)
	    err = kex_send_kexdh_reply (ctx);
	  if (!err)
	    err = kex_send_newkeys (ctx);
	  if (!err)
	    ctx->state = FSM_kex_wait_newkeys;
	  break;

	default:
	  log_error (ctx);
	  ctx->state = FSM_kex_failed;
	  err = gsti_error (GPG_ERR_INV_PACKET);
	}
      break;

    case FSM_kex_wait_newkeys:
      switch (ctx->pkt.type)
	{
	case SSH_MSG_NEWKEYS:
	  err = kex_proc_newkeys (ctx);
	  if (!err)
	    ctx->state = FSM_wait_service_request;
	  break;

	default:
	  log_error (ctx);
	  ctx->state = FSM_kex_failed;
	  err = gsti_error (GPG_ERR_INV_PACKET);
	}
      break;

    case FSM_wait_service_request:
      switch (ctx->pkt.type)
	{
	case SSH_MSG_SERVICE_REQUEST:
	  err = kex_proc_service_request (ctx);
	  if (!err)
	    err = kex_send_service_accept (ctx);
	  if (!err)
	    {
	      _gsti_log_info (ctx, "service `");
	      _gsti_print_string (ctx, gsti_bstr_data (ctx->service_name),
				  gsti_bstr_length (ctx->service_name));
	      _gsti_log_cont (ctx, "' has been started (server)\n");
	      ctx->state = FSM_auth_wait;
	    }
	  break;

	default:
	  log_error (ctx);
	  ctx->state = FSM_kex_failed;
	  err = gsti_error (GPG_ERR_INV_PACKET);
	}
      break;

    case FSM_auth_wait:
      switch (ctx->pkt.type)
	{
	case SSH_MSG_USERAUTH_REQUEST:
	  err = _gsti_auth_proc_request_packet (ctx);
	  if (!err)
            err = _gsti_auth_run_auth_cb (ctx);
          if (!err && ctx->banner)
            err = _gsti_auth_send_banner_packet (ctx);
	  if (!err)
            err = _gsti_auth_send_pkok_packet (ctx);

          if (!err)
            ctx->state = FSM_auth_wait_request;
          else
	    err = _gsti_auth_send_failure_packet (ctx, ctx->auth);
	  break;
	  
	default:
	  log_error (ctx);
	  ctx->state = FSM_auth_failed;
	  err = gsti_error (GPG_ERR_INV_PACKET);
	}
      break;

    case FSM_auth_wait_request:
      switch (ctx->pkt.type)
	{
	case SSH_MSG_USERAUTH_REQUEST:
	  err = _gsti_auth_proc_request_packet (ctx);
	  if (!err)
	    err = _gsti_auth_send_success_packet (ctx);
	  if (!err)
	    {
	      /* Signal to the user that the connection can be used now.  */
	      if (ctx->control_cb)
		(*ctx->control_cb) (ctx, ctx->control_cb_value,
				    GSTI_CONTROL_FLAG_KEX,
				    ~GSTI_CONTROL_FLAG_KEX);
	      ctx->state = FSM_read;
	    }
	  break;
	}
      break;

    case FSM_read:
      if (ctx->pkt.type >= SSH_MSG_CHANNEL_BEGIN
	  && ctx->pkt.type <= SSH_MSG_CHANNEL_END)
	err = _gsti_handle_channel_packet (ctx);
      else if (ctx->pkt.type < SSH_MSG_USER_BEGIN)
	err = gsti_error (GPG_ERR_INV_PACKET);
      else		  
	{
	  struct gsti_pktdesc_s pkt;
	  u32 seqno = ctx->recv_seqno - 1;

	  pkt.datalen = ctx->pkt.payload_len;
	  pkt.data = ctx->pkt.payload;
	  pkt.seqno = seqno;

	  if (ctx->user_pkt_handler_cb)
	    err = (*ctx->user_pkt_handler_cb) (ctx,
					       ctx->user_pkt_handler_cb_value,
					       &pkt);
	}
      break;

    default:
      log_state_error (ctx);
      err = gsti_error (GPG_ERR_BUG);
    }
  return err;
}