Commit f0bf11db authored by Konrad Sztyber's avatar Konrad Sztyber Committed by Jim Harris
Browse files

nvmf/auth: execute DH-HMAC-CHAP_reply message



The reply message is executed by calculating the response to the
challenge and comparing it from the response received from the host.

Initially, bidirectional authentication won't be supported and a message
with the cvalid bit set is treated as an error.  The code to enable that
will be added later on, in a separate commit.

Signed-off-by: default avatarKonrad Sztyber <konrad.sztyber@intel.com>
Change-Id: Icf3ea216dcddec66e768485f894cb51807313ffb
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/22668


Reviewed-by: default avatarSeung yeon Shin <syeon.shin@samsung.com>
Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarBen Walker <ben@nvidia.com>
Reviewed-by: default avatarJim Harris <jim.harris@samsung.com>
Community-CI: Mellanox Build Bot
parent 2b14ffc3
Loading
Loading
Loading
Loading
+106 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
#include "spdk/string.h"
#include "spdk/thread.h"
#include "spdk/util.h"
#include "spdk_internal/nvme.h"

#include <openssl/rand.h>

@@ -22,11 +23,14 @@
#define AUTH_DEBUGLOG(q, fmt, ...) \
	SPDK_DEBUGLOG(nvmf_auth, "[%s:%s:%u] " fmt, \
		      (q)->ctrlr->subsys->subnqn, (q)->ctrlr->hostnqn, (q)->qid, ## __VA_ARGS__)
#define AUTH_LOGDUMP(msg, buf, len) \
	SPDK_LOGDUMP(nvmf_auth, msg, buf, len)

enum nvmf_qpair_auth_state {
	NVMF_QPAIR_AUTH_NEGOTIATE,
	NVMF_QPAIR_AUTH_CHALLENGE,
	NVMF_QPAIR_AUTH_REPLY,
	NVMF_QPAIR_AUTH_SUCCESS1,
	NVMF_QPAIR_AUTH_FAILURE1,
	NVMF_QPAIR_AUTH_ERROR,
};
@@ -67,6 +71,7 @@ nvmf_auth_get_state_name(enum nvmf_qpair_auth_state state)
		[NVMF_QPAIR_AUTH_NEGOTIATE] = "negotiate",
		[NVMF_QPAIR_AUTH_CHALLENGE] = "challenge",
		[NVMF_QPAIR_AUTH_REPLY] = "reply",
		[NVMF_QPAIR_AUTH_SUCCESS1] = "success1",
		[NVMF_QPAIR_AUTH_FAILURE1] = "failure1",
		[NVMF_QPAIR_AUTH_ERROR] = "error",
	};
@@ -282,6 +287,96 @@ nvmf_auth_negotiate_exec(struct spdk_nvmf_request *req, struct spdk_nvmf_auth_ne
	nvmf_auth_request_complete(req, SPDK_NVME_SCT_GENERIC, SPDK_NVME_SC_SUCCESS, 0);
}

