VirtualBox

Changeset 90322 in vbox


Ignore:
Timestamp:
Jul 23, 2021 6:21:09 PM (4 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
145883
Message:

Runtime/tools/RTEfiSigDb: Add some sub-command to initialize a NVRAM file for secure boot, work in progress, bugref:9580

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/tools/RTEfiSigDb.cpp

    r90317 r90322  
    3131#include <iprt/assert.h>
    3232#include <iprt/buildconfig.h>
    33 #include <iprt/errcore.h>
     33#include <iprt/err.h>
    3434#include <iprt/efi.h>
    3535#include <iprt/file.h>
     
    4444#include <iprt/vfs.h>
    4545
     46#include <iprt/formats/efi-signature.h>
     47
    4648
    4749/*********************************************************************************************************************************
     
    9799    if (!pszCommand || !strcmp(pszCommand, "add"))
    98100        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"
    99119                 , RTPathFilename(pszArg0));
    100120
     
    343363
    344364
     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 */
     374static 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 */
     410static 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 */
     517static 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
    345688int main(int argc, char **argv)
    346689{
     
    359702    else if (!strcmp(argv[1], "add"))
    360703        rcExit = rtEfiSgDbCmdAdd(argv[0], argc - 2, argv + 2);
     704    else if (!strcmp(argv[1], "initnvram"))
     705        rcExit = rtEfiSgDbCmdInitNvram(argv[0], argc - 2, argv + 2);
    361706    else if (   !strcmp(argv[1], "-h")
    362707             || !strcmp(argv[1], "-?")
Note: See TracChangeset for help on using the changeset viewer.

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