Skip to content

Commit fa2e0f8

Browse files
Hannes Reineckekeithbusch
authored andcommitted
nvmet-tcp: support secure channel concatenation
Evaluate the SC_C flag during DH-CHAP-HMAC negotiation to check if secure concatenation as specified in the NVMe Base Specification v2.1, section 8.3.4.3: "Secure Channel Concatenationand" is requested. If requested the generated PSK is inserted into the keyring once negotiation has finished allowing for an encrypted connection once the admin queue is restarted. Signed-off-by: Hannes Reinecke <[email protected]> Reviewed-by: Sagi Grimberg <[email protected]> Signed-off-by: Keith Busch <[email protected]>
1 parent 5032167 commit fa2e0f8

File tree

6 files changed

+199
-13
lines changed

6 files changed

+199
-13
lines changed

drivers/nvme/target/auth.c

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <linux/ctype.h>
1616
#include <linux/random.h>
1717
#include <linux/nvme-auth.h>
18+
#include <linux/nvme-keyring.h>
1819
#include <linux/unaligned.h>
1920

2021
#include "nvmet.h"
@@ -165,6 +166,11 @@ u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq)
165166
goto out_unlock;
166167
}
167168

169+
if (nvmet_queue_tls_keyid(sq)) {
170+
pr_debug("host %s tls enabled\n", ctrl->hostnqn);
171+
goto out_unlock;
172+
}
173+
168174
ret = nvmet_setup_dhgroup(ctrl, host->dhchap_dhgroup_id);
169175
if (ret < 0) {
170176
pr_warn("Failed to setup DH group");
@@ -233,6 +239,9 @@ u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq)
233239
void nvmet_auth_sq_free(struct nvmet_sq *sq)
234240
{
235241
cancel_delayed_work(&sq->auth_expired_work);
242+
#ifdef CONFIG_NVME_TARGET_TCP_TLS
243+
sq->tls_key = 0;
244+
#endif
236245
kfree(sq->dhchap_c1);
237246
sq->dhchap_c1 = NULL;
238247
kfree(sq->dhchap_c2);
@@ -261,6 +270,12 @@ void nvmet_destroy_auth(struct nvmet_ctrl *ctrl)
261270
nvme_auth_free_key(ctrl->ctrl_key);
262271
ctrl->ctrl_key = NULL;
263272
}
273+
#ifdef CONFIG_NVME_TARGET_TCP_TLS
274+
if (ctrl->tls_key) {
275+
key_put(ctrl->tls_key);
276+
ctrl->tls_key = NULL;
277+
}
278+
#endif
264279
}
265280

266281
bool nvmet_check_auth_status(struct nvmet_req *req)
@@ -542,3 +557,58 @@ int nvmet_auth_ctrl_sesskey(struct nvmet_req *req,
542557

543558
return ret;
544559
}
560+
561+
void nvmet_auth_insert_psk(struct nvmet_sq *sq)
562+
{
563+
int hash_len = nvme_auth_hmac_hash_len(sq->ctrl->shash_id);
564+
u8 *psk, *digest, *tls_psk;
565+
size_t psk_len;
566+
int ret;
567+
#ifdef CONFIG_NVME_TARGET_TCP_TLS
568+
struct key *tls_key = NULL;
569+
#endif
570+
571+
ret = nvme_auth_generate_psk(sq->ctrl->shash_id,
572+
sq->dhchap_skey,
573+
sq->dhchap_skey_len,
574+
sq->dhchap_c1, sq->dhchap_c2,
575+
hash_len, &psk, &psk_len);
576+
if (ret) {
577+
pr_warn("%s: ctrl %d qid %d failed to generate PSK, error %d\n",
578+
__func__, sq->ctrl->cntlid, sq->qid, ret);
579+
return;
580+
}
581+
ret = nvme_auth_generate_digest(sq->ctrl->shash_id, psk, psk_len,
582+
sq->ctrl->subsysnqn,
583+
sq->ctrl->hostnqn, &digest);
584+
if (ret) {
585+
pr_warn("%s: ctrl %d qid %d failed to generate digest, error %d\n",
586+
__func__, sq->ctrl->cntlid, sq->qid, ret);
587+
goto out_free_psk;
588+
}
589+
ret = nvme_auth_derive_tls_psk(sq->ctrl->shash_id, psk, psk_len,
590+
digest, &tls_psk);
591+
if (ret) {
592+
pr_warn("%s: ctrl %d qid %d failed to derive TLS PSK, error %d\n",
593+
__func__, sq->ctrl->cntlid, sq->qid, ret);
594+
goto out_free_digest;
595+
}
596+
#ifdef CONFIG_NVME_TARGET_TCP_TLS
597+
tls_key = nvme_tls_psk_refresh(NULL, sq->ctrl->hostnqn, sq->ctrl->subsysnqn,
598+
sq->ctrl->shash_id, tls_psk, psk_len, digest);
599+
if (IS_ERR(tls_key)) {
600+
pr_warn("%s: ctrl %d qid %d failed to refresh key, error %ld\n",
601+
__func__, sq->ctrl->cntlid, sq->qid, PTR_ERR(tls_key));
602+
tls_key = NULL;
603+
kfree_sensitive(tls_psk);
604+
}
605+
if (sq->ctrl->tls_key)
606+
key_put(sq->ctrl->tls_key);
607+
sq->ctrl->tls_key = tls_key;
608+
#endif
609+
610+
out_free_digest:
611+
kfree_sensitive(digest);
612+
out_free_psk:
613+
kfree_sensitive(psk);
614+
}

drivers/nvme/target/core.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1664,11 +1664,12 @@ struct nvmet_ctrl *nvmet_alloc_ctrl(struct nvmet_alloc_ctrl_args *args)
16641664

16651665
args->status = NVME_SC_SUCCESS;
16661666

1667-
pr_info("Created %s controller %d for subsystem %s for NQN %s%s%s.\n",
1667+
pr_info("Created %s controller %d for subsystem %s for NQN %s%s%s%s.\n",
16681668
nvmet_is_disc_subsys(ctrl->subsys) ? "discovery" : "nvm",
16691669
ctrl->cntlid, ctrl->subsys->subsysnqn, ctrl->hostnqn,
16701670
ctrl->pi_support ? " T10-PI is enabled" : "",
1671-
nvmet_has_auth(ctrl) ? " with DH-HMAC-CHAP" : "");
1671+
nvmet_has_auth(ctrl, args->sq) ? " with DH-HMAC-CHAP" : "",
1672+
nvmet_queue_tls_keyid(args->sq) ? ", TLS" : "");
16721673

16731674
return ctrl;
16741675

drivers/nvme/target/fabrics-cmd-auth.c

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,26 @@ static u8 nvmet_auth_negotiate(struct nvmet_req *req, void *d)
4343
data->auth_protocol[0].dhchap.halen,
4444
data->auth_protocol[0].dhchap.dhlen);
4545
req->sq->dhchap_tid = le16_to_cpu(data->t_id);
46-
if (data->sc_c)
47-
return NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH;
46+
if (data->sc_c != NVME_AUTH_SECP_NOSC) {
47+
if (!IS_ENABLED(CONFIG_NVME_TARGET_TCP_TLS))
48+
return NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH;
49+
/* Secure concatenation can only be enabled on the admin queue */
50+
if (req->sq->qid)
51+
return NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH;
52+
switch (data->sc_c) {
53+
case NVME_AUTH_SECP_NEWTLSPSK:
54+
if (nvmet_queue_tls_keyid(req->sq))
55+
return NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH;
56+
break;
57+
case NVME_AUTH_SECP_REPLACETLSPSK:
58+
if (!nvmet_queue_tls_keyid(req->sq))
59+
return NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH;
60+
break;
61+
default:
62+
return NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH;
63+
}
64+
ctrl->concat = true;
65+
}
4866