static void
nvmf_auth_reply_exec(struct spdk_nvmf_request *req, struct spdk_nvmf_dhchap_reply *msg)
{
	struct spdk_nvmf_qpair *qpair = req->qpair;
	struct spdk_nvmf_ctrlr *ctrlr = qpair->ctrlr;
	struct spdk_nvmf_qpair_auth *auth = qpair->auth;
	uint8_t response[NVMF_AUTH_DIGEST_MAX_SIZE];
	struct spdk_key *key = NULL;
	uint8_t hl;
	int rc;

	if (auth->state != NVMF_QPAIR_AUTH_REPLY) {
		AUTH_ERRLOG(qpair, "invalid state=%s\n", nvmf_auth_get_state_name(auth->state));
		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_INCORRECT_PROTOCOL_MESSAGE);
		goto out;
	}
	if (req->length < sizeof(*msg)) {
		AUTH_ERRLOG(qpair, "invalid message length=%"PRIu32"\n", req->length);
		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD);
		goto out;
	}

	hl = spdk_nvme_dhchap_get_digest_length(auth->digest);
	if (hl == 0 || msg->hl != hl) {
		AUTH_ERRLOG(qpair, "hash length mismatch: %u != %u\n", msg->hl, hl);
		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD);
		goto out;
	}
	if (req->length != sizeof(*msg) + 2 * hl) {
		AUTH_ERRLOG(qpair, "invalid message length: %"PRIu32" != %zu\n",
			    req->length, sizeof(*msg) + 2 * hl);
		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD);
		goto out;
	}
	if (msg->t_id != auth->tid) {
		AUTH_ERRLOG(qpair, "transaction id mismatch: %u != %u\n", msg->t_id, auth->tid);
		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD);
		goto out;
	}
	if (msg->cvalid) {
		AUTH_ERRLOG(qpair, "bidirection authentication isn't supported yet\n");
		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD);
		goto out;
	}
	if (msg->dhvlen != 0) {
		AUTH_ERRLOG(qpair, "dhgroup length mismatch: %u != %u\n", msg->dhvlen, 0);
		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD);
		goto out;
	}

	key = nvmf_subsystem_get_dhchap_key(ctrlr->subsys, ctrlr->hostnqn);
	if (key == NULL) {
		AUTH_ERRLOG(qpair, "couldn't get DH-HMAC-CHAP key\n");
		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_FAILED);
		goto out;
	}

	assert(hl <= sizeof(response) && hl <= sizeof(auth->cval));
	rc = spdk_nvme_dhchap_calculate(key, (enum spdk_nvmf_dhchap_hash)auth->digest,
					"HostHost", auth->seqnum, auth->tid, 0,
					ctrlr->hostnqn, ctrlr->subsys->subnqn, NULL, 0,
					auth->cval, response);
	if (rc != 0) {
		AUTH_ERRLOG(qpair, "failed to calculate challenge response: %s\n",
			    spdk_strerror(-rc));
		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_FAILED);
		goto out;
	}

	if (memcmp(msg->rval, response, hl) != 0) {
		AUTH_ERRLOG(qpair, "challenge response mismatch\n");
		AUTH_LOGDUMP("response:", msg->rval, hl);
		AUTH_LOGDUMP("expected:", response, hl);
		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_FAILED);
		goto out;
	}

	if (nvmf_auth_rearm_poller(qpair)) {
		nvmf_auth_request_complete(req, SPDK_NVME_SCT_GENERIC,
					   SPDK_NVME_SC_INTERNAL_DEVICE_ERROR, 1);
		nvmf_auth_disconnect_qpair(qpair);
		goto out;
	}

	nvmf_auth_set_state(qpair, NVMF_QPAIR_AUTH_SUCCESS1);
	nvmf_auth_request_complete(req, SPDK_NVME_SCT_GENERIC, SPDK_NVME_SC_SUCCESS, 0);
out:
	spdk_keyring_put_key(key);
}

static void
nvmf_auth_send_exec(struct spdk_nvmf_request *req)
{
@@ -316,7 +411,18 @@ nvmf_auth_send_exec(struct spdk_nvmf_request *req)
		}
		break;
	case SPDK_NVMF_AUTH_TYPE_DHCHAP:
		switch (header->auth_id) {
		case SPDK_NVMF_AUTH_ID_DHCHAP_REPLY:
			nvmf_auth_reply_exec(req, (void *)header);
			break;
		default:
			AUTH_ERRLOG(qpair, "unexpected auth_id=%u\n", header->auth_id);
			nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_INCORRECT_PROTOCOL_MESSAGE);
			break;
		}
		break;
	default:
		AUTH_ERRLOG(qpair, "unexpected auth_type=%u\n", header->auth_type);
		nvmf_auth_request_fail1(req, SPDK_NVMF_AUTH_INCORRECT_PROTOCOL_MESSAGE);
		break;
	}
+179 −0
Original line number Diff line number Diff line
@@ -12,6 +12,9 @@
DEFINE_STUB(spdk_nvme_dhchap_get_digest_name, const char *, (int d), NULL);
DEFINE_STUB(spdk_nvme_dhchap_get_dhgroup_name, const char *, (int d), NULL);
DEFINE_STUB(spdk_nvme_dhchap_get_digest_length, uint8_t, (int d), 0);
DEFINE_STUB_V(spdk_keyring_put_key, (struct spdk_key *k));
DEFINE_STUB(nvmf_subsystem_get_dhchap_key, struct spdk_key *,
	    (struct spdk_nvmf_subsystem *s, const char *h), NULL);
DECLARE_WRAPPER(RAND_bytes, int, (unsigned char *buf, int num));

static uint8_t g_rand_val;
@@ -40,6 +43,19 @@ spdk_nvmf_request_complete(struct spdk_nvmf_request *req)
	return 0;
}

static uint8_t g_rval;
DEFINE_RETURN_MOCK(spdk_nvme_dhchap_calculate, int);

