Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0
  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.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>
 14
 15static struct key *nvme_keyring;
 16
 17key_serial_t nvme_keyring_id(void)
 18{
 19	return nvme_keyring->serial;
 20}
 21EXPORT_SYMBOL_GPL(nvme_keyring_id);
 22
 23static void nvme_tls_psk_describe(const struct key *key, struct seq_file *m)
 24{
 25	seq_puts(m, key->description);
 26	seq_printf(m, ": %u", key->datalen);
 27}
 28
 29static bool nvme_tls_psk_match(const struct key *key,
 30			       const struct key_match_data *match_data)
 31{
 32	const char *match_id;
 33	size_t match_len;
 34
 35	if (!key->description) {
 36		pr_debug("%s: no key description\n", __func__);
 37		return false;
 38	}
 39	match_len = strlen(key->description);
 40	pr_debug("%s: id %s len %zd\n", __func__, key->description, match_len);
 41
 42	if (!match_data->raw_data) {
 43		pr_debug("%s: no match data\n", __func__);
 44		return false;
 45	}
 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);
 50}
 51
 52static int nvme_tls_psk_match_preparse(struct key_match_data *match_data)
 53{
 54	match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE;
 55	match_data->cmp = nvme_tls_psk_match;
 56	return 0;
 57}
 58
 59static struct key_type nvme_tls_psk_key_type = {
 60	.name           = "psk",
 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,
 69	.read           = user_read,
 70};
 71
 72static struct key *nvme_tls_psk_lookup(struct key *keyring,
 73		const char *hostnqn, const char *subnqn,
 74		int hmac, bool generated)
 75{
 76	char *identity;
 77	size_t identity_len = (NVMF_NQN_SIZE) * 2 + 11;
 78	key_ref_t keyref;
 79	key_serial_t keyring_id;
 80
 81	identity = kzalloc(identity_len, GFP_KERNEL);
 82	if (!identity)
 83		return ERR_PTR(-ENOMEM);
 84
 85	snprintf(identity, identity_len, "NVMe0%c%02d %s %s",
 86		 generated ? 'G' : 'R', hmac, hostnqn, subnqn);
 87
 88	if (!keyring)
 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,
 95				identity, false);
 96	if (IS_ERR(keyref)) {
 97		pr_debug("lookup tls psk '%s' failed, error %ld\n",
 98			 identity, PTR_ERR(keyref));
 99		kfree(identity);
100		return ERR_PTR(-ENOKEY);
101	}
102	kfree(identity);
103
104	return key_ref_to_ptr(keyref);
105}
106
107/*
108 * NVMe PSK priority list
109 *
110 * 'Retained' PSKs (ie 'generated == false')
111 * should be preferred to 'generated' PSKs,
112 * and SHA-384 should be preferred to SHA-256.
113 */
114static struct nvme_tls_psk_priority_list {
115	bool generated;
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, },
122	{ .generated = true,
123	  .cipher = NVME_TCP_TLS_CIPHER_SHA384, },
124	{ .generated = true,
125	  .cipher = NVME_TCP_TLS_CIPHER_SHA256, },
126};
127
128/*
129 * nvme_tls_psk_default - Return the preferred PSK to use for TLS ClientHello
130 */
131key_serial_t nvme_tls_psk_default(struct key *keyring,
132		      const char *hostnqn, const char *subnqn)
133{
134	struct key *tls_key;
135	key_serial_t tls_key_id;
136	int prio;
137
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;
141
142		tls_key = nvme_tls_psk_lookup(keyring, hostnqn, subnqn,
143					      cipher, generated);
144		if (!IS_ERR(tls_key)) {
145			tls_key_id = tls_key->serial;
146			key_put(tls_key);
147			return tls_key_id;
148		}
149	}
150	return 0;
151}
152EXPORT_SYMBOL_GPL(nvme_tls_psk_default);
153
154static int __init nvme_keyring_init(void)
155{
156	int err;
157
158	nvme_keyring = keyring_alloc(".nvme",
159				     GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
160				     current_cred(),
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);
166
167	err = register_key_type(&nvme_tls_psk_key_type);
168	if (err) {
169		key_put(nvme_keyring);
170		return err;
171	}
172	return 0;
173}
174
175static void __exit nvme_keyring_exit(void)
176{
177	unregister_key_type(&nvme_tls_psk_key_type);
178	key_revoke(nvme_keyring);
179	key_put(nvme_keyring);
180}
181
182MODULE_LICENSE("GPL v2");
183MODULE_AUTHOR("Hannes Reinecke <hare@suse.de>");
184MODULE_DESCRIPTION("NVMe Keyring implementation");
185module_init(nvme_keyring_init);
186module_exit(nvme_keyring_exit);