Bug Summary

File:.build-ci/../plugins/rpmb/rpmb.c
Warning:line 942, column 9
Potential leak of memory pointed to by 'msg_buf'

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 rpmb.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/rpmb/rpmb.c
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (C) 2020 Micron Technology Inc. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16 * rpmb.c - Implementation of NVMe RPMB support commands
17 */
18#include <errno(*__errno_location ()).h>
19#include <fcntl.h>
20#include <limits.h>
21#include <stddef.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <sys/stat.h>
26#include <sys/types.h>
27#include <unistd.h>
28
29#include <libnvme.h>
30
31#include <ccan/array_size/array_size.h>
32#include <ccan/endian/endian.h>
33#include <shared/compiler-attributes-util.h>
34#include <shared/crypto-util.h>
35#include <shared/fs-util.h>
36
37#include "cleanup.h"
38#include "global-ctx.h"
39#include "nvme-print.h"
40#include "plugin.h"
41
42/* Read data from given file into buffer and return its length */
43static int read_file(const char *file, unsigned char **data, unsigned int *len)
44{
45 struct stat sb;
46 size_t size;
47 unsigned char *buf = NULL((void*)0);
48 int fd;
49 int err = -EINVAL22;
50
51 if (!file)
52 return err;
53
54 fd = shr_open_rawdata(file, O_RDONLY)open((file), (00));
55 if (fd < 0) {
56 nvme_show_error("Failed to open %s: %s", file, libnvme_strerror(errno))nvme_show_message(1, "Failed to open %s: %s", file, libnvme_strerror
((*__errno_location ())))
;
57 return fd;
58 }
59
60 err = fstat(fd, &sb);
61 if (err < 0) {
62 nvme_show_perror("fstat");
63 goto out;
64 }
65
66 size = sb.st_size;
67 buf = libnvme_alloc(size);
68 if (!buf) {
69 nvme_show_error("No memory for reading file: %s", file)nvme_show_message(1, "No memory for reading file: %s", file);
70 err = -ENOMEM12;
71 goto out;
72 }
73
74 err = read(fd, buf, size);
75 if (err < 0) {
76 err = -errno(*__errno_location ());
77 nvme_show_error("Failed to read data from file %s with %s", file,nvme_show_message(1, "Failed to read data from file %s with %s"
, file, libnvme_strerror((*__errno_location ())))
78 libnvme_strerror(errno))nvme_show_message(1, "Failed to read data from file %s with %s"
, file, libnvme_strerror((*__errno_location ())))
;
79 libnvme_free(buf);
80 goto out;
81 }
82 *data = buf;
83 *len = err;
84 err = 0;
85out:
86 close(fd);
87 return err;
88}
89
90/* Write given buffer data to specified file */
91static void write_file(unsigned char *data, size_t len, const char *file,
92 const char *msg)
93{
94 __cleanup_file__attribute__((cleanup(shr_cleanup_file))) FILE *fp = NULL((void*)0);
95
96 fp = fopen(file, "ab+");
97 if (fp) {
98 if (fwrite(data, 1, len, fp) != len)
99 nvme_show_error("Failed to write %s data to %s", msg ? msg : "",nvme_show_message(1, "Failed to write %s data to %s", msg ? msg
: "", file)
100 file)nvme_show_message(1, "Failed to write %s data to %s", msg ? msg
: "", file)
;
101 } else {
102 nvme_show_error("Failed to open %s file to write %s", file,nvme_show_message(1, "Failed to open %s file to write %s", file
, msg ? msg : "")
103 msg ? msg : "")nvme_show_message(1, "Failed to open %s file to write %s", file
, msg ? msg : "")
;
104 }
105}
106
107/* Various definitions used in RPMB related support */
108enum rpmb_request_type {
109 RPMB_REQ_AUTH_KEY_PROGRAM = 0x0001,
110 RPMB_REQ_READ_WRITE_CNTR = 0x0002,
111 RPMB_REQ_AUTH_DATA_WRITE = 0x0003,
112 RPMB_REQ_AUTH_DATA_READ = 0x0004,
113 RPMB_REQ_READ_RESULT = 0x0005,
114 RPMB_REQ_AUTH_DCB_WRITE = 0x0006,
115 RPMB_REQ_AUTH_DCB_READ = 0x0007,
116};
117
118/* RPMB data frame structure */
119#pragma pack(1)
120struct rpmb_data_frame_t {
121 unsigned char pad[191];
122 unsigned char mac[32];
123 unsigned char target; /* 0-6, should match with NSSF with SS, SR */
124 unsigned char nonce[16];
125 unsigned int write_counter;
126 unsigned int address;
127 unsigned int sectors;
128 unsigned short result;
129 unsigned short type; /* req or response */
130 unsigned char data[0]; /* in sector count times */
131};
132#pragma pack()
133
134struct rpmb_config_block_t {
135 unsigned char bp_enable;
136 unsigned char bp_lock;
137 unsigned char rsvd[510];
138};
139
140#define RPMB_DATA_FRAME_SIZE256 256
141#define RPMB_NVME_SECP0xEA 0xEA
142#define RPMB_NVME_SPSP0x0001 0x0001
143
144/* The RPMBS field of Identify Controller and its fields */
145struct ctrl_rpmbs_reg {
146 unsigned int rpmbs;
147 unsigned int num_targets;
148 unsigned int total_size; /* 128K units */
149 unsigned int access_size; /* in 512 byte count */
150};
151
152static int send_rpmb_req(struct libnvme_transport_handle *hdl, unsigned char tgt,
153 int size, struct rpmb_data_frame_t *req)
154{
155 struct libnvme_passthru_cmd cmd;
156
157 nvme_init_security_send(&cmd, NVME_NSID_NONE, tgt, RPMB_NVME_SPSP0x0001,
158 RPMB_NVME_SECP0xEA, 0, req, size);
159 return libnvme_exec_admin_passthru(hdl, &cmd);
160}
161
162static int recv_rpmb_rsp(struct libnvme_transport_handle *hdl, int tgt, int size,
163 struct rpmb_data_frame_t *rsp)
164{
165 struct libnvme_passthru_cmd cmd;
166
167 nvme_init_security_receive(&cmd, 0, tgt, RPMB_NVME_SPSP0x0001, RPMB_NVME_SECP0xEA, 0, rsp,
168 size);
169 return libnvme_exec_admin_passthru(hdl, &cmd);
170}
171
172/* Initialize nonce value in rpmb request frame. Returns 0 on success, -1 on failure. */
173static int rpmb_nonce_init(struct rpmb_data_frame_t *req)
174{
175 if (shr_getrandom(req->nonce, sizeof(req->nonce)) != 0) {
176 nvme_show_error("Failed to generate RPMB nonce")nvme_show_message(1, "Failed to generate RPMB nonce");
177 return -1;
178 }
179
180 return 0;
181}
182
183/* Read key from a given key buffer or key file */
184static unsigned char *read_rpmb_key(char *keystr, char *keyfile, unsigned int *keysize)
185{
186 unsigned char *keybuf = NULL((void*)0);
187 int err;
188
189 if (!keystr) {
190 if (keyfile) {
191 err = read_file(keyfile, &keybuf, keysize);
192 if (err < 0)
193 return NULL((void*)0);
194 }
195 } else {
196 size_t len = strlen(keystr);
197
198 keybuf = libnvme_alloc(len + 1);
199 if (keybuf) {
200 *keysize = len;
201 memcpy(keybuf, keystr, len);
202 }
203 }
204
205 return keybuf;
206}
207
208/* Initialize RPMB request frame with given values */
209static struct rpmb_data_frame_t *
210rpmb_request_init(unsigned int req_size, unsigned short type, unsigned char target,
211 unsigned char nonce, unsigned int addr, unsigned int sectors,
212 unsigned char *data, unsigned short data_offset, unsigned int data_size)
213{
214 struct rpmb_data_frame_t *req;
215
216 req = calloc(req_size, 1);
217 if (!req) {
218 nvme_show_error("Memory allocation failed for request 0x%04x", type)nvme_show_message(1, "Memory allocation failed for request 0x%04x"
, type)
;
219 return req;
220 }
221
222 req->type = type;
223 req->target = target;
224 req->address = addr;
225 req->sectors = sectors;
226
227 if (nonce && rpmb_nonce_init(req) != 0) {
228 free(req);
229 return NULL((void*)0);
230 }
231 if (data)
232 memcpy((unsigned char *)req + data_offset, data, data_size);
233
234 return req;
235}
236
237/* Process rpmb response and print appropriate error message */
238static int check_rpmb_response(struct rpmb_data_frame_t *req, struct rpmb_data_frame_t *rsp,
239 char *msg)
240{
241 const char *rpmb_result_string[] = {
242 "Operation successful",
243 "General failure",
244 "Authentication (MAC) failure",
245 "Counter failure (not matching/incrementing failure)",
246 "Address failure (out of range or wrong alignment)",
247 "Write (data/counter/result) failure",
248 "Read (data/counter/result) failure",
249 "Authentication key not yet programmed",
250 "Invalid device configuration block",
251 "Unknown error",
252 };
253
254 /* check error status before comparing nonce and mac */
255 if (rsp->result != 0) {
256 if (rsp->type != ((req->type << 8) & 0xFF00)) {
257 nvme_show_error("%s ! non-matching response 0x%04x for 0x%04x", msg,nvme_show_message(1, "%s ! non-matching response 0x%04x for 0x%04x"
, msg, rsp->type, req->type)
258 rsp->type, req->type)nvme_show_message(1, "%s ! non-matching response 0x%04x for 0x%04x"
, msg, rsp->type, req->type)
;
259 } else if ((rsp->result & 0x80) == 0x80) {
260 nvme_show_error("%s ! Expired write-counter !", msg)nvme_show_message(1, "%s ! Expired write-counter !", msg);
261 } else if (rsp->result) {
262 unsigned int code = rsp->result & 0x7F;
263
264 nvme_show_error("%s ! %s", msg,nvme_show_message(1, "%s ! %s", msg, code < (sizeof(rpmb_result_string
) / sizeof((rpmb_result_string)[0]) + 0) ? rpmb_result_string
[code] : "Unknown error")
265 code < ARRAY_SIZE(rpmb_result_string) ?nvme_show_message(1, "%s ! %s", msg, code < (sizeof(rpmb_result_string
) / sizeof((rpmb_result_string)[0]) + 0) ? rpmb_result_string
[code] : "Unknown error")
266 rpmb_result_string[code] : "Unknown error")nvme_show_message(1, "%s ! %s", msg, code < (sizeof(rpmb_result_string
) / sizeof((rpmb_result_string)[0]) + 0) ? rpmb_result_string
[code] : "Unknown error")
;
267 } else if (memcmp(req->nonce, rsp->nonce, 16)) {
268 nvme_show_error("%s ! non-matching nonce", msg)nvme_show_message(1, "%s ! non-matching nonce", msg);
269 } else if (memcmp(req->mac, rsp->mac, 32)) {
270 nvme_show_error("%s ! non-matching MAC", msg)nvme_show_message(1, "%s ! non-matching MAC", msg);
271 } else if ((req->write_counter + 1) != rsp->write_counter) {
272 nvme_show_error("%s ! out-of-sync write-counters", msg)nvme_show_message(1, "%s ! out-of-sync write-counters", msg);
273 }
274 }
275
276 return (int)rsp->result;
277}
278
279/*
280 * send an initialized rpmb request to the controller and read its response;
281 * expected response size given in 'rsp_size'. returns response buffer upon
282 * successful completion (caller must free), NULL otherwise
283 */
284static struct rpmb_data_frame_t *
285rpmb_read_request(struct libnvme_transport_handle *hdl, struct rpmb_data_frame_t *req,
286 int req_size, int rsp_size)
287{
288 struct rpmb_data_frame_t *rsp = NULL((void*)0);
289 unsigned char msg[1024] = { 0 };
290 int error;
291
292 snprintf((char *)msg, sizeof(msg), "RPMB request 0x%04x to target 0x%x",
293 req->type, req->target);
294
295 error = send_rpmb_req(hdl, req->target, req_size, req);
296 if (error != 0) {
297 nvme_show_error("%s failed with error = 0x%x", msg, error)nvme_show_message(1, "%s failed with error = 0x%x", msg, error
)
;
298 goto error_out;
299 }
300
301 /* read the result back */
302 rsp = calloc(rsp_size, 1);
303 if (!rsp) {
304 nvme_show_error("memory alloc failed for %s", msg)nvme_show_message(1, "memory alloc failed for %s", msg);
305 goto error_out;
306 }
307
308 /* Read result of previous request */
309 error = recv_rpmb_rsp(hdl, req->target, rsp_size, rsp);
310 if (error) {
311 nvme_show_error("error 0x%x receiving response for %s", error, msg)nvme_show_message(1, "error 0x%x receiving response for %s", error
, msg)
;
312 goto error_out;
313 }
314
315 /* validate response buffer - match target, nonce, and mac */
316 error = check_rpmb_response(req, rsp, (char *)msg);
317 if (error == 0)
318 return rsp;
319
320error_out:
321 free(rsp);
322 return NULL((void*)0);
323}
324
325/* read current write counter value from controller */
326static int rpmb_read_write_counter(struct libnvme_transport_handle *hdl, unsigned char target,
327 unsigned int *counter)
328{
329 int error = -1;
330 int req_size = sizeof(struct rpmb_data_frame_t);
331 struct rpmb_data_frame_t *req = NULL((void*)0);
332 struct rpmb_data_frame_t *rsp = NULL((void*)0);
333
334 req = rpmb_request_init(req_size, RPMB_REQ_READ_WRITE_CNTR, target, 1, 0, 0, NULL((void*)0), 0,
335 0);
336 if (!req)
337 goto out;
338 rsp = rpmb_read_request(hdl, req, req_size, req_size);
339 if (!rsp)
340 goto out;
341 *counter = rsp->write_counter;
342 error = 0;
343
344out:
345 free(req);
346 free(rsp);
347 return error;
348}
349
350/*
351 * Read current device configuration block into specified buffer. It also
352 * returns current write counter value returned as part of response, in case
353 * of error it returns 0
354 */
355static unsigned int rpmb_read_config_block(struct libnvme_transport_handle *hdl,
356 unsigned char **config_buf)
357{
358 int req_size = sizeof(struct rpmb_data_frame_t);
359 int cfg_size = sizeof(struct rpmb_config_block_t);
360 int rsp_size = req_size + cfg_size;
361
362 struct rpmb_data_frame_t *req = NULL((void*)0);
363 struct rpmb_data_frame_t *rsp = NULL((void*)0);
364 struct rpmb_config_block_t *cfg = NULL((void*)0);
365 unsigned int retval = 0;
366
367 /* initialize request with nonce, no data on input */
368 req = rpmb_request_init(req_size, RPMB_REQ_AUTH_DCB_READ, 0, 1, 0, 1, 0, 0, 0);
369 if (!req)
370 return 0;
371 rsp = rpmb_read_request(hdl, req, req_size, rsp_size);
372 if (!rsp) {
373 free(req);
374 return 0;
375 }
376
377 /* copy configuration data to be sent back to caller */
378 cfg = calloc(cfg_size, 1);
379 if (!cfg) {
380 nvme_show_error("failed to allocate RPMB config buffer")nvme_show_message(1, "failed to allocate RPMB config buffer");
381 goto out;
382 }
383
384 memcpy(cfg, rsp->data, cfg_size);
385 *config_buf = (unsigned char *)cfg;
386 cfg = NULL((void*)0);
387 retval = rsp->write_counter;
388out:
389 free(req);
390 free(rsp);
391 return retval;
392}
393
394static int rpmb_auth_data_read(struct libnvme_transport_handle *hdl, unsigned char target,
395 unsigned int offset, unsigned char **msg_buf, int msg_size,
396 int acc_size)
397{
398 struct rpmb_data_frame_t *req = NULL((void*)0);
399 struct rpmb_data_frame_t *rsp = NULL((void*)0);
400 int req_size = sizeof(struct rpmb_data_frame_t);
401 int chunk_size = (acc_size < msg_size) ? acc_size : msg_size;
6
Assuming 'acc_size' is < 'msg_size'
7
'?' condition is true
402 int xfer = chunk_size;
403 unsigned char *bufp = malloc(msg_size * 512);
8
Memory is allocated
404 unsigned char *tbufp = bufp;
405 int data_size, rsp_size;
406
407 if (!bufp) {
9
Assuming 'bufp' is non-null
10
Taking false branch
408 nvme_show_error("Failed to allocated memory for read-data req")nvme_show_message(1, "Failed to allocated memory for read-data req"
)
;
409 return -1;
410 }
411
412 while (xfer > 0) {
11
Assuming 'xfer' is <= 0
12
Loop condition is false. Execution continues on line 444
413 rsp_size = req_size + xfer * 512;
414 req = rpmb_request_init(req_size, RPMB_REQ_AUTH_DATA_READ, target, 1, offset,
415 xfer, 0, 0, 0);
416 if (!req) {
417 nvme_show_error("failed to allocate read-data request")nvme_show_message(1, "failed to allocate read-data request");
418 goto err_free_bufp;
419 }
420 rsp = rpmb_read_request(hdl, req, req_size, rsp_size);
421 if (!rsp) {
422 nvme_show_error("read_request failed")nvme_show_message(1, "read_request failed");
423 goto err_free_req;
424 }
425
426 if (rsp->sectors > (unsigned int)xfer) {
427 nvme_show_error("device reported %u sectors, more than the %d requested",nvme_show_message(1, "device reported %u sectors, more than the %d requested"
, rsp->sectors, xfer)
428 rsp->sectors, xfer)nvme_show_message(1, "device reported %u sectors, more than the %d requested"
, rsp->sectors, xfer)
;
429 goto err_free_rsp;
430 }
431
432 data_size = rsp->sectors * 512;
433 memcpy(tbufp, rsp->data, data_size);
434 offset += rsp->sectors;
435 tbufp += data_size;
436 if (offset + chunk_size > msg_size)
437 xfer = msg_size - offset;
438 else
439 xfer = chunk_size;
440 free(req);
441 free(rsp);
442 }
443
444 *msg_buf = bufp;
445 return offset;
446
447err_free_rsp:
448 free(rsp);
449err_free_req:
450 free(req);
451err_free_bufp:
452 free(bufp);
453 return -1;
454}
455
456/* Implementation of programming authentication key to given RPMB target */
457static int rpmb_program_auth_key(struct libnvme_transport_handle *hdl, unsigned char target,
458 unsigned char *key_buf, int key_size)
459{
460 int req_size = sizeof(struct rpmb_data_frame_t);
461 int rsp_size = sizeof(struct rpmb_data_frame_t);
462
463 struct rpmb_data_frame_t *req = NULL((void*)0);
464 struct rpmb_data_frame_t *rsp = NULL((void*)0);
465
466 int err = -ENOMEM12;
467
468 req = rpmb_request_init(req_size, RPMB_REQ_AUTH_KEY_PROGRAM, target, 0, 0, 0, key_buf,
469 (223 - key_size), key_size);
470 if (!req) {
471 nvme_show_error("failed to allocate request buffer memory")nvme_show_message(1, "failed to allocate request buffer memory"
)
;
472 goto out;
473 }
474
475 /* send the request and get response */
476 err = send_rpmb_req(hdl, req->target, req_size, req);
477 if (err) {
478 nvme_show_error("RPMB request 0x%04x for 0x%x, err: %d", req->type, req->target,nvme_show_message(1, "RPMB request 0x%04x for 0x%x, err: %d",
req->type, req->target, err)
479 err)nvme_show_message(1, "RPMB request 0x%04x for 0x%x, err: %d",
req->type, req->target, err)
;
480 goto out;
481 }
482
483 /* send the request to get the result and then request to get the response */
484 rsp = calloc(rsp_size, 1);
485 if (!rsp) {
486 nvme_show_error("failed to allocate response buffer memory")nvme_show_message(1, "failed to allocate response buffer memory"
)
;
487 err = -ENOMEM12;
488 goto out;
489 }
490
491 rsp->target = req->target;
492 rsp->type = RPMB_REQ_READ_RESULT;
493 err = send_rpmb_req(hdl, req->target, rsp_size, rsp);
494 if (err || rsp->result) {
495 nvme_show_error("Program auth key read result 0x%x, error = 0x%x", rsp->result,nvme_show_message(1, "Program auth key read result 0x%x, error = 0x%x"
, rsp->result, err)
496 err)nvme_show_message(1, "Program auth key read result 0x%x, error = 0x%x"
, rsp->result, err)
;
497 goto out;
498 }
499
500 /* reuse response buffer */
501 memset(rsp, 0, rsp_size);
502 err = recv_rpmb_rsp(hdl, req->target, rsp_size, rsp);
503 if (err != 0)
504 nvme_show_error("Program Key recv error = 0x%x", err)nvme_show_message(1, "Program Key recv error = 0x%x", err);
505 else
506 err = check_rpmb_response(req, rsp, "Failed to Program Key");
507out:
508 free(req);
509 free(rsp);
510
511 return err;
512}
513
514/*
515 * Implementation of RPMB authenticated data write command; this function
516 * transfers msg_size bytes from msg_buf to controller 'addr'. Returns
517 * number of bytes actually written to, otherwise negative error code on
518 * failures.
519 */
520static int auth_data_write_chunk(struct libnvme_transport_handle *hdl, unsigned char tgt,
521 unsigned int addr, unsigned char *msg_buf, int msg_size,
522 unsigned char *keybuf, int keysize)
523{
524 int req_size = sizeof(struct rpmb_data_frame_t) + msg_size;
525 int rsp_size = sizeof(struct rpmb_data_frame_t);
526
527 struct rpmb_data_frame_t *req = NULL((void*)0);
528 struct rpmb_data_frame_t *rsp = NULL((void*)0);
529
530 unsigned int write_cntr = 0;
531 unsigned char *mac = NULL((void*)0);
532 int error = -ENOMEM12;
533
534 /* get current write counter and copy to the request */
535 error = rpmb_read_write_counter(hdl, tgt, &write_cntr);
536 if (error != 0) {
537 nvme_show_error("Failed to read write counter for write-data")nvme_show_message(1, "Failed to read write counter for write-data"
)
;
538 goto out;
539 }
540
541 req = rpmb_request_init(req_size, RPMB_REQ_AUTH_DATA_WRITE, tgt, 0, addr,
542 (msg_size / 512), msg_buf,
543 offsetof(struct rpmb_data_frame_t, data)__builtin_offsetof(struct rpmb_data_frame_t, data), msg_size);
544 if (!req) {
545 nvme_show_error("Memory alloc failed for write-data command")nvme_show_message(1, "Memory alloc failed for write-data command"
)
;
546 goto out;
547 }
548
549 req->write_counter = write_cntr;
550
551 /* compute HMAC hash */
552 mac = shr_hmac_sha256(((unsigned char *)req + 223), req_size - 223, keybuf, keysize);
553 if (!mac) {
554 nvme_show_error("failed to compute HMAC-SHA256")nvme_show_message(1, "failed to compute HMAC-SHA256");
555 error = -1;
556 goto out;
557 }
558
559 memcpy(req->mac, mac, 32);
560
561 /* send the request and get response */
562 error = send_rpmb_req(hdl, tgt, req_size, req);
563 if (error != 0) {
564 nvme_show_error("RPMB request 0x%04x for 0x%x, error: %d", req->type, tgt,nvme_show_message(1, "RPMB request 0x%04x for 0x%x, error: %d"
, req->type, tgt, error)
565 error)nvme_show_message(1, "RPMB request 0x%04x for 0x%x, error: %d"
, req->type, tgt, error)
;
566 goto out;
567 }
568
569 /* send the request to get the result and then request to get the response */
570 rsp = calloc(rsp_size, 1);
571 if (!rsp) {
572 nvme_show_error("failed to allocate response buffer memory")nvme_show_message(1, "failed to allocate response buffer memory"
)
;
573 error = -ENOMEM12;
574 goto out;
575 }
576 rsp->target = req->target;
577 rsp->type = RPMB_REQ_READ_RESULT;
578 error = send_rpmb_req(hdl, tgt, rsp_size, rsp);
579 if (error != 0 || rsp->result != 0) {
580 nvme_show_error("Write-data read result 0x%x, error = 0x%x", rsp->result,nvme_show_message(1, "Write-data read result 0x%x, error = 0x%x"
, rsp->result, error)
581 error)nvme_show_message(1, "Write-data read result 0x%x, error = 0x%x"
, rsp->result, error)
;
582 goto out;
583 }
584
585 /* Read final response */
586 memset(rsp, 0, rsp_size);
587 error = recv_rpmb_rsp(hdl, tgt, rsp_size, rsp);
588 if (error != 0)
589 nvme_show_error("Auth data write recv error = 0x%x", error)nvme_show_message(1, "Auth data write recv error = 0x%x", error
)
;
590 else
591 error = check_rpmb_response(req, rsp, "Failed to write-data");
592out:
593 free(req);
594 free(rsp);
595 free(mac);
596
597 return error;
598}
599
600/* send the request and get response */
601static int rpmb_auth_data_write(struct libnvme_transport_handle *hdl, unsigned char target,
602 unsigned int addr, int acc_size, unsigned char *msg_buf,
603 int msg_size, unsigned char *keybuf, int keysize)
604{
605 int chunk_size = acc_size < msg_size ? acc_size : msg_size;
606 int xfer = chunk_size;
607 int offset = 0;
608
609 while (xfer > 0) {
610 if (auth_data_write_chunk(hdl, target, (addr + offset / 512), msg_buf + offset,
611 xfer, keybuf, keysize) != 0) {
612 /* error writing chunk data */
613 break;
614 }
615
616 offset += xfer;
617 if (offset + chunk_size > msg_size)
618 xfer = msg_size - offset;
619 else
620 xfer = chunk_size;
621 }
622
623 return offset;
624}
625
626/* writes given config_block buffer to the drive target 0 */
627static int rpmb_write_config_block(struct libnvme_transport_handle *hdl,
628 unsigned char *cfg_buf, unsigned char *keybuf, int keysize)
629{
630 int cfg_size = sizeof(struct rpmb_config_block_t);
631 int rsp_size = sizeof(struct rpmb_data_frame_t);
632 int req_size = rsp_size + cfg_size;
633
634 struct rpmb_data_frame_t *req = NULL((void*)0);
635 struct rpmb_data_frame_t *rsp = NULL((void*)0);
636 unsigned char *cfg_buf_read = NULL((void*)0), *mac = NULL((void*)0);
637 unsigned int write_cntr = 0;
638 int error = -ENOMEM12;
639
640 /* initialize request */
641 req = rpmb_request_init(req_size, RPMB_REQ_AUTH_DCB_WRITE, 0, 0, 0, 1, cfg_buf,
642 offsetof(struct rpmb_data_frame_t, data)__builtin_offsetof(struct rpmb_data_frame_t, data), cfg_size);
643 if (!req) {
644 nvme_show_error("failed to allocate rpmb request buffer")nvme_show_message(1, "failed to allocate rpmb request buffer"
)
;
645 goto out;
646 }
647
648 /* read config block write_counter from controller */
649 write_cntr = rpmb_read_config_block(hdl, &cfg_buf_read);
650 if (!cfg_buf_read) {
651 nvme_show_error("failed to read config block write counter")nvme_show_message(1, "failed to read config block write counter"
)
;
652 error = -EIO5;
653 goto out;
654 }
655
656 free(cfg_buf_read);
657 req->write_counter = write_cntr;
658 mac = shr_hmac_sha256(((unsigned char *)req + 223), req_size - 223, keybuf, keysize);
659 if (!mac) {
660 nvme_show_error("failed to compute hmac-sha256 hash")nvme_show_message(1, "failed to compute hmac-sha256 hash");
661 error = -EINVAL22;
662 goto out;
663 }
664
665 memcpy(req->mac, mac, sizeof(req->mac));
666
667 error = send_rpmb_req(hdl, 0, req_size, req);
668 if (error != 0) {
669 nvme_show_error("Write-config RPMB request, error = 0x%x", error)nvme_show_message(1, "Write-config RPMB request, error = 0x%x"
, error)
;
670 goto out;
671 }
672
673 /* get response */
674 rsp = calloc(rsp_size, 1);
675 if (!rsp) {
676 nvme_show_error("failed to allocate response buffer memory")nvme_show_message(1, "failed to allocate response buffer memory"
)
;
677 error = -ENOMEM12;
678 goto out;
679 }
680
681 /* get result first */
682 memset(rsp, 0, rsp_size);
683 rsp->target = req->target;
684 rsp->type = RPMB_REQ_READ_RESULT;
685 /* get the response and validate */
686 error = recv_rpmb_rsp(hdl, req->target, rsp_size, rsp);
687 if (error != 0) {
688 nvme_show_error("Failed getting write-config response error = 0x%x", error)nvme_show_message(1, "Failed getting write-config response error = 0x%x"
, error)
;
689 goto out;
690 }
691 error = check_rpmb_response(req, rsp, "Failed to retrieve write-config response");
692out:
693 free(req);
694 free(rsp);
695 free(mac);
696
697 return error;
698}
699
700static bool_Bool invalid_xfer_size(int blocks, unsigned int bpsz)
701{
702 return (blocks <= 0) || (blocks * 512) > ((bpsz + 1) * 128 * 1024);
703}
704
705/* Identify the controller and fetch its RPMBS support register */
706static int rpmb_get_regs(struct libnvme_transport_handle *hdl,
707 struct ctrl_rpmbs_reg *regs)
708{
709 struct nvme_id_ctrl ctrl;
710 struct libnvme_passthru_cmd cmd;
711 int err;
712
713 nvme_init_identify_ctrl(&cmd, &ctrl);
714 err = libnvme_exec_admin_passthru(hdl, &cmd);
715 if (err)
716 return err;
717
718 regs->rpmbs = le32_to_cpu(ctrl.rpmbs);
719 regs->num_targets = regs->rpmbs & NVME_CTRL_RPMBS_NR_UNITS;
720 regs->total_size = (regs->rpmbs & NVME_CTRL_RPMBS_TOTAL_SIZE) >> 16;
721 regs->access_size = (regs->rpmbs & NVME_CTRL_RPMBS_ACCESS_SIZE) >> 24;
722 if (regs->num_targets == 0) {
723 nvme_show_error("No RPMB targets are supported by the drive")nvme_show_message(1, "No RPMB targets are supported by the drive"
)
;
724 return -1;
725 }
726
727 return 0;
728}
729
730/* Read key from a given key buffer or key file and validate its size */
731static unsigned char *rpmb_read_and_validate_key(char *keystr, char *keyfile,
732 unsigned int *keysize)
733{
734 unsigned char *keybuf = read_rpmb_key(keystr, keyfile, keysize);
735
736 if (!keybuf) {
737 nvme_show_error("Failed to read key")nvme_show_message(1, "Failed to read key");
738 return NULL((void*)0);
739 }
740
741 if (*keysize > 223 || *keysize == 0) {
742 nvme_show_error("Invalid key size %u, valid input 1 to 223", *keysize)nvme_show_message(1, "Invalid key size %u, valid input 1 to 223"
, *keysize)
;
743 libnvme_free(keybuf);
744 return NULL((void*)0);
745 }
746
747 return keybuf;
748}
749
750/* Read msg data from either a literal string or a file */
751static int rpmb_read_msg(char *msgstr, char *msgfile, unsigned char **msg_buf,
752 unsigned int *msg_size)
753{
754 if (msgstr) {
755 size_t len = strlen(msgstr);
756
757 *msg_buf = libnvme_alloc(len + 1);
758 if (!*msg_buf)
759 return -ENOMEM12;
760 *msg_size = len;
761 memcpy(*msg_buf, msgstr, len);
762 return 0;
763 }
764
765 return read_file(msgfile, msg_buf, msg_size);
766}
767
768static int rpmb_info(int argc, char **argv, struct command *acmd, struct plugin *plugin)
769{
770 const char *desc = "Print RPMB support information for the controller";
771
772 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
773 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
774 struct ctrl_rpmbs_reg regs;
775 int err;
776
777 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
778 OPT_END(){ ((void*)0) }
779 };
780
781 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
782 if (err)
783 return err;
784
785 err = rpmb_get_regs(hdl, &regs);
786 if (err)
787 return err;
788
789 nvme_show_id_ctrl_rpmbs(regs.rpmbs, 0);
790
791 return 0;
792}
793
794static int rpmb_program_key(int argc, char **argv, struct command *acmd, struct plugin *plugin)
795{
796 const char *desc = "Program the RPMB authentication key for a given target";
797 const char *key = "key to be used for authentication";
798 const char *kfile = "key file that has authentication key to be used";
799 const char *target = "RPMB target - numerical value of 0 to 6, default 0";
800
801 struct config {
802 char *key;
803 char *keyfile;
804 __u8 target;
805 };
806
807 struct config cfg = {
808 .key = NULL((void*)0),
809 .keyfile = NULL((void*)0),
810 .target = 0,
811 };
812
813 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
814 OPT_STRING("key", 'k', "key", &cfg.key, key){"key", 'k', "key", CFG_STRING, _Generic(&cfg.key, char *
* : (&cfg.key), const char ** : (&cfg.key)), ARGCONFIG_ARG_REQUIRED
, key, 0, }
,
815 OPT_FILE("keyfile", 'g', &cfg.keyfile, kfile){"keyfile", 'g', "FILE", CFG_FILE, _Generic(&cfg.keyfile,
char ** : (&cfg.keyfile), const char ** : (&cfg.keyfile
)), ARGCONFIG_ARG_REQUIRED, kfile, 0, }
,
816 OPT_BYTE("target", 't', &cfg.target, target){"target", 't', "NUM", CFG_BYTE, _Generic(&cfg.target, uint8_t
* : (&cfg.target)), ARGCONFIG_ARG_REQUIRED, target, 0, }
,
817 OPT_END(){ ((void*)0) }
818 };
819
820 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
821 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
822 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) unsigned char *key_buf = NULL((void*)0);
823 struct ctrl_rpmbs_reg regs;
824 unsigned int key_size = 0;
825 int err;
826
827 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
828 if (err)
829 return err;
830
831 err = rpmb_get_regs(hdl, &regs);
832 if (err)
833 return err;
834
835 key_buf = rpmb_read_and_validate_key(cfg.key, cfg.keyfile, &key_size);
836 if (!key_buf)
837 return -1;
838
839 err = rpmb_program_auth_key(hdl, cfg.target, key_buf, key_size);
840 if (!err)
841 nvme_show_verbose_result("RPMB authentication key programmed")nvme_show_verbose_message("RPMB authentication key programmed"
)
;
842
843 return err;
844}
845
846static int rpmb_read_counter(int argc, char **argv, struct command *acmd,
847 struct plugin *plugin)
848{
849 const char *desc = "Read the current RPMB write counter for a given target";
850 const char *target = "RPMB target - numerical value of 0 to 6, default 0";
851
852 struct config {
853 __u8 target;
854 };
855
856 struct config cfg = {
857 .target = 0,
858 };
859
860 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
861 OPT_BYTE("target", 't', &cfg.target, target){"target", 't', "NUM", CFG_BYTE, _Generic(&cfg.target, uint8_t
* : (&cfg.target)), ARGCONFIG_ARG_REQUIRED, target, 0, }
,
862 OPT_END(){ ((void*)0) }
863 };
864
865 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
866 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
867 struct ctrl_rpmbs_reg regs;
868 unsigned int write_cntr = 0;
869 int err;
870
871 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
872 if (err)
873 return err;
874
875 err = rpmb_get_regs(hdl, &regs);
876 if (err)
877 return err;
878
879 err = rpmb_read_write_counter(hdl, cfg.target, &write_cntr);
880 if (!err)
881 nvme_show_result("Write Counter is: %u", write_cntr)nvme_show_message(0, "Write Counter is: %u", write_cntr);
882
883 return err;
884}
885
886static int rpmb_read_data(int argc, char **argv, struct command *acmd, struct plugin *plugin)
887{
888 const char *desc = "Read authenticated data from an RPMB target";
889 const char *mfile = "data file to save read data to";
890 const char *target = "RPMB target - numerical value of 0 to 6, default 0";
891 const char *address = "Sector offset to read from for an RPMB target, default 0";
892 const char *blocks = "Number of 512 byte blocks to read";
893
894 struct config {
895 char *msgfile;
896 __u32 address;
897 __u32 blocks;
898 __u8 target;
899 };
900
901 struct config cfg = {
902 .msgfile = NULL((void*)0),
903 .address = 0,
904 .blocks = 0,
905 .target = 0,
906 };
907
908 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
909 OPT_FILE("msgfile", 'f', &cfg.msgfile, mfile){"msgfile", 'f', "FILE", CFG_FILE, _Generic(&cfg.msgfile,
char ** : (&cfg.msgfile), const char ** : (&cfg.msgfile
)), ARGCONFIG_ARG_REQUIRED, mfile, 0, }
,
910 OPT_UINT("address", 0, &cfg.address, address){"address", 0, "NUM", CFG_POSITIVE, _Generic(&cfg.address
, uint32_t * : (&cfg.address)), ARGCONFIG_ARG_REQUIRED, address
, 0, }
,
911 OPT_UINT("blocks", 'b', &cfg.blocks, blocks){"blocks", 'b', "NUM", CFG_POSITIVE, _Generic(&cfg.blocks
, uint32_t * : (&cfg.blocks)), ARGCONFIG_ARG_REQUIRED, blocks
, 0, }
,
912 OPT_BYTE("target", 't', &cfg.target, target){"target", 't', "NUM", CFG_BYTE, _Generic(&cfg.target, uint8_t
* : (&cfg.target)), ARGCONFIG_ARG_REQUIRED, target, 0, }
,
913 OPT_END(){ ((void*)0) }
914 };
915
916 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
917 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
918 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) unsigned char *msg_buf = NULL((void*)0);
919 struct ctrl_rpmbs_reg regs;
920 int err;
921
922 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
923 if (err)
1
Assuming 'err' is 0
2
Taking false branch
924 return err;
925
926 err = rpmb_get_regs(hdl, &regs);
927 if (err
2.1
'err' is 0
)
3
Taking false branch
928 return err;
929
930 if (invalid_xfer_size(cfg.blocks, regs.total_size)) {
4
Taking false branch
931 nvme_show_error("invalid transfer size %u", cfg.blocks * 512)nvme_show_message(1, "invalid transfer size %u", cfg.blocks *
512)
;
932 return -1;
933 }
934
935 err = rpmb_auth_data_read(hdl, cfg.target, cfg.address, &msg_buf, cfg.blocks,
5
Calling 'rpmb_auth_data_read'
13
Returned allocated memory via 4th parameter
936 (regs.access_size + 1));
937 if (err > 0 && msg_buf) {
14
Assuming 'err' is <= 0
938 nvme_show_result("Writing %d bytes to file %s", err * 512, cfg.msgfile)nvme_show_message(0, "Writing %d bytes to file %s", err * 512
, cfg.msgfile)
;
939 write_file(msg_buf, err * 512, cfg.msgfile, NULL((void*)0));
940 }
941
942 return err > 0 ? 0 : err;
15
Potential leak of memory pointed to by 'msg_buf'
943}
944
945static int rpmb_write_data(int argc, char **argv, struct command *acmd, struct plugin *plugin)
946{
947 const char *desc = "Write authenticated data to an RPMB target";
948 const char *msg = "data to be written";
949 const char *mfile = "data file to be written";
950 const char *kfile = "key file that has authentication key to be used";
951 const char *target = "RPMB target - numerical value of 0 to 6, default 0";
952 const char *address = "Sector offset to write to for an RPMB target, default 0";
953 const char *blocks = "Number of 512 byte blocks to write";
954 const char *key = "key to be used for authentication";
955
956 struct config {
957 char *key;
958 char *msg;
959 char *keyfile;
960 char *msgfile;
961 __u32 address;
962 __u32 blocks;
963 __u8 target;
964 };
965
966 struct config cfg = {
967 .key = NULL((void*)0),
968 .msg = NULL((void*)0),
969 .msgfile = NULL((void*)0),
970 .keyfile = NULL((void*)0),
971 .address = 0,
972 .blocks = 0,
973 .target = 0,
974 };
975
976 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
977 OPT_FILE("msgfile", 'f', &cfg.msgfile, mfile){"msgfile", 'f', "FILE", CFG_FILE, _Generic(&cfg.msgfile,
char ** : (&cfg.msgfile), const char ** : (&cfg.msgfile
)), ARGCONFIG_ARG_REQUIRED, mfile, 0, }
,
978 OPT_FILE("keyfile", 'g', &cfg.keyfile, kfile){"keyfile", 'g', "FILE", CFG_FILE, _Generic(&cfg.keyfile,
char ** : (&cfg.keyfile), const char ** : (&cfg.keyfile
)), ARGCONFIG_ARG_REQUIRED, kfile, 0, }
,
979 OPT_STRING("key", 'k', "key", &cfg.key, key){"key", 'k', "key", CFG_STRING, _Generic(&cfg.key, char *
* : (&cfg.key), const char ** : (&cfg.key)), ARGCONFIG_ARG_REQUIRED
, key, 0, }
,
980 OPT_STRING("msg", 'd', "data", &cfg.msg, msg){"msg", 'd', "data", CFG_STRING, _Generic(&cfg.msg, char *
* : (&cfg.msg), const char ** : (&cfg.msg)), ARGCONFIG_ARG_REQUIRED
, msg, 0, }
,
981 OPT_UINT("address", 0, &cfg.address, address){"address", 0, "NUM", CFG_POSITIVE, _Generic(&cfg.address
, uint32_t * : (&cfg.address)), ARGCONFIG_ARG_REQUIRED, address
, 0, }
,
982 OPT_UINT("blocks", 'b', &cfg.blocks, blocks){"blocks", 'b', "NUM", CFG_POSITIVE, _Generic(&cfg.blocks
, uint32_t * : (&cfg.blocks)), ARGCONFIG_ARG_REQUIRED, blocks
, 0, }
,
983 OPT_BYTE("target", 't', &cfg.target, target){"target", 't', "NUM", CFG_BYTE, _Generic(&cfg.target, uint8_t
* : (&cfg.target)), ARGCONFIG_ARG_REQUIRED, target, 0, }
,
984 OPT_END(){ ((void*)0) }
985 };
986
987 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
988 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
989 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) unsigned char *key_buf = NULL((void*)0);
990 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) unsigned char *msg_buf = NULL((void*)0);
991 struct ctrl_rpmbs_reg regs;
992 unsigned int key_size = 0;
993 unsigned int msg_size = 0;
994 int err;
995
996 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
997 if (err)
998 return err;
999
1000 err = rpmb_get_regs(hdl, &regs);
1001 if (err)
1002 return err;
1003
1004 key_buf = rpmb_read_and_validate_key(cfg.key, cfg.keyfile, &key_size);
1005 if (!key_buf)
1006 return -1;
1007
1008 err = rpmb_read_msg(cfg.msg, cfg.msgfile, &msg_buf, &msg_size);
1009 if (err || !msg_size) {
1010 nvme_show_error("Failed to read msg data")nvme_show_message(1, "Failed to read msg data");
1011 return -1;
1012 }
1013
1014 if (invalid_xfer_size(cfg.blocks, regs.total_size) || (cfg.blocks * 512) > msg_size) {
1015 nvme_show_error("invalid transfer size %u", cfg.blocks * 512)nvme_show_message(1, "invalid transfer size %u", cfg.blocks *
512)
;
1016 return -1;
1017 } else if ((cfg.blocks * 512) < msg_size) {
1018 msg_size = cfg.blocks * 512;
1019 }
1020
1021 err = rpmb_auth_data_write(hdl, cfg.target, cfg.address, ((regs.access_size + 1) * 512),
1022 msg_buf, msg_size, key_buf, key_size);
1023
1024 /* print whatever extent of data written to target */
1025 nvme_show_result("Written %d sectors out of %u @target(%d):0x%x", err / 512,nvme_show_message(0, "Written %d sectors out of %u @target(%d):0x%x"
, err / 512, msg_size / 512, cfg.target, cfg.address)
1026 msg_size / 512, cfg.target, cfg.address)nvme_show_message(0, "Written %d sectors out of %u @target(%d):0x%x"
, err / 512, msg_size / 512, cfg.target, cfg.address)
;
1027
1028 return err == (int)msg_size ? 0 : -1;
1029}
1030
1031static int rpmb_read_config(int argc, char **argv, struct command *acmd,
1032 struct plugin *plugin)
1033{
1034 const char *desc = "Read the RPMB device configuration block";
1035 const char *mfile = "data file to save the read configuration block to";
1036
1037 struct config {
1038 char *msgfile;
1039 };
1040
1041 struct config cfg = {
1042 .msgfile = NULL((void*)0),
1043 };
1044
1045 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
1046 OPT_FILE("msgfile", 'f', &cfg.msgfile, mfile){"msgfile", 'f', "FILE", CFG_FILE, _Generic(&cfg.msgfile,
char ** : (&cfg.msgfile), const char ** : (&cfg.msgfile
)), ARGCONFIG_ARG_REQUIRED, mfile, 0, }
,
1047 OPT_END(){ ((void*)0) }
1048 };
1049
1050 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
1051 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
1052 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) unsigned char *msg_buf = NULL((void*)0);
1053 struct ctrl_rpmbs_reg regs;
1054 unsigned int write_cntr;
1055 int err;
1056
1057 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
1058 if (err)
1059 return err;
1060
1061 err = rpmb_get_regs(hdl, &regs);
1062 if (err)
1063 return err;
1064
1065 write_cntr = rpmb_read_config_block(hdl, &msg_buf);
1066 if (!msg_buf) {
1067 nvme_show_error("failed to read config block")nvme_show_message(1, "failed to read config block");
1068 return -1;
1069 }
1070
1071 if (!cfg.msgfile) {
1072 struct rpmb_config_block_t *rcfg = (struct rpmb_config_block_t *)msg_buf;
1073
1074 nvme_show_result("Boot Partition Protection is %s",nvme_show_message(0, "Boot Partition Protection is %s", (rcfg
->bp_enable & 0x1) ? "Enabled" : "Disabled")
1075 (rcfg->bp_enable & 0x1) ? "Enabled" : "Disabled")nvme_show_message(0, "Boot Partition Protection is %s", (rcfg
->bp_enable & 0x1) ? "Enabled" : "Disabled")
;
1076 nvme_show_result("Boot Partition 1 is %s",nvme_show_message(0, "Boot Partition 1 is %s", (rcfg->bp_lock
& 0x2) ? "Locked" : "Unlocked")
1077 (rcfg->bp_lock & 0x2) ? "Locked" : "Unlocked")nvme_show_message(0, "Boot Partition 1 is %s", (rcfg->bp_lock
& 0x2) ? "Locked" : "Unlocked")
;
1078 nvme_show_result("Boot Partition 0 is %s",nvme_show_message(0, "Boot Partition 0 is %s", (rcfg->bp_lock
& 0x1) ? "Locked" : "Unlocked")
1079 (rcfg->bp_lock & 0x1) ? "Locked" : "Unlocked")nvme_show_message(0, "Boot Partition 0 is %s", (rcfg->bp_lock
& 0x1) ? "Locked" : "Unlocked")
;
1080 } else {
1081 nvme_show_result("Saving received config data to %s file", cfg.msgfile)nvme_show_message(0, "Saving received config data to %s file"
, cfg.msgfile)
;
1082 write_file(msg_buf, sizeof(struct rpmb_config_block_t),
1083 cfg.msgfile, NULL((void*)0));
1084 }
1085
1086 return write_cntr == 0 ? -1 : 0;
1087}
1088
1089static int rpmb_write_config(int argc, char **argv, struct command *acmd,
1090 struct plugin *plugin)
1091{
1092 const char *desc = "Write the RPMB device configuration block";
1093 const char *msg = "configuration block data to be written";
1094 const char *mfile = "data file with the configuration block to be written";
1095 const char *kfile = "key file that has authentication key to be used";
1096 const char *key = "key to be used for authentication";
1097
1098 struct config {
1099 char *key;
1100 char *msg;
1101 char *keyfile;
1102 char *msgfile;
1103 };
1104
1105 struct config cfg = {
1106 .key = NULL((void*)0),
1107 .msg = NULL((void*)0),
1108 .msgfile = NULL((void*)0),
1109 .keyfile = NULL((void*)0),
1110 };
1111
1112 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
1113 OPT_FILE("msgfile", 'f', &cfg.msgfile, mfile){"msgfile", 'f', "FILE", CFG_FILE, _Generic(&cfg.msgfile,
char ** : (&cfg.msgfile), const char ** : (&cfg.msgfile
)), ARGCONFIG_ARG_REQUIRED, mfile, 0, }
,
1114 OPT_FILE("keyfile", 'g', &cfg.keyfile, kfile){"keyfile", 'g', "FILE", CFG_FILE, _Generic(&cfg.keyfile,
char ** : (&cfg.keyfile), const char ** : (&cfg.keyfile
)), ARGCONFIG_ARG_REQUIRED, kfile, 0, }
,
1115 OPT_STRING("key", 'k', "key", &cfg.key, key){"key", 'k', "key", CFG_STRING, _Generic(&cfg.key, char *
* : (&cfg.key), const char ** : (&cfg.key)), ARGCONFIG_ARG_REQUIRED
, key, 0, }
,
1116 OPT_STRING("msg", 'd', "data", &cfg.msg, msg){"msg", 'd', "data", CFG_STRING, _Generic(&cfg.msg, char *
* : (&cfg.msg), const char ** : (&cfg.msg)), ARGCONFIG_ARG_REQUIRED
, msg, 0, }
,
1117 OPT_END(){ ((void*)0) }
1118 };
1119
1120 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
1121 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
1122 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) unsigned char *key_buf = NULL((void*)0);
1123 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) unsigned char *msg_buf = NULL((void*)0);
1124 struct ctrl_rpmbs_reg regs;
1125 unsigned int key_size = 0;
1126 unsigned int msg_size = 0;
1127 int err;
1128
1129 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
1130 if (err)
1131 return err;
1132
1133 err = rpmb_get_regs(hdl, &regs);
1134 if (err)
1135 return err;
1136
1137 key_buf = rpmb_read_and_validate_key(cfg.key, cfg.keyfile, &key_size);
1138 if (!key_buf)
1139 return -1;
1140
1141 err = rpmb_read_msg(cfg.msg, cfg.msgfile, &msg_buf, &msg_size);
1142 if (err || !msg_size) {
1143 nvme_show_error("Failed to read msg data")nvme_show_message(1, "Failed to read msg data");
1144 return -1;
1145 }
1146
1147 if (msg_size < sizeof(struct rpmb_config_block_t)) {
1148 nvme_show_error("invalid config block size %u, expected %zu", msg_size,nvme_show_message(1, "invalid config block size %u, expected %zu"
, msg_size, sizeof(struct rpmb_config_block_t))
1149 sizeof(struct rpmb_config_block_t))nvme_show_message(1, "invalid config block size %u, expected %zu"
, msg_size, sizeof(struct rpmb_config_block_t))
;
1150 return -1;
1151 }
1152
1153 err = rpmb_write_config_block(hdl, msg_buf, key_buf, key_size);
1154 if (!err)
1155 nvme_show_verbose_result("RPMB configuration block written")nvme_show_verbose_message("RPMB configuration block written");
1156
1157 return err;
1158}
1159
1160static struct command rpmb_info_cmd = {
1161 .name = "info",
1162 .help = "Print RPMB support information for the controller",
1163 .fn = rpmb_info,
1164};
1165
1166static struct command rpmb_program_key_cmd = {
1167 .name = "program-key",
1168 .help = "Program the RPMB authentication key",
1169 .fn = rpmb_program_key,
1170};
1171
1172static struct command rpmb_read_counter_cmd = {
1173 .name = "read-counter",
1174 .help = "Read the RPMB write counter",
1175 .fn = rpmb_read_counter,
1176};
1177
1178static struct command rpmb_read_data_cmd = {
1179 .name = "read-data",
1180 .help = "Read authenticated data from an RPMB target",
1181 .fn = rpmb_read_data,
1182};
1183
1184static struct command rpmb_write_data_cmd = {
1185 .name = "write-data",
1186 .help = "Write authenticated data to an RPMB target",
1187 .fn = rpmb_write_data,
1188};
1189
1190static struct command rpmb_read_config_cmd = {
1191 .name = "read-config",
1192 .help = "Read the RPMB device configuration block",
1193 .fn = rpmb_read_config,
1194};
1195
1196static struct command rpmb_write_config_cmd = {
1197 .name = "write-config",
1198 .help = "Write the RPMB device configuration block",
1199 .fn = rpmb_write_config,
1200};
1201
1202static struct command *commands[] = {
1203 &rpmb_info_cmd,
1204 &rpmb_program_key_cmd,
1205 &rpmb_read_counter_cmd,
1206 &rpmb_read_data_cmd,
1207 &rpmb_write_data_cmd,
1208 &rpmb_read_config_cmd,
1209 &rpmb_write_config_cmd,
1210 NULL((void*)0),
1211};
1212
1213static struct plugin plugin = {
1214 .name = "rpmb",
1215 .desc = "Replay Protected Memory Block commands",
1216 .version = NVME_VERSION"3.1",
1217 .core = true1,
1218 .group = "Security & Access Control",
1219};
1220
1221static void __shr_constructor__attribute__((constructor)) register_plugin(void)
1222{
1223 plugin_add_group(&plugin, NULL((void*)0), commands);
1224 register_extension(&plugin);
1225}