int
spdk_nvme_dhchap_calculate(struct spdk_key *key, enum spdk_nvmf_dhchap_hash hash,
			   const char *type, uint32_t seq, uint16_t tid, uint8_t scc,
			   const char *nqn1, const char *nqn2, const void *dhkey, size_t dhlen,
			   const void *cval, void *rval)
{
	memset(rval, g_rval, spdk_nvme_dhchap_get_digest_length(hash));
	return MOCK_GET(spdk_nvme_dhchap_calculate);
}

static void
ut_clear_resp(struct spdk_nvmf_request *req)
{
@@ -678,6 +694,168 @@ test_auth_challenge(void)
	nvmf_qpair_auth_destroy(&qpair);
}

static void
test_auth_reply(void)
{
	union nvmf_c2h_msg rsp = {};
	struct spdk_nvmf_subsystem subsys = {};
	struct spdk_nvmf_ctrlr ctrlr = { .subsys = &subsys };
	struct spdk_nvmf_qpair qpair = { .ctrlr = &ctrlr };
	struct spdk_nvmf_request req = { .qpair = &qpair, .rsp = &rsp };
	struct spdk_nvmf_fabric_auth_send_cmd cmd = {};
	struct spdk_nvmf_qpair_auth *auth;
	struct spdk_nvmf_dhchap_reply *msg;
	uint8_t hl = 48, msgbuf[4096];
	int rc;

	msg = (void *)msgbuf;
	rc = nvmf_qpair_auth_init(&qpair);
	SPDK_CU_ASSERT_FATAL(rc == 0);
	ut_prep_send_cmd(&req, &cmd, msgbuf, sizeof(*msg) + 2 * hl);
	auth = qpair.auth;
	qpair.state = SPDK_NVMF_QPAIR_AUTHENTICATING;
	auth->tid = 8;

	/* Execute a reply containing a correct response */
	g_req_completed = false;
	MOCK_SET(nvmf_subsystem_get_dhchap_key, (struct spdk_key *)0xdeadbeef);
	MOCK_SET(spdk_nvme_dhchap_get_digest_length, hl);
	auth->state = NVMF_QPAIR_AUTH_REPLY;
	msg->auth_type = SPDK_NVMF_AUTH_TYPE_DHCHAP;
	msg->auth_id = SPDK_NVMF_AUTH_ID_DHCHAP_REPLY;
	msg->t_id = auth->tid;
	msg->hl = hl;
	msg->cvalid = 0;
	msg->dhvlen = 0;
	msg->seqnum = 0;
	memset(msg->rval, 0xa5, hl);
	g_rval = 0xa5;

	nvmf_auth_send_exec(&req);
	CU_ASSERT(g_req_completed);
	CU_ASSERT_EQUAL(auth->state, NVMF_QPAIR_AUTH_SUCCESS1);

	/* Execute a reply while not in the REPLY state */
	g_req_completed = false;
	auth->state = NVMF_QPAIR_AUTH_CHALLENGE;

	nvmf_auth_send_exec(&req);
	CU_ASSERT(g_req_completed);
	CU_ASSERT_EQUAL(auth->state, NVMF_QPAIR_AUTH_FAILURE1);
	CU_ASSERT_EQUAL(auth->fail_reason, SPDK_NVMF_AUTH_INCORRECT_PROTOCOL_MESSAGE);

	/* Bad message length (smaller than a base reply message) */
	g_req_completed = false;
	auth->state = NVMF_QPAIR_AUTH_REPLY;
	cmd.tl = req.iov[0].iov_len = req.length = sizeof(*msg) - 1;

	nvmf_auth_send_exec(&req);
	CU_ASSERT(g_req_completed);
	CU_ASSERT_EQUAL(auth->state, NVMF_QPAIR_AUTH_FAILURE1);
	CU_ASSERT_EQUAL(auth->fail_reason, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD);

	/* Hash length mismatch */
	g_req_completed = false;
	auth->state = NVMF_QPAIR_AUTH_REPLY;
	msg->hl = 32;

	nvmf_auth_send_exec(&req);
	CU_ASSERT(g_req_completed);
	CU_ASSERT_EQUAL(auth->state, NVMF_QPAIR_AUTH_FAILURE1);
	CU_ASSERT_EQUAL(auth->fail_reason, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD);
	msg->hl = hl;

	/* Bad message length (smaller than size of msg + 2 * hl) */
	g_req_completed = false;
	auth->state = NVMF_QPAIR_AUTH_REPLY;
	cmd.tl = req.iov[0].iov_len = req.length = sizeof(*msg) + 2 * hl - 1;

	nvmf_auth_send_exec(&req);
	CU_ASSERT(g_req_completed);
	CU_ASSERT_EQUAL(auth->state, NVMF_QPAIR_AUTH_FAILURE1);
	CU_ASSERT_EQUAL(auth->fail_reason, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD);
	cmd.tl = req.iov[0].iov_len = req.length = sizeof(*msg) + hl;

	/* Bad message length (larger than size of msg + 2 * hl) */
	g_req_completed = false;
	auth->state = NVMF_QPAIR_AUTH_REPLY;
	cmd.tl = req.iov[0].iov_len = req.length = sizeof(*msg) + 2 * hl + 1;

	nvmf_auth_send_exec(&req);
	CU_ASSERT(g_req_completed);
	CU_ASSERT_EQUAL(auth->state, NVMF_QPAIR_AUTH_FAILURE1);
	CU_ASSERT_EQUAL(auth->fail_reason, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD);
	cmd.tl = req.iov[0].iov_len = req.length = sizeof(*msg) + 2 * hl;

	/* Transaction ID mismatch */
	g_req_completed = false;
	auth->state = NVMF_QPAIR_AUTH_REPLY;
	msg->t_id = auth->tid + 1;

	nvmf_auth_send_exec(&req);
	CU_ASSERT(g_req_completed);
	CU_ASSERT_EQUAL(auth->state, NVMF_QPAIR_AUTH_FAILURE1);
	CU_ASSERT_EQUAL(auth->fail_reason, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD);
	msg->t_id = auth->tid;

	/* Bad cvalid value */
	g_req_completed = false;
	auth->state = NVMF_QPAIR_AUTH_REPLY;
	msg->cvalid = 1;

	nvmf_auth_send_exec(&req);
	CU_ASSERT(g_req_completed);
	CU_ASSERT_EQUAL(auth->state, NVMF_QPAIR_AUTH_FAILURE1);
	CU_ASSERT_EQUAL(auth->fail_reason, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD);
	msg->cvalid = 0;

	/* Bad dhvlen (non-zero) */
	g_req_completed = false;
	auth->state = NVMF_QPAIR_AUTH_REPLY;
	msg->dhvlen = 1;

	nvmf_auth_send_exec(&req);
	CU_ASSERT(g_req_completed);
	CU_ASSERT_EQUAL(auth->state, NVMF_QPAIR_AUTH_FAILURE1);
	CU_ASSERT_EQUAL(auth->fail_reason, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD);
	msg->dhvlen = 0;

	/* Failure to get the key */
	g_req_completed = false;
	auth->state = NVMF_QPAIR_AUTH_REPLY;
	MOCK_SET(nvmf_subsystem_get_dhchap_key, NULL);

	nvmf_auth_send_exec(&req);
	CU_ASSERT(g_req_completed);
	CU_ASSERT_EQUAL(auth->state, NVMF_QPAIR_AUTH_FAILURE1);
	CU_ASSERT_EQUAL(auth->fail_reason, SPDK_NVMF_AUTH_FAILED);
	MOCK_SET(nvmf_subsystem_get_dhchap_key, (struct spdk_key *)0xdeadbeef);

	/* Calculation failure */
	g_req_completed = false;
	auth->state = NVMF_QPAIR_AUTH_REPLY;
	MOCK_SET(spdk_nvme_dhchap_calculate, -EPERM);

	nvmf_auth_send_exec(&req);
	CU_ASSERT(g_req_completed);
	CU_ASSERT_EQUAL(auth->state, NVMF_QPAIR_AUTH_FAILURE1);
	CU_ASSERT_EQUAL(auth->fail_reason, SPDK_NVMF_AUTH_FAILED);
	MOCK_SET(spdk_nvme_dhchap_calculate, 0);

	/* Response mismatch */
	g_req_completed = false;
	auth->state = NVMF_QPAIR_AUTH_REPLY;
	g_rval = 0x5a;

	nvmf_auth_send_exec(&req);
	CU_ASSERT(g_req_completed);
	CU_ASSERT_EQUAL(auth->state, NVMF_QPAIR_AUTH_FAILURE1);
	CU_ASSERT_EQUAL(auth->fail_reason, SPDK_NVMF_AUTH_FAILED);
	g_rval = 0xa5;

	nvmf_qpair_auth_destroy(&qpair);
}

int
main(int argc, char **argv)
{
@@ -691,6 +869,7 @@ main(int argc, char **argv)
	CU_ADD_TEST(suite, test_auth_timeout);
	CU_ADD_TEST(suite, test_auth_failure1);
	CU_ADD_TEST(suite, test_auth_challenge);
	CU_ADD_TEST(suite, test_auth_reply);

	allocate_threads(1);
	set_thread(0);