Commit 8a4b7226 authored by Konrad Sztyber's avatar Konrad Sztyber
Browse files

nvmf/auth: add method for changing keys



This allows users to change the DH-HMAC-CHAP keys without
removing/re-adding hosts to a subsystem.  That means that keys can be
changed without terminating existing connections.

Once the keys are changed, new connections, as well as reauthentication
requests on the already established connections, will be required to use
the new keys.

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


Reviewed-by: default avatarKrzysztof Karas <krzysztof.karas@intel.com>
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>
Community-CI: Mellanox Build Bot
parent 9ce869c2
Loading
Loading
Loading
Loading
+44 −0
Original line number Diff line number Diff line
@@ -8744,6 +8744,50 @@ Example response:
}
~~~

### nvmf_subsystem_set_keys {#rpc_nvmf_subsystem_set_keys}

Set keys required for a host to connect to a given subsystem.  This will overwrite the keys set by
`nvmf_subsystem_add_host`.  If none of the keys are provided, host's keys will be cleared, allowing
it to connect without authentication.

#### Parameters

Name                    | Optional | Type        | Description
----------------------- | -------- | ----------- | -----------
nqn                     | Required | string      | Subsystem NQN
host                    | Required | string      | Host NQN
tgt_name                | Optional | string      | NVMe-oF target name
dhchap_key              | Optional | string      | DH-HMAC-CHAP key name (required if controller key is specified)
dhchap_ctrlr_key        | Optional | string      | DH-HMAC-CHAP controller key name

#### Example

Example request:

~~~json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "nvmf_subsystem_set_keys",
  "params": {
    "nqn": "nqn.2024-06.io.spdk:cnode1",
    "host": "nqn.2024-06.io.spdk:host1",
    "dhchap_key": "key0",
    "dchap_ctrlr_key": "key1"
  }
}
~~~

Example response:

~~~json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": true
}
~~~

### nvmf_subsystem_get_controllers {#rpc_nvmf_subsystem_get_controllers}

#### Parameters
+12 −0
Original line number Diff line number Diff line
@@ -363,6 +363,18 @@ $ scripts/rpc.py nvmf_subsystem_add_host nqn.2024-05.io.spdk:cnode0 nqn.2024-05.
    --dhchap-key key2
```

Additionally, it's possible to change the keys while preserving existing connections to a subsystem
via `nvmf_subsystem_set_keys`.  After that's done, new connections and reauthentication requests
will be required to use the new keys.

```{.sh}
$ scripts/rpc.py nvmf_subsystem_add_host nqn.2024-05.io.spdk:cnode0 nqn.2024-05.io.spdk:host0 \
    --dhchap-key key0 --dhchap-ctrlr-key ctrlr-key0
# Host nqn.2024-05.io.spdk:host0 connects to subsystem nqn.2024-05.io.spdk:cnode0
$ scripts/rpc.py nvmf_subsystem_set_keys nqn.2024-05.io.spdk:cnode0 nqn.2024-05.io.spdk:host0 \
    --dhchap-key key1 --dhchap-ctrlr-key ctrlr-key1
