Commit dbaa0488 authored by Konrad Sztyber's avatar Konrad Sztyber Committed by Tomasz Zawadzki
Browse files

nvme/auth: bidirectional authentication



It is now possible to authenticate the controller at the end of the
authentication process.  This step is optional and will only be
performed if the user specified controller's key.

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


Community-CI: Mellanox Build Bot
Reviewed-by: default avatarJim Harris <jim.harris@samsung.com>
Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarBen Walker <ben@nvidia.com>
parent 54e2282c
Loading
Loading
Loading
Loading
+7 −2
Original line number Diff line number Diff line
@@ -292,10 +292,15 @@ struct spdk_nvme_ctrlr_opts {
	struct spdk_key *tls_psk;

	/**
	 * In-band authentication DH-HMAC-CHAP key.
	 * In-band authentication DH-HMAC-CHAP host key.
	 */
	struct spdk_key *dhchap_key;

	/**
	 * In-band authentication DH-HMAC-CHAP controller key.
	 */
	struct spdk_key *dhchap_ctrlr_key;

	/**
	 * Allowed digests in in-band authentication.  Each bit corresponds to one of the
	 * spdk_nvmf_dhchap_hash values.
@@ -308,7 +313,7 @@ struct spdk_nvme_ctrlr_opts {
	 */
	uint32_t dhchap_dhgroups;
};
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvme_ctrlr_opts) == 848, "Incorrect size");
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvme_ctrlr_opts) == 856, "Incorrect size");

