VirtualBox

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

Last change on this file since 95841 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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