4967
if (data->napd != 1)
5068
return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE;
@@ -103,6 +121,12 @@ static u8 nvmet_auth_negotiate(struct nvmet_req *req, void *d)
103121
nvme_auth_dhgroup_name(fallback_dhgid));
104122
ctrl->dh_gid = fallback_dhgid;
105123
}
124+
if (ctrl->dh_gid == NVME_AUTH_DHGROUP_NULL && ctrl->concat) {
125+
pr_debug("%s: ctrl %d qid %d: NULL DH group invalid "
126+
"for secure channel concatenation\n", __func__,
127+
ctrl->cntlid, req->sq->qid);
128+
return NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH;
129+
}
106130
pr_debug("%s: ctrl %d qid %d: selected DH group %s (%d)\n",
107131
__func__, ctrl->cntlid, req->sq->qid,
108132
nvme_auth_dhgroup_name(ctrl->dh_gid), ctrl->dh_gid);
@@ -148,12 +172,22 @@ static u8 nvmet_auth_reply(struct nvmet_req *req, void *d)
148172
if (memcmp(data->rval, response, data->hl)) {
149173
pr_info("ctrl %d qid %d host response mismatch\n",
150174
ctrl->cntlid, req->sq->qid);
175+
pr_debug("ctrl %d qid %d rval %*ph\n",
176+
ctrl->cntlid, req->sq->qid, data->hl, data->rval);
177+
pr_debug("ctrl %d qid %d response %*ph\n",
178+
ctrl->cntlid, req->sq->qid, data->hl, response);
151179
kfree(response);
152180
return NVME_AUTH_DHCHAP_FAILURE_FAILED;
153181
}
154182
kfree(response);
155183
pr_debug("%s: ctrl %d qid %d host authenticated\n",
156184
__func__, ctrl->cntlid, req->sq->qid);
185+
if (!data->cvalid && ctrl->concat) {
186+
pr_debug("%s: ctrl %d qid %d invalid challenge\n",
187+
__func__, ctrl->cntlid, req->sq->qid);
188+
return NVME_AUTH_DHCHAP_FAILURE_FAILED;
189+
}
190+
req->sq->dhchap_s2 = le32_to_cpu(data->seqnum);
157191
if (data->cvalid) {
158192
req->sq->dhchap_c2 = kmemdup(data->rval + data->hl, data->hl,
159193
GFP_KERNEL);
@@ -163,11 +197,23 @@ static u8 nvmet_auth_reply(struct nvmet_req *req, void *d)
163197
pr_debug("%s: ctrl %d qid %d challenge %*ph\n",
164198
__func__, ctrl->cntlid, req->sq->qid, data->hl,
165199
req->sq->dhchap_c2);
166-
} else {
200+
}
201+
/*
202+
* NVMe Base Spec 2.2 section 8.3.4.5.4: DH-HMAC-CHAP_Reply message
203+
* Sequence Number (SEQNUM): [ .. ]
204+
* The value 0h is used to indicate that bidirectional authentication
205+
* is not performed, but a challenge value C2 is carried in order to
206+
* generate a pre-shared key (PSK) for subsequent establishment of a
207+
* secure channel.
208+
*/
209+
if (req->sq->dhchap_s2 == 0) {
210+
if (ctrl->concat)
211+
nvmet_auth_insert_psk(req->sq);
167212
req->sq->authenticated = true;
213+
kfree(req->sq->dhchap_c2);
168214
req->sq->dhchap_c2 = NULL;
169-
}
170-
req->sq->dhchap_s2 = le32_to_cpu(data->seqnum);
215+
} else if (!data->cvalid)
216+
req->sq->authenticated = true;
171217

172218
return 0;
173219
}
@@ -303,6 +349,8 @@ void nvmet_execute_auth_send(struct nvmet_req *req)
303349
}
304350
goto done_kfree;
305351
case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2:
352+
if (ctrl->concat)
353+
nvmet_auth_insert_psk(req->sq);
306354
req->sq->authenticated = true;
307355
pr_debug("%s: ctrl %d qid %d ctrl authenticated\n",
308356
__func__, ctrl->cntlid, req->sq->qid);

drivers/nvme/target/fabrics-cmd.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,22 @@ static u16 nvmet_install_queue(struct nvmet_ctrl *ctrl, struct nvmet_req *req)
236236

