summaryrefslogtreecommitdiffstats
path: root/src/nvme/linux.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvme/linux.c')
-rw-r--r--src/nvme/linux.c292
1 files changed, 268 insertions, 24 deletions
diff --git a/src/nvme/linux.c b/src/nvme/linux.c
index 163086e..25196fd 100644
--- a/src/nvme/linux.c
+++ b/src/nvme/linux.c
@@ -31,6 +31,8 @@
#ifdef CONFIG_KEYUTILS
#include <keyutils.h>
+
+#define NVME_TLS_DEFAULT_KEYRING ".nvme"
#endif
#include <ccan/endian/endian.h>
@@ -41,6 +43,7 @@
#include "log.h"
#include "private.h"
#include "base64.h"
+#include "crc32.h"
static int __nvme_open(const char *name)
{
@@ -124,7 +127,7 @@ int nvme_fw_download_seq(int fd, __u32 size, __u32 xfer, __u32 offset,
int nvme_get_telemetry_max(int fd, enum nvme_telemetry_da *da, size_t *data_tx)
{
- _cleanup_free_ struct nvme_id_ctrl *id_ctrl;
+ _cleanup_free_ struct nvme_id_ctrl *id_ctrl = NULL;
int err;
id_ctrl = __nvme_alloc(sizeof(*id_ctrl));
@@ -183,7 +186,7 @@ int nvme_get_telemetry_log(int fd, bool create, bool ctrl, bool rae, size_t max_
*size = 0;
- log = malloc(xfer);
+ log = __nvme_alloc(xfer);
if (!log) {
errno = ENOMEM;
return -1;
@@ -236,7 +239,7 @@ int nvme_get_telemetry_log(int fd, bool create, bool ctrl, bool rae, size_t max_
}
*size = (dalb + 1) * xfer;
- tmp = realloc(log, *size);
+ tmp = __nvme_realloc(log, *size);
if (!tmp) {
errno = ENOMEM;
return -1;
@@ -385,7 +388,7 @@ int nvme_namespace_detach_ctrls(int fd, __u32 nsid, __u16 num_ctrls,
int nvme_get_ana_log_len(int fd, size_t *analen)
{
- _cleanup_free_ struct nvme_id_ctrl *ctrl;
+ _cleanup_free_ struct nvme_id_ctrl *ctrl = NULL;
int ret;
ctrl = __nvme_alloc(sizeof(*ctrl));
@@ -405,7 +408,7 @@ int nvme_get_ana_log_len(int fd, size_t *analen)
int nvme_get_logical_block_size(int fd, __u32 nsid, int *blksize)
{
- _cleanup_free_ struct nvme_id_ns *ns;
+ _cleanup_free_ struct nvme_id_ns *ns = NULL;
__u8 flbas;
int ret;
@@ -426,7 +429,7 @@ int nvme_get_logical_block_size(int fd, __u32 nsid, int *blksize)
static int __nvme_set_attr(const char *path, const char *value)
{
- _cleanup_fd_ int fd;
+ _cleanup_fd_ int fd = -1;
fd = open(path, O_WRONLY);
if (fd < 0) {
@@ -725,7 +728,7 @@ int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac,
unsigned char *key)
{
const char hmac_seed[] = "NVMe-over-Fabrics";
- _cleanup_hmac_ctx_ HMAC_CTX *hmac_ctx;
+ _cleanup_hmac_ctx_ HMAC_CTX *hmac_ctx = NULL;
const EVP_MD *md;
ENGINE_load_builtin_engines();
@@ -881,7 +884,7 @@ int nvme_gen_dhchap_key(char *hostnqn, enum nvme_hmac_alg hmac,
{
const char hmac_seed[] = "NVMe-over-Fabrics";
OSSL_PARAM params[2], *p = params;
- _cleanup_ossl_lib_ctx_ OSSL_LIB_CTX *lib_ctx;
+ _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;
@@ -1104,6 +1107,11 @@ static size_t nvme_identity_len(int hmac, int version, const char *hostnqn,
{
size_t len;
+ if (!hostnqn || !subsysnqn) {
+ errno = EINVAL;
+ return -1;
+ }
+
len = strlen(hostnqn) + strlen(subsysnqn) + 12;
if (version == 1) {
len += 66;
@@ -1153,6 +1161,8 @@ long nvme_lookup_keyring(const char *keyring)
{
key_serial_t keyring_id;
+ if (!keyring)
+ keyring = NVME_TLS_DEFAULT_KEYRING;
keyring_id = find_key_by_type_and_desc("keyring", keyring, 0);
if (keyring_id < 0)
return 0;
@@ -1182,12 +1192,119 @@ int nvme_set_keyring(long key_id)
{
long err;
+ if (key_id == 0) {
+ key_id = nvme_lookup_keyring(NULL);
+ if (key_id == 0) {
+ errno = ENOKEY;
+ return -1;
+ }
+ }
+
err = keyctl_link(key_id, KEY_SPEC_SESSION_KEYRING);
if (err < 0)
return -1;
return 0;
}
+unsigned char *nvme_read_key(long keyring_id, long key_id, int *len)
+{
+ void *buffer;
+ int ret;
+
+ ret = nvme_set_keyring(keyring_id);
+ if (ret < 0) {
+ errno = -ret;
+ return NULL;
+ }
+ ret = keyctl_read_alloc(key_id, &buffer);
+ if (ret < 0) {
+ errno = -ret;
+ buffer = NULL;
+ } else
+ *len = ret;
+
+ return buffer;
+}
+
+long nvme_update_key(long keyring_id, const char *key_type,
+ const char *identity, unsigned char *key_data,
+ int key_len)
+{
+ long key;
+
+ key = keyctl_search(keyring_id, key_type, identity, 0);
+ if (key > 0) {
+ if (keyctl_revoke(key) < 0)
+ return 0;
+ }
+ key = add_key(key_type, identity,
+ key_data, key_len, keyring_id);
+ if (key < 0)
+ key = 0;
+ return key;
+}
+
+struct __scan_keys_data {
+ nvme_scan_tls_keys_cb_t cb;
+ key_serial_t keyring;
+ void *data;
+};
+
+int __scan_keys_cb(key_serial_t parent, key_serial_t key,
+ char *desc, int desc_len, void *data)
+{
+ struct __scan_keys_data *d = data;
+ int ver, hmac, uid, gid, perm;
+ char type, *ptr;
+
+ if (desc_len < 6)
+ return 0;
+ if (sscanf(desc, "psk;%d;%d;%08x;NVMe%01d%c%02d %*s",
+ &uid, &gid, &perm, &ver, &type, &hmac) != 6)
+ return 0;
+ /* skip key type */
+ ptr = strchr(desc, ';');
+ if (!ptr)
+ return 0;
+ /* skip key uid */
+ ptr = strchr(ptr + 1, ';');
+ if (!ptr)
+ return 0;
+ /* skip key gid */
+ ptr = strchr(ptr + 1, ';');
+ if (!ptr)
+ return 0;
+ /* skip key permissions */
+ ptr = strchr(ptr + 1, ';');
+ if (!ptr)
+ return 0;
+ /* Only use the key description for the callback */
+ (d->cb)(d->keyring, key, ptr + 1, strlen(ptr) - 1, d->data);
+ return 1;
+}
+
+int nvme_scan_tls_keys(const char *keyring, nvme_scan_tls_keys_cb_t cb,
+ void *data)
+{
+ struct __scan_keys_data d;
+ key_serial_t keyring_id = nvme_lookup_keyring(keyring);
+ int ret;
+
+ if (!keyring_id) {
+ errno = EINVAL;
+ return -1;
+ }
+ ret = nvme_set_keyring(keyring_id);
+ if (ret < 0)
+ return ret;
+
+ d.keyring = keyring_id;
+ d.cb = cb;
+ d.data = data;
+ ret = recursive_key_scan(keyring_id, __scan_keys_cb, &d);
+ return ret;
+}
+
long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type,
const char *hostnqn, const char *subsysnqn,
int version, int hmac,
@@ -1197,21 +1314,28 @@ long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type,
_cleanup_free_ char *identity = NULL;
size_t identity_len;
_cleanup_free_ unsigned char *psk = NULL;
- int ret = -1;
+ int ret;
keyring_id = nvme_lookup_keyring(keyring);
- if (keyring_id == 0)
- return -1;
+ if (keyring_id == 0) {
+ errno = ENOKEY;
+ return 0;
+ }
+
+ ret = nvme_set_keyring(keyring_id);
+ if (ret < 0)
+ return 0;
identity_len = nvme_identity_len(hmac, version, hostnqn, subsysnqn);
if (identity_len < 0)
- return -1;
+ return 0;
identity = malloc(identity_len);
if (!identity) {
errno = ENOMEM;
- return -1;
+ return 0;
}
+ memset(identity, 0, identity_len);
psk = malloc(key_len);
if (!psk) {
@@ -1221,19 +1345,13 @@ long nvme_insert_tls_key_versioned(const char *keyring, const char *key_type,
memset(psk, 0, key_len);
ret = derive_nvme_keys(hostnqn, subsysnqn, identity, version, hmac,
configured_key, psk, key_len);
- if (ret != key_len)
+ if (ret != key_len) {
+ errno = ENOKEY;
return 0;
-
- key = keyctl_search(keyring_id, key_type, identity, 0);
- if (key > 0) {
- if (keyctl_update(key, psk, key_len) < 0)
- key = 0;
- } else {
- key = add_key(key_type, identity,
- psk, key_len, keyring_id);
- if (key < 0)
- key = 0;
}
+
+ key = nvme_update_key(keyring_id, key_type, identity,
+ psk, key_len);
return key;
}
@@ -1270,6 +1388,27 @@ int nvme_set_keyring(long key_id)
return -1;
}
+unsigned char *nvme_read_key(long keyring_id, long key_id, int *len)
+{
+ errno = ENOTSUP;
+ return NULL;
+}
+
+long nvme_update_key(long keyring_id, const char *key_type,
+ const char *identity, unsigned char *key_data,
+ int key_len)
+{
+ errno = ENOTSUP;
+ return 0;
+}
+
+int nvme_scan_tls_keys(const char *keyring, nvme_scan_tls_keys_cb_t cb,
+ void *data)
+{
+ errno = ENOTSUP;
+ 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,
@@ -1290,3 +1429,108 @@ long nvme_insert_tls_key(const char *keyring, const char *key_type,
hostnqn, subsysnqn, 0, hmac,
configured_key, key_len);
}
+
+char *nvme_export_tls_key(const unsigned char *key_data, int key_len)
+{
+ unsigned char raw_secret[52];
+ char *encoded_key;
+ unsigned int raw_len, encoded_len, len;
+ unsigned long crc = crc32(0L, NULL, 0);
+
+ if (key_len == 32) {
+ raw_len = 32;
+ } else if (key_len == 48) {
+ raw_len = 48;
+ } else {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ memcpy(raw_secret, key_data, raw_len);
+ crc = crc32(crc, raw_secret, raw_len);
+ raw_secret[raw_len++] = crc & 0xff;
+ raw_secret[raw_len++] = (crc >> 8) & 0xff;
+ raw_secret[raw_len++] = (crc >> 16) & 0xff;
+ raw_secret[raw_len++] = (crc >> 24) & 0xff;
+
+ encoded_len = (raw_len * 2) + 20;
+ encoded_key = malloc(encoded_len);
+ if (!encoded_key) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ memset(encoded_key, 0, encoded_len);
+ len = sprintf(encoded_key, "NVMeTLSkey-1:%02x:",
+ key_len == 32 ? 1 : 2);
+ len += base64_encode(raw_secret, raw_len, encoded_key + len);
+ encoded_key[len++] = ':';
+ encoded_key[len++] = '\0';
+
+ return encoded_key;
+}
+
+unsigned char *nvme_import_tls_key(const char *encoded_key, int *key_len,
+ unsigned int *hmac)
+{
+ unsigned char decoded_key[128], *key_data;
+ unsigned int crc = crc32(0L, NULL, 0);
+ unsigned int key_crc;
+ int err, decoded_len;
+
+ if (sscanf(encoded_key, "NVMeTLSkey-1:%02x:*s", &err) != 1) {
+ errno = EINVAL;
+ return NULL;
+ }
+ switch (err) {
+ case 1:
+ if (strlen(encoded_key) != 65) {
+ errno = EINVAL;
+ return NULL;
+ }
+ break;
+ case 2:
+ if (strlen(encoded_key) != 89) {
+ errno = EINVAL;
+ return NULL;
+ }
+ break;
+ default:
+ errno = EINVAL;
+ return NULL;
+ }
+
+ *hmac = err;
+ err = base64_decode(encoded_key + 16, strlen(encoded_key) - 17,
+ decoded_key);
+ if (err < 0) {
+ errno = ENOKEY;
+ return NULL;
+ }
+ decoded_len = err;
+ decoded_len -= 4;
+ if (decoded_len != 32 && decoded_len != 48) {
+ errno = ENOKEY;
+ return NULL;
+ }
+ crc = crc32(crc, decoded_key, decoded_len);
+ key_crc = ((u_int32_t)decoded_key[decoded_len]) |
+ ((u_int32_t)decoded_key[decoded_len + 1] << 8) |
+ ((u_int32_t)decoded_key[decoded_len + 2] << 16) |
+ ((u_int32_t)decoded_key[decoded_len + 3] << 24);
+ if (key_crc != crc) {
+ nvme_msg(NULL, LOG_ERR, "CRC mismatch (key %08x, crc %08x)",
+ key_crc, crc);
+ errno = ENOKEY;
+ return NULL;
+ }
+
+ key_data = malloc(decoded_len);
+ if (!key_data) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ memcpy(key_data, decoded_key, decoded_len);
+
+ *key_len = decoded_len;
+ return key_data;
+}