Bug Summary

File:.build-ci/../keysd/src/kmip-psk.c
Warning:line 106, column 2
Branch condition evaluates to a garbage 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 kmip-psk.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 keysd/nvme-keysd-kmip.p -I keysd -I ../keysd -I ../keysd/src -I ccan -I ../ccan -I . -I .. -I libnvme/src -I ../libnvme/src -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 ../keysd/src/kmip-psk.c
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * This file is part of nvme-cli.
4 * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries.
5 *
6 * Authors: Martin Belanger <martin.belanger@dell.com>
7 */
8
9#include <errno(*__errno_location ()).h>
10#include <netdb.h>
11#include <stdbool.h>
12#include <stdlib.h>
13#include <string.h>
14#include <time.h>
15#include <sys/socket.h>
16
17#include <daemon-util/log.h>
18#include <daemon-util/resolve.h>
19#include <nvme/crypto.h>
20
21#include "config.h"
22#include "kmip.h"
23#include "kmip-psk.h"
24#include "kmip-tls.h"
25
26/* Locate and Get responses are small. */
27#define KMIP_MSG_MAX16384 16384
28
29/* Large enough for an interchange string in Secret Data. */
30#define KMIP_KEY_MAX256 256
31
32static void free_secret(void *p, size_t len)
33{
34 if (!p)
35 return;
36 explicit_bzero(p, len);
37 free(p);
38}
39
40static int psk_from_secret_data(struct libnvme_global_ctx *ctx,
41 const uint8_t *data, size_t len, char psk[KMIP_PSK_MAX128])
42{
43 enum libnvmf_hmac_alg hmac;
44 unsigned char version;
45 unsigned char *key;
46 size_t key_len;
47 int ret;
48
49 // A trailing newline is common in a key file.
50 while (len && (data[len - 1] == '\n' || data[len - 1] == '\r'))
51 len--;
52 if (!len || len >= KMIP_PSK_MAX128 || memchr(data, '\0', len)_Generic (0 ? (data) : (void *) 1, const void *: (const void *
) (memchr (data, '\0', len)), default: memchr (data, '\0', len
))
)
53 return -EMEDIUMTYPE124;
54 memcpy(psk, data, len);
55 psk[len] = '\0';
56
57 ret = libnvmf_import_tls_key_versioned(ctx, psk, &version, &hmac,
58 &key_len, &key);
59 if (ret) {
60 explicit_bzero(psk, KMIP_PSK_MAX128);
61 return -EMEDIUMTYPE124;
62 }
63 free_secret(key, key_len);
64
65 return 0;
66}
67
68static int psk_from_symmetric_key(struct libnvme_global_ctx *ctx,
69 const uint8_t *data, size_t len, char psk[KMIP_PSK_MAX128])
70{
71 char *encoded;
72 size_t n;
73 int ret;
74
75 if (len != 32 && len != 48)
76 return -EMEDIUMTYPE124;
77
78 ret = libnvmf_export_tls_key(ctx, data, len, &encoded);
79 if (ret)
80 return ret;
81
82 n = strlen(encoded);
83 if (n < KMIP_PSK_MAX128)
84 memcpy(psk, encoded, n + 1);
85 free_secret(encoded, n);
86
87 return n < KMIP_PSK_MAX128 ? 0 : -EMEDIUMTYPE124;
88}
89
90int kmip_psk_from_object(struct libnvme_global_ctx *ctx, uint32_t obj_type,
91 const uint8_t *data, size_t len, char psk[KMIP_PSK_MAX128])
92{
93 switch (obj_type) {
94 case KMIP_OBJ_SECRET_DATA:
95 return psk_from_secret_data(ctx, data, len, psk);
96 case KMIP_OBJ_SYMMETRIC_KEY:
97 return psk_from_symmetric_key(ctx, data, len, psk);
98 default:
99 return -EMEDIUMTYPE124;
100 }
101}
102
103static void log_kms_failure(const char *server, const char *op,
104 const struct kmip_result *res)
105{
106 log_err("KMIP %s: %s failed: status %u, reason %u%s%s", server, op,dmn_log_msg(DMN_LOG_ERR, "KMIP %s: %s failed: status %u, reason %u%s%s"
, server, op, res->status, res->reason, res->message
[0] ? ": " : "", res->message)
18
Branch condition evaluates to a garbage value
107 res->status, res->reason, res->message[0] ? ": " : "",dmn_log_msg(DMN_LOG_ERR, "KMIP %s: %s failed: status %u, reason %u%s%s"
, server, op, res->status, res->reason, res->message
[0] ? ": " : "", res->message)
108 res->message)dmn_log_msg(DMN_LOG_ERR, "KMIP %s: %s failed: status %u, reason %u%s%s"
, server, op, res->status, res->reason, res->message
[0] ? ": " : "", res->message)
;
109}
110
111/* Send the request in @req and read the response into @resp. */
112static int exchange(struct kmip_tls *t, struct ttlv_buf *req, uint8_t *resp,
113 size_t *len)
114{
115 int ret;
116
117 ret = kmip_tls_exchange(t, req->data, req->len, resp, KMIP_MSG_MAX16384,
118 len);
119 ttlv_buf_free(req);
120 ttlv_buf_init(req);
121
122 return ret;
123}
124
125/* Get the key from one server of @kms, at the addresses @addrs. */
126static int get_from_server(struct libnvme_global_ctx *ctx,
127 const struct keysd_kmip *kms, const char *server,
128 const struct addrinfo *addrs, int timeout_ms,
129 const char *key_path, const char *name, char psk[KMIP_PSK_MAX128])
130{
131 const struct kmip_tls_cfg cfg = {
132 .server = server,
133 .addrs = addrs,
134 .ca = kms->ca,
135 .cert = kms->client_cert,
136 .key = key_path,
137 .timeout_ms = timeout_ms,
138 };
139 const struct kmip_version *v = &kmip_v1_4;
140 uint8_t *resp, key[KMIP_KEY_MAX256];
141 struct kmip_tls *t = NULL((void*)0);
142 struct kmip_result res;
143 struct ttlv_buf req;
144 size_t len, key_len;
145 uint32_t obj_type;
146 char uid[128];
147 int ret;
148
149 resp = malloc(KMIP_MSG_MAX16384);
150 if (!resp)
8
Assuming 'resp' is non-null
9
Taking false branch
151 return -ENOMEM12;
152 ttlv_buf_init(&req);
153
154 ret = kmip_tls_connect(&cfg, &t);
155 if (ret)
10
Assuming 'ret' is 0
11
Taking false branch
156 goto out;
157
158 ret = kmip_build_locate(&req, v, name, KMIP_STATE_ACTIVE);
159 if (!ret)
12
Assuming 'ret' is not equal to 0
13
Taking false branch
160 ret = exchange(t, &req, resp, &len);
161 if (!ret
13.1
'ret' is not equal to 0
)
14
Taking false branch
162 ret = kmip_parse_locate(resp, len, v, uid, sizeof(uid), &res);
163 if (ret == -EREMOTEIO121)
15
Assuming the condition is true
16
Taking true branch
164 log_kms_failure(server, "Locate", &res);
17
Calling 'log_kms_failure'
165 if (ret)
166 goto out;
167
168 ret = kmip_build_get(&req, v, uid);
169 if (!ret)
170 ret = exchange(t, &req, resp, &len);
171 if (!ret)
172 ret = kmip_parse_get(resp, len, v, &obj_type, key, sizeof(key),
173 &key_len, &res);
174 if (ret == -EREMOTEIO121)
175 log_kms_failure(server, "Get", &res);
176 if (ret == -EOPNOTSUPP95)
177 ret = -EMEDIUMTYPE124;
178 if (ret)
179 goto out;
180
181 ret = kmip_psk_from_object(ctx, obj_type, key, key_len, psk);
182
183out:
184 kmip_tls_close(t);
185 ttlv_buf_free(&req);
186 explicit_bzero(key, sizeof(key));
187 free_secret(resp, KMIP_MSG_MAX16384);
188
189 return ret;
190}
191
192/* The answer does not depend on the server: the servers hold the same keys. */
193static bool_Bool final_error(int err)
194{
195 return err == -ENOENT2 || err == -ENOTUNIQ76 || err == -EMEDIUMTYPE124 ||
196 err == -ENOMEM12;
197}
198
199struct kmip_get {
200 struct dmn_resolver *resolver;
201 struct libnvme_global_ctx *ctx;
202 const struct keysd_kmip *kms;
203 struct dmn_resolve_query *query;
204 char *key_path;
205 char *name;
206 size_t first;
207 size_t n; // servers tried
208 int64_t deadline; // of the current server, CLOCK_MONOTONIC in ms
209 int ret;
210 kmip_psk_callback done;
211 void *user_data;
212};
213
214static int64_t now_ms(void)
215{
216 struct timespec ts;
217
218 clock_gettime(CLOCK_MONOTONIC1, &ts);
219
220 return (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
221}
222
223static size_t server_index(const struct kmip_get *g)
224{
225 return (g->first + g->n) % g->kms->nr_servers;
226}
227
228static void get_free(struct kmip_get *g)
229{
230 free(g->key_path);
231 free(g->name);
232 free(g);
233}
234
235static void on_resolved(int gai, const struct addrinfo *res,
236 void *user_data);
237
238/*
239 * Start the lookup of the next server. Return: 0 if on_resolved() will be
240 * called, or the error of the last server when no server is left.
241 */
242static int next_server(struct kmip_get *g)
243{
244 static const struct addrinfo hints = { .ai_socktype = SOCK_STREAMSOCK_STREAM };
245 const struct keysd_kmip *kms = g->kms;
246 const char *server;
247 int ret;
248
249 for (; g->n < kms->nr_servers; g->n++) {
250 server = kms->servers[server_index(g)];
251 g->deadline = now_ms() + kms->timeout_ms;
252 ret = dmn_resolve(g->resolver, server, kms->port, &hints,
253 kms->timeout_ms * 1000ULL, on_resolved, g,
254 &g->query);
255 if (!ret)
256 return 0;
257 log_err("KMIP %s: cannot resolve: %s", server, strerror(-ret))dmn_log_msg(DMN_LOG_ERR, "KMIP %s: cannot resolve: %s", server
, strerror(-ret))
;
258 g->ret = ret;
259 }
260
261 return g->ret;
262}
263
264static void on_resolved(int gai, const struct addrinfo *res,
265 void *user_data)
266{
267 struct kmip_get *g = user_data;
268 const char *server = g->kms->servers[server_index(g)];
269 char psk[KMIP_PSK_MAX128] = "";
270 int err = errno(*__errno_location ()); // for EAI_SYSTEM
271 int64_t left;
272 int ret;
273
274 g->query = NULL((void*)0);
275 left = g->deadline - now_ms();
276 if (gai == EAI_SYSTEM-11) {
1
Assuming the condition is false
2
Taking false branch
277 ret = err ? -err : -EHOSTUNREACH113;
278 log_err("KMIP %s: name lookup: %s", server, strerror(-ret))dmn_log_msg(DMN_LOG_ERR, "KMIP %s: name lookup: %s", server, strerror
(-ret))
;
279 } else if (gai) {
3
Assuming 'gai' is 0
4
Taking false branch
280 log_err("KMIP %s: %s", server, gai_strerror(gai))dmn_log_msg(DMN_LOG_ERR, "KMIP %s: %s", server, gai_strerror(
gai))
;
281 ret = -EHOSTUNREACH113;
282 } else if (left <= 0) {
5
Assuming 'left' is > 0
6
Taking false branch
283 log_err("KMIP %s: %s", server, strerror(ETIMEDOUT))dmn_log_msg(DMN_LOG_ERR, "KMIP %s: %s", server, strerror(110)
)
;
284 ret = -ETIMEDOUT110;
285 } else {
286 ret = get_from_server(g->ctx, g->kms, server, res, (int)left,
7
Calling 'get_from_server'
287 g->key_path, g->name, psk);
288 }
289
290 g->ret = ret;
291 if (ret && !final_error(ret)) {
292 g->n++;
293 ret = next_server(g);
294 if (!ret)
295 return;
296 }
297
298 g->done(ret, psk, server_index(g), g->user_data);
299 explicit_bzero(psk, sizeof(psk));
300 get_free(g);
301}
302
303int kmip_get_psk(struct dmn_resolver *resolver,
304 struct libnvme_global_ctx *ctx, const struct keysd_kmip *kms,
305 size_t first, const char *key_path, const char *name,
306 kmip_psk_callback done, void *user_data, struct kmip_get **ret)
307{
308 struct kmip_get *g;
309 int err;
310
311 if (!kms->nr_servers)
312 return -EHOSTUNREACH113;
313
314 g = calloc(1, sizeof(*g));
315 if (!g)
316 return -ENOMEM12;
317 g->name = strdup(name);
318 g->key_path = key_path ? strdup(key_path) : NULL((void*)0);
319 if (!g->name || (key_path && !g->key_path)) {
320 get_free(g);
321 return -ENOMEM12;
322 }
323 g->resolver = resolver;
324 g->ctx = ctx;
325 g->kms = kms;
326 g->first = first < kms->nr_servers ? first : 0;
327 g->ret = -EHOSTUNREACH113;
328 g->done = done;
329 g->user_data = user_data;
330
331 err = next_server(g);
332 if (err) {
333 get_free(g);
334 return err;
335 }
336 if (ret)
337 *ret = g;
338
339 return 0;
340}
341
342void kmip_get_cancel(struct kmip_get *g)
343{
344 if (!g)
345 return;
346
347 dmn_resolve_cancel(g->query);
348 get_free(g);
349}