diff mbox series

[11/18] nvme-fabrics: parse options 'keyring' and 'tls_key'

Message ID 20230816120608.37135-12-hare@suse.de (mailing list archive)
State Superseded
Headers show
Series nvme: In-kernel TLS support for TCP | expand

Checks

Context Check Description
netdev/tree_selection success Not a local patch, async

Commit Message

Hannes Reinecke Aug. 16, 2023, 12:06 p.m. UTC
Parse the fabrics options 'keyring' and 'tls_key' and store the
referenced keys in the options structure.

Signed-off-by: Hannes Reinecke <hare@suse.de>
Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
---
 drivers/nvme/host/fabrics.c | 52 ++++++++++++++++++++++++++++++++++++-
 drivers/nvme/host/fabrics.h |  6 +++++
 drivers/nvme/host/tcp.c     | 14 +++++++---
 3 files changed, 67 insertions(+), 5 deletions(-)

Comments

Sagi Grimberg Aug. 17, 2023, 9:20 a.m. UTC | #1
On 8/16/23 15:06, Hannes Reinecke wrote:
> Parse the fabrics options 'keyring' and 'tls_key' and store the
> referenced keys in the options structure.
> 
> Signed-off-by: Hannes Reinecke <hare@suse.de>
> Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
> ---
>   drivers/nvme/host/fabrics.c | 52 ++++++++++++++++++++++++++++++++++++-
>   drivers/nvme/host/fabrics.h |  6 +++++
>   drivers/nvme/host/tcp.c     | 14 +++++++---
>   3 files changed, 67 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c
> index ddad482c3537..e453c3871cb1 100644
> --- a/drivers/nvme/host/fabrics.c
> +++ b/drivers/nvme/host/fabrics.c
> @@ -622,6 +622,23 @@ static struct nvmf_transport_ops *nvmf_lookup_transport(
>   	return NULL;
>   }
>   
> +static struct key *nvmf_parse_key(int key_id, char *key_type)
> +{
> +	struct key *key;
> +
> +	if (!IS_ENABLED(CONFIG_NVME_TCP_TLS)) {
> +		pr_err("TLS is not supported\n");
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	key = key_lookup(key_id);
> +	if (IS_ERR(key))
> +		pr_err("%s %08x not found\n", key_type, key_id);
> +	else
> +		pr_debug("Using %s %08x\n", key_type, key_id);

I think that the key_type string is unneeded as an argument solely for
the log print.

I think that you can move the error print to the call-site. Keep
the debug print here, but you don't need the type...

> +	return key;
> +}
> +
>   static const match_table_t opt_tokens = {
>   	{ NVMF_OPT_TRANSPORT,		"transport=%s"		},
>   	{ NVMF_OPT_TRADDR,		"traddr=%s"		},
> @@ -643,6 +660,10 @@ static const match_table_t opt_tokens = {
>   	{ NVMF_OPT_NR_WRITE_QUEUES,	"nr_write_queues=%d"	},
>   	{ NVMF_OPT_NR_POLL_QUEUES,	"nr_poll_queues=%d"	},
>   	{ NVMF_OPT_TOS,			"tos=%d"		},
> +#ifdef CONFIG_NVME_TCP_TLS
> +	{ NVMF_OPT_KEYRING,		"keyring=%d"		},
> +	{ NVMF_OPT_TLS_KEY,		"tls_key=%d"		},
> +#endif
>   	{ NVMF_OPT_FAIL_FAST_TMO,	"fast_io_fail_tmo=%d"	},
>   	{ NVMF_OPT_DISCOVERY,		"discovery"		},
>   	{ NVMF_OPT_DHCHAP_SECRET,	"dhchap_secret=%s"	},
> @@ -660,9 +681,10 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts,
>   	char *options, *o, *p;
>   	int token, ret = 0;
>   	size_t nqnlen  = 0;
> -	int ctrl_loss_tmo = NVMF_DEF_CTRL_LOSS_TMO;
> +	int ctrl_loss_tmo = NVMF_DEF_CTRL_LOSS_TMO, key_id;
>   	uuid_t hostid;
>   	char hostnqn[NVMF_NQN_SIZE];
> +	struct key *key;
>   
>   	/* Set defaults */
>   	opts->queue_size = NVMF_DEF_QUEUE_SIZE;
> @@ -928,6 +950,32 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts,
>   			}
>   			opts->tos = token;
>   			break;
> +		case NVMF_OPT_KEYRING:
> +			if (match_int(args, &key_id) || key_id <= 0) {
> +				ret = -EINVAL;
> +				goto out;
> +			}
> +			key = nvmf_parse_key(key_id, "Keyring");
> +			if (IS_ERR(key)) {
> +				ret = PTR_ERR(key);
> +				goto out;
> +			}
> +			key_put(opts->keyring);

Don't understand how keyring/tls_key are pre-populated though...

> +			opts->keyring = key;
> +			break;
> +		case NVMF_OPT_TLS_KEY:
> +			if (match_int(args, &key_id) || key_id <= 0) {
> +				ret = -EINVAL;
> +				goto out;
> +			}
> +			key = nvmf_parse_key(key_id, "Key");
> +			if (IS_ERR(key)) {
> +				ret = PTR_ERR(key);
> +				goto out;
> +			}
> +			key_put(opts->tls_key);
> +			opts->tls_key = key;
> +			break;
>   		case NVMF_OPT_DISCOVERY:
>   			opts->discovery_nqn = true;
>   			break;
> @@ -1168,6 +1216,8 @@ static int nvmf_check_allowed_opts(struct nvmf_ctrl_options *opts,
>   void nvmf_free_options(struct nvmf_ctrl_options *opts)
>   {
>   	nvmf_host_put(opts->host);
> +	key_put(opts->keyring);
> +	key_put(opts->tls_key);
>   	kfree(opts->transport);
>   	kfree(opts->traddr);
>   	kfree(opts->trsvcid);
> diff --git a/drivers/nvme/host/fabrics.h b/drivers/nvme/host/fabrics.h
> index dac17c3fee26..fbaee5a7be19 100644
> --- a/drivers/nvme/host/fabrics.h
> +++ b/drivers/nvme/host/fabrics.h
> @@ -71,6 +71,8 @@ enum {
>   	NVMF_OPT_DHCHAP_SECRET	= 1 << 23,
>   	NVMF_OPT_DHCHAP_CTRL_SECRET = 1 << 24,
>   	NVMF_OPT_TLS		= 1 << 25,
> +	NVMF_OPT_KEYRING	= 1 << 26,
> +	NVMF_OPT_TLS_KEY	= 1 << 27,
>   };
>   
>   /**
> @@ -103,6 +105,8 @@ enum {
>    * @dhchap_secret: DH-HMAC-CHAP secret
>    * @dhchap_ctrl_secret: DH-HMAC-CHAP controller secret for bi-directional
>    *              authentication
> + * @keyring:    Keyring to use for key lookups
> + * @tls_key:    TLS key for encrypted connections (TCP)
>    * @tls:        Start TLS encrypted connections (TCP)
>    * @disable_sqflow: disable controller sq flow control
>    * @hdr_digest: generate/verify header digest (TCP)
> @@ -130,6 +134,8 @@ struct nvmf_ctrl_options {
>   	struct nvmf_host	*host;
>   	char			*dhchap_secret;
>   	char			*dhchap_ctrl_secret;
> +	struct key		*keyring;
> +	struct key		*tls_key;
>   	bool			tls;
>   	bool			disable_sqflow;
>   	bool			hdr_digest;
> diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
> index ef9cf8c7a113..f48797fcc4ee 100644
> --- a/drivers/nvme/host/tcp.c
> +++ b/drivers/nvme/host/tcp.c
> @@ -1589,6 +1589,8 @@ static int nvme_tcp_start_tls(struct nvme_ctrl *nctrl,
>   
>   	dev_dbg(nctrl->device, "queue %d: start TLS with key %x\n",
>   		qid, pskid);
> +	if (nctrl->opts->keyring)
> +		keyring = key_serial(nctrl->opts->keyring);

Maybe populate opts->keyring with nvme_keyring_id() to begin
with and then you don't need this?

>   	memset(&args, 0, sizeof(args));
>   	args.ta_sock = queue->sock;
>   	args.ta_done = nvme_tcp_tls_done;
> @@ -1914,9 +1916,12 @@ static int nvme_tcp_alloc_admin_queue(struct nvme_ctrl *ctrl)
>   	key_serial_t pskid = 0;
>   
>   	if (ctrl->opts->tls) {
> -		pskid = nvme_tls_psk_default(NULL,
> -					      ctrl->opts->host->nqn,
> -					      ctrl->opts->subsysnqn);
> +		if (ctrl->opts->tls_key)
> +			pskid = key_serial(ctrl->opts->tls_key);
> +		else
> +			pskid = nvme_tls_psk_default(ctrl->opts->keyring,
> +						      ctrl->opts->host->nqn,
> +						      ctrl->opts->subsysnqn);
>   		if (!pskid) {
>   			dev_err(ctrl->device, "no valid PSK found\n");
>   			ret = -ENOKEY;
> @@ -2776,7 +2781,8 @@ static struct nvmf_transport_ops nvme_tcp_transport = {
>   			  NVMF_OPT_HOST_TRADDR | NVMF_OPT_CTRL_LOSS_TMO |
>   			  NVMF_OPT_HDR_DIGEST | NVMF_OPT_DATA_DIGEST |
>   			  NVMF_OPT_NR_WRITE_QUEUES | NVMF_OPT_NR_POLL_QUEUES |
> -			  NVMF_OPT_TOS | NVMF_OPT_HOST_IFACE | NVMF_OPT_TLS,
> +			  NVMF_OPT_TOS | NVMF_OPT_HOST_IFACE | NVMF_OPT_TLS |
> +			  NVMF_OPT_KEYRING | NVMF_OPT_TLS_KEY,
>   	.create_ctrl	= nvme_tcp_create_ctrl,
>   };
>
Hannes Reinecke Aug. 17, 2023, 9:46 a.m. UTC | #2
On 8/17/23 11:20, Sagi Grimberg wrote:
> 
> 
> On 8/16/23 15:06, Hannes Reinecke wrote:
>> Parse the fabrics options 'keyring' and 'tls_key' and store the
>> referenced keys in the options structure.
>>
>> Signed-off-by: Hannes Reinecke <hare@suse.de>
>> Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
>> ---
>>   drivers/nvme/host/fabrics.c | 52 ++++++++++++++++++++++++++++++++++++-
>>   drivers/nvme/host/fabrics.h |  6 +++++
>>   drivers/nvme/host/tcp.c     | 14 +++++++---
>>   3 files changed, 67 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c
>> index ddad482c3537..e453c3871cb1 100644
>> --- a/drivers/nvme/host/fabrics.c
>> +++ b/drivers/nvme/host/fabrics.c
>> @@ -622,6 +622,23 @@ static struct nvmf_transport_ops 
>> *nvmf_lookup_transport(
>>       return NULL;
>>   }
>> +static struct key *nvmf_parse_key(int key_id, char *key_type)
>> +{
>> +    struct key *key;
>> +
>> +    if (!IS_ENABLED(CONFIG_NVME_TCP_TLS)) {
>> +        pr_err("TLS is not supported\n");
>> +        return ERR_PTR(-EINVAL);
>> +    }
>> +
>> +    key = key_lookup(key_id);
>> +    if (IS_ERR(key))
>> +        pr_err("%s %08x not found\n", key_type, key_id);
>> +    else
>> +        pr_debug("Using %s %08x\n", key_type, key_id);
> 
> I think that the key_type string is unneeded as an argument solely for
> the log print.
> 
> I think that you can move the error print to the call-site. Keep
> the debug print here, but you don't need the type...
> 
Ok.

>> +    return key;
>> +}
>> +
>>   static const match_table_t opt_tokens = {
>>       { NVMF_OPT_TRANSPORT,        "transport=%s"        },
>>       { NVMF_OPT_TRADDR,        "traddr=%s"        },
>> @@ -643,6 +660,10 @@ static const match_table_t opt_tokens = {
>>       { NVMF_OPT_NR_WRITE_QUEUES,    "nr_write_queues=%d"    },
>>       { NVMF_OPT_NR_POLL_QUEUES,    "nr_poll_queues=%d"    },
>>       { NVMF_OPT_TOS,            "tos=%d"        },
>> +#ifdef CONFIG_NVME_TCP_TLS
>> +    { NVMF_OPT_KEYRING,        "keyring=%d"        },
>> +    { NVMF_OPT_TLS_KEY,        "tls_key=%d"        },
>> +#endif
>>       { NVMF_OPT_FAIL_FAST_TMO,    "fast_io_fail_tmo=%d"    },
>>       { NVMF_OPT_DISCOVERY,        "discovery"        },
>>       { NVMF_OPT_DHCHAP_SECRET,    "dhchap_secret=%s"    },
>> @@ -660,9 +681,10 @@ static int nvmf_parse_options(struct 
>> nvmf_ctrl_options *opts,
>>       char *options, *o, *p;
>>       int token, ret = 0;
>>       size_t nqnlen  = 0;
>> -    int ctrl_loss_tmo = NVMF_DEF_CTRL_LOSS_TMO;
>> +    int ctrl_loss_tmo = NVMF_DEF_CTRL_LOSS_TMO, key_id;
>>       uuid_t hostid;
>>       char hostnqn[NVMF_NQN_SIZE];
>> +    struct key *key;
>>       /* Set defaults */
>>       opts->queue_size = NVMF_DEF_QUEUE_SIZE;
>> @@ -928,6 +950,32 @@ static int nvmf_parse_options(struct 
>> nvmf_ctrl_options *opts,
>>               }
>>               opts->tos = token;
>>               break;
>> +        case NVMF_OPT_KEYRING:
>> +            if (match_int(args, &key_id) || key_id <= 0) {
>> +                ret = -EINVAL;
>> +                goto out;
>> +            }
>> +            key = nvmf_parse_key(key_id, "Keyring");
>> +            if (IS_ERR(key)) {
>> +                ret = PTR_ERR(key);
>> +                goto out;
>> +            }
>> +            key_put(opts->keyring);
> 
> Don't understand how keyring/tls_key are pre-populated though...
> 
They are not. But they might, as there's nothing in the code preventing 
the user to specify 'keyring' or 'tls_key' several times.
I can make it one-shot and error out if they are already populated, but
really haven't seen the need.

>> +            opts->keyring = key;
>> +            break;
>> +        case NVMF_OPT_TLS_KEY:
>> +            if (match_int(args, &key_id) || key_id <= 0) {
>> +                ret = -EINVAL;
>> +                goto out;
>> +            }
>> +            key = nvmf_parse_key(key_id, "Key");
>> +            if (IS_ERR(key)) {
>> +                ret = PTR_ERR(key);
>> +                goto out;
>> +            }
>> +            key_put(opts->tls_key);
>> +            opts->tls_key = key;
>> +            break;
>>           case NVMF_OPT_DISCOVERY:
>>               opts->discovery_nqn = true;
>>               break;
>> @@ -1168,6 +1216,8 @@ static int nvmf_check_allowed_opts(struct 
>> nvmf_ctrl_options *opts,
>>   void nvmf_free_options(struct nvmf_ctrl_options *opts)
>>   {
>>       nvmf_host_put(opts->host);
>> +    key_put(opts->keyring);
>> +    key_put(opts->tls_key);
>>       kfree(opts->transport);
>>       kfree(opts->traddr);
>>       kfree(opts->trsvcid);
>> diff --git a/drivers/nvme/host/fabrics.h b/drivers/nvme/host/fabrics.h
>> index dac17c3fee26..fbaee5a7be19 100644
>> --- a/drivers/nvme/host/fabrics.h
>> +++ b/drivers/nvme/host/fabrics.h
>> @@ -71,6 +71,8 @@ enum {
>>       NVMF_OPT_DHCHAP_SECRET    = 1 << 23,
>>       NVMF_OPT_DHCHAP_CTRL_SECRET = 1 << 24,
>>       NVMF_OPT_TLS        = 1 << 25,
>> +    NVMF_OPT_KEYRING    = 1 << 26,
>> +    NVMF_OPT_TLS_KEY    = 1 << 27,
>>   };
>>   /**
>> @@ -103,6 +105,8 @@ enum {
>>    * @dhchap_secret: DH-HMAC-CHAP secret
>>    * @dhchap_ctrl_secret: DH-HMAC-CHAP controller secret for 
>> bi-directional
>>    *              authentication
>> + * @keyring:    Keyring to use for key lookups
>> + * @tls_key:    TLS key for encrypted connections (TCP)
>>    * @tls:        Start TLS encrypted connections (TCP)
>>    * @disable_sqflow: disable controller sq flow control
>>    * @hdr_digest: generate/verify header digest (TCP)
>> @@ -130,6 +134,8 @@ struct nvmf_ctrl_options {
>>       struct nvmf_host    *host;
>>       char            *dhchap_secret;
>>       char            *dhchap_ctrl_secret;
>> +    struct key        *keyring;
>> +    struct key        *tls_key;
>>       bool            tls;
>>       bool            disable_sqflow;
>>       bool            hdr_digest;
>> diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
>> index ef9cf8c7a113..f48797fcc4ee 100644
>> --- a/drivers/nvme/host/tcp.c
>> +++ b/drivers/nvme/host/tcp.c
>> @@ -1589,6 +1589,8 @@ static int nvme_tcp_start_tls(struct nvme_ctrl 
>> *nctrl,
>>       dev_dbg(nctrl->device, "queue %d: start TLS with key %x\n",
>>           qid, pskid);
>> +    if (nctrl->opts->keyring)
>> +        keyring = key_serial(nctrl->opts->keyring);
> 
> Maybe populate opts->keyring with nvme_keyring_id() to begin
> with and then you don't need this?
> 
We could; one would lose the distinction between 'not specified' and
'specified with the defaul keyring', but one could argue whether that
brings any benefit.

Cheers,

Hannes
Sagi Grimberg Aug. 17, 2023, 10:41 a.m. UTC | #3
>>> +        case NVMF_OPT_KEYRING:
>>> +            if (match_int(args, &key_id) || key_id <= 0) {
>>> +                ret = -EINVAL;
>>> +                goto out;
>>> +            }
>>> +            key = nvmf_parse_key(key_id, "Keyring");
>>> +            if (IS_ERR(key)) {
>>> +                ret = PTR_ERR(key);
>>> +                goto out;
>>> +            }
>>> +            key_put(opts->keyring);
>>
>> Don't understand how keyring/tls_key are pre-populated though...
>>
> They are not. But they might, as there's nothing in the code preventing 
> the user to specify 'keyring' or 'tls_key' several times.
> I can make it one-shot and error out if they are already populated, but
> really haven't seen the need.

OK, now I understand. its fine.

> 
>>> +            opts->keyring = key;
>>> +            break;
>>> +        case NVMF_OPT_TLS_KEY:
>>> +            if (match_int(args, &key_id) || key_id <= 0) {
>>> +                ret = -EINVAL;
>>> +                goto out;
>>> +            }
>>> +            key = nvmf_parse_key(key_id, "Key");
>>> +            if (IS_ERR(key)) {
>>> +                ret = PTR_ERR(key);
>>> +                goto out;
>>> +            }
>>> +            key_put(opts->tls_key);
>>> +            opts->tls_key = key;
>>> +            break;
>>>           case NVMF_OPT_DISCOVERY:
>>>               opts->discovery_nqn = true;
>>>               break;
>>> @@ -1168,6 +1216,8 @@ static int nvmf_check_allowed_opts(struct 
>>> nvmf_ctrl_options *opts,
>>>   void nvmf_free_options(struct nvmf_ctrl_options *opts)
>>>   {
>>>       nvmf_host_put(opts->host);
>>> +    key_put(opts->keyring);
>>> +    key_put(opts->tls_key);
>>>       kfree(opts->transport);
>>>       kfree(opts->traddr);
>>>       kfree(opts->trsvcid);
>>> diff --git a/drivers/nvme/host/fabrics.h b/drivers/nvme/host/fabrics.h
>>> index dac17c3fee26..fbaee5a7be19 100644
>>> --- a/drivers/nvme/host/fabrics.h
>>> +++ b/drivers/nvme/host/fabrics.h
>>> @@ -71,6 +71,8 @@ enum {
>>>       NVMF_OPT_DHCHAP_SECRET    = 1 << 23,
>>>       NVMF_OPT_DHCHAP_CTRL_SECRET = 1 << 24,
>>>       NVMF_OPT_TLS        = 1 << 25,
>>> +    NVMF_OPT_KEYRING    = 1 << 26,
>>> +    NVMF_OPT_TLS_KEY    = 1 << 27,
>>>   };
>>>   /**
>>> @@ -103,6 +105,8 @@ enum {
>>>    * @dhchap_secret: DH-HMAC-CHAP secret
>>>    * @dhchap_ctrl_secret: DH-HMAC-CHAP controller secret for 
>>> bi-directional
>>>    *              authentication
>>> + * @keyring:    Keyring to use for key lookups
>>> + * @tls_key:    TLS key for encrypted connections (TCP)
>>>    * @tls:        Start TLS encrypted connections (TCP)
>>>    * @disable_sqflow: disable controller sq flow control
>>>    * @hdr_digest: generate/verify header digest (TCP)
>>> @@ -130,6 +134,8 @@ struct nvmf_ctrl_options {
>>>       struct nvmf_host    *host;
>>>       char            *dhchap_secret;
>>>       char            *dhchap_ctrl_secret;
>>> +    struct key        *keyring;
>>> +    struct key        *tls_key;
>>>       bool            tls;
>>>       bool            disable_sqflow;
>>>       bool            hdr_digest;
>>> diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
>>> index ef9cf8c7a113..f48797fcc4ee 100644
>>> --- a/drivers/nvme/host/tcp.c
>>> +++ b/drivers/nvme/host/tcp.c
>>> @@ -1589,6 +1589,8 @@ static int nvme_tcp_start_tls(struct nvme_ctrl 
>>> *nctrl,
>>>       dev_dbg(nctrl->device, "queue %d: start TLS with key %x\n",
>>>           qid, pskid);
>>> +    if (nctrl->opts->keyring)
>>> +        keyring = key_serial(nctrl->opts->keyring);
>>
>> Maybe populate opts->keyring with nvme_keyring_id() to begin
>> with and then you don't need this?
>>
> We could; one would lose the distinction between 'not specified' and
> 'specified with the defaul keyring', but one could argue whether that
> brings any benefit.

Not sure I understand.
What exactly is the distinction and how is it manifested?
diff mbox series

Patch

diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c
index ddad482c3537..e453c3871cb1 100644
--- a/drivers/nvme/host/fabrics.c
+++ b/drivers/nvme/host/fabrics.c
@@ -622,6 +622,23 @@  static struct nvmf_transport_ops *nvmf_lookup_transport(
 	return NULL;
 }
 
+static struct key *nvmf_parse_key(int key_id, char *key_type)
+{
+	struct key *key;
+
+	if (!IS_ENABLED(CONFIG_NVME_TCP_TLS)) {
+		pr_err("TLS is not supported\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	key = key_lookup(key_id);
+	if (IS_ERR(key))
+		pr_err("%s %08x not found\n", key_type, key_id);
+	else
+		pr_debug("Using %s %08x\n", key_type, key_id);
+	return key;
+}
+
 static const match_table_t opt_tokens = {
 	{ NVMF_OPT_TRANSPORT,		"transport=%s"		},
 	{ NVMF_OPT_TRADDR,		"traddr=%s"		},
@@ -643,6 +660,10 @@  static const match_table_t opt_tokens = {
 	{ NVMF_OPT_NR_WRITE_QUEUES,	"nr_write_queues=%d"	},
 	{ NVMF_OPT_NR_POLL_QUEUES,	"nr_poll_queues=%d"	},
 	{ NVMF_OPT_TOS,			"tos=%d"		},
+#ifdef CONFIG_NVME_TCP_TLS
+	{ NVMF_OPT_KEYRING,		"keyring=%d"		},
+	{ NVMF_OPT_TLS_KEY,		"tls_key=%d"		},
+#endif
 	{ NVMF_OPT_FAIL_FAST_TMO,	"fast_io_fail_tmo=%d"	},
 	{ NVMF_OPT_DISCOVERY,		"discovery"		},
 	{ NVMF_OPT_DHCHAP_SECRET,	"dhchap_secret=%s"	},
@@ -660,9 +681,10 @@  static int nvmf_parse_options(struct nvmf_ctrl_options *opts,
 	char *options, *o, *p;
 	int token, ret = 0;
 	size_t nqnlen  = 0;
-	int ctrl_loss_tmo = NVMF_DEF_CTRL_LOSS_TMO;
+	int ctrl_loss_tmo = NVMF_DEF_CTRL_LOSS_TMO, key_id;
 	uuid_t hostid;
 	char hostnqn[NVMF_NQN_SIZE];
+	struct key *key;
 
 	/* Set defaults */
 	opts->queue_size = NVMF_DEF_QUEUE_SIZE;
@@ -928,6 +950,32 @@  static int nvmf_parse_options(struct nvmf_ctrl_options *opts,
 			}
 			opts->tos = token;
 			break;
+		case NVMF_OPT_KEYRING:
+			if (match_int(args, &key_id) || key_id <= 0) {
+				ret = -EINVAL;
+				goto out;
+			}
+			key = nvmf_parse_key(key_id, "Keyring");
+			if (IS_ERR(key)) {
+				ret = PTR_ERR(key);
+				goto out;
+			}
+			key_put(opts->keyring);
+			opts->keyring = key;
+			break;
+		case NVMF_OPT_TLS_KEY:
+			if (match_int(args, &key_id) || key_id <= 0) {
+				ret = -EINVAL;
+				goto out;
+			}
+			key = nvmf_parse_key(key_id, "Key");
+			if (IS_ERR(key)) {
+				ret = PTR_ERR(key);
+				goto out;
+			}
+			key_put(opts->tls_key);
+			opts->tls_key = key;
+			break;
 		case NVMF_OPT_DISCOVERY:
 			opts->discovery_nqn = true;
 			break;
@@ -1168,6 +1216,8 @@  static int nvmf_check_allowed_opts(struct nvmf_ctrl_options *opts,
 void nvmf_free_options(struct nvmf_ctrl_options *opts)
 {
 	nvmf_host_put(opts->host);
+	key_put(opts->keyring);
+	key_put(opts->tls_key);
 	kfree(opts->transport);
 	kfree(opts->traddr);
 	kfree(opts->trsvcid);
diff --git a/drivers/nvme/host/fabrics.h b/drivers/nvme/host/fabrics.h
index dac17c3fee26..fbaee5a7be19 100644
--- a/drivers/nvme/host/fabrics.h
+++ b/drivers/nvme/host/fabrics.h
@@ -71,6 +71,8 @@  enum {
 	NVMF_OPT_DHCHAP_SECRET	= 1 << 23,
 	NVMF_OPT_DHCHAP_CTRL_SECRET = 1 << 24,
 	NVMF_OPT_TLS		= 1 << 25,
+	NVMF_OPT_KEYRING	= 1 << 26,
+	NVMF_OPT_TLS_KEY	= 1 << 27,
 };
 
 /**
@@ -103,6 +105,8 @@  enum {
  * @dhchap_secret: DH-HMAC-CHAP secret
  * @dhchap_ctrl_secret: DH-HMAC-CHAP controller secret for bi-directional
  *              authentication
+ * @keyring:    Keyring to use for key lookups
+ * @tls_key:    TLS key for encrypted connections (TCP)
  * @tls:        Start TLS encrypted connections (TCP)
  * @disable_sqflow: disable controller sq flow control
  * @hdr_digest: generate/verify header digest (TCP)
@@ -130,6 +134,8 @@  struct nvmf_ctrl_options {
 	struct nvmf_host	*host;
 	char			*dhchap_secret;
 	char			*dhchap_ctrl_secret;
+	struct key		*keyring;
+	struct key		*tls_key;
 	bool			tls;
 	bool			disable_sqflow;
 	bool			hdr_digest;
diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
index ef9cf8c7a113..f48797fcc4ee 100644
--- a/drivers/nvme/host/tcp.c
+++ b/drivers/nvme/host/tcp.c
@@ -1589,6 +1589,8 @@  static int nvme_tcp_start_tls(struct nvme_ctrl *nctrl,
 
 	dev_dbg(nctrl->device, "queue %d: start TLS with key %x\n",
 		qid, pskid);
+	if (nctrl->opts->keyring)
+		keyring = key_serial(nctrl->opts->keyring);
 	memset(&args, 0, sizeof(args));
 	args.ta_sock = queue->sock;
 	args.ta_done = nvme_tcp_tls_done;
@@ -1914,9 +1916,12 @@  static int nvme_tcp_alloc_admin_queue(struct nvme_ctrl *ctrl)
 	key_serial_t pskid = 0;
 
 	if (ctrl->opts->tls) {
-		pskid = nvme_tls_psk_default(NULL,
-					      ctrl->opts->host->nqn,
-					      ctrl->opts->subsysnqn);
+		if (ctrl->opts->tls_key)
+			pskid = key_serial(ctrl->opts->tls_key);
+		else
+			pskid = nvme_tls_psk_default(ctrl->opts->keyring,
+						      ctrl->opts->host->nqn,
+						      ctrl->opts->subsysnqn);
 		if (!pskid) {
 			dev_err(ctrl->device, "no valid PSK found\n");
 			ret = -ENOKEY;
@@ -2776,7 +2781,8 @@  static struct nvmf_transport_ops nvme_tcp_transport = {
 			  NVMF_OPT_HOST_TRADDR | NVMF_OPT_CTRL_LOSS_TMO |
 			  NVMF_OPT_HDR_DIGEST | NVMF_OPT_DATA_DIGEST |
 			  NVMF_OPT_NR_WRITE_QUEUES | NVMF_OPT_NR_POLL_QUEUES |
-			  NVMF_OPT_TOS | NVMF_OPT_HOST_IFACE | NVMF_OPT_TLS,
+			  NVMF_OPT_TOS | NVMF_OPT_HOST_IFACE | NVMF_OPT_TLS |
+			  NVMF_OPT_KEYRING | NVMF_OPT_TLS_KEY,
 	.create_ctrl	= nvme_tcp_create_ctrl,
 };