/**
 * NVMe acceleration operation callback.
+1 −0
Original line number Diff line number Diff line
@@ -971,6 +971,7 @@ nvme_ctrlr_opts_init(struct spdk_nvme_ctrlr_opts *opts,
	SET_FIELD_ARRAY(psk);
	SET_FIELD(tls_psk);
	SET_FIELD(dhchap_key);
	SET_FIELD(dhchap_ctrlr_key);
	SET_FIELD(dhchap_digests);
	SET_FIELD(dhchap_dhgroups);

+114 −6
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#include <openssl/dh.h>
#include <openssl/evp.h>
#include <openssl/param_build.h>
#include <openssl/rand.h>
#endif

struct nvme_auth_digest {
@@ -30,7 +31,6 @@ struct nvme_auth_dhgroup {
#define NVME_AUTH_DATA_SIZE			4096
#define NVME_AUTH_DH_KEY_MAX_SIZE		1024
#define NVME_AUTH_CHAP_KEY_MAX_SIZE		256
#define NVME_AUTH_DIGEST_MAX_SIZE		64

#define AUTH_DEBUGLOG(q, fmt, ...) \
	SPDK_DEBUGLOG(nvme_auth, "[%s:%s:%u] " fmt, (q)->ctrlr->trid.subnqn, \
@@ -154,6 +154,7 @@ nvme_auth_set_state(struct spdk_nvme_qpair *qpair, enum nvme_qpair_auth_state st
		[NVME_QPAIR_AUTH_STATE_AWAIT_CHALLENGE] = "await-challenge",
		[NVME_QPAIR_AUTH_STATE_AWAIT_REPLY] = "await-reply",
		[NVME_QPAIR_AUTH_STATE_AWAIT_SUCCESS1] = "await-success1",
		[NVME_QPAIR_AUTH_STATE_AWAIT_SUCCESS2] = "await-success2",
		[NVME_QPAIR_AUTH_STATE_AWAIT_FAILURE2] = "await-failure2",
		[NVME_QPAIR_AUTH_STATE_DONE] = "done",
	};
@@ -183,6 +184,30 @@ nvme_auth_print_cpl(struct spdk_nvme_qpair *qpair, const char *msg)
		    status->cpl.status.sct, status->timed_out ? "true" : "false");
}

static uint32_t
nvme_auth_get_seqnum(struct spdk_nvme_qpair *qpair)
{
	struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
	uint32_t seqnum;
	int rc;

	nvme_robust_mutex_lock(&ctrlr->ctrlr_lock);
	if (ctrlr->auth_seqnum == 0) {
		rc = RAND_bytes((void *)&ctrlr->auth_seqnum, sizeof(ctrlr->auth_seqnum));
		if (rc != 1) {
			nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock);
			return 0;
		}
	}
	if (++ctrlr->auth_seqnum == 0) {
		ctrlr->auth_seqnum = 1;
	}
	seqnum = ctrlr->auth_seqnum;
	nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock);

	return seqnum;
}

static int
nvme_auth_transform_key(struct spdk_key *key, int hash, const char *nqn,
			const void *keyin, size_t keylen, void *out, size_t outlen)
@@ -850,10 +875,13 @@ nvme_auth_send_reply(struct spdk_nvme_qpair *qpair)
	uint8_t hl, response[NVME_AUTH_DATA_SIZE];
	uint8_t pubkey[NVME_AUTH_DH_KEY_MAX_SIZE];
	uint8_t dhsec[NVME_AUTH_DH_KEY_MAX_SIZE];
	uint8_t ctrlr_challenge[NVME_AUTH_DIGEST_MAX_SIZE] = {};
	size_t dhseclen = 0, publen = 0;
	uint32_t seqnum = 0;
	EVP_PKEY *dhkey;
	int rc;

	auth->hash = challenge->hash_id;
	hl = nvme_auth_get_digest_len(challenge->hash_id);
	if (challenge->dhg_id != SPDK_NVMF_DHCHAP_DHGROUP_NULL) {
		dhseclen = sizeof(dhsec);
@@ -891,19 +919,45 @@ nvme_auth_send_reply(struct spdk_nvme_qpair *qpair)
		return rc;
	}

	if (ctrlr->opts.dhchap_ctrlr_key != NULL) {
		seqnum = nvme_auth_get_seqnum(qpair);
		if (seqnum == 0) {
			return -EIO;
		}

		assert(sizeof(ctrlr_challenge) >= hl);
		rc = RAND_bytes(ctrlr_challenge, hl);
		if (rc != 1) {
			return -EIO;
		}

		rc = nvme_auth_calc_response(ctrlr->opts.dhchap_ctrlr_key,
					     (enum spdk_nvmf_dhchap_hash)challenge->hash_id,
					     "Controller", seqnum, auth->tid, 0,
					     ctrlr->trid.subnqn, ctrlr->opts.hostnqn,
					     dhseclen > 0 ? dhsec : NULL, dhseclen,
					     ctrlr_challenge, auth->challenge);
		if (rc != 0) {
			AUTH_ERRLOG(qpair, "failed to calculate controller's response: %s\n",
				    spdk_strerror(-rc));
			return rc;
		}
	}

	/* Now that the response has been calculated, send the reply */
	memset(qpair->poll_status->dma_data, 0, NVME_AUTH_DATA_SIZE);
	assert(sizeof(*reply) + 2 * hl + publen <= NVME_AUTH_DATA_SIZE);
	memcpy(reply->rval, response, hl);
	memcpy(&reply->rval[1 * hl], ctrlr_challenge, hl);
	memcpy(&reply->rval[2 * hl], pubkey, publen);

	reply->auth_type = SPDK_NVMF_AUTH_TYPE_DHCHAP;
	reply->auth_id = SPDK_NVMF_AUTH_ID_DHCHAP_REPLY;
	reply->t_id = auth->tid;
	reply->hl = hl;
	reply->cvalid = 0;
	reply->cvalid = ctrlr->opts.dhchap_ctrlr_key != NULL;
	reply->dhvlen = publen;
	reply->seqnum = 0;
	reply->seqnum = seqnum;

	/* The 2 * reply->hl below is because the spec says that both rval[hl] and cval[hl] must
	 * always be part of the reply message, even cvalid is zero.
@@ -916,8 +970,10 @@ static int
nvme_auth_check_success1(struct spdk_nvme_qpair *qpair)
{
	struct spdk_nvmf_dhchap_success1 *msg = qpair->poll_status->dma_data;
	struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
	struct nvme_auth *auth = &qpair->auth;
	int rc;
	uint8_t hl;
	int rc, status;

	rc = nvme_auth_check_message(qpair, SPDK_NVMF_AUTH_ID_DHCHAP_SUCCESS1);
	if (rc != 0) {
@@ -927,19 +983,59 @@ nvme_auth_check_success1(struct spdk_nvme_qpair *qpair)
	if (msg->t_id != auth->tid) {
		AUTH_ERRLOG(qpair, "unexpected tid: received=%u, expected=%u\n",
			    msg->t_id, auth->tid);
		status = SPDK_NVMF_AUTH_INCORRECT_PAYLOAD;
		goto error;
	}

	if (ctrlr->opts.dhchap_ctrlr_key != NULL) {
		if (!msg->rvalid) {
			AUTH_ERRLOG(qpair, "received rvalid=0, expected response\n");
			status = SPDK_NVMF_AUTH_INCORRECT_PAYLOAD;
			goto error;
		}

		hl = nvme_auth_get_digest_len(auth->hash);
		if (msg->hl != hl) {
			AUTH_ERRLOG(qpair, "received invalid hl=%u, expected=%u\n", msg->hl, hl);
			status = SPDK_NVMF_AUTH_INCORRECT_PAYLOAD;
			goto error;
		}

		if (memcmp(msg->rval, auth->challenge, hl) != 0) {
			AUTH_ERRLOG(qpair, "controller challenge mismatch\n");
			AUTH_LOGDUMP("received:", msg->rval, hl);
			AUTH_LOGDUMP("expected:", auth->challenge, hl);
			status = SPDK_NVMF_AUTH_FAILED;
			goto error;
		}
	}

	return 0;
error:
	nvme_auth_set_failure(qpair, -EACCES,
			      nvme_auth_send_failure2(qpair, SPDK_NVMF_AUTH_INCORRECT_PAYLOAD));
	nvme_auth_set_failure(qpair, -EACCES, nvme_auth_send_failure2(qpair, status));

	return -EACCES;
}

static int
nvme_auth_send_success2(struct spdk_nvme_qpair *qpair)
{
	struct spdk_nvmf_dhchap_success2 *msg = qpair->poll_status->dma_data;
	struct nvme_auth *auth = &qpair->auth;

	memset(qpair->poll_status->dma_data, 0, NVME_AUTH_DATA_SIZE);
	msg->auth_type = SPDK_NVMF_AUTH_TYPE_DHCHAP;
	msg->auth_id = SPDK_NVMF_AUTH_ID_DHCHAP_SUCCESS2;
	msg->t_id = auth->tid;

	return nvme_auth_submit_request(qpair, SPDK_NVMF_FABRIC_COMMAND_AUTHENTICATION_SEND,
					sizeof(*msg));
}

int
nvme_fabric_qpair_authenticate_poll(struct spdk_nvme_qpair *qpair)
{
	struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
	struct nvme_auth *auth = &qpair->auth;
	struct nvme_completion_poll_status *status = qpair->poll_status;
	enum nvme_qpair_auth_state prev_state;
@@ -1033,8 +1129,20 @@ nvme_fabric_qpair_authenticate_poll(struct spdk_nvme_qpair *qpair)
				break;
			}
			AUTH_DEBUGLOG(qpair, "authentication completed successfully\n");
			if (ctrlr->opts.dhchap_ctrlr_key != NULL) {
				rc = nvme_auth_send_success2(qpair);
				if (rc != 0) {
					AUTH_ERRLOG(qpair, "failed to send DH-HMAC-CHAP_success2: "
						    "%s\n", spdk_strerror(rc));
					nvme_auth_set_failure(qpair, rc, false);
					break;
				}
				nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_AWAIT_SUCCESS2);
				break;
			}
			nvme_auth_set_state(qpair, NVME_QPAIR_AUTH_STATE_DONE);
			break;
		case NVME_QPAIR_AUTH_STATE_AWAIT_SUCCESS2:
		case NVME_QPAIR_AUTH_STATE_AWAIT_FAILURE2:
			rc = nvme_wait_for_completion_robust_lock_timeout_poll(qpair, status, NULL);
			if (rc == -EAGAIN) {
+1 −0
Original line number Diff line number Diff line
@@ -235,6 +235,7 @@ spdk_nvme_ctrlr_get_default_ctrlr_opts(struct spdk_nvme_ctrlr_opts *opts, size_t
	SET_FIELD(disable_read_changed_ns_list_log_page, false);
	SET_FIELD(tls_psk, NULL);
	SET_FIELD(dhchap_key, NULL);
	SET_FIELD(dhchap_ctrlr_key, NULL);
	SET_FIELD(dhchap_digests,
		  SPDK_BIT(SPDK_NVMF_DHCHAP_HASH_SHA256) |
		  SPDK_BIT(SPDK_NVMF_DHCHAP_HASH_SHA384) |
+10 −0
Original line number Diff line number Diff line
@@ -423,6 +423,7 @@ enum nvme_qpair_auth_state {
	NVME_QPAIR_AUTH_STATE_AWAIT_CHALLENGE,
	NVME_QPAIR_AUTH_STATE_AWAIT_REPLY,
	NVME_QPAIR_AUTH_STATE_AWAIT_SUCCESS1,
	NVME_QPAIR_AUTH_STATE_AWAIT_SUCCESS2,
	NVME_QPAIR_AUTH_STATE_AWAIT_FAILURE2,
	NVME_QPAIR_AUTH_STATE_DONE,
};
@@ -432,6 +433,9 @@ enum nvme_qpair_auth_state {
/* Authentication and secure channel required (authreq.ascr) */
#define NVME_QPAIR_AUTH_FLAG_ASCR	(1 << 1)

/* Maximum size of a digest */
#define NVME_AUTH_DIGEST_MAX_SIZE	64

struct nvme_auth {
	/* State of the authentication */
	enum nvme_qpair_auth_state	state;
@@ -441,6 +445,10 @@ struct nvme_auth {
	uint16_t			tid;
	/* Flags */
	uint32_t			flags;
	/* Selected hash function */
	uint8_t				hash;
	/* Buffer used for controller challenge */
	uint8_t				challenge[NVME_AUTH_DIGEST_MAX_SIZE];
};

struct spdk_nvme_qpair {
@@ -1065,6 +1073,8 @@ struct spdk_nvme_ctrlr {

	/* Authentication transaction ID */
	uint16_t				auth_tid;
	/* Authentication sequence number */
	uint32_t				auth_seqnum;
};

struct spdk_nvme_probe_ctx {