diff options
Diffstat (limited to 'src/nvme/linux.c')
-rw-r--r-- | src/nvme/linux.c | 829 |
1 files changed, 605 insertions, 224 deletions
diff --git a/src/nvme/linux.c b/src/nvme/linux.c index c6eedc2..163086e 100644 --- a/src/nvme/linux.c +++ b/src/nvme/linux.c @@ -35,15 +35,17 @@ #include <ccan/endian/endian.h> +#include "cleanup.h" #include "linux.h" #include "tree.h" #include "log.h" #include "private.h" +#include "base64.h" static int __nvme_open(const char *name) { - char *path; - int fd, ret; + _cleanup_free_ char *path = NULL; + int ret; ret = asprintf(&path, "%s/%s", "/dev", name); if (ret < 0) { @@ -51,9 +53,7 @@ static int __nvme_open(const char *name) return -1; } - fd = open(path, O_RDONLY); - free(path); - return fd; + return open(path, O_RDONLY); } int nvme_open(const char *name) @@ -122,17 +122,51 @@ int nvme_fw_download_seq(int fd, __u32 size, __u32 xfer, __u32 offset, return err; } -static int nvme_get_telemetry_log(int fd, bool create, bool ctrl, bool rae, - struct nvme_telemetry_log **buf, enum nvme_telemetry_da da, - size_t *size) +int nvme_get_telemetry_max(int fd, enum nvme_telemetry_da *da, size_t *data_tx) +{ + _cleanup_free_ struct nvme_id_ctrl *id_ctrl; + int err; + + id_ctrl = __nvme_alloc(sizeof(*id_ctrl)); + if (!id_ctrl) { + errno = ENOMEM; + return -1; + } + err = nvme_identify_ctrl(fd, id_ctrl); + if (err) + return err; + + if (data_tx) { + *data_tx = id_ctrl->mdts; + if (id_ctrl->mdts) { + /* assuming CAP.MPSMIN is zero minimum Memory Page Size is at least + * 4096 bytes + */ + *data_tx = (1 << id_ctrl->mdts) * 4096; + } + } + if (da) { + if (id_ctrl->lpa & 0x8) + *da = NVME_TELEMETRY_DA_3; + if (id_ctrl->lpa & 0x40) + *da = NVME_TELEMETRY_DA_4; + + } + return err; +} + +int nvme_get_telemetry_log(int fd, bool create, bool ctrl, bool rae, size_t max_data_tx, + enum nvme_telemetry_da da, struct nvme_telemetry_log **buf, + size_t *size) { static const __u32 xfer = NVME_LOG_TELEM_BLOCK_SIZE; struct nvme_telemetry_log *telem; enum nvme_cmd_get_log_lid lid; - struct nvme_id_ctrl id_ctrl; - void *log, *tmp; + _cleanup_free_ void *log; + void *tmp; int err; + size_t dalb; struct nvme_get_log_args args = { .args_size = sizeof(args), .fd = fd, @@ -167,89 +201,101 @@ static int nvme_get_telemetry_log(int fd, bool create, bool ctrl, bool rae, } if (err) - goto free; + return err; telem = log; if (ctrl && !telem->ctrlavail) { *buf = log; + log = NULL; *size = xfer; return 0; } switch (da) { case NVME_TELEMETRY_DA_1: + dalb = le16_to_cpu(telem->dalb1); + break; case NVME_TELEMETRY_DA_2: + dalb = le16_to_cpu(telem->dalb2); + break; case NVME_TELEMETRY_DA_3: /* dalb3 >= dalb2 >= dalb1 */ - *size = (le16_to_cpu(telem->dalb3) + 1) * xfer; + dalb = le16_to_cpu(telem->dalb3); break; case NVME_TELEMETRY_DA_4: - err = nvme_identify_ctrl(fd, &id_ctrl); - if (err) { - perror("identify-ctrl"); - errno = EINVAL; - goto free; - } - - if (id_ctrl.lpa & 0x40) { - *size = (le32_to_cpu(telem->dalb4) + 1) * xfer; - } else { - fprintf(stderr, "Data area 4 unsupported, bit 6 of Log Page Attributes not set\n"); - errno = EINVAL; - err = -1; - goto free; - } + dalb = le32_to_cpu(telem->dalb4); break; default: - fprintf(stderr, "Invalid data area parameter - %d\n", da); errno = EINVAL; - err = -1; - goto free; + return -1; } + if (dalb == 0) { + errno = ENOENT; + return -1; + } + + *size = (dalb + 1) * xfer; tmp = realloc(log, *size); if (!tmp) { errno = ENOMEM; - err = -1; - goto free; + return -1; } log = tmp; args.lid = lid; args.log = log; args.len = *size; - err = nvme_get_log_page(fd, 4096, &args); - if (!err) { - *buf = log; - return 0; + err = nvme_get_log_page(fd, max_data_tx, &args); + if (err) + return err; + + *buf = log; + log = NULL; + return 0; +} + + +static int nvme_check_get_telemetry_log(int fd, bool create, bool ctrl, bool rae, + struct nvme_telemetry_log **log, enum nvme_telemetry_da da, + size_t *size) +{ + enum nvme_telemetry_da max_da = 0; + int err = nvme_get_telemetry_max(fd, &max_da, NULL); + + if (err) + return err; + if (da > max_da) { + errno = ENOENT; + return -1; } -free: - free(log); - return err; + return nvme_get_telemetry_log(fd, create, ctrl, rae, 4096, da, log, size); } + int nvme_get_ctrl_telemetry(int fd, bool rae, struct nvme_telemetry_log **log, enum nvme_telemetry_da da, size_t *size) { - return nvme_get_telemetry_log(fd, false, true, rae, log, da, size); + return nvme_check_get_telemetry_log(fd, false, true, rae, log, da, size); } int nvme_get_host_telemetry(int fd, struct nvme_telemetry_log **log, enum nvme_telemetry_da da, size_t *size) { - return nvme_get_telemetry_log(fd, false, false, false, log, da, size); + return nvme_check_get_telemetry_log(fd, false, false, false, log, da, size); } int nvme_get_new_host_telemetry(int fd, struct nvme_telemetry_log **log, enum nvme_telemetry_da da, size_t *size) { - return nvme_get_telemetry_log(fd, true, false, false, log, da, size); + return nvme_check_get_telemetry_log(fd, true, false, false, log, da, size); } int nvme_get_lba_status_log(int fd, bool rae, struct nvme_lba_status_log **log) { - __u32 size = sizeof(struct nvme_lba_status_log); - void *buf, *tmp; + __u32 size; + _cleanup_free_ struct nvme_lba_status_log *buf; + void *tmp; int err; struct nvme_get_log_args args = { .args_size = sizeof(args), @@ -265,38 +311,42 @@ int nvme_get_lba_status_log(int fd, bool rae, struct nvme_lba_status_log **log) .ot = false, }; - buf = malloc(size); + buf = malloc(sizeof(*buf)); if (!buf) return -1; - *log = buf; - err = nvme_get_log_lba_status(fd, true, 0, size, buf); - if (err) - goto free; + err = nvme_get_log_lba_status(fd, true, 0, sizeof(*buf), buf); + if (err) { + *log = NULL; + return err; + } - size = le32_to_cpu((*log)->lslplen); - if (!size) + size = le32_to_cpu(buf->lslplen); + if (!size) { + *log = buf; + buf = NULL; return 0; + } tmp = realloc(buf, size); if (!tmp) { - err = -1; - goto free; + *log = NULL; + return -1; } buf = tmp; - *log = buf; args.lid = NVME_LOG_LID_LBA_STATUS; args.log = buf; args.len = size; err = nvme_get_log_page(fd, 4096, &args); - if (!err) - return 0; + if (err) { + *log = NULL; + return err; + } -free: - *log = NULL; - free(buf); - return err; + *log = buf; + buf = NULL; + return 0; } static int nvme_ns_attachment(int fd, __u32 nsid, __u16 num_ctrls, @@ -335,38 +385,48 @@ int nvme_namespace_detach_ctrls(int fd, __u32 nsid, __u16 num_ctrls, int nvme_get_ana_log_len(int fd, size_t *analen) { - struct nvme_id_ctrl ctrl; + _cleanup_free_ struct nvme_id_ctrl *ctrl; int ret; - ret = nvme_identify_ctrl(fd, &ctrl); + ctrl = __nvme_alloc(sizeof(*ctrl)); + if (!ctrl) { + errno = ENOMEM; + return -1; + } + ret = nvme_identify_ctrl(fd, ctrl); if (ret) return ret; *analen = sizeof(struct nvme_ana_log) + - le32_to_cpu(ctrl.nanagrpid) * sizeof(struct nvme_ana_group_desc) + - le32_to_cpu(ctrl.mnan) * sizeof(__le32); + le32_to_cpu(ctrl->nanagrpid) * sizeof(struct nvme_ana_group_desc) + + le32_to_cpu(ctrl->mnan) * sizeof(__le32); return 0; } int nvme_get_logical_block_size(int fd, __u32 nsid, int *blksize) { - struct nvme_id_ns ns; + _cleanup_free_ struct nvme_id_ns *ns; __u8 flbas; int ret; - ret = nvme_identify_ns(fd, nsid, &ns); + ns = __nvme_alloc(sizeof(*ns)); + if (!ns) { + errno = ENOMEM; + return -1; + } + ret = nvme_identify_ns(fd, nsid, ns); if (ret) return ret; - nvme_id_ns_flbas_to_lbaf_inuse(ns.flbas, &flbas); - *blksize = 1 << ns.lbaf[flbas].ds; + nvme_id_ns_flbas_to_lbaf_inuse(ns->flbas, &flbas); + *blksize = 1 << ns->lbaf[flbas].ds; return 0; } static int __nvme_set_attr(const char *path, const char *value) { - int ret, fd; + _cleanup_fd_ int fd; fd = open(path, O_WRONLY); if (fd < 0) { @@ -376,23 +436,19 @@ static int __nvme_set_attr(const char *path, const char *value) #endif return -1; } - ret = write(fd, value, strlen(value)); - close(fd); - return ret; + return write(fd, value, strlen(value)); } int nvme_set_attr(const char *dir, const char *attr, const char *value) { - char *path; + _cleanup_free_ char *path = NULL; int ret; ret = asprintf(&path, "%s/%s", dir, attr); if (ret < 0) return -1; - ret = __nvme_set_attr(path, value); - free(path); - return ret; + return __nvme_set_attr(path, value); } static char *__nvme_get_attr(const char *path) @@ -426,7 +482,7 @@ static char *__nvme_get_attr(const char *path) char *nvme_get_attr(const char *dir, const char *attr) { - char *path, *value; + _cleanup_free_ char *path = NULL; int ret; ret = asprintf(&path, "%s/%s", dir, attr); @@ -435,9 +491,7 @@ char *nvme_get_attr(const char *dir, const char *attr) return NULL; } - value = __nvme_get_attr(path); - free(path); - return value; + return __nvme_get_attr(path); } char *nvme_get_subsys_attr(nvme_subsystem_t s, const char *attr) @@ -476,21 +530,80 @@ int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac, return 0; } -static int derive_nvme_keys(const char *hostnqn, const char *identity, - int hmac, unsigned char *configured, - unsigned char *psk, int key_len) +static int derive_retained_key(int hmac, const char *hostnqn, + unsigned char *generated, + unsigned char *retained, + size_t key_len) +{ + nvme_msg(NULL, LOG_ERR, "NVMe TLS is not supported; " + "recompile with OpenSSL support.\n"); + errno = ENOTSUP; + return -1; +} + +static int gen_tls_identity(const char *hostnqn, const char *subsysnqn, + int version, int hmac, char *identity, + unsigned char *retained, size_t key_len) { - errno = EOPNOTSUPP; + if (version != 0) { + nvme_msg(NULL, LOG_ERR, "NVMe TLS 2.0 is not supported; " + "recompile with OpenSSL support.\n"); + errno = ENOTSUP; + return -1; + } + sprintf(identity, "NVMe0R%02d %s %s", + hmac, hostnqn, subsysnqn); + return strlen(identity); +} + +static int derive_tls_key(int hmac, const char *identity, + unsigned char *retained, + unsigned char *psk, size_t key_len) +{ + nvme_msg(NULL, LOG_ERR, "NVMe TLS is not supported; " + "recompile with OpenSSL support.\n"); + errno = ENOTSUP; return -1; } #else /* CONFIG_OPENSSL */ -static int derive_retained_key(const EVP_MD *md, const char *hostnqn, +static const EVP_MD *select_hmac(int hmac, size_t *key_len) +{ + const EVP_MD *md = NULL; + + switch (hmac) { + case NVME_HMAC_ALG_SHA2_256: + md = EVP_sha256(); + *key_len = 32; + break; + case NVME_HMAC_ALG_SHA2_384: + md = EVP_sha384(); + *key_len = 48; + break; + default: + break; + } + return md; +} + +static DEFINE_CLEANUP_FUNC( + cleanup_evp_pkey_ctx, EVP_PKEY_CTX *, EVP_PKEY_CTX_free) +#define _cleanup_evp_pkey_ctx_ __cleanup__(cleanup_evp_pkey_ctx) + +static int derive_retained_key(int hmac, const char *hostnqn, unsigned char *generated, unsigned char *retained, size_t key_len) { - EVP_PKEY_CTX *ctx; - int ret; + const EVP_MD *md; + _cleanup_evp_pkey_ctx_ EVP_PKEY_CTX *ctx = NULL; + uint16_t length = key_len & 0xFFFF; + size_t hmac_len; + + md = select_hmac(hmac, &hmac_len); + if (!md || hmac_len > key_len) { + errno = EINVAL; + return -1; + } ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); if (!ctx) { @@ -499,42 +612,60 @@ static int derive_retained_key(const EVP_MD *md, const char *hostnqn, } if (EVP_PKEY_derive_init(ctx) <= 0) { - ret = -ENOMEM; - goto out_free_ctx; - } - ret = -ENOKEY; - if (EVP_PKEY_CTX_set_hkdf_md(ctx, md) <= 0) - goto out_free_ctx; - if (EVP_PKEY_CTX_set1_hkdf_key(ctx, generated, key_len) <= 0) - goto out_free_ctx; + errno = ENOMEM; + return -1; + } + if (EVP_PKEY_CTX_set_hkdf_md(ctx, md) <= 0) { + errno = ENOKEY; + return -1; + } + if (EVP_PKEY_CTX_set1_hkdf_key(ctx, generated, key_len) <= 0) { + errno = ENOKEY; + return -1; + } if (EVP_PKEY_CTX_add1_hkdf_info(ctx, - (const unsigned char *)"tls13 ", 6) <= 0) - goto out_free_ctx; + (const unsigned char *)&length, 2) <= 0) { + errno = ENOKEY; + return -1; + } if (EVP_PKEY_CTX_add1_hkdf_info(ctx, - (const unsigned char *)"HostNQN", 7) <= 0) - goto out_free_ctx; + (const unsigned char *)"tls13 ", 6) <= 0) { + errno = ENOKEY; + return -1; + } if (EVP_PKEY_CTX_add1_hkdf_info(ctx, - (const unsigned char *)hostnqn, strlen(hostnqn)) <= 0) - goto out_free_ctx; - - if (EVP_PKEY_derive(ctx, retained, &key_len) > 0) - ret = key_len; + (const unsigned char *)"HostNQN", 7) <= 0) { + errno = ENOKEY; + return -1; + } + if (EVP_PKEY_CTX_add1_hkdf_info(ctx, + (const unsigned char *)hostnqn, strlen(hostnqn)) <= 0) { + errno = ENOKEY; + return -1; + } -out_free_ctx: - if (ret < 0) { - errno = -ret; - ret = -1; + if (EVP_PKEY_derive(ctx, retained, &key_len) <= 0) { + errno = ENOKEY; + return -1; } - EVP_PKEY_CTX_free(ctx); - return ret; + + return key_len; } -static int derive_tls_key(const EVP_MD *md, const char *identity, +static int derive_tls_key(int hmac, const char *identity, unsigned char *retained, unsigned char *psk, size_t key_len) { - EVP_PKEY_CTX *ctx; - int ret; + const EVP_MD *md; + _cleanup_evp_pkey_ctx_ EVP_PKEY_CTX *ctx = NULL; + size_t hmac_len; + uint16_t length = key_len & 0xFFFF; + + md = select_hmac(hmac, &hmac_len); + if (!md || hmac_len > key_len) { + errno = EINVAL; + return -1; + } ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); if (!ctx) { @@ -543,85 +674,59 @@ static int derive_tls_key(const EVP_MD *md, const char *identity, } if (EVP_PKEY_derive_init(ctx) <= 0) { - ret = -ENOMEM; - goto out_free_ctx; - } - ret = -ENOKEY; - if (EVP_PKEY_CTX_set_hkdf_md(ctx, md) <= 0) - goto out_free_ctx; - if (EVP_PKEY_CTX_set1_hkdf_key(ctx, retained, key_len) <= 0) - goto out_free_ctx; - if (EVP_PKEY_CTX_add1_hkdf_info(ctx, - (const unsigned char *)"tls13 ", 6) <= 0) - goto out_free_ctx; + errno = ENOMEM; + return -1; + } + if (EVP_PKEY_CTX_set_hkdf_md(ctx, md) <= 0) { + errno = ENOKEY; + return -1; + } + if (EVP_PKEY_CTX_set1_hkdf_key(ctx, retained, key_len) <= 0) { + errno = ENOKEY; + return -1; + } if (EVP_PKEY_CTX_add1_hkdf_info(ctx, - (const unsigned char *)"nvme-tls-psk", 12) <= 0) - goto out_free_ctx; + (const unsigned char *)&length, 2) <= 0) { + errno = ENOKEY; + return -1; + } if (EVP_PKEY_CTX_add1_hkdf_info(ctx, - (const unsigned char *)identity, - strlen(identity)) <= 0) - goto out_free_ctx; - - if (EVP_PKEY_derive(ctx, psk, &key_len) > 0) - ret = key_len; - -out_free_ctx: - EVP_PKEY_CTX_free(ctx); - if (ret < 0) { - errno = -ret; - ret = -1; + (const unsigned char *)"tls13 ", 6) <= 0) { + errno = ENOKEY; + return -1; } - - return ret; -} - -static int derive_nvme_keys(const char *hostnqn, const char *identity, - int hmac, unsigned char *configured, - unsigned char *psk, int key_len) -{ - const EVP_MD *md; - unsigned char *retained; - int ret = -1; - - if (!hostnqn || !identity) { - errno = EINVAL; + if (EVP_PKEY_CTX_add1_hkdf_info(ctx, + (const unsigned char *)"nvme-tls-psk", 12) <= 0) { + errno = ENOKEY; return -1; } - - switch (hmac) { - case 1: - md = EVP_sha256(); - break; - case 2: - md = EVP_sha384(); - break; - default: - errno = EINVAL; + if (EVP_PKEY_CTX_add1_hkdf_info(ctx, + (const unsigned char *)identity, + strlen(identity)) <= 0) { + errno = ENOKEY; return -1; } - retained = malloc(key_len); - if (!retained) { - errno = ENOMEM; + if (EVP_PKEY_derive(ctx, psk, &key_len) <= 0) { + errno = ENOKEY; return -1; } - ret = derive_retained_key(md, hostnqn, configured, retained, key_len); - if (ret > 0) - ret = derive_tls_key(md, identity, retained, psk, key_len); - free(retained); - return ret; + + return key_len; } #endif /* CONFIG_OPENSSL */ #ifdef CONFIG_OPENSSL_1 +static DEFINE_CLEANUP_FUNC(cleanup_hmac_ctx, HMAC_CTX *, HMAC_CTX_free) +#define _cleanup_hmac_ctx_ __cleanup__(cleanup_hmac_ctx) + int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac, unsigned int key_len, unsigned char *secret, unsigned char *key) { const char hmac_seed[] = "NVMe-over-Fabrics"; - HMAC_CTX *hmac_ctx; + _cleanup_hmac_ctx_ HMAC_CTX *hmac_ctx; const EVP_MD *md; - int err = -1; ENGINE_load_builtin_engines(); ENGINE_register_all_complete(); @@ -629,14 +734,13 @@ int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac, hmac_ctx = HMAC_CTX_new(); if (!hmac_ctx) { errno = ENOMEM; - return err; + return -1; } switch (hmac) { case NVME_HMAC_ALG_NONE: memcpy(key, secret, key_len); - err = 0; - goto out; + return 0; case NVME_HMAC_ALG_SHA2_256: md = EVP_sha256(); break; @@ -648,82 +752,164 @@ int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac, break; default: errno = EINVAL; - goto out; + return -1; } if (!md) { errno = EINVAL; - goto out; + return -1; } if (!HMAC_Init_ex(hmac_ctx, secret, key_len, md, NULL)) { errno = ENOMEM; - goto out; + return -1; } if (!HMAC_Update(hmac_ctx, (unsigned char *)hostnqn, strlen(hostnqn))) { errno = ENOKEY; - goto out; + return -1; } if (!HMAC_Update(hmac_ctx, (unsigned char *)hmac_seed, strlen(hmac_seed))) { errno = ENOKEY; - goto out; + return -1; } if (!HMAC_Final(hmac_ctx, key, &key_len)) { errno = ENOKEY; - goto out; + return -1; } - err = 0; + return 0; +} -out: - HMAC_CTX_free(hmac_ctx); - return err; +static int gen_tls_identity(const char *hostnqn, const char *subsysnqn, + int version, int hmac, char *identity, + unsigned char *retained, size_t key_len) +{ + static const char hmac_seed[] = "NVMe-over-Fabrics"; + size_t hmac_len; + const EVP_MD *md = select_hmac(hmac, &hmac_len); + _cleanup_hmac_ctx_ HMAC_CTX *hmac_ctx = NULL; + _cleanup_free_ unsigned char *psk_ctx = NULL; + _cleanup_free_ char *enc_ctx = NULL; + size_t len; + + if (version == 0) { + sprintf(identity, "NVMe%01dR%02d %s %s", + version, hmac, hostnqn, subsysnqn); + return strlen(identity); + } + if (version > 1) { + errno = EINVAL; + return -1; + } + + hmac_ctx = HMAC_CTX_new(); + if (!hmac_ctx) { + errno = ENOMEM; + return -1; + } + if (!md) { + errno = EINVAL; + return -1; + } + + psk_ctx = malloc(key_len); + if (!psk_ctx) { + errno = ENOMEM; + return -1; + } + if (!HMAC_Init_ex(hmac_ctx, retained, key_len, md, NULL)) { + errno = ENOMEM; + return -1; + } + if (!HMAC_Update(hmac_ctx, (unsigned char *)hostnqn, + strlen(hostnqn))) { + errno = ENOKEY; + return -1; + } + if (!HMAC_Update(hmac_ctx, (unsigned char *)" ", 1)) { + errno = ENOKEY; + return -1; + } + if (!HMAC_Update(hmac_ctx, (unsigned char *)subsysnqn, + strlen(subsysnqn))) { + errno = ENOKEY; + return -1; + } + if (!HMAC_Update(hmac_ctx, (unsigned char *)" ", 1)) { + errno = ENOKEY; + return -1; + } + if (!HMAC_Update(hmac_ctx, (unsigned char *)hmac_seed, + strlen(hmac_seed))) { + errno = ENOKEY; + return -1; + } + if (!HMAC_Final(hmac_ctx, psk_ctx, (unsigned int *)&key_len)) { + errno = ENOKEY; + return -1; + } + enc_ctx = malloc(key_len * 2); + memset(enc_ctx, 0, key_len * 2); + len = base64_encode(psk_ctx, key_len, enc_ctx); + if (len < 0) { + errno = ENOKEY; + return len; + } + sprintf(identity, "NVMe%01dR%02d %s %s %s", + version, hmac, hostnqn, subsysnqn, enc_ctx); + return strlen(identity); } #endif /* !CONFIG_OPENSSL_1 */ #ifdef CONFIG_OPENSSL_3 +static DEFINE_CLEANUP_FUNC( + cleanup_ossl_lib_ctx, OSSL_LIB_CTX *, OSSL_LIB_CTX_free) +#define _cleanup_ossl_lib_ctx_ __cleanup__(cleanup_ossl_lib_ctx) +static DEFINE_CLEANUP_FUNC(cleanup_evp_mac_ctx, EVP_MAC_CTX *, EVP_MAC_CTX_free) +#define _cleanup_evp_mac_ctx_ __cleanup__(cleanup_evp_mac_ctx) +static DEFINE_CLEANUP_FUNC(cleanup_evp_mac, EVP_MAC *, EVP_MAC_free) +#define _cleanup_evp_mac_ __cleanup__(cleanup_evp_mac) + int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac, unsigned int key_len, unsigned char *secret, unsigned char *key) { const char hmac_seed[] = "NVMe-over-Fabrics"; OSSL_PARAM params[2], *p = params; - OSSL_LIB_CTX *lib_ctx; - EVP_MAC_CTX *mac_ctx = NULL; - EVP_MAC *mac = NULL; + _cleanup_ossl_lib_ctx_ OSSL_LIB_CTX *lib_ctx; + _cleanup_evp_mac_ctx_ EVP_MAC_CTX *mac_ctx = NULL; + _cleanup_evp_mac_ EVP_MAC *mac = NULL; char *progq = NULL; char *digest; size_t len; - int err = -1; lib_ctx = OSSL_LIB_CTX_new(); if (!lib_ctx) { errno = ENOMEM; - return err; + return -1; } mac = EVP_MAC_fetch(lib_ctx, OSSL_MAC_NAME_HMAC, progq); if (!mac) { errno = ENOMEM; - goto out; + return -1; } mac_ctx = EVP_MAC_CTX_new(mac); if (!mac_ctx) { errno = ENOMEM; - goto out; + return -1; } switch (hmac) { case NVME_HMAC_ALG_NONE: memcpy(key, secret, key_len); - err = 0; - goto out; + return 0; case NVME_HMAC_ALG_SHA2_256: digest = OSSL_DIGEST_NAME_SHA2_256; break; @@ -735,7 +921,7 @@ int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac, break; default: errno = EINVAL; - goto out; + return -1; } *p++ = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, digest, @@ -744,42 +930,224 @@ int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac, if (!EVP_MAC_init(mac_ctx, secret, key_len, params)) { errno = ENOKEY; - goto out; + return -1; } if (!EVP_MAC_update(mac_ctx, (unsigned char *)hostnqn, strlen(hostnqn))) { errno = ENOKEY; - goto out; + return -1; } if (!EVP_MAC_update(mac_ctx, (unsigned char *)hmac_seed, strlen(hmac_seed))) { errno = ENOKEY; - goto out; + return -1; } if (!EVP_MAC_final(mac_ctx, key, &len, key_len)) { errno = ENOKEY; - goto out; + return -1; } if (len != key_len) { errno = EMSGSIZE; - goto out; + return -1; } - err = 0; + return 0; +} -out: - EVP_MAC_CTX_free(mac_ctx); - EVP_MAC_free(mac); - OSSL_LIB_CTX_free(lib_ctx); +static int gen_tls_identity(const char *hostnqn, const char *subsysnqn, + int version, int hmac, char *identity, + unsigned char *retained, size_t key_len) +{ + static const char hmac_seed[] = "NVMe-over-Fabrics"; + size_t hmac_len; + OSSL_PARAM params[2], *p = params; + _cleanup_ossl_lib_ctx_ OSSL_LIB_CTX *lib_ctx = NULL; + _cleanup_evp_mac_ctx_ EVP_MAC_CTX *mac_ctx = NULL; + _cleanup_evp_mac_ EVP_MAC *mac = NULL; + char *progq = NULL; + char *digest = NULL; + _cleanup_free_ unsigned char *psk_ctx = NULL; + _cleanup_free_ char *enc_ctx = NULL; + size_t len; - return err; + if (version == 0) { + sprintf(identity, "NVMe%01dR%02d %s %s", + version, hmac, hostnqn, subsysnqn); + return strlen(identity); + } + if (version > 1) { + errno = EINVAL; + return -1; + } + + lib_ctx = OSSL_LIB_CTX_new(); + if (!lib_ctx) { + errno = ENOMEM; + return -1; + } + mac = EVP_MAC_fetch(lib_ctx, OSSL_MAC_NAME_HMAC, progq); + if (!mac) { + errno = ENOMEM; + return -1; + } + + mac_ctx = EVP_MAC_CTX_new(mac); + if (!mac_ctx) { + errno = ENOMEM; + return -1; + } + switch (hmac) { + case NVME_HMAC_ALG_SHA2_256: + digest = OSSL_DIGEST_NAME_SHA2_256; + break; + case NVME_HMAC_ALG_SHA2_384: + digest = OSSL_DIGEST_NAME_SHA2_384; + break; + default: + errno = EINVAL; + break; + } + if (!digest) + return -1; + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, + digest, 0); + *p = OSSL_PARAM_construct_end(); + + psk_ctx = malloc(key_len); + if (!psk_ctx) { + errno = ENOMEM; + return -1; + } + + if (!EVP_MAC_init(mac_ctx, retained, key_len, params)) { + errno = ENOKEY; + return -1; + } + if (!EVP_MAC_update(mac_ctx, (unsigned char *)hostnqn, + strlen(hostnqn))) { + errno = ENOKEY; + return -1; + } + if (!EVP_MAC_update(mac_ctx, (unsigned char *)" ", 1)) { + errno = ENOKEY; + return -1; + } + if (!EVP_MAC_update(mac_ctx, (unsigned char *)subsysnqn, + strlen(subsysnqn))) { + errno = ENOKEY; + return -1; + } + if (!EVP_MAC_update(mac_ctx, (unsigned char *)" ", 1)) { + errno = ENOKEY; + return -1; + } + if (!EVP_MAC_update(mac_ctx, (unsigned char *)hmac_seed, + strlen(hmac_seed))) { + errno = ENOKEY; + return -1; + } + if (!EVP_MAC_final(mac_ctx, psk_ctx, &hmac_len, key_len)) { + errno = ENOKEY; + return -1; + } + if (hmac_len > key_len) { + errno = EMSGSIZE; + return -1; + } + enc_ctx = malloc(hmac_len * 2); + memset(enc_ctx, 0, hmac_len * 2); + len = base64_encode(psk_ctx, hmac_len, enc_ctx); + if (len < 0) { + errno = ENOKEY; + return len; + } + sprintf(identity, "NVMe%01dR%02d %s %s %s", + version, hmac, hostnqn, subsysnqn, enc_ctx); + return strlen(identity); } #endif /* !CONFIG_OPENSSL_3 */ +static int derive_nvme_keys(const char *hostnqn, const char *subsysnqn, + char *identity, int version, + int hmac, unsigned char *configured, + unsigned char *psk, int key_len) +{ + _cleanup_free_ unsigned char *retained = NULL; + int ret = -1; + + if (!hostnqn || !subsysnqn || !identity || !psk) { + errno = EINVAL; + return -1; + } + + retained = malloc(key_len); + if (!retained) { + errno = ENOMEM; + return -1; + } + ret = derive_retained_key(hmac, hostnqn, configured, retained, key_len); + if (ret < 0) + return ret; + ret = gen_tls_identity(hostnqn, subsysnqn, version, hmac, + identity, retained, key_len); + if (ret < 0) + return ret; + return derive_tls_key(hmac, identity, retained, psk, key_len); +} + +static size_t nvme_identity_len(int hmac, int version, const char *hostnqn, + const char *subsysnqn) +{ + size_t len; + + len = strlen(hostnqn) + strlen(subsysnqn) + 12; + if (version == 1) { + len += 66; + if (hmac == NVME_HMAC_ALG_SHA2_384) + len += 32; + } else if (version > 1) { + errno = EINVAL; + return -1; + } + return len; +} + +char *nvme_generate_tls_key_identity(const char *hostnqn, const char *subsysnqn, + int version, int hmac, + unsigned char *configured_key, int key_len) +{ + char *identity; + size_t identity_len; + _cleanup_free_ unsigned char *psk = NULL; + int ret = -1; + + identity_len = nvme_identity_len(hmac, version, hostnqn, subsysnqn); + if (identity_len < 0) + return NULL; + + identity = malloc(identity_len); + if (!identity) + return NULL; + + psk = malloc(key_len); + if (!psk) + goto out_free_identity; + + memset(psk, 0, key_len); + ret = derive_nvme_keys(hostnqn, subsysnqn, identity, version, hmac, + configured_key, psk, key_len); +out_free_identity: + if (ret < 0) { + free(identity); + identity = NULL; + } + return identity; +} + #ifdef CONFIG_KEYUTILS long nvme_lookup_keyring(const char *keyring) { @@ -820,37 +1188,41 @@ int nvme_set_keyring(long key_id) return 0; } -long nvme_insert_tls_key(const char *keyring, const char *key_type, - const char *hostnqn, const char *subsysnqn, int hmac, - unsigned char *configured_key, int key_len) +long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type, + const char *hostnqn, const char *subsysnqn, + int version, int hmac, + unsigned char *configured_key, int key_len) { - key_serial_t keyring_id, key = 0; - char *identity; - unsigned char *psk; + key_serial_t keyring_id, key; + _cleanup_free_ char *identity = NULL; + size_t identity_len; + _cleanup_free_ unsigned char *psk = NULL; int ret = -1; keyring_id = nvme_lookup_keyring(keyring); if (keyring_id == 0) return -1; - identity = malloc(strlen(hostnqn) + strlen(subsysnqn) + 12); + identity_len = nvme_identity_len(hmac, version, hostnqn, subsysnqn); + if (identity_len < 0) + return -1; + + identity = malloc(identity_len); if (!identity) { errno = ENOMEM; return -1; } - sprintf(identity, "NVMe0R%02d %s %s", hmac, hostnqn, subsysnqn); - psk = malloc(key_len); if (!psk) { errno = ENOMEM; - goto out_free_identity; + return 0; } memset(psk, 0, key_len); - ret = derive_nvme_keys(hostnqn, identity, hmac, + ret = derive_nvme_keys(hostnqn, subsysnqn, identity, version, hmac, configured_key, psk, key_len); if (ret != key_len) - goto out_free_psk; + return 0; key = keyctl_search(keyring_id, key_type, identity, 0); if (key > 0) { @@ -862,10 +1234,6 @@ long nvme_insert_tls_key(const char *keyring, const char *key_type, if (key < 0) key = 0; } -out_free_psk: - free(psk); -out_free_identity: - free(identity); return key; } @@ -902,10 +1270,23 @@ int nvme_set_keyring(long key_id) return -1; } +long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type, + const char *hostnqn, const char *subsysnqn, + int version, int hmac, + unsigned char *configured_key, int key_len) +{ + nvme_msg(NULL, LOG_ERR, "key operations not supported; " + "recompile with keyutils support.\n"); + errno = ENOTSUP; + return -1; +} +#endif + long nvme_insert_tls_key(const char *keyring, const char *key_type, const char *hostnqn, const char *subsysnqn, int hmac, unsigned char *configured_key, int key_len) { - return derive_nvme_keys(NULL, NULL, 0, NULL, NULL, 0); + return nvme_insert_tls_key_versioned(keyring, key_type, + hostnqn, subsysnqn, 0, hmac, + configured_key, key_len); } -#endif |