1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2023 Hannes Reinecke, SUSE Labs
6 #include <linux/module.h>
7 #include <linux/seq_file.h>
9 #include <linux/key-type.h>
10 #include <keys/user-type.h>
11 #include <linux/nvme.h>
12 #include <linux/nvme-tcp.h>
13 #include <linux/nvme-keyring.h>
15 static struct key *nvme_keyring;
17 key_serial_t nvme_keyring_id(void)
19 return nvme_keyring->serial;
21 EXPORT_SYMBOL_GPL(nvme_keyring_id);
23 static void nvme_tls_psk_describe(const struct key *key, struct seq_file *m)
25 seq_puts(m, key->description);
26 seq_printf(m, ": %u", key->datalen);
29 static bool nvme_tls_psk_match(const struct key *key,
30 const struct key_match_data *match_data)
35 if (!key->description) {
36 pr_debug("%s: no key description\n", __func__);
39 match_len = strlen(key->description);
40 pr_debug("%s: id %s len %zd\n", __func__, key->description, match_len);
42 if (!match_data->raw_data) {
43 pr_debug("%s: no match data\n", __func__);
46 match_id = match_data->raw_data;
47 pr_debug("%s: match '%s' '%s' len %zd\n",
48 __func__, match_id, key->description, match_len);
49 return !memcmp(key->description, match_id, match_len);
52 static int nvme_tls_psk_match_preparse(struct key_match_data *match_data)
54 match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE;
55 match_data->cmp = nvme_tls_psk_match;
59 static struct key_type nvme_tls_psk_key_type = {
61 .flags = KEY_TYPE_NET_DOMAIN,
62 .preparse = user_preparse,
63 .free_preparse = user_free_preparse,
64 .match_preparse = nvme_tls_psk_match_preparse,
65 .instantiate = generic_key_instantiate,
66 .revoke = user_revoke,
67 .destroy = user_destroy,
68 .describe = nvme_tls_psk_describe,
72 static struct key *nvme_tls_psk_lookup(struct key *keyring,
73 const char *hostnqn, const char *subnqn,
74 int hmac, bool generated)
77 size_t identity_len = (NVMF_NQN_SIZE) * 2 + 11;
79 key_serial_t keyring_id;
81 identity = kzalloc(identity_len, GFP_KERNEL);
83 return ERR_PTR(-ENOMEM);
85 snprintf(identity, identity_len, "NVMe0%c%02d %s %s",
86 generated ? 'G' : 'R', hmac, hostnqn, subnqn);
89 keyring = nvme_keyring;
90 keyring_id = key_serial(keyring);
91 pr_debug("keyring %x lookup tls psk '%s'\n",
92 keyring_id, identity);
93 keyref = keyring_search(make_key_ref(keyring, true),
94 &nvme_tls_psk_key_type,
97 pr_debug("lookup tls psk '%s' failed, error %ld\n",
98 identity, PTR_ERR(keyref));
100 return ERR_PTR(-ENOKEY);
104 return key_ref_to_ptr(keyref);
108 * NVMe PSK priority list
110 * 'Retained' PSKs (ie 'generated == false')
111 * should be preferred to 'generated' PSKs,
112 * and SHA-384 should be preferred to SHA-256.
114 struct nvme_tls_psk_priority_list {
116 enum nvme_tcp_tls_cipher cipher;
117 } nvme_tls_psk_prio[] = {
118 { .generated = false,
119 .cipher = NVME_TCP_TLS_CIPHER_SHA384, },
120 { .generated = false,
121 .cipher = NVME_TCP_TLS_CIPHER_SHA256, },
123 .cipher = NVME_TCP_TLS_CIPHER_SHA384, },
125 .cipher = NVME_TCP_TLS_CIPHER_SHA256, },
129 * nvme_tls_psk_default - Return the preferred PSK to use for TLS ClientHello
131 key_serial_t nvme_tls_psk_default(struct key *keyring,
132 const char *hostnqn, const char *subnqn)
135 key_serial_t tls_key_id;
138 for (prio = 0; prio < ARRAY_SIZE(nvme_tls_psk_prio); prio++) {
139 bool generated = nvme_tls_psk_prio[prio].generated;
140 enum nvme_tcp_tls_cipher cipher = nvme_tls_psk_prio[prio].cipher;
142 tls_key = nvme_tls_psk_lookup(keyring, hostnqn, subnqn,
144 if (!IS_ERR(tls_key)) {
145 tls_key_id = tls_key->serial;
152 EXPORT_SYMBOL_GPL(nvme_tls_psk_default);
154 static int __init nvme_keyring_init(void)
158 nvme_keyring = keyring_alloc(".nvme",
159 GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
161 (KEY_POS_ALL & ~KEY_POS_SETATTR) |
162 (KEY_USR_ALL & ~KEY_USR_SETATTR),
163 KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
164 if (IS_ERR(nvme_keyring))
165 return PTR_ERR(nvme_keyring);
167 err = register_key_type(&nvme_tls_psk_key_type);
169 key_put(nvme_keyring);
175 static void __exit nvme_keyring_exit(void)
177 unregister_key_type(&nvme_tls_psk_key_type);
178 key_revoke(nvme_keyring);
179 key_put(nvme_keyring);
182 MODULE_LICENSE("GPL v2");
183 MODULE_AUTHOR("Hannes Reinecke <hare@suse.de>");
184 module_init(nvme_keyring_init);
185 module_exit(nvme_keyring_exit);