diff options
Diffstat (limited to 'src/nvme/linux.c')
-rw-r--r-- | src/nvme/linux.c | 275 |
1 files changed, 257 insertions, 18 deletions
diff --git a/src/nvme/linux.c b/src/nvme/linux.c index e29d9e7..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) { @@ -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; @@ -1158,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; @@ -1187,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, @@ -1202,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) { @@ -1226,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; } @@ -1275,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, @@ -1295,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; +} |