Bug Summary

File:.build-ci/../plugins/keys/keys-plugin.c
Warning:line 627, column 3
Potential leak of memory pointed to by 'key_value'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-redhat-linux-gnu -O3 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name keys-plugin.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -fcoverage-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -resource-dir /usr/bin/../lib/clang/22 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I nvme.p -I . -I .. -I src -I ../src -I ccan -I ../ccan -I libnvme/src -I ../libnvme/src -I /usr/include/json-c -D _FILE_OFFSET_BITS=64 -D _GNU_SOURCE -U NDEBUG -internal-isystem /usr/bin/../lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/16/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -std=gnu11 -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-opt-analyze-headers -analyzer-output=html -faddrsig -fdwarf2-cfi-asm -o /__w/nvme-cli/nvme-cli/.build-ci/scan-results/2026-10-24-125112-639-1 -x c ../plugins/keys/keys-plugin.c
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * This file is part of nvme-cli.
4 * Copyright (c) 2026 SUSE Software Solutions
5 *
6 * Authors: Daniel Wagner <dwagner@suse.de>
7 */
8
9#include <errno(*__errno_location ()).h>
10#include <stdio.h>
11#include <string.h>
12#include <sys/stat.h>
13
14#include <libnvme.h>
15
16#include <shared/base64-util.h>
17#include <shared/compiler-attributes-util.h>
18#include <shared/crc32-util.h>
19
20#include "cleanup.h"
21#include "global-ctx.h"
22#include "logging.h"
23#include "nvme-print.h"
24#include "plugin.h"
25
26static int read_key_value(const char *inline_value, char **out)
27{
28 char line[512];
29
30 if (inline_value) {
6
Assuming 'inline_value' is non-null
7
Taking true branch
31 *out = strdup(inline_value);
8
Memory is allocated
32 return *out ? 0 : -ENOMEM12;
9
Assuming the condition is true
10
'?' condition is true
33 }
34
35 if (!fgets(line, sizeof(line), stdinstdin))
36 return -EINVAL22;
37 line[strcspn(line, "\n")] = '\0';
38
39 *out = strdup(line);
40 return *out ? 0 : -ENOMEM12;
41}
42
43static bool_Bool valid_kxchap_secret_len(unsigned int len)
44{
45 return len == 32 || len == 48 || len == 64;
46}
47
48static int gen_kxchap(int argc, char **argv, struct command *acmd, struct plugin *plugin)
49{
50 const char *desc =
51 "Generate a KX-HMAC-CHAP secret in the DHHC-1 representation, usable for\n"
52 "NVMe In-Band Authentication.";
53 const char *secret =
54 "Optional secret (in hexadecimal characters) to be placed in the representation.";
55 const char *secret_len = "Length of the secret (32, 48, or 64 bytes).";
56 const char *hmac =
57 "Hash function the consumer is to apply to the secret (0 = none, 1 = SHA-256, 2 = SHA-384, 3 = SHA-512).";
58
59 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
60 __cleanup_free__attribute__((cleanup(shr_freep))) unsigned char *raw_secret = NULL((void*)0);
61 unsigned char key[68];
62 char encoded_key[128];
63 unsigned long crc = shr_crc32(0L, NULL((void*)0), 0);
64 int err = 0;
65
66 struct config {
67 char *secret;
68 unsigned int secret_len;
69 unsigned int hmac;
70 };
71
72 struct config cfg = {
73 .secret = NULL((void*)0),
74 .secret_len = 0,
75 .hmac = 0,
76 };
77
78 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
79 OPT_STR("secret", 's', &cfg.secret, secret){"secret", 's', "STRING", CFG_STRING, _Generic(&cfg.secret
, char ** : (&cfg.secret), const char ** : (&cfg.secret
)), ARGCONFIG_ARG_REQUIRED, secret, 0, }
,
80 OPT_UINT("secret-length", 'l', &cfg.secret_len, secret_len){"secret-length", 'l', "NUM", CFG_POSITIVE, _Generic(&cfg
.secret_len, uint32_t * : (&cfg.secret_len)), ARGCONFIG_ARG_REQUIRED
, secret_len, 0, }
,
81 OPT_UINT("hmac", 'm', &cfg.hmac, hmac){"hmac", 'm', "NUM", CFG_POSITIVE, _Generic(&cfg.hmac, uint32_t
* : (&cfg.hmac)), ARGCONFIG_ARG_REQUIRED, hmac, 0, }
,
82 OPT_END(){ ((void*)0) }
83 };
84
85 err = parse_args(argc, argv, desc, opts);
86 if (err)
87 return err;
88
89 err = nvme_create_global_ctx(&ctx);
90 if (err)
91 return err;
92
93 if (cfg.hmac > 3) {
94 nvme_show_error("Invalid HMAC identifier %u", cfg.hmac)nvme_show_message(1, "Invalid HMAC identifier %u", cfg.hmac);
95 return -EINVAL22;
96 }
97 /*
98 * The hash identifier records the transform the consumer applies; it
99 * does not constrain the secret's own length, so accept all three
100 * with any identifier. A secret given as hex is the payload, so it
101 * takes an even number of characters and fixes the length; an
102 * explicit one may only confirm it. Only a secret generated here
103 * (none given, or from a pin) falls back to the digest size.
104 */
105 if (cfg.secret && strncmp(cfg.secret, "pin:", 4)) {
106 size_t len = strlen(cfg.secret);
107
108 if (len % 2) {
109 nvme_show_error("Secret has an odd number of hexadecimal characters (%zu)",nvme_show_message(1, "Secret has an odd number of hexadecimal characters (%zu)"
, len)
110 len)nvme_show_message(1, "Secret has an odd number of hexadecimal characters (%zu)"
, len)
;
111 return -EINVAL22;
112 }
113 if (cfg.secret_len && cfg.secret_len != len / 2) {
114 nvme_show_error("Secret length %u does not match the secret given (%zu bytes)",nvme_show_message(1, "Secret length %u does not match the secret given (%zu bytes)"
, cfg.secret_len, len / 2)
115 cfg.secret_len, len / 2)nvme_show_message(1, "Secret length %u does not match the secret given (%zu bytes)"
, cfg.secret_len, len / 2)
;
116 return -EINVAL22;
117 }
118 cfg.secret_len = len / 2;
119 } else if (!cfg.secret_len) {
120 switch (cfg.hmac) {
121 case 1:
122 cfg.secret_len = 32;
123 break;
124 case 2:
125 cfg.secret_len = 48;
126 break;
127 case 3:
128 cfg.secret_len = 64;
129 break;
130 default:
131 cfg.secret_len = 32;
132 break;
133 }
134 }
135 if (!valid_kxchap_secret_len(cfg.secret_len)) {
136 nvme_show_error("Invalid secret length %u", cfg.secret_len)nvme_show_message(1, "Invalid secret length %u", cfg.secret_len
)
;
137 return -EINVAL22;
138 }
139
140 err = libnvmf_create_raw_secret(ctx, cfg.secret, cfg.secret_len, &raw_secret);
141 if (err)
142 return err;
143
144 memcpy(key, raw_secret, cfg.secret_len);
145
146 crc = shr_crc32(crc, key, cfg.secret_len);
147 key[cfg.secret_len++] = crc & 0xff;
148 key[cfg.secret_len++] = (crc >> 8) & 0xff;
149 key[cfg.secret_len++] = (crc >> 16) & 0xff;
150 key[cfg.secret_len++] = (crc >> 24) & 0xff;
151
152 memset(encoded_key, 0, sizeof(encoded_key));
153 shr_base64_encode(key, cfg.secret_len, encoded_key);
154
155 nvme_show_result("DHHC-1:%02x:%s:", cfg.hmac, encoded_key)nvme_show_message(0, "DHHC-1:%02x:%s:", cfg.hmac, encoded_key
)
;
156 return 0;
157}
158
159static int validate_kxchap_key(const char *key, int *hmac_out,
160 unsigned char *decoded_key, int *decoded_len_out, uint32_t *crc_out)
161{
162 uint32_t crc = shr_crc32(0L, NULL((void*)0), 0);
163 uint32_t key_crc;
164 size_t len = strlen(key);
165 int decoded_len, hmac, err;
166
167 if (sscanf(key, "DHHC-1:%02x:%*s", &hmac) != 1) {
168 nvme_show_error("Invalid secret header '%s'", key)nvme_show_message(1, "Invalid secret header '%s'", key);
169 return -EINVAL22;
170 }
171 if (hmac > 3) {
172 nvme_show_error("Invalid HMAC identifier %d", hmac)nvme_show_message(1, "Invalid HMAC identifier %d", hmac);
173 return -EINVAL22;
174 }
175
176 /*
177 * The hash identifier selects the function used to transform the
178 * secret into a key; it does not constrain the length of the secret
179 * itself, so apply the same length check whichever one is selected.
180 * A 32, 48 or 64 byte secret plus a 4 byte CRC encodes to 48, 72 or
181 * 92 base64 characters, giving a total of 59, 83 or 103.
182 *
183 * This does not pin the length of the secret - the same number of
184 * base64 characters can carry three different byte counts - so the
185 * decoded length is still checked below. What it does do is bound
186 * the decode before it runs.
187 */
188 if (len != 59 && len != 83 && len != 103) {
189 nvme_show_error("Invalid DHHC-1 string length %zu", len)nvme_show_message(1, "Invalid DHHC-1 string length %zu", len);
190 return -EINVAL22;
191 }
192
193 if (key[len - 1] != ':') {
194 nvme_show_error("Invalid secret format (missing trailing ':')")nvme_show_message(1, "Invalid secret format (missing trailing ':')"
)
;
195 return -EINVAL22;
196 }
197
198 err = shr_base64_decode(key + 10, len - 11, decoded_key);
199 if (err < 0) {
200 nvme_show_error("Base64 decoding failed, error %d", err)nvme_show_message(1, "Base64 decoding failed, error %d", err);
201 return err;
202 }
203 decoded_len = err;
204 decoded_len -= 4;
205 if (!valid_kxchap_secret_len(decoded_len)) {
206 nvme_show_error("Invalid secret length %d", decoded_len)nvme_show_message(1, "Invalid secret length %d", decoded_len);
207 return -EINVAL22;
208 }
209 crc = shr_crc32(crc, decoded_key, decoded_len);
210 key_crc = ((uint32_t)decoded_key[decoded_len]) |
211 ((uint32_t)decoded_key[decoded_len + 1] << 8) |
212 ((uint32_t)decoded_key[decoded_len + 2] << 16) |
213 ((uint32_t)decoded_key[decoded_len + 3] << 24);
214 if (key_crc != crc) {
215 nvme_show_error("CRC mismatch (secret %08x, crc %08x)", key_crc, crc)nvme_show_message(1, "CRC mismatch (secret %08x, crc %08x)", key_crc
, crc)
;
216 return -EINVAL22;
217 }
218
219 *hmac_out = hmac;
220 *decoded_len_out = decoded_len;
221 *crc_out = crc;
222 return 0;
223}
224
225static int check_kxchap(int argc, char **argv, struct command *acmd, struct plugin *plugin)
226{
227 const char *desc =
228 "Check a KX-HMAC-CHAP host secret for usability for NVMe In-Band Authentication,\n"
229 "and, if --identity is given, check whether it is already loaded into a keyring.";
230 const char *keydata = "KX-HMAC-CHAP secret (in DHHC-1 interchange format) to be validated. Reads from stdin if not given.";
231 const char *keyring = "Keyring to check for an already loaded secret.";
232 const char *keytype = "Key type of the secret to look up.";
233 const char *identity = "Identity to look up in the keyring to check if the secret is already loaded.";
234
235 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
236 __cleanup_free__attribute__((cleanup(shr_freep))) char *key = NULL((void*)0);
237 __cleanup_free__attribute__((cleanup(shr_freep))) unsigned char *stored = NULL((void*)0);
238 unsigned char decoded_key[128];
239 long keyring_id = 0, key_id = 0;
240 int decoded_len, hmac, err, stored_len;
241 uint32_t crc;
242 struct config {
243 char *keydata;
244 char *keyring;
245 char *keytype;
246 char *identity;
247 };
248
249 struct config cfg = {
250 .keydata = NULL((void*)0),
251 .keyring = ".nvme",
252 .keytype = "kxchap",
253 .identity = NULL((void*)0),
254 };
255
256 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
257 OPT_STR("keydata", 'd', &cfg.keydata, keydata){"keydata", 'd', "STRING", CFG_STRING, _Generic(&cfg.keydata
, char ** : (&cfg.keydata), const char ** : (&cfg.keydata
)), ARGCONFIG_ARG_REQUIRED, keydata, 0, }
,
258 OPT_STR("keyring", 'k', &cfg.keyring, keyring){"keyring", 'k', "STRING", CFG_STRING, _Generic(&cfg.keyring
, char ** : (&cfg.keyring), const char ** : (&cfg.keyring
)), ARGCONFIG_ARG_REQUIRED, keyring, 0, }
,
259 OPT_STR("keytype", 't', &cfg.keytype, keytype){"keytype", 't', "STRING", CFG_STRING, _Generic(&cfg.keytype
, char ** : (&cfg.keytype), const char ** : (&cfg.keytype
)), ARGCONFIG_ARG_REQUIRED, keytype, 0, }
,
260 OPT_STR("identity", 'i', &cfg.identity, identity){"identity", 'i', "STRING", CFG_STRING, _Generic(&cfg.identity
, char ** : (&cfg.identity), const char ** : (&cfg.identity
)), ARGCONFIG_ARG_REQUIRED, identity, 0, }
,
261 OPT_END(){ ((void*)0) }
262 };
263
264 err = parse_args(argc, argv, desc, opts);
265 if (err)
266 return err;
267
268 err = read_key_value(cfg.keydata, &key);
269 if (err) {
270 nvme_show_error("No secret data")nvme_show_message(1, "No secret data");
271 return err;
272 }
273
274 err = validate_kxchap_key(key, &hmac, decoded_key, &decoded_len, &crc);
275 if (err)
276 return err;
277
278 nvme_show_verbose_result("Secret is valid (HMAC %d, length %d, CRC %08x)", hmac, decoded_len, crc)nvme_show_verbose_message("Secret is valid (HMAC %d, length %d, CRC %08x)"
, hmac, decoded_len, crc)
;
279
280 if (!cfg.identity)
281 return 0;
282
283 err = nvme_create_global_ctx(&ctx);
284 if (err) {
285 nvme_show_error("Failed to create global context")nvme_show_message(1, "Failed to create global context");
286 return err;
287 }
288 libnvme_set_logging_level(ctx, log_level, false0, false0);
289
290 err = libnvmf_lookup_keyring(ctx, cfg.keyring, &keyring_id);
291 if (err) {
292 nvme_show_error("Failed to lookup keyring '%s', %s",nvme_show_message(1, "Failed to lookup keyring '%s', %s", cfg
.keyring, libnvme_strerror(-err))
293 cfg.keyring, libnvme_strerror(-err))nvme_show_message(1, "Failed to lookup keyring '%s', %s", cfg
.keyring, libnvme_strerror(-err))
;
294 return err;
295 }
296
297 err = libnvmf_set_keyring(ctx, keyring_id);
298 if (err) {
299 nvme_show_error("Failed to link keyring '%s', %s",nvme_show_message(1, "Failed to link keyring '%s', %s", cfg.keyring
, libnvme_strerror(-err))
300 cfg.keyring, libnvme_strerror(-err))nvme_show_message(1, "Failed to link keyring '%s', %s", cfg.keyring
, libnvme_strerror(-err))
;
301 return err;
302 }
303
304 err = libnvmf_lookup_key(ctx, cfg.keytype, cfg.identity, &key_id);
305 if (err) {
306 nvme_show_verbose_result("Secret is not loaded for identity '%s'", cfg.identity)nvme_show_verbose_message("Secret is not loaded for identity '%s'"
, cfg.identity)
;
307 return 0;
308 }
309
310 err = libnvmf_read_key(ctx, keyring_id, key_id, &stored_len, &stored);
311 if (err) {
312 nvme_show_error("Failed to read back loaded key, %s",nvme_show_message(1, "Failed to read back loaded key, %s", libnvme_strerror
(-err))
313 libnvme_strerror(-err))nvme_show_message(1, "Failed to read back loaded key, %s", libnvme_strerror
(-err))
;
314 return err;
315 }
316
317 if ((size_t)stored_len == strlen(key) && !memcmp(stored, key, stored_len))
318 nvme_show_verbose_result("Secret is loaded (serial %08x) and matches", (unsigned int)key_id)nvme_show_verbose_message("Secret is loaded (serial %08x) and matches"
, (unsigned int)key_id)
;
319 else
320 nvme_show_verbose_result("Secret is loaded (serial %08x) but differs", (unsigned int)key_id)nvme_show_verbose_message("Secret is loaded (serial %08x) but differs"
, (unsigned int)key_id)
;
321
322 return 0;
323}
324
325static int append_keyfile(struct libnvme_global_ctx *ctx, const char *keyring,
326 long id, const char *keyfile)
327{
328 __cleanup_free__attribute__((cleanup(shr_freep))) unsigned char *key_data = NULL((void*)0);
329 __cleanup_free__attribute__((cleanup(shr_freep))) char *exported_key = NULL((void*)0);
330 __cleanup_free__attribute__((cleanup(shr_freep))) char *identity = NULL((void*)0);
331 __cleanup_file__attribute__((cleanup(shr_cleanup_file))) FILE *fd = NULL((void*)0);
332 int err, hmac, key_len;
333 mode_t old_umask;
334 long kr_id;
335 char type;
336
337 err = libnvmf_lookup_keyring(ctx, keyring, &kr_id);
338 if (err) {
339 nvme_show_error("Failed to lookup keyring '%s', %s",nvme_show_message(1, "Failed to lookup keyring '%s', %s", keyring
, libnvme_strerror(-err))
340 keyring, libnvme_strerror(-err))nvme_show_message(1, "Failed to lookup keyring '%s', %s", keyring
, libnvme_strerror(-err))
;
341 return err;
342 }
343
344 identity = libnvmf_describe_key_serial(ctx, id);
345 if (!identity) {
346 nvme_show_error("Failed to get identity info")nvme_show_message(1, "Failed to get identity info");
347 return -EINVAL22;
348 }
349
350 if (sscanf(identity, "NVMe%*1d%c%02d %*s", &type, &hmac) != 2) {
351 nvme_show_error("Failed to parse identity\n")nvme_show_message(1, "Failed to parse identity\n");
352 return -EINVAL22;
353 }
354
355 err = libnvmf_read_key(ctx, kr_id, id, &key_len, &key_data);
356 if (err) {
357 nvme_show_error("Failed to read back derived TLS PSK, %s",nvme_show_message(1, "Failed to read back derived TLS PSK, %s"
, libnvme_strerror(-err))
358 libnvme_strerror(-err))nvme_show_message(1, "Failed to read back derived TLS PSK, %s"
, libnvme_strerror(-err))
;
359 return err;
360 }
361
362 err = libnvmf_export_tls_key_versioned(ctx, 1, hmac, key_data,
363 key_len, &exported_key);
364 if (err) {
365 nvme_show_error("Failed to export key, %s",nvme_show_message(1, "Failed to export key, %s", libnvme_strerror
(-err))
366 libnvme_strerror(-err))nvme_show_message(1, "Failed to export key, %s", libnvme_strerror
(-err))
;
367 return err;
368 }
369
370 old_umask = umask(S_IRWXG((0400|0200|0100) >> 3) | S_IRWXO(((0400|0200|0100) >> 3) >> 3));
371
372 fd = fopen(keyfile, "a");
373 if (!fd) {
374 nvme_show_error("Failed to open '%s', %s",nvme_show_message(1, "Failed to open '%s', %s", keyfile, libnvme_strerror
((*__errno_location ())))
375 keyfile, libnvme_strerror(errno))nvme_show_message(1, "Failed to open '%s', %s", keyfile, libnvme_strerror
((*__errno_location ())))
;
376 err = -errno(*__errno_location ());
377 goto out;
378 }
379
380 /*
381 * The umask above only affects newly created files: an existing
382 * keyfile could already be more permissive, so restrict the mode of
383 * the file we actually opened, via its descriptor, before writing
384 * the key to it. Using the descriptor rather than the path avoids a
385 * rename/symlink race between checking and fixing up the mode.
386 */
387 if (fchmod(fileno(fd), 0600) < 0) {
388 nvme_show_error("Failed to restrict permissions on '%s', %s",nvme_show_message(1, "Failed to restrict permissions on '%s', %s"
, keyfile, libnvme_strerror((*__errno_location ())))
389 keyfile, libnvme_strerror(errno))nvme_show_message(1, "Failed to restrict permissions on '%s', %s"
, keyfile, libnvme_strerror((*__errno_location ())))
;
390 err = -errno(*__errno_location ());
391 goto out;
392 }
393
394 err = fprintf(fd, "%s %s\n", identity, exported_key);
395 if (err < 0) {
396 nvme_show_error("Failed to append key to '%s', %s",nvme_show_message(1, "Failed to append key to '%s', %s", keyfile
, libnvme_strerror((*__errno_location ())))
397 keyfile, libnvme_strerror(errno))nvme_show_message(1, "Failed to append key to '%s', %s", keyfile
, libnvme_strerror((*__errno_location ())))
;
398 err = -errno(*__errno_location ());
399 } else {
400 err = 0;
401 }
402
403out:
404 umask(old_umask);
405
406 return err;
407}
408
409static int do_insert_tls_key(struct libnvme_global_ctx *ctx, const char *keyring,
410 const char *keytype, const char *hostnqn, const char *subsysnqn,
411 int identity, int hmac, unsigned char *key_data, int key_len,
412 bool_Bool compat, const char *keyfile, long *tls_key)
413{
414 int err;
415
416 if (compat)
417 err = libnvmf_insert_tls_key_compat(ctx, keyring, keytype, hostnqn,
418 subsysnqn, identity, hmac, key_data, key_len, tls_key);
419 else
420 err = libnvmf_insert_tls_key_versioned(ctx, keyring, keytype, hostnqn,
421 subsysnqn, identity, hmac, key_data, key_len, tls_key);
422 if (err) {
423 nvme_show_error("Failed to insert key, %s", libnvme_strerror(-err))nvme_show_message(1, "Failed to insert key, %s", libnvme_strerror
(-err))
;
424 return err;
425 }
426 nvme_show_result("Inserted TLS PSK %08x", (unsigned int)*tls_key)nvme_show_message(0, "Inserted TLS PSK %08x", (unsigned int)*
tls_key)
;
427
428 if (keyfile) {
429 err = append_keyfile(ctx, keyring, *tls_key, keyfile);
430 if (err)
431 return err;
432 }
433
434 return 0;
435}
436
437static int gen_tls(int argc, char **argv, struct command *acmd, struct plugin *plugin)
438{
439 const char *desc = "Generate a TLS configured PSK in NVMe PSK Interchange format.";
440 const char *secret =
441 "Optional configured PSK (in hexadecimal characters) to be used.";
442 const char *hmac =
443 "Hash function for the retained PSK and for the cipher suite the TLS PSK is bound to (1 = SHA-256, 2 = SHA-384).";
444 const char *version = "TLS PSK identity version to use (0 = obsolete; 1 = TLS 1.3 with the PSK digest in the identity)";
445 const char *hostnqn = "Host NQN for the retained PSK and the TLS PSK identity.";
446 const char *subsysnqn = "Subsystem NQN for the TLS PSK identity.";
447 const char *keyring = "Keyring for the TLS PSK.";
448 const char *keytype = "Key type of the TLS PSK.";
449 const char *insert = "Additionally insert the TLS PSK into the keyring.";
450 const char *keyfile = "Append the identity of the TLS PSK, and the TLS PSK itself, to keyfile.";
451 const char *compat = "Use non-RFC 8446 compliant algorithm for deriving the retained PSK and the TLS PSK for older implementations.";
452
453 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
454 __cleanup_free__attribute__((cleanup(shr_freep))) unsigned char *raw_secret = NULL((void*)0);
455 __cleanup_free__attribute__((cleanup(shr_freep))) char *encoded_key = NULL((void*)0);
456 __cleanup_free__attribute__((cleanup(shr_freep))) char *hnqn = NULL((void*)0);
457 int key_len = 32;
458 int err;
459 long tls_key;
460
461 struct config {
462 char *keyring;
463 char *keytype;
464 char *hostnqn;
465 char *subsysnqn;
466 char *secret;
467 char *keyfile;
468 unsigned char hmac;
469 unsigned char version;
470 bool_Bool insert;
471 bool_Bool compat;
472 };
473
474 struct config cfg = {
475 .keyring = ".nvme",
476 .keytype = "psk",
477 .hostnqn = NULL((void*)0),
478 .subsysnqn = NULL((void*)0),
479 .secret = NULL((void*)0),
480 .keyfile = NULL((void*)0),
481 .hmac = 1,
482 .version = 0,
483 .insert = false0,
484 .compat = false0,
485 };
486
487 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
488 OPT_STR("keyring", 'k', &cfg.keyring, keyring){"keyring", 'k', "STRING", CFG_STRING, _Generic(&cfg.keyring
, char ** : (&cfg.keyring), const char ** : (&cfg.keyring
)), ARGCONFIG_ARG_REQUIRED, keyring, 0, }
,
489 OPT_STR("keytype", 't', &cfg.keytype, keytype){"keytype", 't', "STRING", CFG_STRING, _Generic(&cfg.keytype
, char ** : (&cfg.keytype), const char ** : (&cfg.keytype
)), ARGCONFIG_ARG_REQUIRED, keytype, 0, }
,
490 OPT_STR("hostnqn", 'n', &cfg.hostnqn, hostnqn){"hostnqn", 'n', "STRING", CFG_STRING, _Generic(&cfg.hostnqn
, char ** : (&cfg.hostnqn), const char ** : (&cfg.hostnqn
)), ARGCONFIG_ARG_REQUIRED, hostnqn, 0, }
,
491 OPT_STR("subsysnqn", 'c', &cfg.subsysnqn, subsysnqn){"subsysnqn", 'c', "STRING", CFG_STRING, _Generic(&cfg.subsysnqn
, char ** : (&cfg.subsysnqn), const char ** : (&cfg.subsysnqn
)), ARGCONFIG_ARG_REQUIRED, subsysnqn, 0, }
,
492 OPT_STR("secret", 's', &cfg.secret, secret){"secret", 's', "STRING", CFG_STRING, _Generic(&cfg.secret
, char ** : (&cfg.secret), const char ** : (&cfg.secret
)), ARGCONFIG_ARG_REQUIRED, secret, 0, }
,
493 OPT_FILE("keyfile", 'f', &cfg.keyfile, keyfile){"keyfile", 'f', "FILE", CFG_FILE, _Generic(&cfg.keyfile,
char ** : (&cfg.keyfile), const char ** : (&cfg.keyfile
)), ARGCONFIG_ARG_REQUIRED, keyfile, 0, }
,
494 OPT_BYTE("hmac", 'm', &cfg.hmac, hmac){"hmac", 'm', "NUM", CFG_BYTE, _Generic(&cfg.hmac, uint8_t
* : (&cfg.hmac)), ARGCONFIG_ARG_REQUIRED, hmac, 0, }
,
495 OPT_BYTE("identity", 'I', &cfg.version, version){"identity", 'I', "NUM", CFG_BYTE, _Generic(&cfg.version,
uint8_t * : (&cfg.version)), ARGCONFIG_ARG_REQUIRED, version
, 0, }
,
496 OPT_FLAG("insert", 'i', &cfg.insert, insert){"insert", 'i', ((void*)0), CFG_FLAG, _Generic(&cfg.insert
, _Bool * : (&cfg.insert)), ARGCONFIG_ARG_NONE, insert, 0
, }
,
497 OPT_FLAG("compat", 'C', &cfg.compat, compat){"compat", 'C', ((void*)0), CFG_FLAG, _Generic(&cfg.compat
, _Bool * : (&cfg.compat)), ARGCONFIG_ARG_NONE, compat, 0
, }
,
498 OPT_END(){ ((void*)0) }
499 };
500
501 err = parse_args(argc, argv, desc, opts);
502 if (err)
503 return err;
504 if (cfg.hmac < 1 || cfg.hmac > 2) {
505 nvme_show_error("Invalid HMAC identifier %u", cfg.hmac)nvme_show_message(1, "Invalid HMAC identifier %u", cfg.hmac);
506 return -EINVAL22;
507 }
508 if (cfg.version > 1) {
509 nvme_show_error("Invalid TLS PSK identity version %u",nvme_show_message(1, "Invalid TLS PSK identity version %u", cfg
.version)
510 cfg.version)nvme_show_message(1, "Invalid TLS PSK identity version %u", cfg
.version)
;
511 return -EINVAL22;
512 }
513 if (cfg.insert) {
514 if (!cfg.subsysnqn) {
515 nvme_show_error("No subsystem NQN specified")nvme_show_message(1, "No subsystem NQN specified");
516 return -EINVAL22;
517 }
518 }
519 if (cfg.hmac == 2)
520 key_len = 48;
521
522 err = nvme_create_global_ctx(&ctx);
523 if (err)
524 return err;
525
526 err = libnvmf_create_raw_secret(ctx, cfg.secret, key_len, &raw_secret);
527 if (err)
528 return err;
529
530 err = libnvmf_export_tls_key(ctx, raw_secret, key_len, &encoded_key);
531 if (err) {
532 nvme_show_error("Failed to export key, %s", libnvme_strerror(-err))nvme_show_message(1, "Failed to export key, %s", libnvme_strerror
(-err))
;
533 return err;
534 }
535 nvme_show_result("%s", encoded_key)nvme_show_message(0, "%s", encoded_key);
536
537 if (cfg.insert) {
538 if (!cfg.hostnqn) {
539 err = libnvmf_host_get_ids(ctx, NULL((void*)0), NULL((void*)0), &hnqn, NULL((void*)0));
540 if (err)
541 return err;
542 cfg.hostnqn = hnqn;
543 }
544
545 err = do_insert_tls_key(ctx, cfg.keyring, cfg.keytype,
546 cfg.hostnqn, cfg.subsysnqn, cfg.version,
547 cfg.hmac, raw_secret, key_len, cfg.compat,
548 cfg.keyfile, &tls_key);
549 if (err)
550 return err;
551 }
552
553 return 0;
554}
555
556static int check_tls(int argc, char **argv, struct command *acmd, struct plugin *plugin)
557{
558 const char *desc =
559 "Check a TLS configured PSK for NVMe PSK Interchange format, and, if a\n"
560 "subsystem NQN is given, check whether the corresponding TLS PSK is\n"
561 "already loaded into a keyring.";
562 const char *keydata = "Configured PSK (in PSK Interchange format) to be validated. Reads from stdin if not given.";
563 const char *identity = "TLS PSK identity version to use (0 = obsolete; 1 = TLS 1.3 with the PSK digest in the identity)";
564 const char *hostnqn = "Host NQN for the TLS PSK identity.";
565 const char *subsysnqn = "Subsystem NQN for the TLS PSK identity. If not given, only the format is validated.";
566 const char *keyring = "Keyring to check for an already loaded TLS PSK.";
567 const char *keytype = "Key type of the TLS PSK to look up.";
568 const char *compat = "Use non-RFC 8446 compliant algorithm for deriving the TLS PSK identity for older implementations.";
569
570 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
571 __cleanup_free__attribute__((cleanup(shr_freep))) char *key_value = NULL((void*)0);
572 __cleanup_free__attribute__((cleanup(shr_freep))) unsigned char *decoded_key = NULL((void*)0);
573 __cleanup_free__attribute__((cleanup(shr_freep))) char *hnqn = NULL((void*)0);
574 __cleanup_free__attribute__((cleanup(shr_freep))) char *tls_id = NULL((void*)0);
575 int decoded_len, err = 0;
576 enum libnvmf_hmac_alg hmac;
577 long keyring_id, key_id;
578 struct config {
579 char *keyring;
580 char *keytype;
581 char *hostnqn;
582 char *subsysnqn;
583 char *keydata;
584 unsigned char identity;
585 bool_Bool compat;
586 };
587
588 struct config cfg = {
589 .keyring = ".nvme",
590 .keytype = "psk",
591 .hostnqn = NULL((void*)0),
592 .subsysnqn = NULL((void*)0),
593 .keydata = NULL((void*)0),
594 .identity = 0,
595 .compat = false0,
596 };
597
598 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
599 OPT_STR("keyring", 'k', &cfg.keyring, keyring){"keyring", 'k', "STRING", CFG_STRING, _Generic(&cfg.keyring
, char ** : (&cfg.keyring), const char ** : (&cfg.keyring
)), ARGCONFIG_ARG_REQUIRED, keyring, 0, }
,
600 OPT_STR("keytype", 't', &cfg.keytype, keytype){"keytype", 't', "STRING", CFG_STRING, _Generic(&cfg.keytype
, char ** : (&cfg.keytype), const char ** : (&cfg.keytype
)), ARGCONFIG_ARG_REQUIRED, keytype, 0, }
,
601 OPT_STR("hostnqn", 'n', &cfg.hostnqn, hostnqn){"hostnqn", 'n', "STRING", CFG_STRING, _Generic(&cfg.hostnqn
, char ** : (&cfg.hostnqn), const char ** : (&cfg.hostnqn
)), ARGCONFIG_ARG_REQUIRED, hostnqn, 0, }
,
602 OPT_STR("subsysnqn", 'c', &cfg.subsysnqn, subsysnqn){"subsysnqn", 'c', "STRING", CFG_STRING, _Generic(&cfg.subsysnqn
, char ** : (&cfg.subsysnqn), const char ** : (&cfg.subsysnqn
)), ARGCONFIG_ARG_REQUIRED, subsysnqn, 0, }
,
603 OPT_STR("keydata", 'd', &cfg.keydata, keydata){"keydata", 'd', "STRING", CFG_STRING, _Generic(&cfg.keydata
, char ** : (&cfg.keydata), const char ** : (&cfg.keydata
)), ARGCONFIG_ARG_REQUIRED, keydata, 0, }
,
604 OPT_BYTE("identity", 'I', &cfg.identity, identity){"identity", 'I', "NUM", CFG_BYTE, _Generic(&cfg.identity
, uint8_t * : (&cfg.identity)), ARGCONFIG_ARG_REQUIRED, identity
, 0, }
,
605 OPT_FLAG("compat", 'C', &cfg.compat, compat){"compat", 'C', ((void*)0), CFG_FLAG, _Generic(&cfg.compat
, _Bool * : (&cfg.compat)), ARGCONFIG_ARG_NONE, compat, 0
, }
,
606 OPT_END(){ ((void*)0) }
607 };
608
609 err = parse_args(argc, argv, desc, opts);
610 if (err)
1
Assuming 'err' is 0
2
Taking false branch
611 return err;
612
613 if (cfg.identity > 1) {
3
Assuming field 'identity' is <= 1
4
Taking false branch
614 nvme_show_error("Invalid TLS PSK identity version %u",nvme_show_message(1, "Invalid TLS PSK identity version %u", cfg
.identity)
615 cfg.identity)nvme_show_message(1, "Invalid TLS PSK identity version %u", cfg
.identity)
;
616 return -EINVAL22;
617 }
618
619 err = read_key_value(cfg.keydata, &key_value);
5
Calling 'read_key_value'
11
Returned allocated memory via 2nd parameter
620 if (err
11.1
'err' is 0
) {
12
Taking false branch
621 nvme_show_error("No key data")nvme_show_message(1, "No key data");
622 return err;
623 }
624
625 err = nvme_create_global_ctx(&ctx);
626 if (err) {
13
Assuming 'err' is not equal to 0
14
Taking true branch
627 nvme_show_error("Failed to create global context")nvme_show_message(1, "Failed to create global context");
15
Potential leak of memory pointed to by 'key_value'
628 return err;
629 }
630 libnvme_set_logging_level(ctx, log_level, false0, false0);
631
632 err = libnvmf_import_tls_key(ctx, key_value, &decoded_len,
633 &hmac, &decoded_key);
634 if (err) {
635 nvme_show_error("Key decoding failed, %s", libnvme_strerror(-err))nvme_show_message(1, "Key decoding failed, %s", libnvme_strerror
(-err))
;
636 return err;
637 }
638 nvme_show_verbose_result("Configured PSK is valid (HMAC %u, length %d)", hmac, decoded_len)nvme_show_verbose_message("Configured PSK is valid (HMAC %u, length %d)"
, hmac, decoded_len)
;
639
640 if (!cfg.subsysnqn)
641 return 0;
642
643 if (!cfg.hostnqn) {
644 err = libnvmf_host_get_ids(ctx, NULL((void*)0), NULL((void*)0), &hnqn, NULL((void*)0));
645 if (err)
646 return err;
647 cfg.hostnqn = hnqn;
648 }
649
650 if (cfg.compat)
651 err = libnvmf_generate_tls_key_identity_compat(ctx,
652 cfg.hostnqn, cfg.subsysnqn, cfg.identity,
653 hmac, decoded_key, decoded_len, &tls_id);
654 else
655 err = libnvmf_generate_tls_key_identity(ctx,
656 cfg.hostnqn, cfg.subsysnqn, cfg.identity,
657 hmac, decoded_key, decoded_len, &tls_id);
658 if (err) {
659 nvme_show_error("Failed to generate identity, %s",nvme_show_message(1, "Failed to generate identity, %s", libnvme_strerror
(-err))
660 libnvme_strerror(-err))nvme_show_message(1, "Failed to generate identity, %s", libnvme_strerror
(-err))
;
661 return err;
662 }
663 nvme_show_result("%s", tls_id)nvme_show_message(0, "%s", tls_id);
664
665 err = libnvmf_lookup_keyring(ctx, cfg.keyring, &keyring_id);
666 if (err) {
667 nvme_show_error("Failed to lookup keyring '%s', %s",nvme_show_message(1, "Failed to lookup keyring '%s', %s", cfg
.keyring, libnvme_strerror(-err))
668 cfg.keyring, libnvme_strerror(-err))nvme_show_message(1, "Failed to lookup keyring '%s', %s", cfg
.keyring, libnvme_strerror(-err))
;
669 return err;
670 }
671
672 err = libnvmf_set_keyring(ctx, keyring_id);
673 if (err) {
674 nvme_show_error("Failed to link keyring '%s', %s",nvme_show_message(1, "Failed to link keyring '%s', %s", cfg.keyring
, libnvme_strerror(-err))
675 cfg.keyring, libnvme_strerror(-err))nvme_show_message(1, "Failed to link keyring '%s', %s", cfg.keyring
, libnvme_strerror(-err))
;
676 return err;
677 }
678
679 err = libnvmf_lookup_key(ctx, cfg.keytype, tls_id, &key_id);
680 if (err) {
681 nvme_show_verbose_result("TLS PSK is not loaded")nvme_show_verbose_message("TLS PSK is not loaded");
682 return 0;
683 }
684
685 nvme_show_verbose_result("TLS PSK is loaded (serial %08x)", (unsigned int)key_id)nvme_show_verbose_message("TLS PSK is loaded (serial %08x)", (
unsigned int)key_id)
;
686 return 0;
687}
688
689static int insert_tls(int argc, char **argv, struct command *acmd, struct plugin *plugin)
690{
691 const char *desc =
692 "Derive a TLS PSK from a configured PSK (in NVMe PSK Interchange\n"
693 "format) and insert it into a keyring.\n";
694 const char *keydata = "Configured PSK (in PSK Interchange format) to derive from. Reads from stdin if not given.";
695 const char *identity = "TLS PSK identity version to use (0 = obsolete; 1 = TLS 1.3 with the PSK digest in the identity)";
696 const char *hostnqn = "Host NQN for the retained PSK and the TLS PSK identity.";
697 const char *subsysnqn = "Subsystem NQN for the TLS PSK identity.";
698 const char *keyring = "Keyring for the TLS PSK.";
699 const char *keytype = "Key type of the TLS PSK.";
700 const char *keyfile = "Append the identity of the TLS PSK, and the TLS PSK itself, to keyfile.";
701 const char *compat = "Use non-RFC 8446 compliant algorithm for deriving the retained PSK and the TLS PSK for older implementations.";
702
703 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
704 __cleanup_free__attribute__((cleanup(shr_freep))) char *key_value = NULL((void*)0);
705 __cleanup_free__attribute__((cleanup(shr_freep))) unsigned char *decoded_key = NULL((void*)0);
706 __cleanup_free__attribute__((cleanup(shr_freep))) char *hnqn = NULL((void*)0);
707 int decoded_len, err = 0;
708 enum libnvmf_hmac_alg hmac;
709 long tls_key;
710 struct config {
711 char *keyring;
712 char *keytype;
713 char *hostnqn;
714 char *subsysnqn;
715 char *keydata;
716 char *keyfile;
717 unsigned char identity;
718 bool_Bool compat;
719 };
720
721 struct config cfg = {
722 .keyring = ".nvme",
723 .keytype = "psk",
724 .hostnqn = NULL((void*)0),
725 .subsysnqn = NULL((void*)0),
726 .keydata = NULL((void*)0),
727 .keyfile = NULL((void*)0),
728 .identity = 0,
729 .compat = false0,
730 };
731
732 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
733 OPT_STR("keyring", 'k', &cfg.keyring, keyring){"keyring", 'k', "STRING", CFG_STRING, _Generic(&cfg.keyring
, char ** : (&cfg.keyring), const char ** : (&cfg.keyring
)), ARGCONFIG_ARG_REQUIRED, keyring, 0, }
,
734 OPT_STR("keytype", 't', &cfg.keytype, keytype){"keytype", 't', "STRING", CFG_STRING, _Generic(&cfg.keytype
, char ** : (&cfg.keytype), const char ** : (&cfg.keytype
)), ARGCONFIG_ARG_REQUIRED, keytype, 0, }
,
735 OPT_STR("hostnqn", 'n', &cfg.hostnqn, hostnqn){"hostnqn", 'n', "STRING", CFG_STRING, _Generic(&cfg.hostnqn
, char ** : (&cfg.hostnqn), const char ** : (&cfg.hostnqn
)), ARGCONFIG_ARG_REQUIRED, hostnqn, 0, }
,
736 OPT_STR("subsysnqn", 'c', &cfg.subsysnqn, subsysnqn){"subsysnqn", 'c', "STRING", CFG_STRING, _Generic(&cfg.subsysnqn
, char ** : (&cfg.subsysnqn), const char ** : (&cfg.subsysnqn
)), ARGCONFIG_ARG_REQUIRED, subsysnqn, 0, }
,
737 OPT_STR("keydata", 'd', &cfg.keydata, keydata){"keydata", 'd', "STRING", CFG_STRING, _Generic(&cfg.keydata
, char ** : (&cfg.keydata), const char ** : (&cfg.keydata
)), ARGCONFIG_ARG_REQUIRED, keydata, 0, }
,
738 OPT_FILE("keyfile", 'f', &cfg.keyfile, keyfile){"keyfile", 'f', "FILE", CFG_FILE, _Generic(&cfg.keyfile,
char ** : (&cfg.keyfile), const char ** : (&cfg.keyfile
)), ARGCONFIG_ARG_REQUIRED, keyfile, 0, }
,
739 OPT_BYTE("identity", 'I', &cfg.identity, identity){"identity", 'I', "NUM", CFG_BYTE, _Generic(&cfg.identity
, uint8_t * : (&cfg.identity)), ARGCONFIG_ARG_REQUIRED, identity
, 0, }
,
740 OPT_FLAG("compat", 'C', &cfg.compat, compat){"compat", 'C', ((void*)0), CFG_FLAG, _Generic(&cfg.compat
, _Bool * : (&cfg.compat)), ARGCONFIG_ARG_NONE, compat, 0
, }
,
741 OPT_END(){ ((void*)0) }
742 };
743
744 err = parse_args(argc, argv, desc, opts);
745 if (err)
746 return err;
747
748 if (!cfg.subsysnqn) {
749 nvme_show_error("Need to specify a subsystem NQN")nvme_show_message(1, "Need to specify a subsystem NQN");
750 return -EINVAL22;
751 }
752 if (cfg.identity > 1) {
753 nvme_show_error("Invalid TLS PSK identity version %u",nvme_show_message(1, "Invalid TLS PSK identity version %u", cfg
.identity)
754 cfg.identity)nvme_show_message(1, "Invalid TLS PSK identity version %u", cfg
.identity)
;
755 return -EINVAL22;
756 }
757
758 err = read_key_value(cfg.keydata, &key_value);
759 if (err) {
760 nvme_show_error("No key data")nvme_show_message(1, "No key data");
761 return err;
762 }
763
764 err = nvme_create_global_ctx(&ctx);
765 if (err) {
766 nvme_show_error("Failed to create global context")nvme_show_message(1, "Failed to create global context");
767 return err;
768 }
769 libnvme_set_logging_level(ctx, log_level, false0, false0);
770
771 err = libnvmf_import_tls_key(ctx, key_value, &decoded_len,
772 &hmac, &decoded_key);
773 if (err) {
774 nvme_show_error("Key decoding failed, %s", libnvme_strerror(-err))nvme_show_message(1, "Key decoding failed, %s", libnvme_strerror
(-err))
;
775 return err;
776 }
777
778 if (!cfg.hostnqn) {
779 err = libnvmf_host_get_ids(ctx, NULL((void*)0), NULL((void*)0), &hnqn, NULL((void*)0));
780 if (err)
781 return err;
782 cfg.hostnqn = hnqn;
783 }
784
785 return do_insert_tls_key(ctx, cfg.keyring, cfg.keytype, cfg.hostnqn,
786 cfg.subsysnqn, cfg.identity, hmac, decoded_key, decoded_len,
787 cfg.compat, cfg.keyfile, &tls_key);
788}
789
790static void __scan_tls_key(struct libnvme_global_ctx *ctx, long keyring_id,
791 long key_id, char *desc, int desc_len, void *data)
792{
793 FILE *fd = data;
794 __cleanup_free__attribute__((cleanup(shr_freep))) unsigned char *key_data = NULL((void*)0);
795 __cleanup_free__attribute__((cleanup(shr_freep))) char *encoded_key = NULL((void*)0);
796 int key_len;
797 int hmac;
798 char type;
799 int err;
800
801 err = libnvmf_read_key(ctx, keyring_id, key_id, &key_len, &key_data);
802 if (err)
803 return;
804
805 if (sscanf(desc, "NVMe%*1d%c%02d %*s", &type, &hmac) != 2)
806 return;
807
808 err = libnvmf_export_tls_key_versioned(ctx, 1, hmac, key_data, key_len,
809 &encoded_key);
810 if (err)
811 return;
812 fprintf(fd, "%s %s\n", desc, encoded_key);
813}
814
815static int key_export(int argc, char **argv, struct command *acmd, struct plugin *plugin)
816{
817 const char *desc = "Export NVMeoF TLS PSKs from a keyring.\n";
818 const char *keyring = "Keyring to export the TLS PSKs from.";
819 const char *keyfile = "File to write the exported keys to (default: stdout).";
820
821 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
822 __cleanup_file__attribute__((cleanup(shr_cleanup_file))) FILE *fd = NULL((void*)0);
823 mode_t old_umask = 0;
824 int err = 0;
825
826 struct config {
827 char *keyring;
828 char *keyfile;
829 };
830
831 struct config cfg = {
832 .keyring = ".nvme",
833 .keyfile = NULL((void*)0),
834 };
835
836 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
837 OPT_STR("keyring", 'k', &cfg.keyring, keyring){"keyring", 'k', "STRING", CFG_STRING, _Generic(&cfg.keyring
, char ** : (&cfg.keyring), const char ** : (&cfg.keyring
)), ARGCONFIG_ARG_REQUIRED, keyring, 0, }
,
838 OPT_FILE("keyfile", 'f', &cfg.keyfile, keyfile){"keyfile", 'f', "FILE", CFG_FILE, _Generic(&cfg.keyfile,
char ** : (&cfg.keyfile), const char ** : (&cfg.keyfile
)), ARGCONFIG_ARG_REQUIRED, keyfile, 0, }
,
839 OPT_END(){ ((void*)0) }
840 };
841
842 err = parse_args(argc, argv, desc, opts);
843 if (err)
844 return err;
845
846 err = nvme_create_global_ctx(&ctx);
847 if (err) {
848 nvme_show_error("Failed to create global context")nvme_show_message(1, "Failed to create global context");
849 return err;
850 }
851 libnvme_set_logging_level(ctx, log_level, false0, false0);
852
853 if (cfg.keyfile) {
854 old_umask = umask(0);
855
856 fd = fopen(cfg.keyfile, "w");
857 if (!fd) {
858 nvme_show_error("Cannot open keyfile %s, error %d",nvme_show_message(1, "Cannot open keyfile %s, error %d", cfg.
keyfile, (*__errno_location ()))
859 cfg.keyfile, errno)nvme_show_message(1, "Cannot open keyfile %s, error %d", cfg.
keyfile, (*__errno_location ()))
;
860 umask(old_umask);
861 return -errno(*__errno_location ());
862 }
863 } else {
864 fd = freopen(NULL((void*)0), "w", stdoutstdout);
865 }
866
867 err = libnvmf_scan_tls_keys(ctx, cfg.keyring, __scan_tls_key, fd);
868 if (err < 0) {
869 nvme_show_error("Export of TLS PSKs failed with '%s'",nvme_show_message(1, "Export of TLS PSKs failed with '%s'", libnvme_strerror
(-err))
870 libnvme_strerror(-err))nvme_show_message(1, "Export of TLS PSKs failed with '%s'", libnvme_strerror
(-err))
;
871 return err;
872 }
873
874 nvme_show_verbose_info("exporting to %s", cfg.keyfile)nvme_show_verbose_message("exporting to %s", cfg.keyfile);
875
876 if (cfg.keyfile) {
877 umask(old_umask);
878 chmod(cfg.keyfile, 0600);
879 }
880
881 return 0;
882}
883
884static bool_Bool is_kxchap_key(const char *key)
885{
886 return !strncmp(key, "DHHC-1:", 7);
887}
888
889static int import_one_key(struct libnvme_global_ctx *ctx, long keyring_id,
890 const char *identity, const char *key_str)
891{
892 __cleanup_free__attribute__((cleanup(shr_freep))) unsigned char *psk = NULL((void*)0);
893 unsigned char decoded_key[128];
894 long key_serial;
895 int decoded_len, kxchap_hmac, err;
896 enum libnvmf_hmac_alg tls_hmac;
897 uint32_t crc;
898
899 if (is_kxchap_key(key_str)) {
900 err = validate_kxchap_key(key_str, &kxchap_hmac, decoded_key,
901 &decoded_len, &crc);
902 if (err)
903 return err;
904
905 return libnvmf_update_key(ctx, keyring_id, "kxchap", identity,
906 (unsigned char *)key_str, strlen(key_str),
907 &key_serial);
908 }
909
910 err = libnvmf_import_tls_key(ctx, key_str, &decoded_len, &tls_hmac, &psk);
911 if (err)
912 return err;
913
914 return libnvmf_update_key(ctx, keyring_id, "psk", identity, psk,
915 decoded_len, &key_serial);
916}
917
918static int import_key(struct libnvme_global_ctx *ctx, const char *keyring,
919 FILE *fd)
920{
921 long keyring_id;
922 char line[512];
923 char *key_str;
924 int linenum = 0;
925 int err;
926
927 err = libnvmf_lookup_keyring(ctx, keyring, &keyring_id);
928 if (err) {
929 nvme_show_error("Invalid keyring '%s'", keyring)nvme_show_message(1, "Invalid keyring '%s'", keyring);
930 return err;
931 }
932
933 while (fgets(line, sizeof(line), fd)) {
934 linenum++;
935 key_str = strrchr(line, ' ')_Generic (0 ? (line) : (void *) 1, const void *: (const char *
) (strrchr (line, ' ')), default: strrchr (line, ' '))
;
936 if (!key_str) {
937 nvme_show_error("Parse error in line %d",nvme_show_message(1, "Parse error in line %d", linenum)
938 linenum)nvme_show_message(1, "Parse error in line %d", linenum);
939 continue;
940 }
941 *key_str = '\0';
942 key_str++;
943 key_str[strcspn(key_str, "\n")] = 0;
944
945 err = import_one_key(ctx, keyring_id, line, key_str);
946 if (err)
947 nvme_show_error("Failed to import key in line %d, %s",nvme_show_message(1, "Failed to import key in line %d, %s", linenum
, libnvme_strerror(-err))
948 linenum, libnvme_strerror(-err))nvme_show_message(1, "Failed to import key in line %d, %s", linenum
, libnvme_strerror(-err))
;
949 }
950
951 return 0;
952}
953
954static int key_import(int argc, char **argv, struct command *acmd, struct plugin *plugin)
955{
956 const char *desc = "Import NVMeoF TLS PSKs and KX-HMAC-CHAP secrets into a keyring.\n";
957 const char *keyring = "Keyring to import the keys into.";
958 const char *keyfile = "File to read the keys from (default: stdin).";
959 const char *keydata = "Key to insert directly under --identity. Reads from stdin if not given.";
960 const char *identity = "Identity to store a single key under. If given, --keydata (or stdin) is read as a single key instead of a bulk <identity> <key> list.";
961
962 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
963 __cleanup_file__attribute__((cleanup(shr_cleanup_file))) FILE *fd = NULL((void*)0);
964 __cleanup_free__attribute__((cleanup(shr_freep))) char *key = NULL((void*)0);
965 long keyring_id;
966 int err = 0;
967
968 struct config {
969 char *keyring;
970 char *keyfile;
971 char *keydata;
972 char *identity;
973 };
974
975 struct config cfg = {
976 .keyring = ".nvme",
977 .keyfile = NULL((void*)0),
978 .keydata = NULL((void*)0),
979 .identity = NULL((void*)0),
980 };
981
982 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
983 OPT_STR("keyring", 'k', &cfg.keyring, keyring){"keyring", 'k', "STRING", CFG_STRING, _Generic(&cfg.keyring
, char ** : (&cfg.keyring), const char ** : (&cfg.keyring
)), ARGCONFIG_ARG_REQUIRED, keyring, 0, }
,
984 OPT_FILE("keyfile", 'f', &cfg.keyfile, keyfile){"keyfile", 'f', "FILE", CFG_FILE, _Generic(&cfg.keyfile,
char ** : (&cfg.keyfile), const char ** : (&cfg.keyfile
)), ARGCONFIG_ARG_REQUIRED, keyfile, 0, }
,
985 OPT_STR("keydata", 'd', &cfg.keydata, keydata){"keydata", 'd', "STRING", CFG_STRING, _Generic(&cfg.keydata
, char ** : (&cfg.keydata), const char ** : (&cfg.keydata
)), ARGCONFIG_ARG_REQUIRED, keydata, 0, }
,
986 OPT_STR("identity", 'i', &cfg.identity, identity){"identity", 'i', "STRING", CFG_STRING, _Generic(&cfg.identity
, char ** : (&cfg.identity), const char ** : (&cfg.identity
)), ARGCONFIG_ARG_REQUIRED, identity, 0, }
,
987 OPT_END(){ ((void*)0) }
988 };
989
990 err = parse_args(argc, argv, desc, opts);
991 if (err)
992 return err;
993
994 err = nvme_create_global_ctx(&ctx);
995 if (err) {
996 nvme_show_error("Failed to create global context")nvme_show_message(1, "Failed to create global context");
997 return err;
998 }
999 libnvme_set_logging_level(ctx, log_level, false0, false0);
1000
1001 if (!cfg.identity) {
1002 if (cfg.keyfile) {
1003 fd = fopen(cfg.keyfile, "r");
1004 if (!fd) {
1005 int saved_errno = errno(*__errno_location ());
1006
1007 nvme_show_error("Cannot open keyfile %s, error %d",nvme_show_message(1, "Cannot open keyfile %s, error %d", cfg.
keyfile, saved_errno)
1008 cfg.keyfile, saved_errno)nvme_show_message(1, "Cannot open keyfile %s, error %d", cfg.
keyfile, saved_errno)
;
1009 return -saved_errno;
1010 }
1011 } else {
1012 fd = freopen(NULL((void*)0), "r", stdinstdin);
1013 if (!fd) {
1014 int saved_errno = errno(*__errno_location ());
1015
1016 nvme_show_error("Cannot reopen stdin, error %d", saved_errno)nvme_show_message(1, "Cannot reopen stdin, error %d", saved_errno
)
;
1017 return -saved_errno;
1018 }
1019 }
1020
1021 err = import_key(ctx, cfg.keyring, fd);
1022 if (err) {
1023 nvme_show_error("Import of keys failed with '%s'",nvme_show_message(1, "Import of keys failed with '%s'", libnvme_strerror
(err))
1024 libnvme_strerror(err))nvme_show_message(1, "Import of keys failed with '%s'", libnvme_strerror
(err))
;
1025 return err;
1026 }
1027
1028 nvme_show_verbose_info("importing from %s", cfg.keyfile)nvme_show_verbose_message("importing from %s", cfg.keyfile);
1029 return 0;
1030 }
1031
1032 err = read_key_value(cfg.keydata, &key);
1033 if (err) {
1034 nvme_show_error("No key data")nvme_show_message(1, "No key data");
1035 return err;
1036 }
1037
1038 err = libnvmf_lookup_keyring(ctx, cfg.keyring, &keyring_id);
1039 if (err) {
1040 nvme_show_error("Failed to lookup keyring '%s', %s",nvme_show_message(1, "Failed to lookup keyring '%s', %s", cfg
.keyring, libnvme_strerror(-err))
1041 cfg.keyring, libnvme_strerror(-err))nvme_show_message(1, "Failed to lookup keyring '%s', %s", cfg
.keyring, libnvme_strerror(-err))
;
1042 return err;
1043 }
1044
1045 err = import_one_key(ctx, keyring_id, cfg.identity, key);
1046 if (err) {
1047 nvme_show_error("Failed to insert key, %s", libnvme_strerror(-err))nvme_show_message(1, "Failed to insert key, %s", libnvme_strerror
(-err))
;
1048 return err;
1049 }
1050
1051 nvme_show_result("Inserted key for identity '%s'", cfg.identity)nvme_show_message(0, "Inserted key for identity '%s'", cfg.identity
)
;
1052 return 0;
1053}
1054
1055static int key_revoke(int argc, char **argv, struct command *acmd, struct plugin *plugin)
1056{
1057 const char *desc = "Revoke an NVMeoF TLS PSK from a keyring.\n";
1058 const char *keyring = "Keyring to revoke the key from.";
1059 const char *keytype = "Key type of the key to revoke.";
1060 const char *identity = "Identity (description) of the key to revoke.";
1061
1062 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
1063 int err = 0;
1064
1065 struct config {
1066 char *keyring;
1067 char *keytype;
1068 char *identity;
1069 };
1070
1071 struct config cfg = {
1072 .keyring = ".nvme",
1073 .keytype = "psk",
1074 .identity = NULL((void*)0),
1075 };
1076
1077 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
1078 OPT_STR("keyring", 'k', &cfg.keyring, keyring){"keyring", 'k', "STRING", CFG_STRING, _Generic(&cfg.keyring
, char ** : (&cfg.keyring), const char ** : (&cfg.keyring
)), ARGCONFIG_ARG_REQUIRED, keyring, 0, }
,
1079 OPT_STR("keytype", 't', &cfg.keytype, keytype){"keytype", 't', "STRING", CFG_STRING, _Generic(&cfg.keytype
, char ** : (&cfg.keytype), const char ** : (&cfg.keytype
)), ARGCONFIG_ARG_REQUIRED, keytype, 0, }
,
1080 OPT_STR("identity", 'i', &cfg.identity, identity){"identity", 'i', "STRING", CFG_STRING, _Generic(&cfg.identity
, char ** : (&cfg.identity), const char ** : (&cfg.identity
)), ARGCONFIG_ARG_REQUIRED, identity, 0, }
,
1081 OPT_END(){ ((void*)0) }
1082 };
1083
1084 err = parse_args(argc, argv, desc, opts);
1085 if (err)
1086 return err;
1087
1088 if (!cfg.identity) {
1089 nvme_show_error("Must specify --identity")nvme_show_message(1, "Must specify --identity");
1090 return -EINVAL22;
1091 }
1092
1093 err = nvme_create_global_ctx(&ctx);
1094 if (err) {
1095 nvme_show_error("Failed to create global context")nvme_show_message(1, "Failed to create global context");
1096 return err;
1097 }
1098 libnvme_set_logging_level(ctx, log_level, false0, false0);
1099
1100 err = libnvmf_revoke_tls_key(ctx, cfg.keyring, cfg.keytype, cfg.identity);
1101 if (err) {
1102 nvme_show_error("Failed to revoke key '%s'",nvme_show_message(1, "Failed to revoke key '%s'", libnvme_strerror
(err))
1103 libnvme_strerror(err))nvme_show_message(1, "Failed to revoke key '%s'", libnvme_strerror
(err))
;
1104 return err;
1105 }
1106
1107 nvme_show_verbose_info("revoking key")nvme_show_verbose_message("revoking key");
1108
1109 return 0;
1110}
1111
1112static struct command gen_kxchap_cmd = {
1113 .name = "gen-kxchap-secret",
1114 .help = "Generate NVMeoF KX-HMAC-CHAP host secret",
1115 .fn = gen_kxchap,
1116 .device = COMMAND_NO_DEVICE,
1117 .needs_root = true1,
1118};
1119
1120static struct command check_kxchap_cmd = {
1121 .name = "check-kxchap-secret",
1122 .help = "Validate NVMeoF KX-HMAC-CHAP host secret format or check if loaded",
1123 .fn = check_kxchap,
1124 .device = COMMAND_NO_DEVICE,
1125 .needs_root = true1,
1126};
1127
1128static struct command gen_tls_cmd = {
1129 .name = "gen-tls-psk",
1130 .help = "Generate NVMeoF TLS PSK",
1131 .fn = gen_tls,
1132 .device = COMMAND_NO_DEVICE,
1133 .needs_root = true1,
1134};
1135
1136static struct command check_tls_cmd = {
1137 .name = "check-tls-psk",
1138 .help = "Validate NVMeoF TLS PSK format or check if loaded",
1139 .fn = check_tls,
1140 .device = COMMAND_NO_DEVICE,
1141 .needs_root = true1,
1142};
1143
1144static struct command insert_tls_cmd = {
1145 .name = "insert-tls-psk",
1146 .help = "Insert NVMeoF TLS PSK into a keyring",
1147 .fn = insert_tls,
1148 .device = COMMAND_NO_DEVICE,
1149 .needs_root = true1,
1150};
1151
1152static struct command key_import_cmd = {
1153 .name = "import",
1154 .help = "Import NVMeoF TLS PSKs and KX-HMAC-CHAP secrets into a keyring",
1155 .fn = key_import,
1156 .device = COMMAND_NO_DEVICE,
1157 .needs_root = true1,
1158};
1159
1160static struct command key_export_cmd = {
1161 .name = "export",
1162 .help = "Export NVMeoF TLS PSKs from a keyring",
1163 .fn = key_export,
1164 .device = COMMAND_NO_DEVICE,
1165 .needs_root = true1,
1166};
1167
1168static struct command key_revoke_cmd = {
1169 .name = "revoke",
1170 .help = "Revoke an NVMeoF TLS PSK from a keyring",
1171 .fn = key_revoke,
1172 .device = COMMAND_NO_DEVICE,
1173 .needs_root = true1,
1174};
1175
1176static struct command *commands[] = {
1177 &gen_kxchap_cmd,
1178 &check_kxchap_cmd,
1179 &gen_tls_cmd,
1180 &check_tls_cmd,
1181 &insert_tls_cmd,
1182 &key_import_cmd,
1183 &key_export_cmd,
1184 &key_revoke_cmd,
1185 NULL((void*)0),
1186};
1187
1188static struct plugin plugin = {
1189 .name = "keys",
1190 .desc = "Manage NVMeoF KX-HMAC-CHAP secrets and TLS PSKs",
1191 .version = NVME_VERSION"3.1",
1192 .core = true1,
1193 .group = "Fabrics",
1194};
1195
1196static void __shr_constructor__attribute__((constructor)) register_plugin(void)
1197{
1198 plugin_add_group(&plugin, NULL((void*)0), commands);
1199 register_extension(&plugin);
1200}