237237
static u32 nvmet_connect_result(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq)
238238
{
239-
bool needs_auth = nvmet_has_auth(ctrl);
239+
bool needs_auth = nvmet_has_auth(ctrl, sq);
240+
key_serial_t keyid = nvmet_queue_tls_keyid(sq);
240241

242+
/* Do not authenticate I/O queues for secure concatenation */
243+
if (ctrl->concat && sq->qid)
244+
needs_auth = false;
245+
246+
if (keyid)
247+
pr_debug("%s: ctrl %d qid %d should %sauthenticate, tls psk %08x\n",
248+
__func__, ctrl->cntlid, sq->qid,
249+
needs_auth ? "" : "not ", keyid);
250+
else
251+
pr_debug("%s: ctrl %d qid %d should %sauthenticate%s\n",
252+
__func__, ctrl->cntlid, sq->qid,
253+
needs_auth ? "" : "not ",
254+
ctrl->concat ? ", secure concatenation" : "");
241255
return (u32)ctrl->cntlid |
242256
(needs_auth ? NVME_CONNECT_AUTHREQ_ATR : 0);
243257
}

drivers/nvme/target/nvmet.h

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,9 @@ struct nvmet_sq {
164164
u32 dhchap_s2;
165165
u8 *dhchap_skey;
166166
int dhchap_skey_len;
167+
#endif
168+
#ifdef CONFIG_NVME_TARGET_TCP_TLS
169+
struct key *tls_key;
167170
#endif
168171
struct completion free_done;
169172
struct completion confirm_done;
@@ -289,6 +292,7 @@ struct nvmet_ctrl {
289292
u64 err_counter;
290293
struct nvme_error_slot slots[NVMET_ERROR_LOG_SLOTS];
291294
bool pi_support;
295+
bool concat;
292296
#ifdef CONFIG_NVME_TARGET_AUTH
293297
struct nvme_dhchap_key *host_key;
294298
struct nvme_dhchap_key *ctrl_key;
@@ -297,6 +301,9 @@ struct nvmet_ctrl {
297301
u8 dh_gid;
298302
u8 *dh_key;
299303
size_t dh_keysize;
304+
#endif
305+
#ifdef CONFIG_NVME_TARGET_TCP_TLS
306+
struct key *tls_key;
300307
#endif
301308
struct nvmet_pr_log_mgr pr_log_mgr;
302309
};
@@ -853,6 +860,22 @@ static inline void nvmet_req_bio_put(struct nvmet_req *req, struct bio *bio)
853860
bio_put(bio);
854861
}
855862

863+
#ifdef CONFIG_NVME_TARGET_TCP_TLS
864+
static inline key_serial_t nvmet_queue_tls_keyid(struct nvmet_sq *sq)
865+
{
866+
return sq->tls_key ? key_serial(sq->tls_key) : 0;
867+
}
868+
static inline void nvmet_sq_put_tls_key(struct nvmet_sq *sq)
869+
{
870+
if (sq->tls_key) {
871+
key_put(sq->tls_key);
872+
sq->tls_key = NULL;
873+
}
874+
}
875+
#else
876+
static inline key_serial_t nvmet_queue_tls_keyid(struct nvmet_sq *sq) { return 0; }
877+
static inline void nvmet_sq_put_tls_key(struct nvmet_sq *sq) {}
878+
#endif
856879
#ifdef CONFIG_NVME_TARGET_AUTH
857880
u32 nvmet_auth_send_data_len(struct nvmet_req *req);
858881
void nvmet_execute_auth_send(struct nvmet_req *req);
@@ -871,14 +894,15 @@ int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
871894
unsigned int hash_len);
872895
int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response,
873896
unsigned int hash_len);
874-
static inline bool nvmet_has_auth(struct nvmet_ctrl *ctrl)
897+
static inline bool nvmet_has_auth(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq)
875898
{
876-
return ctrl->host_key != NULL;
899+
return ctrl->host_key != NULL && !nvmet_queue_tls_keyid(sq);
877900
}
878901
int nvmet_auth_ctrl_exponential(struct nvmet_req *req,
879902
u8 *buf, int buf_size);
880903
int nvmet_auth_ctrl_sesskey(struct nvmet_req *req,
881904
u8 *buf, int buf_size);
905+
void nvmet_auth_insert_psk(struct nvmet_sq *sq);
882906
#else
883907
static inline u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl,
884908
struct nvmet_sq *sq)
@@ -894,11 +918,13 @@ static inline bool nvmet_check_auth_status(struct nvmet_req *req)
894918
{
895919
return true;
896920
}
897-
static inline bool nvmet_has_auth(struct nvmet_ctrl *ctrl)
921+
static inline bool nvmet_has_auth(struct nvmet_ctrl *ctrl,
922+
struct nvmet_sq *sq)
898923
{
899924
return false;
900925
}
901926
static inline const char *nvmet_dhchap_dhgroup_name(u8 dhgid) { return NULL; }
927+
static inline void nvmet_auth_insert_psk(struct nvmet_sq *sq) {};
902928
#endif
903929