```

On the host side, the keys are specified when attaching controllers, e.g.:

```{.sh}
+17 −0
Original line number Diff line number Diff line
@@ -654,6 +654,23 @@ int spdk_nvmf_subsystem_add_host_ext(struct spdk_nvmf_subsystem *subsystem,
 */
int spdk_nvmf_subsystem_remove_host(struct spdk_nvmf_subsystem *subsystem, const char *hostnqn);

struct spdk_nvmf_subsystem_key_opts {
	/** Size of this structure */
	size_t				size;
	/** DH-HMAC-CHAP key */
	struct spdk_key			*dhchap_key;
	/** DH-HMAC-CHAP controller key */
	struct spdk_key			*dhchap_ctrlr_key;
};

/**
 * Set keys required for a host to connect to a given subsystem.  This will override the keys set
 * by `spdk_nvmf_subsystem_add_host_ext()`.
 */
int spdk_nvmf_subsystem_set_keys(struct spdk_nvmf_subsystem *subsystem, const char *hostnqn,
				 struct spdk_nvmf_subsystem_key_opts *opts);


/**
 * Disconnect all connections originating from the provided hostnqn
 *
+68 −0
Original line number Diff line number Diff line
@@ -2034,6 +2034,73 @@ rpc_nvmf_subsystem_remove_host(struct spdk_jsonrpc_request *request,
SPDK_RPC_REGISTER("nvmf_subsystem_remove_host", rpc_nvmf_subsystem_remove_host,
		  SPDK_RPC_RUNTIME)

static void
rpc_nvmf_subsystem_set_keys(struct spdk_jsonrpc_request *request,
			    const struct spdk_json_val *params)
{
	struct nvmf_rpc_host_ctx ctx = {};
	struct spdk_nvmf_subsystem *subsystem;
	struct spdk_nvmf_subsystem_key_opts opts = {};
	struct spdk_nvmf_tgt *tgt;
	struct spdk_key *key = NULL, *ckey = NULL;
	int rc;

	if (spdk_json_decode_object(params, nvmf_rpc_subsystem_host_decoder,
				    SPDK_COUNTOF(nvmf_rpc_subsystem_host_decoder), &ctx)) {
		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
						 "Invalid parameters");
		goto out;
	}

	tgt = spdk_nvmf_get_tgt(ctx.tgt_name);
	if (!tgt) {
		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
						 "Invalid parameters");
		goto out;
	}
	subsystem = spdk_nvmf_tgt_find_subsystem(tgt, ctx.nqn);
	if (!subsystem) {
		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
						 "Invalid parameters");
		goto out;
	}

	if (ctx.dhchap_key != NULL) {
		key = spdk_keyring_get_key(ctx.dhchap_key);
		if (key == NULL) {
			SPDK_ERRLOG("Unable to find DH-HMAC-CHAP key: %s\n", ctx.dhchap_key);
			spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
							 "Invalid parameters");
			goto out;
		}
	}
	if (ctx.dhchap_ctrlr_key != NULL) {
		ckey = spdk_keyring_get_key(ctx.dhchap_ctrlr_key);
		if (ckey == NULL) {
			SPDK_ERRLOG("Unable to find DH-HMAC-CHAP ctrlr key: %s\n",
				    ctx.dhchap_ctrlr_key);
			spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
							 "Invalid parameters");
			goto out;
		}
	}

	opts.size = SPDK_SIZEOF(&opts, dhchap_ctrlr_key);
	opts.dhchap_key = key;
	opts.dhchap_ctrlr_key = ckey;
	rc = spdk_nvmf_subsystem_set_keys(subsystem, ctx.host, &opts);
	if (rc != 0) {
		spdk_jsonrpc_send_error_response(request, rc, spdk_strerror(-rc));
		goto out;
	}

	spdk_jsonrpc_send_bool_response(request, true);
out:
	spdk_keyring_put_key(ckey);
	spdk_keyring_put_key(key);
	nvmf_rpc_host_ctx_free(&ctx);
}
SPDK_RPC_REGISTER("nvmf_subsystem_set_keys", rpc_nvmf_subsystem_set_keys, SPDK_RPC_RUNTIME)

static const struct spdk_json_object_decoder nvmf_rpc_subsystem_any_host_decoder[] = {
	{"nqn", offsetof(struct nvmf_rpc_host_ctx, nqn), spdk_json_decode_string},
@@ -2706,6 +2773,7 @@ dump_nvmf_qpair(struct spdk_json_write_ctx *w, struct spdk_nvmf_qpair *qpair)
	spdk_json_write_named_uint32(w, "qid", qpair->qid);
	spdk_json_write_named_string(w, "state", nvmf_qpair_state_str(qpair->state));
	spdk_json_write_named_string(w, "thread", spdk_thread_get_name(spdk_get_thread()));
	spdk_json_write_named_string(w, "hostnqn", qpair->ctrlr->hostnqn);

	if (spdk_nvmf_qpair_get_listen_trid(qpair, &trid) == 0) {
		spdk_json_write_named_object_begin(w, "listen_address");
+1 −0
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@
	spdk_nvmf_subsystem_host_allowed;
	spdk_nvmf_subsystem_get_first_host;
	spdk_nvmf_subsystem_get_next_host;
	spdk_nvmf_subsystem_set_keys;
	spdk_nvmf_host_get_nqn;
	spdk_nvmf_subsystem_add_listener;
	spdk_nvmf_subsystem_add_listener_ext;
Loading