| 1 | |
| 2 | |
| 3 | * Copyright (c) 2023 Hannes Reinecke, SUSE Labs
|
| 4 |
|
| 5 |
|
| 6 | #include <linux/module.h>
|
| 7 | #include <linux/seq_file.h>
|
| 8 | #include <linux/key-type.h>
|
| 9 | #include <keys/user-type.h>
|
| 10 | #include <linux/nvme.h>
|
| 11 | #include <linux/nvme-tcp.h>
|
| 12 | #include <linux/nvme-keyring.h>
|
| 13 |
|
| 14 | static struct key *nvme_keyring;
|
| 15 |
|
| 16 | key_serial_t nvme_keyring_id(void)
|
| 17 | {
|
| 18 | return nvme_keyring->serial;
|
| 19 | }
|
| 20 | EXPORT_SYMBOL_GPL(nvme_keyring_id);
|
| 21 |
|
| 22 | static bool nvme_tls_psk_revoked(struct key *psk)
|
| 23 | {
|
| 24 | return test_bit(KEY_FLAG_REVOKED, &psk->flags) ||
|
| 25 | test_bit(KEY_FLAG_INVALIDATED, &psk->flags);
|
| 26 | }
|
| 27 |
|
| 28 | struct key *nvme_tls_key_lookup(key_serial_t key_id)
|
| 29 | {
|
| 30 | struct key *key = key_lookup(key_id);
|
| 31 |
|
| 32 | if (IS_ERR(key)) {
|
| 33 | pr_err("key id %08x not found\n", key_id);
|
| 34 | return key;
|
| 35 | }
|
| 36 | if (nvme_tls_psk_revoked(key)) {
|
| 37 | pr_err("key id %08x revoked\n", key_id);
|
| 38 | return ERR_PTR(-EKEYREVOKED);
|
| 39 | }
|
| 40 | return key;
|
| 41 | }
|
| 42 | EXPORT_SYMBOL_GPL(nvme_tls_key_lookup);
|
| 43 |
|
| 44 | static void nvme_tls_psk_describe(const struct key *key, struct seq_file *m)
|
| 45 | {
|
| 46 | seq_puts(m, key->description);
|
| 47 | seq_printf(m, ": %u", key->datalen);
|
| 48 | }
|
| 49 |
|
| 50 | static bool nvme_tls_psk_match(const struct key *key,
|
| 51 | const struct key_match_data *match_data)
|
| 52 | {
|
| 53 | const char *match_id;
|
| 54 | size_t match_len;
|
| 55 |
|
| 56 | if (!key->description) {
|
| 57 | pr_debug("%s: no key description\n", __func__);
|
| 58 | return false;
|
| 59 | }
|
| 60 | if (!match_data->raw_data) {
|
| 61 | pr_debug("%s: no match data\n", __func__);
|
| 62 | return false;
|
| 63 | }
|
| 64 | match_id = match_data->raw_data;
|
| 65 | match_len = strlen(match_id);
|
| 66 | pr_debug("%s: match '%s' '%s' len %zd\n",
|
| 67 | __func__, match_id, key->description, match_len);
|
| 68 | return !memcmp(key->description, match_id, match_len);
|
| 69 | }
|
| 70 |
|
| 71 | static int nvme_tls_psk_match_preparse(struct key_match_data *match_data)
|
| 72 | {
|
| 73 | match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE;
|
| 74 | match_data->cmp = nvme_tls_psk_match;
|
| 75 | return 0;
|
| 76 | }
|
| 77 |
|
| 78 | static struct key_type nvme_tls_psk_key_type = {
|
| 79 | .name = "psk",
|
| 80 | .flags = KEY_TYPE_NET_DOMAIN,
|
| 81 | .preparse = user_preparse,
|
| 82 | .free_preparse = user_free_preparse,
|
| 83 | .match_preparse = nvme_tls_psk_match_preparse,
|
| 84 | .instantiate = generic_key_instantiate,
|
| 85 | .revoke = user_revoke,
|
| 86 | .destroy = user_destroy,
|
| 87 | .describe = nvme_tls_psk_describe,
|
| 88 | .read = user_read,
|
| 89 | };
|
| 90 |
|
| 91 | static struct key *nvme_tls_psk_lookup(struct key *keyring,
|
| 92 | const char *hostnqn, const char *subnqn,
|
| 93 | u8 hmac, u8 psk_ver, bool generated)
|
| 94 | {
|
| 95 | char *identity;
|
| 96 | size_t identity_len = (NVMF_NQN_SIZE) * 2 + 11;
|
| 97 | key_ref_t keyref;
|
| 98 | key_serial_t keyring_id;
|
| 99 |
|
| 100 | identity = kzalloc(identity_len, GFP_KERNEL);
|
| 101 | if (!identity)
|
| 102 | return ERR_PTR(-ENOMEM);
|
| 103 |
|
| 104 | snprintf(identity, identity_len, "NVMe%u%c%02u %s %s",
|
| 105 | psk_ver, generated ? 'G' : 'R', hmac, hostnqn, subnqn);
|
| 106 |
|
| 107 | if (!keyring)
|
| 108 | keyring = nvme_keyring;
|
| 109 | keyring_id = key_serial(keyring);
|
| 110 | pr_debug("keyring %x lookup tls psk '%s'\n",
|
| 111 | keyring_id, identity);
|
| 112 | keyref = keyring_search(make_key_ref(keyring, true),
|
| 113 | &nvme_tls_psk_key_type,
|
| 114 | identity, false);
|
| 115 | if (IS_ERR(keyref)) {
|
| 116 | pr_debug("lookup tls psk '%s' failed, error %ld\n",
|
| 117 | identity, PTR_ERR(keyref));
|
| 118 | kfree(identity);
|
| 119 | return ERR_PTR(-ENOKEY);
|
| 120 | }
|
| 121 | kfree(identity);
|
| 122 |
|
| 123 | return key_ref_to_ptr(keyref);
|
| 124 | }
|
| 125 |
|
| 126 | |
| 127 | * nvme_tls_psk_refresh - Refresh TLS PSK
|
| 128 | * @keyring: Keyring holding the TLS PSK
|
| 129 | * @hostnqn: Host NQN to use
|
| 130 | * @subnqn: Subsystem NQN to use
|
| 131 | * @hmac_id: Hash function identifier
|
| 132 | * @data: TLS PSK key material
|
| 133 | * @data_len: Length of @data
|
| 134 | * @digest: TLS PSK digest
|
| 135 | *
|
| 136 | * Refresh a generated version 1 TLS PSK with the identity generated
|
| 137 | * from @hmac_id, @hostnqn, @subnqn, and @digest in the keyring given
|
| 138 | * by @keyring.
|
| 139 | *
|
| 140 | * Returns the updated key success or an error pointer otherwise.
|
| 141 |
|
| 142 | struct key *nvme_tls_psk_refresh(struct key *keyring,
|
| 143 | const char *hostnqn, const char *subnqn, u8 hmac_id,
|
| 144 | u8 *data, size_t data_len, const char *digest)
|
| 145 | {
|
| 146 | key_perm_t keyperm =
|
| 147 | KEY_POS_SEARCH | KEY_POS_VIEW | KEY_POS_READ |
|
| 148 | KEY_POS_WRITE | KEY_POS_LINK | KEY_POS_SETATTR |
|
| 149 | KEY_USR_SEARCH | KEY_USR_VIEW | KEY_USR_READ;
|
| 150 | char *identity;
|
| 151 | key_ref_t keyref;
|
| 152 | key_serial_t keyring_id;
|
| 153 | struct key *key;
|
| 154 |
|
| 155 | if (!hostnqn || !subnqn || !data || !data_len)
|
| 156 | return ERR_PTR(-EINVAL);
|
| 157 |
|
| 158 | identity = kasprintf(GFP_KERNEL, "NVMe1G%02d %s %s %s",
|
| 159 | hmac_id, hostnqn, subnqn, digest);
|
| 160 | if (!identity)
|
| 161 | return ERR_PTR(-ENOMEM);
|
| 162 |
|
| 163 | if (!keyring)
|
| 164 | keyring = nvme_keyring;
|
| 165 | keyring_id = key_serial(keyring);
|
| 166 | pr_debug("keyring %x refresh tls psk '%s'\n",
|
| 167 | keyring_id, identity);
|
| 168 | keyref = key_create_or_update(make_key_ref(keyring, true),
|
| 169 | "psk", identity, data, data_len,
|
| 170 | keyperm, KEY_ALLOC_NOT_IN_QUOTA |
|
| 171 | KEY_ALLOC_BUILT_IN |
|
| 172 | KEY_ALLOC_BYPASS_RESTRICTION);
|
| 173 | if (IS_ERR(keyref)) {
|
| 174 | pr_debug("refresh tls psk '%s' failed, error %ld\n",
|
| 175 | identity, PTR_ERR(keyref));
|
| 176 | kfree(identity);
|
| 177 | return ERR_PTR(-ENOKEY);
|
| 178 | }
|
| 179 | kfree(identity);
|
| 180 | |
| 181 | * Set the default timeout to 1 hour
|
| 182 | * as suggested in TP8018.
|
| 183 |
|
| 184 | key = key_ref_to_ptr(keyref);
|
| 185 | key_set_timeout(key, 3600);
|
| 186 | return key;
|
| 187 | }
|
| 188 | EXPORT_SYMBOL_GPL(nvme_tls_psk_refresh);
|
| 189 |
|
| 190 | |
| 191 | * NVMe PSK priority list
|
| 192 | *
|
| 193 | * 'Retained' PSKs (ie 'generated == false') should be preferred to 'generated'
|
| 194 | * PSKs, PSKs with hash (psk_ver 1) should be preferred to PSKs without hash
|
| 195 | * (psk_ver 0), and SHA-384 should be preferred to SHA-256.
|
| 196 |
|
| 197 | static struct nvme_tls_psk_priority_list {
|
| 198 | bool generated;
|
| 199 | u8 psk_ver;
|
| 200 | enum nvme_tcp_tls_cipher cipher;
|
| 201 | } nvme_tls_psk_prio[] = {
|
| 202 | { .generated = false,
|
| 203 | .psk_ver = 1,
|
| 204 | .cipher = NVME_TCP_TLS_CIPHER_SHA384, },
|
| 205 | { .generated = false,
|
| 206 | .psk_ver = 1,
|
| 207 | .cipher = NVME_TCP_TLS_CIPHER_SHA256, },
|
| 208 | { .generated = false,
|
| 209 | .psk_ver = 0,
|
| 210 | .cipher = NVME_TCP_TLS_CIPHER_SHA384, },
|
| 211 | { .generated = false,
|
| 212 | .psk_ver = 0,
|
| 213 | .cipher = NVME_TCP_TLS_CIPHER_SHA256, },
|
| 214 | { .generated = true,
|
| 215 | .psk_ver = 1,
|
| 216 | .cipher = NVME_TCP_TLS_CIPHER_SHA384, },
|
| 217 | { .generated = true,
|
| 218 | .psk_ver = 1,
|
| 219 | .cipher = NVME_TCP_TLS_CIPHER_SHA256, },
|
| 220 | { .generated = true,
|
| 221 | .psk_ver = 0,
|
| 222 | .cipher = NVME_TCP_TLS_CIPHER_SHA384, },
|
| 223 | { .generated = true,
|
| 224 | .psk_ver = 0,
|
| 225 | .cipher = NVME_TCP_TLS_CIPHER_SHA256, },
|
| 226 | };
|
| 227 |
|
| 228 | |
| 229 | * nvme_tls_psk_default - Return the preferred PSK to use for TLS ClientHello
|
| 230 |
|
| 231 | key_serial_t nvme_tls_psk_default(struct key *keyring,
|
| 232 | const char *hostnqn, const char *subnqn)
|
| 233 | {
|
| 234 | struct key *tls_key;
|
| 235 | key_serial_t tls_key_id;
|
| 236 | int prio;
|
| 237 |
|
| 238 | for (prio = 0; prio < ARRAY_SIZE(nvme_tls_psk_prio); prio++) {
|
| 239 | bool generated = nvme_tls_psk_prio[prio].generated;
|
| 240 | u8 ver = nvme_tls_psk_prio[prio].psk_ver;
|
| 241 | enum nvme_tcp_tls_cipher cipher = nvme_tls_psk_prio[prio].cipher;
|
| 242 |
|
| 243 | tls_key = nvme_tls_psk_lookup(keyring, hostnqn, subnqn,
|
| 244 | cipher, ver, generated);
|
| 245 | if (!IS_ERR(tls_key)) {
|
| 246 | tls_key_id = tls_key->serial;
|
| 247 | key_put(tls_key);
|
| 248 | return tls_key_id;
|
| 249 | }
|
| 250 | }
|
| 251 | return 0;
|
| 252 | }
|
| 253 | EXPORT_SYMBOL_GPL(nvme_tls_psk_default);
|
| 254 |
|
| 255 | static int __init nvme_keyring_init(void)
|
| 256 | {
|
| 257 | int err;
|
| 258 |
|
| 259 | nvme_keyring = keyring_alloc(".nvme",
|
| 260 | GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
|
| 261 | current_cred(),
|
| 262 | (KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
| 263 | (KEY_USR_ALL & ~KEY_USR_SETATTR),
|
| 264 | KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
|
| 265 | if (IS_ERR(nvme_keyring))
|
| 266 | return PTR_ERR(nvme_keyring);
|
| 267 |
|
| 268 | err = register_key_type(&nvme_tls_psk_key_type);
|
| 269 | if (err) {
|
| 270 | key_put(nvme_keyring);
|
| 271 | return err;
|
| 272 | }
|
| 273 | return 0;
|
| 274 | }
|
| 275 |
|
| 276 | static void __exit nvme_keyring_exit(void)
|
| 277 | {
|
| 278 | unregister_key_type(&nvme_tls_psk_key_type);
|
| 279 | key_revoke(nvme_keyring);
|
| 280 | key_put(nvme_keyring);
|
| 281 | }
|
| 282 |
|
| 283 | MODULE_LICENSE("GPL v2");
|
| 284 | MODULE_AUTHOR("Hannes Reinecke <[email protected]>");
|
| 285 | MODULE_DESCRIPTION("NVMe Keyring implementation");
|
| 286 | module_init(nvme_keyring_init);
|
| 287 | module_exit(nvme_keyring_exit);
|