904930
int nvmet_pr_init_ns(struct nvmet_ns *ns);

drivers/nvme/target/tcp.c

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,10 +1072,11 @@ static int nvmet_tcp_done_recv_pdu(struct nvmet_tcp_queue *queue)
10721072

10731073
if (unlikely(!nvmet_req_init(req, &queue->nvme_cq,
10741074
&queue->nvme_sq, &nvmet_tcp_ops))) {
1075-
pr_err("failed cmd %p id %d opcode %d, data_len: %d\n",
1075+
pr_err("failed cmd %p id %d opcode %d, data_len: %d, status: %04x\n",
10761076
req->cmd, req->cmd->common.command_id,
10771077
req->cmd->common.opcode,
1078-
le32_to_cpu(req->cmd->common.dptr.sgl.length));
1078+
le32_to_cpu(req->cmd->common.dptr.sgl.length),
1079+
le16_to_cpu(req->cqe->status));
10791080

10801081
nvmet_tcp_handle_req_failure(queue, queue->cmd, req);
10811082
return 0;
@@ -1601,6 +1602,7 @@ static void nvmet_tcp_release_queue_work(struct work_struct *w)
16011602
/* stop accepting incoming data */
16021603
queue->rcv_state = NVMET_TCP_RECV_ERR;
16031604

1605+
nvmet_sq_put_tls_key(&queue->nvme_sq);
16041606
nvmet_tcp_uninit_data_in_cmds(queue);
16051607
nvmet_sq_destroy(&queue->nvme_sq);
16061608
cancel_work_sync(&queue->io_work);
@@ -1786,6 +1788,27 @@ static int nvmet_tcp_try_peek_pdu(struct nvmet_tcp_queue *queue)
17861788
return 0;
17871789
}
17881790

1791+
static int nvmet_tcp_tls_key_lookup(struct nvmet_tcp_queue *queue,
1792+
key_serial_t peerid)
1793+
{
1794+
struct key *tls_key = nvme_tls_key_lookup(peerid);
1795+
int status = 0;
1796+
1797+
if (IS_ERR(tls_key)) {
1798+
pr_warn("%s: queue %d failed to lookup key %x\n",
1799+
__func__, queue->idx, peerid);
1800+
spin_lock_bh(&queue->state_lock);
1801+
queue->state = NVMET_TCP_Q_FAILED;
1802+
spin_unlock_bh(&queue->state_lock);
1803+
status = PTR_ERR(tls_key);
1804+
} else {
1805+
pr_debug("%s: queue %d using TLS PSK %x\n",
1806+
__func__, queue->idx, peerid);
1807+
queue->nvme_sq.tls_key = tls_key;
1808+
}
1809+
return status;
1810+
}
1811+
17891812
static void nvmet_tcp_tls_handshake_done(void *data, int status,
17901813
key_serial_t peerid)
17911814
{
@@ -1806,6 +1829,10 @@ static void nvmet_tcp_tls_handshake_done(void *data, int status,
18061829
spin_unlock_bh(&queue->state_lock);
18071830

18081831
cancel_delayed_work_sync(&queue->tls_handshake_tmo_work);
1832+
1833+
if (!status)
1834+
status = nvmet_tcp_tls_key_lookup(queue, peerid);
1835+
18091836
if (status)
18101837
nvmet_tcp_schedule_release_queue(queue);
18111838
else

0 commit comments

Comments
 (0)