Skip to content
3 changes: 2 additions & 1 deletion drivers/nvme/host/auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <asm/unaligned.h>
#include <crypto/hash.h>
#include <crypto/dh.h>
#include <crypto/algapi.h>
#include "nvme.h"
#include "fabrics.h"
#include <linux/nvme-auth.h>
Expand Down Expand Up @@ -365,7 +366,7 @@ static int nvme_auth_process_dhchap_success1(struct nvme_ctrl *ctrl,
return 0;

/* Validate controller response */
if (memcmp(chap->response, data->rval, data->hl)) {
if (crypto_memneq(chap->response, data->rval, data->hl)) {
dev_dbg(ctrl->device, "%s: qid %d ctrl response %*ph\n",
__func__, chap->qid, (int)chap->hash_len, data->rval);
dev_dbg(ctrl->device, "%s: qid %d host response %*ph\n",
Expand Down
21 changes: 21 additions & 0 deletions drivers/nvme/target/admin-cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -1008,6 +1008,27 @@ void nvmet_execute_keep_alive(struct nvmet_req *req)
nvmet_req_complete(req, status);
}

u32 nvmet_admin_cmd_data_len(struct nvmet_req *req)
{
struct nvme_command *cmd = req->cmd;

if (nvme_is_fabrics(cmd))
return nvmet_fabrics_admin_cmd_data_len(req);
if (nvmet_is_disc_subsys(nvmet_req_subsys(req)))
return nvmet_discovery_cmd_data_len(req);

switch (cmd->common.opcode) {
case nvme_admin_get_log_page:
return nvmet_get_log_page_len(cmd);
case nvme_admin_identify:
return NVME_IDENTIFY_DATA_SIZE;
case nvme_admin_get_features:
return nvmet_feat_data_len(req, le32_to_cpu(cmd->common.cdw10));
default:
return 0;
}
}

u16 nvmet_parse_admin_cmd(struct nvmet_req *req)
{
struct nvme_command *cmd = req->cmd;
Expand Down
37 changes: 37 additions & 0 deletions drivers/nvme/target/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,33 @@ static inline u16 nvmet_io_cmd_check_access(struct nvmet_req *req)
return 0;
}

static u32 nvmet_io_cmd_transfer_len(struct nvmet_req *req)
{
struct nvme_command *cmd = req->cmd;
u32 metadata_len = 0;

if (nvme_is_fabrics(cmd))
return nvmet_fabrics_io_cmd_data_len(req);

if (!req->ns)
return 0;

switch (req->cmd->common.opcode) {
case nvme_cmd_read:
case nvme_cmd_write:
case nvme_cmd_zone_append:
if (req->sq->ctrl->pi_support && nvmet_ns_has_pi(req->ns))
metadata_len = nvmet_rw_metadata_len(req);
return nvmet_rw_data_len(req) + metadata_len;
case nvme_cmd_dsm:
return nvmet_dsm_len(req);
case nvme_cmd_zone_mgmt_recv:
return (le32_to_cpu(req->cmd->zmr.numd) + 1) << 2;
default:
return 0;
}
}

static u16 nvmet_parse_io_cmd(struct nvmet_req *req)
{
struct nvme_command *cmd = req->cmd;
Expand Down Expand Up @@ -1020,6 +1047,16 @@ void nvmet_req_uninit(struct nvmet_req *req)
}
EXPORT_SYMBOL_GPL(nvmet_req_uninit);

size_t nvmet_req_transfer_len(struct nvmet_req *req)
{
if (likely(req->sq->qid != 0))
return nvmet_io_cmd_transfer_len(req);
if (unlikely(!req->sq->ctrl))
return nvmet_connect_cmd_data_len(req);
return nvmet_admin_cmd_data_len(req);
}
EXPORT_SYMBOL_GPL(nvmet_req_transfer_len);

bool nvmet_check_transfer_len(struct nvmet_req *req, size_t len)
{
if (unlikely(len != req->transfer_len)) {
Expand Down
14 changes: 14 additions & 0 deletions drivers/nvme/target/discovery.c
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,20 @@ static void nvmet_execute_disc_get_features(struct nvmet_req *req)
nvmet_req_complete(req, stat);
}

u32 nvmet_discovery_cmd_data_len(struct nvmet_req *req)
{
struct nvme_command *cmd = req->cmd;

switch (cmd->common.opcode) {
case nvme_admin_get_log_page:
return nvmet_get_log_page_len(req->cmd);
case nvme_admin_identify:
return NVME_IDENTIFY_DATA_SIZE;
default:
return 0;
}
}

u16 nvmet_parse_discovery_cmd(struct nvmet_req *req)
{
struct nvme_command *cmd = req->cmd;
Expand Down
72 changes: 62 additions & 10 deletions drivers/nvme/target/fabrics-cmd-auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <linux/nvme-auth.h>
#include <crypto/hash.h>
#include <crypto/kpp.h>
#include <crypto/algapi.h>
#include "nvmet.h"

static void nvmet_auth_expired_work(struct work_struct *work)
Expand All @@ -31,12 +32,16 @@ void nvmet_auth_sq_init(struct nvmet_sq *sq)
sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE;
}

static u8 nvmet_auth_negotiate(struct nvmet_req *req, void *d)
static u8 nvmet_auth_negotiate(struct nvmet_req *req, void *d, u32 tl)
{
struct nvmet_ctrl *ctrl = req->sq->ctrl;
struct nvmf_auth_dhchap_negotiate_data *data = d;
int i, hash_id = 0, fallback_hash_id = 0, dhgid, fallback_dhgid;

if (tl < sizeof(*data) +
sizeof(struct nvmf_auth_dhchap_protocol_descriptor))
return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;

pr_debug("%s: ctrl %d qid %d: data sc_d %d napd %d authid %d halen %d dhlen %d\n",
__func__, ctrl->cntlid, req->sq->qid,
data->sc_c, data->napd, data->auth_protocol[0].dhchap.authid,
Expand All @@ -53,6 +58,10 @@ static u8 nvmet_auth_negotiate(struct nvmet_req *req, void *d)
NVME_AUTH_DHCHAP_AUTH_ID)
return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;

if (data->auth_protocol[0].dhchap.dhlen > NVME_AUTH_DHCHAP_MAX_DH_IDS ||
data->auth_protocol[0].dhchap.halen > NVME_AUTH_DHCHAP_MAX_HASH_IDS)
return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;

for (i = 0; i < data->auth_protocol[0].dhchap.halen; i++) {
u8 host_hmac_id = data->auth_protocol[0].dhchap.idlist[i];

Expand Down Expand Up @@ -109,13 +118,22 @@ static u8 nvmet_auth_negotiate(struct nvmet_req *req, void *d)
return 0;
}

static u8 nvmet_auth_reply(struct nvmet_req *req, void *d)
static u8 nvmet_auth_reply(struct nvmet_req *req, void *d, u32 tl)
{
struct nvmet_ctrl *ctrl = req->sq->ctrl;
struct nvmf_auth_dhchap_reply_data *data = d;
u16 dhvlen = le16_to_cpu(data->dhvlen);
u16 dhvlen;
u8 *response;

if (tl < sizeof(*data))
return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;

dhvlen = le16_to_cpu(data->dhvlen);

/* Validate that hl and dhvlen fit within the transfer length */
if (sizeof(*data) + 2 * (size_t)data->hl + dhvlen > tl)
return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD;

pr_debug("%s: ctrl %d qid %d: data hl %d cvalid %d dhvlen %u\n",
__func__, ctrl->cntlid, req->sq->qid,
data->hl, data->cvalid, dhvlen);
Expand Down Expand Up @@ -145,7 +163,7 @@ static u8 nvmet_auth_reply(struct nvmet_req *req, void *d)
return NVME_AUTH_DHCHAP_FAILURE_FAILED;
}

if (memcmp(data->rval, response, data->hl)) {
if (crypto_memneq(data->rval, response, data->hl)) {
pr_info("ctrl %d qid %d host response mismatch\n",
ctrl->cntlid, req->sq->qid);
kfree(response);
Expand Down Expand Up @@ -179,6 +197,11 @@ static u8 nvmet_auth_failure2(void *d)
return data->rescode_exp;
}

u32 nvmet_auth_send_data_len(struct nvmet_req *req)
{
return le32_to_cpu(req->cmd->auth_send.tl);
}

void nvmet_execute_auth_send(struct nvmet_req *req)
{
struct nvmet_ctrl *ctrl = req->sq->ctrl;
Expand Down Expand Up @@ -206,7 +229,7 @@ void nvmet_execute_auth_send(struct nvmet_req *req)
offsetof(struct nvmf_auth_send_command, spsp1);
goto done;
}
tl = le32_to_cpu(req->cmd->auth_send.tl);
tl = nvmet_auth_send_data_len(req);
if (!tl) {
status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
req->error_loc =
Expand Down Expand Up @@ -256,7 +279,7 @@ void nvmet_execute_auth_send(struct nvmet_req *req)
} else if (data->auth_id != req->sq->dhchap_step)
goto done_failure1;
/* Validate negotiation parameters */
dhchap_status = nvmet_auth_negotiate(req, d);
dhchap_status = nvmet_auth_negotiate(req, d, tl);
if (dhchap_status == 0)
req->sq->dhchap_step =
NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE;
Expand Down Expand Up @@ -287,7 +310,7 @@ void nvmet_execute_auth_send(struct nvmet_req *req)

switch (data->auth_id) {
case NVME_AUTH_DHCHAP_MESSAGE_REPLY:
dhchap_status = nvmet_auth_reply(req, d);
dhchap_status = nvmet_auth_reply(req, d, tl);
if (dhchap_status == 0)
req->sq->dhchap_step =
NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1;
Expand Down Expand Up @@ -356,7 +379,7 @@ static int nvmet_auth_challenge(struct nvmet_req *req, void *d, int al)
struct nvmet_ctrl *ctrl = req->sq->ctrl;
int ret = 0;
int hash_len = nvme_auth_hmac_hash_len(ctrl->shash_id);
int data_size = sizeof(*d) + hash_len;
int data_size = sizeof(*data) + hash_len;

if (ctrl->dh_tfm)
data_size += ctrl->dh_keysize;
Expand Down Expand Up @@ -429,6 +452,35 @@ static void nvmet_auth_failure1(struct nvmet_req *req, void *d, int al)
data->rescode_exp = req->sq->dhchap_status;
}

u32 nvmet_auth_receive_data_len(struct nvmet_req *req)
{
struct nvmet_ctrl *ctrl = req->sq->ctrl;
u32 al = le32_to_cpu(req->cmd->auth_receive.al);
u32 min_len;

/*
* Reject too-short al before kmalloc(al), since the SUCCESS1 and
* FAILURE1/default builders write fixed response headers into it.
*/
switch (req->sq->dhchap_step) {
case NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE:
return al;
case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1:
min_len = sizeof(struct nvmf_auth_dhchap_success1_data);
if (req->sq->dhchap_c2)
min_len += nvme_auth_hmac_hash_len(ctrl->shash_id);
break;
default:
min_len = sizeof(struct nvmf_auth_dhchap_failure_data);
break;
}

if (al < min_len)
return 0;

return al;
}

void nvmet_execute_auth_receive(struct nvmet_req *req)
{
struct nvmet_ctrl *ctrl = req->sq->ctrl;
Expand All @@ -454,7 +506,7 @@ void nvmet_execute_auth_receive(struct nvmet_req *req)
offsetof(struct nvmf_auth_receive_command, spsp1);
goto done;
}
al = le32_to_cpu(req->cmd->auth_receive.al);
al = nvmet_auth_receive_data_len(req);
if (!al) {
status = NVME_SC_INVALID_FIELD | NVME_STATUS_DNR;
req->error_loc =
Expand All @@ -466,7 +518,7 @@ void nvmet_execute_auth_receive(struct nvmet_req *req)
return;
}

d = kmalloc(al, GFP_KERNEL);
d = kzalloc(al, GFP_KERNEL);
if (!d) {
status = NVME_SC_INTERNAL;
goto done;
Expand Down
43 changes: 43 additions & 0 deletions drivers/nvme/target/fabrics-cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,22 @@ static void nvmet_execute_prop_get(struct nvmet_req *req)
nvmet_req_complete(req, status);
}

u32 nvmet_fabrics_admin_cmd_data_len(struct nvmet_req *req)
{
struct nvme_command *cmd = req->cmd;

switch (cmd->fabrics.fctype) {
#ifdef CONFIG_NVME_TARGET_AUTH
case nvme_fabrics_type_auth_send:
return nvmet_auth_send_data_len(req);
case nvme_fabrics_type_auth_receive:
return nvmet_auth_receive_data_len(req);
#endif
default:
return 0;
}
}

u16 nvmet_parse_fabrics_admin_cmd(struct nvmet_req *req)
{
struct nvme_command *cmd = req->cmd;
Expand Down Expand Up @@ -111,6 +127,22 @@ u16 nvmet_parse_fabrics_admin_cmd(struct nvmet_req *req)
return 0;
}

u32 nvmet_fabrics_io_cmd_data_len(struct nvmet_req *req)
{
struct nvme_command *cmd = req->cmd;

switch (cmd->fabrics.fctype) {
#ifdef CONFIG_NVME_TARGET_AUTH
case nvme_fabrics_type_auth_send:
return nvmet_auth_send_data_len(req);
case nvme_fabrics_type_auth_receive:
return nvmet_auth_receive_data_len(req);
#endif
default:
return 0;
}
}

u16 nvmet_parse_fabrics_io_cmd(struct nvmet_req *req)
{
struct nvme_command *cmd = req->cmd;
Expand Down Expand Up @@ -342,6 +374,17 @@ static void nvmet_execute_io_connect(struct nvmet_req *req)
goto out;
}

u32 nvmet_connect_cmd_data_len(struct nvmet_req *req)
{
struct nvme_command *cmd = req->cmd;

if (!nvme_is_fabrics(cmd) ||
cmd->fabrics.fctype != nvme_fabrics_type_connect)
return 0;

return sizeof(struct nvmf_connect_data);
}

u16 nvmet_parse_connect_cmd(struct nvmet_req *req)
{
struct nvme_command *cmd = req->cmd;
Expand Down
8 changes: 8 additions & 0 deletions drivers/nvme/target/nvmet.h
Original file line number Diff line number Diff line change
Expand Up @@ -465,18 +465,24 @@ void nvmet_start_keep_alive_timer(struct nvmet_ctrl *ctrl);
void nvmet_stop_keep_alive_timer(struct nvmet_ctrl *ctrl);

u16 nvmet_parse_connect_cmd(struct nvmet_req *req);
u32 nvmet_connect_cmd_data_len(struct nvmet_req *req);
void nvmet_bdev_set_limits(struct block_device *bdev, struct nvme_id_ns *id);
u16 nvmet_bdev_parse_io_cmd(struct nvmet_req *req);
u16 nvmet_file_parse_io_cmd(struct nvmet_req *req);
u16 nvmet_bdev_zns_parse_io_cmd(struct nvmet_req *req);
u32 nvmet_admin_cmd_data_len(struct nvmet_req *req);
u16 nvmet_parse_admin_cmd(struct nvmet_req *req);
u32 nvmet_discovery_cmd_data_len(struct nvmet_req *req);
u16 nvmet_parse_discovery_cmd(struct nvmet_req *req);
u16 nvmet_parse_fabrics_admin_cmd(struct nvmet_req *req);
u32 nvmet_fabrics_admin_cmd_data_len(struct nvmet_req *req);
u16 nvmet_parse_fabrics_io_cmd(struct nvmet_req *req);
u32 nvmet_fabrics_io_cmd_data_len(struct nvmet_req *req);

bool nvmet_req_init(struct nvmet_req *req, struct nvmet_cq *cq,
struct nvmet_sq *sq, const struct nvmet_fabrics_ops *ops);
void nvmet_req_uninit(struct nvmet_req *req);
size_t nvmet_req_transfer_len(struct nvmet_req *req);
bool nvmet_check_transfer_len(struct nvmet_req *req, size_t len);
bool nvmet_check_data_len_lte(struct nvmet_req *req, size_t data_len);
void nvmet_req_complete(struct nvmet_req *req, u16 status);
Expand Down Expand Up @@ -717,7 +723,9 @@ static inline void nvmet_req_bio_put(struct nvmet_req *req, struct bio *bio)
}

#ifdef CONFIG_NVME_TARGET_AUTH
u32 nvmet_auth_send_data_len(struct nvmet_req *req);
void nvmet_execute_auth_send(struct nvmet_req *req);
u32 nvmet_auth_receive_data_len(struct nvmet_req *req);
void nvmet_execute_auth_receive(struct nvmet_req *req);
int nvmet_auth_set_key(struct nvmet_host *host, const char *secret,
bool set_ctrl);
Expand Down
4 changes: 4 additions & 0 deletions include/linux/nvme.h
Original file line number Diff line number Diff line change
Expand Up @@ -2023,4 +2023,8 @@ struct nvme_completion {
#define NVME_MINOR(ver) (((ver) >> 8) & 0xff)
#define NVME_TERTIARY(ver) ((ver) & 0xff)

/* Section 8.3.4.5.2 of the NVMe 2.1 */
#define NVME_AUTH_DHCHAP_MAX_HASH_IDS 30
#define NVME_AUTH_DHCHAP_MAX_DH_IDS 30

#endif /* _LINUX_NVME_H */
Loading
Loading