VirtualBox

source: vbox/trunk/src/VBox/Runtime/tools/RTEfiSigDb.cpp@ 104932

Last change on this file since 104932 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.1 KB
Line 
1/* $Id: RTEfiSigDb.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - Utility for manipulating EFI signature databases.
4 */
5
6/*
7 * Copyright (C) 2021-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <iprt/assert.h>
42#include <iprt/buildconfig.h>
43#include <iprt/err.h>
44#include <iprt/efi.h>
45#include <iprt/file.h>
46#include <iprt/getopt.h>
47#include <iprt/initterm.h>
48#include <iprt/mem.h>
49#include <iprt/message.h>
50#include <iprt/path.h>
51#include <iprt/stream.h>
52#include <iprt/string.h>
53#include <iprt/uuid.h>
54#include <iprt/vfs.h>
55
56#include <iprt/formats/efi-signature.h>
57#include <iprt/formats/efi-varstore.h>
58
59
60/*********************************************************************************************************************************
61* Structures and Typedefs *
62*********************************************************************************************************************************/
63
64
65/*********************************************************************************************************************************
66* Global Variables *
67*********************************************************************************************************************************/
68/** Signature type identifier to internal type mapping. */
69struct RTEFISIGDBID2TYPEENTRY
70{
71 const char *pszId;
72 RTEFISIGTYPE enmType;
73} g_aId2SigType[] =
74{
75 { "sha256", RTEFISIGTYPE_SHA256 },
76 { "rsa2048", RTEFISIGTYPE_RSA2048 },
77 { "x509", RTEFISIGTYPE_X509 }
78};
79
80
81/*********************************************************************************************************************************
82* Internal Functions *
83*********************************************************************************************************************************/
84
85/**
86 * Display the version of the cache program.
87 *
88 * @returns exit code.
89 */
90static RTEXITCODE rtEfiSigDbVersion(void)
91{
92 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
93 return RTEXITCODE_SUCCESS;
94}
95
96
97/**
98 * Shows the usage of the program.
99 *
100 * @returns Exit code.
101 * @param pszArg0 Program name.
102 * @param pszCommand Command selector, NULL if all.
103 */
104static RTEXITCODE rtEfiSigDbUsage(const char *pszArg0, const char *pszCommand)
105{
106 if (!pszCommand || !strcmp(pszCommand, "list"))
107 RTPrintf("Usage: %s list <signature database path>\n"
108 , RTPathFilename(pszArg0));
109
110 if (!pszCommand || !strcmp(pszCommand, "add"))
111 RTPrintf("Usage: %s add <signature database path> <x509|sha256|rsa2048> <owner uuid> <signature path> ...\n"
112 , RTPathFilename(pszArg0));
113
114 if (!pszCommand || !strcmp(pszCommand, "initnvram"))
115 RTPrintf("Usage: %s initnvram <nvram path> <init options>\n"
116 "\n"
117 "Init Options:\n"
118 " --pk <path>\n"
119 " Init the PK with the given signature.\n"
120 " --pk-owner <uuid>\n"
121 " Set the given UUID as the owner of the PK.\n"
122 " --kek <path>\n"
123 " Init the KEK with the given signature.\n"
124 " --kek-owner <uuid>\n"
125 " Set the given UUID as the owner of the KEK.\n"
126 " --db <x509|sha256|rsa2048>:<owner uuid>:<path>\n"
127 " Adds the given signature with the owner UUID and type to the db, can be given multiple times.\n"
128 " --secure-boot <on|off>\n"
129 " Enables or disables secure boot\n"
130 , RTPathFilename(pszArg0));
131
132 return RTEXITCODE_SUCCESS;
133}
134
135
136static RTEFISIGTYPE rtEfiSigDbGetTypeById(const char *pszId)
137{
138 for (uint32_t i = 0; i < RT_ELEMENTS(g_aId2SigType); i++)
139 if (!strcmp(pszId, g_aId2SigType[i].pszId))
140 return g_aId2SigType[i].enmType;
141
142 return RTEFISIGTYPE_INVALID;
143}
144
145
146/**
147 * Opens the specified signature database, returning an VFS file handle on success.
148 *
149 * @returns IPRT status code.
150 * @param pszPath Path to the signature database.
151 * @param phVfsFile Where to return the VFS file handle on success.
152 */
153static int rtEfiSigDbOpen(const char *pszPath, PRTVFSFILE phVfsFile)
154{
155 int rc;
156
157 if (RTVfsChainIsSpec(pszPath))
158 {
159 RTVFSOBJ hVfsObj;
160 rc = RTVfsChainOpenObj(pszPath, RTFILE_O_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
161 RTVFSOBJ_F_OPEN_ANY | RTVFSOBJ_F_CREATE_NOTHING | RTPATH_F_ON_LINK,
162 &hVfsObj, NULL, NULL);
163 if ( RT_SUCCESS(rc)
164 && RTVfsObjGetType(hVfsObj) == RTVFSOBJTYPE_FILE)
165 {
166 *phVfsFile = RTVfsObjToFile(hVfsObj);
167 RTVfsObjRelease(hVfsObj);
168 }
169 else
170 {
171 RTPrintf("'%s' doesn't point to a file\n", pszPath);
172 rc = VERR_INVALID_PARAMETER;
173 }
174 }
175 else
176 rc = RTVfsFileOpenNormal(pszPath, RTFILE_O_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
177 phVfsFile);
178
179 return rc;
180}
181
182
183/**
184 * Signature database enumeration callback.
185 */
186static DECLCALLBACK(int) rtEfiSgDbEnum(RTEFISIGDB hEfiSigDb, RTEFISIGTYPE enmSigType, PCRTUUID pUuidOwner,
187 const void *pvSig, size_t cbSig, void *pvUser)
188{
189 RT_NOREF(hEfiSigDb, pvUser);
190
191 uint32_t *pidxSig = (uint32_t *)pvUser;
192
193 RTPrintf("%02u: %s\n", (*pidxSig)++, RTEfiSigDbTypeStringify(enmSigType));
194 RTPrintf(" Owner: %RTuuid\n", pUuidOwner);
195 RTPrintf(" Signature:\n"
196 "%.*Rhxd\n\n", cbSig, pvSig);
197 return VINF_SUCCESS;
198}
199
200
201/**
202 * Handles the 'list' command.
203 *
204 * @returns Program exit code.
205 * @param pszArg0 The program name.
206 * @param cArgs The number of arguments to the 'add' command.
207 * @param papszArgs The argument vector, starting after 'add'.
208 */
209static RTEXITCODE rtEfiSgDbCmdList(const char *pszArg0, int cArgs, char **papszArgs)
210{
211 RT_NOREF(pszArg0);
212
213 if (!cArgs)
214 {
215 RTPrintf("An input path must be given\n");
216 return RTEXITCODE_FAILURE;
217 }
218
219 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
220 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
221 int rc = rtEfiSigDbOpen(papszArgs[0], &hVfsFile);
222 if (RT_SUCCESS(rc))
223 {
224 RTEFISIGDB hEfiSigDb;
225 rc = RTEfiSigDbCreate(&hEfiSigDb);
226 if (RT_SUCCESS(rc))
227 {
228 uint32_t idxSig = 0;
229
230 rc = RTEfiSigDbAddFromExistingDb(hEfiSigDb, hVfsFile);
231 if (RT_SUCCESS(rc))
232 RTEfiSigDbEnum(hEfiSigDb, rtEfiSgDbEnum, &idxSig);
233 else
234 {
235 RTPrintf("Loading the signature database failed with %Rrc\n", rc);
236 rcExit = RTEXITCODE_FAILURE;
237 }
238
239 RTEfiSigDbDestroy(hEfiSigDb);
240 }
241 else
242 {
243 RTPrintf("Creating the signature database failed with %Rrc\n", rc);
244 rcExit = RTEXITCODE_FAILURE;
245 }
246
247 RTVfsFileRelease(hVfsFile);
248 }
249 else
250 rcExit = RTEXITCODE_FAILURE;
251
252 return rcExit;
253}
254
255
256/**
257 * Handles the 'add' command.
258 *
259 * @returns Program exit code.
260 * @param pszArg0 The program name.
261 * @param cArgs The number of arguments to the 'add' command.
262 * @param papszArgs The argument vector, starting after 'add'.
263 */
264static RTEXITCODE rtEfiSgDbCmdAdd(const char *pszArg0, int cArgs, char **papszArgs)
265{
266 RT_NOREF(pszArg0);
267
268 if (!cArgs)
269 {
270 RTPrintf("The signature database path is missing\n");
271 return RTEXITCODE_FAILURE;
272 }
273
274 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
275 RTVFSFILE hVfsFile = NIL_RTVFSFILE;
276 int rc = rtEfiSigDbOpen(papszArgs[0], &hVfsFile);
277 if (RT_SUCCESS(rc))
278 {
279 RTEFISIGDB hEfiSigDb;
280 rc = RTEfiSigDbCreate(&hEfiSigDb);
281 if (RT_SUCCESS(rc))
282 {
283 uint64_t cbSigDb = 0;
284 rc = RTVfsFileQuerySize(hVfsFile, &cbSigDb);
285 if ( RT_SUCCESS(rc)
286 && cbSigDb)
287 rc = RTEfiSigDbAddFromExistingDb(hEfiSigDb, hVfsFile);
288 if (RT_SUCCESS(rc))
289 {
290 cArgs--;
291 papszArgs++;
292
293 while (cArgs >= 3)
294 {
295 RTEFISIGTYPE enmSigType = rtEfiSigDbGetTypeById(papszArgs[0]);
296 const char *pszUuidOwner = papszArgs[1];
297 const char *pszSigDataPath = papszArgs[2];
298
299 if (enmSigType == RTEFISIGTYPE_INVALID)
300 {
301 RTPrintf("Signature type '%s' is not known\n", papszArgs[0]);
302 break;
303 }
304
305 RTUUID UuidOwner;
306 rc = RTUuidFromStr(&UuidOwner, pszUuidOwner);
307 if (RT_FAILURE(rc))
308 {
309 RTPrintf("UUID '%s' is malformed\n", pszUuidOwner);
310 break;
311 }
312
313 RTVFSFILE hVfsFileSigData = NIL_RTVFSFILE;
314 rc = rtEfiSigDbOpen(pszSigDataPath, &hVfsFileSigData);
315 if (RT_FAILURE(rc))
316 {
317 RTPrintf("Opening '%s' failed with %Rrc\n", pszSigDataPath, rc);
318 break;
319 }
320
321 rc = RTEfiSigDbAddSignatureFromFile(hEfiSigDb, enmSigType, &UuidOwner, hVfsFileSigData);
322 RTVfsFileRelease(hVfsFileSigData);
323 if (RT_FAILURE(rc))
324 {
325 RTPrintf("Adding signature data from '%s' failed with %Rrc\n", pszSigDataPath, rc);
326 break;
327 }
328 papszArgs += 3;
329 cArgs -= 3;
330 }
331
332 if (RT_SUCCESS(rc))
333 {
334 if (!cArgs)
335 {
336 rc = RTVfsFileSeek(hVfsFile, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
337 AssertRC(rc);
338
339 rc = RTEfiSigDbWriteToFile(hEfiSigDb, hVfsFile);
340 if (RT_FAILURE(rc))
341 {
342 RTPrintf("Writing the updated signature database failed with %Rrc\n", rc);
343 rcExit = RTEXITCODE_FAILURE;
344 }
345 }
346 else
347 {
348 RTPrintf("Incomplete list of entries to add given\n");
349 rcExit = RTEXITCODE_FAILURE;
350 }
351 }
352 }
353 else
354 {
355 RTPrintf("Loading the signature database failed with %Rrc\n", rc);
356 rcExit = RTEXITCODE_FAILURE;
357 }
358
359 RTEfiSigDbDestroy(hEfiSigDb);
360 }
361 else
362 {
363 RTPrintf("Creating the signature database failed with %Rrc\n", rc);
364 rcExit = RTEXITCODE_FAILURE;
365 }
366
367 RTVfsFileRelease(hVfsFile);
368 }
369 else
370 rcExit = RTEXITCODE_FAILURE;
371
372 return rcExit;
373}
374
375
376/**
377 * Adds the given signature to the given database.
378 *
379 * @returns IPRT status code.
380 * @param hEfiSigDb The EFI signature database handle.
381 * @param pszSigPath The signature data path.
382 * @param pszSigType The signature type.
383 * @param pszUuidOwner The owner UUID.
384 */
385static int rtEfiSigDbAddSig(RTEFISIGDB hEfiSigDb, const char *pszSigPath, const char *pszSigType, const char *pszUuidOwner)
386{
387 RTEFISIGTYPE enmSigType = rtEfiSigDbGetTypeById(pszSigType);
388 if (enmSigType == RTEFISIGTYPE_INVALID)
389 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "Signature type '%s' is unknown!", pszSigType);
390
391 RTUUID UuidOwner;
392 int rc = RTUuidFromStr(&UuidOwner, pszUuidOwner);
393 if (RT_FAILURE(rc))
394 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "Owner UUID '%s' is malformed!", pszUuidOwner);
395
396 RTVFSFILE hVfsFileSigData = NIL_RTVFSFILE;
397 rc = rtEfiSigDbOpen(pszSigPath, &hVfsFileSigData);
398 if (RT_FAILURE(rc))
399 return RTMsgErrorRc(rc, "Opening '%s' failed: %Rrc", pszSigPath, rc);
400
401 rc = RTEfiSigDbAddSignatureFromFile(hEfiSigDb, enmSigType, &UuidOwner, hVfsFileSigData);
402 RTVfsFileRelease(hVfsFileSigData);
403 if (RT_FAILURE(rc))
404 return RTMsgErrorRc(rc, "Adding signature '%s' failed: %Rrc", pszSigPath, rc);
405
406 return VINF_SUCCESS;
407}
408
409
410/**
411 * Sets the given attributes for the given EFI variable store variable.
412 *
413 * @returns IPRT status code.
414 * @param hVfsVarStore Handle of the EFI variable store VFS.
415 * @param pszVar The variable to set the attributes for.
416 * @param fAttr The attributes to set, see EFI_VAR_HEADER_ATTR_XXX.
417 */
418static int rtEfiSigDbSetVarAttr(RTVFS hVfsVarStore, const char *pszVar, uint32_t fAttr)
419{
420 char szVarPath[_1K];
421 ssize_t cch = RTStrPrintf2(szVarPath, sizeof(szVarPath), "/raw/%s/attr", pszVar);
422 Assert(cch > 0); RT_NOREF(cch);
423
424 RTVFSFILE hVfsFileAttr = NIL_RTVFSFILE;
425 int rc = RTVfsFileOpen(hVfsVarStore, szVarPath,
426 RTFILE_O_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
427 &hVfsFileAttr);
428 if (RT_SUCCESS(rc))
429 {
430 uint32_t fAttrLe = RT_H2LE_U32(fAttr);
431 rc = RTVfsFileWrite(hVfsFileAttr, &fAttrLe, sizeof(fAttrLe), NULL /*pcbWritten*/);
432 RTVfsFileRelease(hVfsFileAttr);
433 }
434
435 return rc;
436}
437
438
439/**
440 * Adds the given variable to the variable store.
441 *
442 * @returns IPRT status code.
443 * @param hVfsVarStore Handle of the EFI variable store VFS.
444 * @param pGuid The EFI GUID of the variable.
445 * @param pszVar The variable name.
446 * @param fAttr Attributes for the variable.
447 * @param phVfsFile Where to return the VFS file handle to the created variable on success.
448 */
449static int rtEfiSigDbVarStoreAddVar(RTVFS hVfsVarStore, PCEFI_GUID pGuid, const char *pszVar, uint32_t fAttr, PRTVFSFILE phVfsFile)
450{
451 RTUUID UuidVar;
452 RTEfiGuidToUuid(&UuidVar, pGuid);
453
454 char szVarPath[_1K];
455 ssize_t cch = RTStrPrintf2(szVarPath, sizeof(szVarPath), "/by-uuid/%RTuuid/%s", &UuidVar, pszVar);
456 Assert(cch > 0); RT_NOREF(cch);
457
458 int rc = RTVfsFileOpen(hVfsVarStore, szVarPath,
459 RTFILE_O_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
460 phVfsFile);
461 if ( rc == VERR_PATH_NOT_FOUND
462 || rc == VERR_FILE_NOT_FOUND)
463 {
464 /*
465 * Try to create the owner GUID of the variable by creating the appropriate directory,
466 * ignore error if it exists already.
467 */
468 RTVFSDIR hVfsDirRoot = NIL_RTVFSDIR;
469 rc = RTVfsOpenRoot(hVfsVarStore, &hVfsDirRoot);
470 if (RT_SUCCESS(rc))
471 {
472 char szGuidPath[_1K];
473 cch = RTStrPrintf2(szGuidPath, sizeof(szGuidPath), "by-uuid/%RTuuid", &UuidVar);
474 Assert(cch > 0);
475
476 RTVFSDIR hVfsDirGuid = NIL_RTVFSDIR;
477 rc = RTVfsDirCreateDir(hVfsDirRoot, szGuidPath, 0755, 0 /*fFlags*/, &hVfsDirGuid);
478 if (RT_SUCCESS(rc))
479 RTVfsDirRelease(hVfsDirGuid);
480 else if (rc == VERR_ALREADY_EXISTS)
481 rc = VINF_SUCCESS;
482
483 RTVfsDirRelease(hVfsDirRoot);
484 }
485 else
486 rc = RTMsgErrorRc(rc, "Opening variable storage root directory failed: %Rrc", rc);
487
488 if (RT_SUCCESS(rc))
489 {
490 rc = RTVfsFileOpen(hVfsVarStore, szVarPath,
491 RTFILE_O_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_CREATE,
492 phVfsFile);
493 if (RT_SUCCESS(rc))
494 rc = rtEfiSigDbSetVarAttr(hVfsVarStore, pszVar, fAttr);
495 }
496
497 if (RT_FAILURE(rc))
498 rc = RTMsgErrorRc(rc, "Creating the variable '%s' failed: %Rrc", pszVar, rc);
499 }
500
501 return rc;
502}
503
504
505/**
506 * Creates the given variable and sets the data.
507 *
508 * @returns IPRT status code.
509 * @param hVfsVarStore Handle of the EFI variable store VFS.
510 * @param pGuid The EFI GUID of the variable.
511 * @param pszVar The variable name.
512 * @param fAttr Attributes for the variable.
513 * @param pvBuf The data to write.
514 * @param cbBuf Number of bytes of data.
515 */
516static int rtEfiSigDbVarStoreSetVar(RTVFS hVfsVarStore, PCEFI_GUID pGuid, const char *pszVar, uint32_t fAttr,
517 const void *pvBuf, size_t cbBuf)
518{
519 RTVFSFILE hVfsFileVar = NIL_RTVFSFILE;
520 int rc = rtEfiSigDbVarStoreAddVar(hVfsVarStore, pGuid, pszVar, fAttr, &hVfsFileVar);
521 if (RT_SUCCESS(rc))
522 {
523 rc = RTVfsFileWrite(hVfsFileVar, pvBuf, cbBuf, NULL /*pcbWritten*/);
524 if (RT_FAILURE(rc))
525 rc = RTMsgErrorRc(rc, "Writing variable '%s' failed: %Rrc", pszVar, rc);
526 RTVfsFileRelease(hVfsFileVar);
527 }
528 else
529 rc = RTMsgErrorRc(rc, "Creating variable '%s' failed: %Rrc", pszVar, rc);
530
531 return rc;
532}
533
534
535/**
536 * Adds the given signature to the given signature database of the given EFI variable store.
537 *
538 * @returns IPRT status code.
539 * @param hVfsVarStore Handle of the EFI variable store VFS.
540 * @param pGuid The EFI GUID of the variable.
541 * @param pszDb The signature database to update.
542 * @param fWipeDbBefore Flag whether to wipe the database before adding the signature.
543 * @param cSigs Number of signatures following.
544 * @param ... A triple of signature path, signature type and owner uuid string pointers for each
545 * signature.
546 */
547static int rtEfiSigDbVarStoreAddToDb(RTVFS hVfsVarStore, PCEFI_GUID pGuid, const char *pszDb, bool fWipeDbBefore, uint32_t cSigs,
548 ... /*const char *pszSigPath, const char *pszSigType, const char *pszUuidOwner*/)
549{
550 RTVFSFILE hVfsFileSigDb = NIL_RTVFSFILE;
551 int rc = rtEfiSigDbVarStoreAddVar(hVfsVarStore, pGuid, pszDb,
552 EFI_VAR_HEADER_ATTR_NON_VOLATILE
553 | EFI_VAR_HEADER_ATTR_BOOTSERVICE_ACCESS
554 | EFI_VAR_HEADER_ATTR_RUNTIME_ACCESS
555 | EFI_AUTH_VAR_HEADER_ATTR_TIME_BASED_AUTH_WRITE_ACCESS,
556 &hVfsFileSigDb);
557 if (RT_SUCCESS(rc))
558 {
559 RTEFISIGDB hEfiSigDb;
560 rc = RTEfiSigDbCreate(&hEfiSigDb);
561 if (RT_SUCCESS(rc))
562 {
563 if (!fWipeDbBefore)
564 rc = RTEfiSigDbAddFromExistingDb(hEfiSigDb, hVfsFileSigDb);
565 if (RT_SUCCESS(rc))
566 {
567 va_list VarArgs;
568 va_start(VarArgs, cSigs);
569
570 while ( cSigs--
571 && RT_SUCCESS(rc))
572 {
573 const char *pszSigPath = va_arg(VarArgs, const char *);
574 const char *pszSigType = va_arg(VarArgs, const char *);
575 const char *pszUuidOwner = va_arg(VarArgs, const char *);
576
577 rc = rtEfiSigDbAddSig(hEfiSigDb, pszSigPath, pszSigType, pszUuidOwner);
578 }
579
580 va_end(VarArgs);
581 if (RT_SUCCESS(rc))
582 {
583 rc = RTVfsFileSeek(hVfsFileSigDb, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/);
584 AssertRC(rc);
585
586 rc = RTEfiSigDbWriteToFile(hEfiSigDb, hVfsFileSigDb);
587 if (RT_FAILURE(rc))
588 rc = RTMsgErrorRc(rc, "Writing updated signature database failed: %Rrc", rc);
589 }
590 }
591 else
592 rc = RTMsgErrorRc(rc, "Loading signature database failed: %Rrc", rc);
593
594 RTEfiSigDbDestroy(hEfiSigDb);
595 }
596 else
597 rc = RTMsgErrorRc(rc, "Creating signature database failed: %Rrc", rc);
598
599 RTVfsFileRelease(hVfsFileSigDb);
600 }
601 else
602 rc = RTMsgErrorRc(rc, "Opening signature database '%s' failed: %Rrc", pszDb, rc);
603
604 return rc;
605}
606
607
608/**
609 * Handles the 'initnvram' command.
610 *
611 * @returns Program exit code.
612 * @param pszArg0 The program name.
613 * @param cArgs The number of arguments to the 'add' command.
614 * @param papszArgs The argument vector, starting after 'add'.
615 */
616static RTEXITCODE rtEfiSgDbCmdInitNvram(const char *pszArg0, int cArgs, char **papszArgs)
617{
618 RT_NOREF(pszArg0);
619 RTERRINFOSTATIC ErrInfo;
620
621 /*
622 * Parse the command line.
623 */
624 static RTGETOPTDEF const s_aOptions[] =
625 {
626 { "--pk", 'p', RTGETOPT_REQ_STRING },
627 { "--pk-owner", 'o', RTGETOPT_REQ_STRING },
628 { "--kek", 'k', RTGETOPT_REQ_STRING },
629 { "--kek-owner", 'w', RTGETOPT_REQ_STRING },
630 { "--db", 'd', RTGETOPT_REQ_STRING },
631 { "--secure-boot", 's', RTGETOPT_REQ_BOOL_ONOFF }
632 };
633
634 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
635 RTGETOPTSTATE State;
636 int rc = RTGetOptInit(&State, cArgs, papszArgs, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST);
637 if (RT_FAILURE(rc))
638 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc", rc);
639
640 const char *pszNvram = NULL;
641 const char *pszPkPath = NULL;
642 const char *pszUuidPkOwner = NULL;
643 const char *pszKekPath = NULL;
644 const char *pszUuidKekOwner = NULL;
645 const char **papszDb = NULL;
646 bool fSecureBoot = true;
647 bool fSetSecureBoot = false;
648 uint32_t cDbEntries = 0;
649 uint32_t cDbEntriesMax = 0;
650
651 RTGETOPTUNION ValueUnion;
652 int chOpt;
653 while ((chOpt = RTGetOpt(&State, &ValueUnion)) != 0)
654 {
655 switch (chOpt)
656 {
657 case 'p':
658 pszPkPath = ValueUnion.psz;
659 break;
660 case 'o':
661 pszUuidPkOwner = ValueUnion.psz;
662 break;
663
664 case 'k':
665 pszKekPath = ValueUnion.psz;
666 break;
667 case 'w':
668 pszUuidKekOwner = ValueUnion.psz;
669 break;
670
671 case 'd':
672 {
673 if (cDbEntries == cDbEntriesMax)
674 {
675 uint32_t cDbEntriesMaxNew = cDbEntriesMax + 10;
676 const char **papszDbNew = (const char **)RTMemRealloc(papszDb, cDbEntriesMaxNew * sizeof(const char *));
677 if (!papszDbNew)
678 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Out of memory allocating memory for '%s'", ValueUnion.psz);
679
680 papszDb = papszDbNew;
681 cDbEntriesMax = cDbEntriesMaxNew;
682 }
683
684 papszDb[cDbEntries++] = ValueUnion.psz;
685 break;
686 }
687
688 case 's':
689 fSecureBoot = ValueUnion.f;
690 fSetSecureBoot = true;
691 break;
692
693 case VINF_GETOPT_NOT_OPTION:
694 /* The first non-option is the NVRAM file. */
695 if (!pszNvram)
696 pszNvram = ValueUnion.psz;
697 else
698 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid option '%s'", ValueUnion.psz);
699 break;
700
701 default:
702 return RTGetOptPrintError(chOpt, &ValueUnion);
703 }
704 }
705
706 if (!pszNvram)
707 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The NVRAM file path is missing");
708
709 if ( pszPkPath
710 && !pszUuidPkOwner)
711 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The PK is missing the owner UUID");
712
713 if ( pszKekPath
714 && !pszUuidKekOwner)
715 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The KEK is missing the owner UUID");
716
717 RTVFSFILE hVfsFileNvram = NIL_RTVFSFILE;
718 rc = RTVfsFileOpenNormal(pszNvram, RTFILE_O_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
719 &hVfsFileNvram);
720 if (RT_SUCCESS(rc))
721 {
722 RTVFS hVfsEfiVarStore = NIL_RTVFS;
723 rc = RTEfiVarStoreOpenAsVfs(hVfsFileNvram, 0 /*fMntFlags*/, 0 /*fVarStoreFlags*/, &hVfsEfiVarStore, RTErrInfoInitStatic(&ErrInfo));
724 if (RT_SUCCESS(rc))
725 {
726 EFI_GUID GuidSecurityDb = EFI_IMAGE_SECURITY_DATABASE_GUID;
727 EFI_GUID GuidGlobalVar = EFI_GLOBAL_VARIABLE_GUID;
728
729 if (pszPkPath)
730 rc = rtEfiSigDbVarStoreAddToDb(hVfsEfiVarStore, &GuidGlobalVar, "PK", true /*fWipeDbBefore*/, 1 /*cSigs*/, pszPkPath, "x509", pszUuidPkOwner);
731 if ( RT_SUCCESS(rc)
732 && pszKekPath)
733 rc = rtEfiSigDbVarStoreAddToDb(hVfsEfiVarStore, &GuidGlobalVar ,"KEK", true /*fWipeDbBefore*/, 1 /*cSigs*/, pszKekPath, "x509", pszUuidKekOwner);
734
735 if ( RT_SUCCESS(rc)
736 && cDbEntries)
737 {
738 /** @todo Optimize to avoid re-opening and re-parsing the database for every entry. */
739 for (uint32_t i = 0; i < cDbEntries && RT_SUCCESS(rc); i++)
740 {
741 const char *pszDbEntry = papszDb[i];
742
743 const char *pszSigType = pszDbEntry;
744 const char *pszUuidOwner = strchr(pszSigType, ':');
745 if (pszUuidOwner)
746 pszUuidOwner++;
747 const char *pszSigPath = pszUuidOwner ? strchr(pszUuidOwner, ':') : NULL;
748 if (pszSigPath)
749 pszSigPath++;
750
751 if ( pszUuidOwner
752 && pszSigPath)
753 {
754 char *pszSigTypeFree = RTStrDupN(pszSigType, pszUuidOwner - pszSigType - 1);
755 char *pszUuidOwnerFree = RTStrDupN(pszUuidOwner, pszSigPath - pszUuidOwner - 1);
756
757 if ( pszSigTypeFree
758 && pszUuidOwnerFree)
759 rc = rtEfiSigDbVarStoreAddToDb(hVfsEfiVarStore, &GuidSecurityDb, "db",
760 i == 0 ? true : false /*fWipeDbBefore*/,
761 1 /*cSigs*/,
762 pszSigPath, pszSigTypeFree, pszUuidOwnerFree);
763 else
764 rc = RTMsgErrorRc(VERR_NO_MEMORY, "Out of memory!");
765
766 if (pszSigTypeFree)
767 RTStrFree(pszSigTypeFree);
768 if (pszUuidOwnerFree)
769 RTStrFree(pszUuidOwnerFree);
770 }
771 else
772 rc = RTMsgErrorRc(VERR_INVALID_PARAMETER, "DB entry '%s' is malformed!", pszDbEntry);
773 }
774
775 if (RT_FAILURE(rc))
776 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Initializing the NVRAM '%s' failed: %Rrc", pszNvram, rc);
777 }
778
779 if ( RT_SUCCESS(rc)
780 && fSetSecureBoot)
781 {
782 EFI_GUID GuidSecureBootEnable = EFI_SECURE_BOOT_ENABLE_DISABLE_GUID;
783 uint8_t bVar = fSecureBoot ? 0x1 : 0x0;
784 rtEfiSigDbVarStoreSetVar(hVfsEfiVarStore, &GuidSecureBootEnable, "SecureBootEnable",
785 EFI_VAR_HEADER_ATTR_NON_VOLATILE
786 | EFI_VAR_HEADER_ATTR_BOOTSERVICE_ACCESS
787 | EFI_VAR_HEADER_ATTR_RUNTIME_ACCESS,
788 &bVar, sizeof(bVar));
789 }
790
791 RTVfsRelease(hVfsEfiVarStore);
792 }
793
794 RTVfsFileRelease(hVfsFileNvram);
795 }
796
797 if (papszDb)
798 RTMemFree(papszDb);
799 return rcExit;
800}
801
802
803int main(int argc, char **argv)
804{
805 int rc = RTR3InitExe(argc, &argv, 0);
806 if (RT_FAILURE(rc))
807 return RTMsgInitFailure(rc);
808
809 /*
810 * Switch on the command.
811 */
812 RTEXITCODE rcExit = RTEXITCODE_SYNTAX;
813 if (argc < 2)
814 rtEfiSigDbUsage(argv[0], NULL);
815 else if (!strcmp(argv[1], "list"))
816 rcExit = rtEfiSgDbCmdList(argv[0], argc - 2, argv + 2);
817 else if (!strcmp(argv[1], "add"))
818 rcExit = rtEfiSgDbCmdAdd(argv[0], argc - 2, argv + 2);
819 else if (!strcmp(argv[1], "initnvram"))
820 rcExit = rtEfiSgDbCmdInitNvram(argv[0], argc - 2, argv + 2);
821 else if ( !strcmp(argv[1], "-h")
822 || !strcmp(argv[1], "-?")
823 || !strcmp(argv[1], "--help"))
824 rcExit = rtEfiSigDbUsage(argv[0], NULL);
825 else if ( !strcmp(argv[1], "-V")
826 || !strcmp(argv[1], "--version"))
827 rcExit = rtEfiSigDbVersion();
828 else
829 RTMsgError("Unknown command: '%s'", argv[1]);
830
831 return rcExit;
832}
833
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette