Changeset 90322 in vbox
- Timestamp:
- Jul 23, 2021 6:21:09 PM (4 years ago)
- svn:sync-xref-src-repo-rev:
- 145883
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/tools/RTEfiSigDb.cpp
r90317 r90322 31 31 #include <iprt/assert.h> 32 32 #include <iprt/buildconfig.h> 33 #include <iprt/err core.h>33 #include <iprt/err.h> 34 34 #include <iprt/efi.h> 35 35 #include <iprt/file.h> … … 44 44 #include <iprt/vfs.h> 45 45 46 #include <iprt/formats/efi-signature.h> 47 46 48 47 49 /********************************************************************************************************************************* … … 97 99 if (!pszCommand || !strcmp(pszCommand, "add")) 98 100 RTPrintf("Usage: %s add <signature database path> <x509|sha256|rsa2048> <owner uuid> <signature path> ...\n" 101 , RTPathFilename(pszArg0)); 102 103 if (!pszCommand || !strcmp(pszCommand, "initnvram")) 104 RTPrintf("Usage: %s initnvram <nvram path> <init options>\n" 105 "\n" 106 "Init Options:\n" 107 " --pk <path>\n" 108 " Init the PK with the given signature.\n" 109 " --pk-owner <uuid>\n" 110 " Set the given UUID as the owner of the PK.\n" 111 " --kek <path>\n" 112 " Init the KEK with the given signature.\n" 113 " --kek-owner <uuid>\n" 114 " Set the given UUID as the owner of the KEK.\n" 115 " --db <x509|sha256|rsa2048>:<owner uuid>:<path>\n" 116 " Adds the given signature with the owner UUID and type to the db, can be given multiple times.\n" 117 " --secure-boot <on|off>\n" 118 " Enables or disables secure boot\n" 99 119 , RTPathFilename(pszArg0)); 100 120 … … 343 363 344 364 365 /** 366 * Adds the given signature to the given database. 367 * 368 * @returns IPRT status code. 369 * @param hEfiSigDb The EFI signature database handle. 370 * @param pszSigPath The signature data path. 371 * @param pszSigType The signature type. 372 * @param pszUuidOwner The owner UUID. 373 */ 374 static int rtEfiSigDbAddSig(RTEFISIGDB hEfiSigDb, const char *pszSigPath, const char *pszSigType, const char *pszUuidOwner) 375 { 376 RTEFISIGTYPE enmSigType = rtEfiSigDbGetTypeById(pszSigType); 377 if (enmSigType == RTEFISIGTYPE_INVALID) 378 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "Signature type '%s' is unknown!", pszSigType); 379 380 RTUUID UuidOwner; 381 int rc = RTUuidFromStr(&UuidOwner, pszUuidOwner); 382 if (RT_FAILURE(rc)) 383 return RTMsgErrorRc(VERR_INVALID_PARAMETER, "Owner UUID '%s' is malformed!", pszUuidOwner); 384 385 RTVFSFILE hVfsFileSigData = NIL_RTVFSFILE; 386 rc = rtEfiSigDbOpen(pszSigPath, &hVfsFileSigData); 387 if (RT_FAILURE(rc)) 388 return RTMsgErrorRc(rc, "Opening '%s' failed: %Rrc", pszSigPath, rc); 389 390 rc = RTEfiSigDbAddSignatureFromFile(hEfiSigDb, enmSigType, &UuidOwner, hVfsFileSigData); 391 RTVfsFileRelease(hVfsFileSigData); 392 if (RT_FAILURE(rc)) 393 return RTMsgErrorRc(rc, "Adding signature '%s' failed: %Rrc", pszSigPath, rc); 394 395 return VINF_SUCCESS; 396 } 397 398 399 /** 400 * Adds the given signature to the given signature database of the given EFI variable store. 401 * 402 * @returns IPRT status code. 403 * @param hVfsVarStore Handle of the EFI variable store VFS. 404 * @param pszDb The signature database to update. 405 * @param fWipeDbBefore Flag whether to wipe the database before adding the signature. 406 * @param cSigs Number of signatures following. 407 * @param ... A triple of signature path, signature type and owner uuid string pointers for each 408 * signature. 409 */ 410 static int rtEfiSigDbVarStoreAddToDb(RTVFS hVfsVarStore, const char *pszDb, bool fWipeDbBefore, uint32_t cSigs, 411 ... /*const char *pszSigPath, const char *pszSigType, const char *pszUuidOwner*/) 412 { 413 EFI_GUID GuidSecurityDb = EFI_IMAGE_SECURITY_DATABASE_GUID; 414 RTUUID UuidSecurityDb; 415 RTEfiGuidToUuid(&UuidSecurityDb, &GuidSecurityDb); 416 417 char szDbPath[_1K]; 418 ssize_t cch = RTStrPrintf2(szDbPath, sizeof(szDbPath), "/by-uuid/%RTuuid/%s", &UuidSecurityDb, pszDb); 419 Assert(cch > 0); 420 421 RTVFSFILE hVfsFileSigDb = NIL_RTVFSFILE; 422 int rc = RTVfsFileOpen(hVfsVarStore, szDbPath, 423 RTFILE_O_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, 424 &hVfsFileSigDb); 425 if (rc == VERR_PATH_NOT_FOUND) 426 { 427 /* 428 * Try to create the owner GUID of the variable by creating the appropriate directory, 429 * ignore error if it exists already. 430 */ 431 RTVFSDIR hVfsDirRoot = NIL_RTVFSDIR; 432 rc = RTVfsOpenRoot(hVfsVarStore, &hVfsDirRoot); 433 if (RT_SUCCESS(rc)) 434 { 435 char szGuidPath[_1K]; 436 cch = RTStrPrintf2(szGuidPath, sizeof(szGuidPath), "by-uuid/%RTuuid", &UuidSecurityDb); 437 Assert(cch > 0); 438 439 RTVFSDIR hVfsDirGuid = NIL_RTVFSDIR; 440 rc = RTVfsDirCreateDir(hVfsDirRoot, szGuidPath, 0755, 0 /*fFlags*/, &hVfsDirGuid); 441 if (RT_SUCCESS(rc)) 442 RTVfsDirRelease(hVfsDirGuid); 443 444 RTVfsDirRelease(hVfsDirRoot); 445 } 446 else 447 rc = RTMsgErrorRc(rc, "Opening variable storage root directory failed: %Rrc", rc); 448 449 if (RT_SUCCESS(rc)) 450 rc = RTVfsFileOpen(hVfsVarStore, szDbPath, 451 RTFILE_O_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_CREATE, 452 &hVfsFileSigDb); 453 454 if (RT_FAILURE(rc)) 455 rc = RTMsgErrorRc(rc, "Creating the signature database '%s' failed: %Rrc", pszDb, rc); 456 } 457 458 if (RT_SUCCESS(rc)) 459 { 460 RTEFISIGDB hEfiSigDb; 461 rc = RTEfiSigDbCreate(&hEfiSigDb); 462 if (RT_SUCCESS(rc)) 463 { 464 if (!fWipeDbBefore) 465 rc = RTEfiSigDbAddFromExistingDb(hEfiSigDb, hVfsFileSigDb); 466 if (RT_SUCCESS(rc)) 467 { 468 va_list VarArgs; 469 va_start(VarArgs, cSigs); 470 471 while ( cSigs-- 472 && RT_SUCCESS(rc)) 473 { 474 const char *pszSigPath = va_arg(VarArgs, const char *); 475 const char *pszSigType = va_arg(VarArgs, const char *); 476 const char *pszUuidOwner = va_arg(VarArgs, const char *); 477 478 rc = rtEfiSigDbAddSig(hEfiSigDb, pszSigPath, pszSigType, pszUuidOwner); 479 } 480 481 va_end(VarArgs); 482 if (RT_SUCCESS(rc)) 483 { 484 rc = RTVfsFileSeek(hVfsFileSigDb, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/); 485 AssertRC(rc); 486 487 rc = RTEfiSigDbWriteToFile(hEfiSigDb, hVfsFileSigDb); 488 if (RT_FAILURE(rc)) 489 rc = RTMsgErrorRc(rc, "Writing updated signature database failed: %Rrc", rc); 490 } 491 } 492 else 493 rc = RTMsgErrorRc(rc, "Loading signature database failed: %Rrc", rc); 494 495 RTEfiSigDbDestroy(hEfiSigDb); 496 } 497 else 498 rc = RTMsgErrorRc(rc, "Creating signature database failed: %Rrc", rc); 499 500 RTVfsFileRelease(hVfsFileSigDb); 501 } 502 else 503 rc = RTMsgErrorRc(rc, "Opening signature database '%s' failed: %Rrc", szDbPath, rc); 504 505 return rc; 506 } 507 508 509 /** 510 * Handles the 'initnvram' command. 511 * 512 * @returns Program exit code. 513 * @param pszArg0 The program name. 514 * @param cArgs The number of arguments to the 'add' command. 515 * @param papszArgs The argument vector, starting after 'add'. 516 */ 517 static RTEXITCODE rtEfiSgDbCmdInitNvram(const char *pszArg0, int cArgs, char **papszArgs) 518 { 519 RT_NOREF(pszArg0); 520 RTERRINFOSTATIC ErrInfo; 521 522 /* 523 * Parse the command line. 524 */ 525 static RTGETOPTDEF const s_aOptions[] = 526 { 527 { "--pk", 'p', RTGETOPT_REQ_STRING }, 528 { "--pk-owner", 'o', RTGETOPT_REQ_STRING }, 529 { "--kek", 'k', RTGETOPT_REQ_STRING }, 530 { "--kek-owner", 'w', RTGETOPT_REQ_STRING }, 531 { "--db", 'd', RTGETOPT_REQ_STRING }, 532 { "--secure-boot", 's', RTGETOPT_REQ_BOOL_ONOFF } 533 }; 534 535 RTEXITCODE rcExit = RTEXITCODE_SUCCESS; 536 RTGETOPTSTATE State; 537 int rc = RTGetOptInit(&State, cArgs, papszArgs, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST); 538 if (RT_FAILURE(rc)) 539 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc", rc); 540 541 const char *pszNvram = NULL; 542 const char *pszPkPath = NULL; 543 const char *pszUuidPkOwner = NULL; 544 const char *pszKekPath = NULL; 545 const char *pszUuidKekOwner = NULL; 546 const char **papszDb = NULL; 547 bool fSecureBoot = true; 548 bool fSetSecureBoot = false; 549 uint32_t cDbEntries = 0; 550 uint32_t cDbEntriesMax = 0; 551 552 RTGETOPTUNION ValueUnion; 553 int chOpt; 554 while ((chOpt = RTGetOpt(&State, &ValueUnion)) != 0) 555 { 556 switch (chOpt) 557 { 558 case 'p': 559 pszPkPath = ValueUnion.psz; 560 break; 561 case 'o': 562 pszUuidPkOwner = ValueUnion.psz; 563 break; 564 565 case 'k': 566 pszKekPath = ValueUnion.psz; 567 break; 568 case 'w': 569 pszUuidKekOwner = ValueUnion.psz; 570 break; 571 572 case 'd': 573 { 574 if (cDbEntries == cDbEntriesMax) 575 { 576 uint32_t cDbEntriesMaxNew = cDbEntriesMax + 10; 577 const char **papszDbNew = (const char **)RTMemRealloc(papszDb, cDbEntriesMaxNew * sizeof(const char *)); 578 if (!papszDbNew) 579 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Out of memory allocating memory for '%s'", ValueUnion.psz); 580 581 papszDb = papszDbNew; 582 cDbEntriesMax = cDbEntriesMaxNew; 583 } 584 585 papszDb[cDbEntries++] = ValueUnion.psz; 586 break; 587 } 588 589 case 's': 590 fSecureBoot = ValueUnion.f; 591 fSetSecureBoot = true; 592 break; 593 594 case VINF_GETOPT_NOT_OPTION: 595 /* The first non-option is the NVRAM file. */ 596 if (!pszNvram) 597 pszNvram = ValueUnion.psz; 598 else 599 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid option '%s'", ValueUnion.psz); 600 break; 601 602 default: 603 return RTGetOptPrintError(chOpt, &ValueUnion); 604 } 605 } 606 607 if (!pszNvram) 608 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The NVRAM file path is missing"); 609 610 if ( pszPkPath 611 && !pszUuidPkOwner) 612 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The PK is missing the owner UUID"); 613 614 if ( pszKekPath 615 && !pszUuidKekOwner) 616 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The KEK is missing the owner UUID"); 617 618 RTVFSFILE hVfsFileNvram = NIL_RTVFSFILE; 619 rc = RTVfsFileOpenNormal(pszNvram, RTFILE_O_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, 620 &hVfsFileNvram); 621 if (RT_SUCCESS(rc)) 622 { 623 RTVFS hVfsEfiVarStore = NIL_RTVFS; 624 rc = RTEfiVarStoreOpenAsVfs(hVfsFileNvram, 0 /*fMntFlags*/, 0 /*fVarStoreFlags*/, &hVfsEfiVarStore, RTErrInfoInitStatic(&ErrInfo)); 625 if (RT_SUCCESS(rc)) 626 { 627 if (pszPkPath) 628 rc = rtEfiSigDbVarStoreAddToDb(hVfsEfiVarStore, "PK", true /*fWipeDbBefore*/, 1 /*cSigs*/, pszPkPath, "x509", pszUuidPkOwner); 629 if ( RT_SUCCESS(rc) 630 && pszKekPath) 631 rc = rtEfiSigDbVarStoreAddToDb(hVfsEfiVarStore, "KEK", true /*fWipeDbBefore*/, 1 /*cSigs*/, pszKekPath, "x509", pszUuidKekOwner); 632 633 if ( RT_SUCCESS(rc) 634 && cDbEntries) 635 { 636 /** @todo Optimize to avoid re-opening and re-parsing the database for every entry. */ 637 for (uint32_t i = 0; i < cDbEntries && RT_SUCCESS(rc); i++) 638 { 639 const char *pszDbEntry = papszDb[i]; 640 641 const char *pszSigType = pszDbEntry; 642 const char *pszUuidOwner = strchr(pszSigType, ':'); 643 if (pszUuidOwner) 644 pszUuidOwner++; 645 const char *pszSigPath = pszUuidOwner ? strchr(pszUuidOwner, ':') : NULL; 646 if (pszSigPath) 647 pszSigPath++; 648 649 if ( pszUuidOwner 650 && pszSigPath) 651 { 652 char *pszSigTypeFree = RTStrDupN(pszSigType, pszUuidOwner - pszSigType - 1); 653 char *pszUuidOwnerFree = RTStrDupN(pszUuidOwner, pszSigPath - pszUuidOwner - 1); 654 655 if ( pszSigTypeFree 656 && pszUuidOwnerFree) 657 rc = rtEfiSigDbVarStoreAddToDb(hVfsEfiVarStore, "db", 658 i == 0 ? true : false /*fWipeDbBefore*/, 659 1 /*cSigs*/, 660 pszSigPath, pszSigTypeFree, pszUuidOwnerFree); 661 else 662 rc = RTMsgErrorRc(VERR_NO_MEMORY, "Out of memory!"); 663 664 if (pszSigTypeFree) 665 RTStrFree(pszSigTypeFree); 666 if (pszUuidOwnerFree) 667 RTStrFree(pszUuidOwnerFree); 668 } 669 else 670 rc = RTMsgErrorRc(VERR_INVALID_PARAMETER, "DB entry '%s' is malformed!", pszDbEntry); 671 } 672 673 if (RT_FAILURE(rc)) 674 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Initializing the NVRAM '%s' failed: %Rrc", pszNvram, rc); 675 } 676 677 RTVfsRelease(hVfsEfiVarStore); 678 } 679 680 RTVfsFileRelease(hVfsFileNvram); 681 } 682 683 if (papszDb) 684 RTMemFree(papszDb); 685 return rcExit; 686 } 687 345 688 int main(int argc, char **argv) 346 689 { … … 359 702 else if (!strcmp(argv[1], "add")) 360 703 rcExit = rtEfiSgDbCmdAdd(argv[0], argc - 2, argv + 2); 704 else if (!strcmp(argv[1], "initnvram")) 705 rcExit = rtEfiSgDbCmdInitNvram(argv[0], argc - 2, argv + 2); 361 706 else if ( !strcmp(argv[1], "-h") 362 707 || !strcmp(argv[1], "-?")
Note:
See TracChangeset
for help on using the changeset viewer.