diff options
Diffstat (limited to 'src/VBox/Runtime/tools/RTEfiSigDb.cpp')
-rw-r--r-- | src/VBox/Runtime/tools/RTEfiSigDb.cpp | 833 |
1 files changed, 833 insertions, 0 deletions
diff --git a/src/VBox/Runtime/tools/RTEfiSigDb.cpp b/src/VBox/Runtime/tools/RTEfiSigDb.cpp new file mode 100644 index 00000000..911831d4 --- /dev/null +++ b/src/VBox/Runtime/tools/RTEfiSigDb.cpp @@ -0,0 +1,833 @@ +/* $Id: RTEfiSigDb.cpp $ */ +/** @file + * IPRT - Utility for manipulating EFI signature databases. + */ + +/* + * Copyright (C) 2021-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/assert.h> +#include <iprt/buildconfig.h> +#include <iprt/err.h> +#include <iprt/efi.h> +#include <iprt/file.h> +#include <iprt/getopt.h> +#include <iprt/initterm.h> +#include <iprt/mem.h> +#include <iprt/message.h> +#include <iprt/path.h> +#include <iprt/stream.h> +#include <iprt/string.h> +#include <iprt/uuid.h> +#include <iprt/vfs.h> + +#include <iprt/formats/efi-signature.h> +#include <iprt/formats/efi-varstore.h> + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** Signature type identifier to internal type mapping. */ +struct RTEFISIGDBID2TYPEENTRY +{ + const char *pszId; + RTEFISIGTYPE enmType; +} g_aId2SigType[] = +{ + { "sha256", RTEFISIGTYPE_SHA256 }, + { "rsa2048", RTEFISIGTYPE_RSA2048 }, + { "x509", RTEFISIGTYPE_X509 } +}; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ + +/** + * Display the version of the cache program. + * + * @returns exit code. + */ +static RTEXITCODE rtEfiSigDbVersion(void) +{ + RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision()); + return RTEXITCODE_SUCCESS; +} + + +/** + * Shows the usage of the program. + * + * @returns Exit code. + * @param pszArg0 Program name. + * @param pszCommand Command selector, NULL if all. + */ +static RTEXITCODE rtEfiSigDbUsage(const char *pszArg0, const char *pszCommand) +{ + if (!pszCommand || !strcmp(pszCommand, "list")) + RTPrintf("Usage: %s list <signature database path>\n" + , RTPathFilename(pszArg0)); + + if (!pszCommand || !strcmp(pszCommand, "add")) + RTPrintf("Usage: %s add <signature database path> <x509|sha256|rsa2048> <owner uuid> <signature path> ...\n" + , RTPathFilename(pszArg0)); + + if (!pszCommand || !strcmp(pszCommand, "initnvram")) + RTPrintf("Usage: %s initnvram <nvram path> <init options>\n" + "\n" + "Init Options:\n" + " --pk <path>\n" + " Init the PK with the given signature.\n" + " --pk-owner <uuid>\n" + " Set the given UUID as the owner of the PK.\n" + " --kek <path>\n" + " Init the KEK with the given signature.\n" + " --kek-owner <uuid>\n" + " Set the given UUID as the owner of the KEK.\n" + " --db <x509|sha256|rsa2048>:<owner uuid>:<path>\n" + " Adds the given signature with the owner UUID and type to the db, can be given multiple times.\n" + " --secure-boot <on|off>\n" + " Enables or disables secure boot\n" + , RTPathFilename(pszArg0)); + + return RTEXITCODE_SUCCESS; +} + + +static RTEFISIGTYPE rtEfiSigDbGetTypeById(const char *pszId) +{ + for (uint32_t i = 0; i < RT_ELEMENTS(g_aId2SigType); i++) + if (!strcmp(pszId, g_aId2SigType[i].pszId)) + return g_aId2SigType[i].enmType; + + return RTEFISIGTYPE_INVALID; +} + + +/** + * Opens the specified signature database, returning an VFS file handle on success. + * + * @returns IPRT status code. + * @param pszPath Path to the signature database. + * @param phVfsFile Where to return the VFS file handle on success. + */ +static int rtEfiSigDbOpen(const char *pszPath, PRTVFSFILE phVfsFile) +{ + int rc; + + if (RTVfsChainIsSpec(pszPath)) + { + RTVFSOBJ hVfsObj; + rc = RTVfsChainOpenObj(pszPath, RTFILE_O_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, + RTVFSOBJ_F_OPEN_ANY | RTVFSOBJ_F_CREATE_NOTHING | RTPATH_F_ON_LINK, + &hVfsObj, NULL, NULL); + if ( RT_SUCCESS(rc) + && RTVfsObjGetType(hVfsObj) == RTVFSOBJTYPE_FILE) + { + *phVfsFile = RTVfsObjToFile(hVfsObj); + RTVfsObjRelease(hVfsObj); + } + else + { + RTPrintf("'%s' doesn't point to a file\n", pszPath); + rc = VERR_INVALID_PARAMETER; + } + } + else + rc = RTVfsFileOpenNormal(pszPath, RTFILE_O_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, + phVfsFile); + + return rc; +} + + +/** + * Signature database enumeration callback. + */ +static DECLCALLBACK(int) rtEfiSgDbEnum(RTEFISIGDB hEfiSigDb, RTEFISIGTYPE enmSigType, PCRTUUID pUuidOwner, + const void *pvSig, size_t cbSig, void *pvUser) +{ + RT_NOREF(hEfiSigDb, pvUser); + + uint32_t *pidxSig = (uint32_t *)pvUser; + + RTPrintf("%02u: %s\n", (*pidxSig)++, RTEfiSigDbTypeStringify(enmSigType)); + RTPrintf(" Owner: %RTuuid\n", pUuidOwner); + RTPrintf(" Signature:\n" + "%.*Rhxd\n\n", cbSig, pvSig); + return VINF_SUCCESS; +} + + +/** + * Handles the 'list' command. + * + * @returns Program exit code. + * @param pszArg0 The program name. + * @param cArgs The number of arguments to the 'add' command. + * @param papszArgs The argument vector, starting after 'add'. + */ +static RTEXITCODE rtEfiSgDbCmdList(const char *pszArg0, int cArgs, char **papszArgs) +{ + RT_NOREF(pszArg0); + + if (!cArgs) + { + RTPrintf("An input path must be given\n"); + return RTEXITCODE_FAILURE; + } + + RTEXITCODE rcExit = RTEXITCODE_SUCCESS; + RTVFSFILE hVfsFile = NIL_RTVFSFILE; + int rc = rtEfiSigDbOpen(papszArgs[0], &hVfsFile); + if (RT_SUCCESS(rc)) + { + RTEFISIGDB hEfiSigDb; + rc = RTEfiSigDbCreate(&hEfiSigDb); + if (RT_SUCCESS(rc)) + { + uint32_t idxSig = 0; + + rc = RTEfiSigDbAddFromExistingDb(hEfiSigDb, hVfsFile); + if (RT_SUCCESS(rc)) + RTEfiSigDbEnum(hEfiSigDb, rtEfiSgDbEnum, &idxSig); + else + { + RTPrintf("Loading the signature database failed with %Rrc\n", rc); + rcExit = RTEXITCODE_FAILURE; + } + + RTEfiSigDbDestroy(hEfiSigDb); + } + else + { + RTPrintf("Creating the signature database failed with %Rrc\n", rc); + rcExit = RTEXITCODE_FAILURE; + } + + RTVfsFileRelease(hVfsFile); + } + else + rcExit = RTEXITCODE_FAILURE; + + return rcExit; +} + + +/** + * Handles the 'add' command. + * + * @returns Program exit code. + * @param pszArg0 The program name. + * @param cArgs The number of arguments to the 'add' command. + * @param papszArgs The argument vector, starting after 'add'. + */ +static RTEXITCODE rtEfiSgDbCmdAdd(const char *pszArg0, int cArgs, char **papszArgs) +{ + RT_NOREF(pszArg0); + + if (!cArgs) + { + RTPrintf("The signature database path is missing\n"); + return RTEXITCODE_FAILURE; + } + + RTEXITCODE rcExit = RTEXITCODE_SUCCESS; + RTVFSFILE hVfsFile = NIL_RTVFSFILE; + int rc = rtEfiSigDbOpen(papszArgs[0], &hVfsFile); + if (RT_SUCCESS(rc)) + { + RTEFISIGDB hEfiSigDb; + rc = RTEfiSigDbCreate(&hEfiSigDb); + if (RT_SUCCESS(rc)) + { + uint64_t cbSigDb = 0; + rc = RTVfsFileQuerySize(hVfsFile, &cbSigDb); + if ( RT_SUCCESS(rc) + && cbSigDb) + rc = RTEfiSigDbAddFromExistingDb(hEfiSigDb, hVfsFile); + if (RT_SUCCESS(rc)) + { + cArgs--; + papszArgs++; + + while (cArgs >= 3) + { + RTEFISIGTYPE enmSigType = rtEfiSigDbGetTypeById(papszArgs[0]); + const char *pszUuidOwner = papszArgs[1]; + const char *pszSigDataPath = papszArgs[2]; + + if (enmSigType == RTEFISIGTYPE_INVALID) + { + RTPrintf("Signature type '%s' is not known\n", papszArgs[0]); + break; + } + + RTUUID UuidOwner; + rc = RTUuidFromStr(&UuidOwner, pszUuidOwner); + if (RT_FAILURE(rc)) + { + RTPrintf("UUID '%s' is malformed\n", pszUuidOwner); + break; + } + + RTVFSFILE hVfsFileSigData = NIL_RTVFSFILE; + rc = rtEfiSigDbOpen(pszSigDataPath, &hVfsFileSigData); + if (RT_FAILURE(rc)) + { + RTPrintf("Opening '%s' failed with %Rrc\n", pszSigDataPath, rc); + break; + } + + rc = RTEfiSigDbAddSignatureFromFile(hEfiSigDb, enmSigType, &UuidOwner, hVfsFileSigData); + RTVfsFileRelease(hVfsFileSigData); + if (RT_FAILURE(rc)) + { + RTPrintf("Adding signature data from '%s' failed with %Rrc\n", pszSigDataPath, rc); + break; + } + papszArgs += 3; + cArgs -= 3; + } + + if (RT_SUCCESS(rc)) + { + if (!cArgs) + { + rc = RTVfsFileSeek(hVfsFile, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/); + AssertRC(rc); + + rc = RTEfiSigDbWriteToFile(hEfiSigDb, hVfsFile); + if (RT_FAILURE(rc)) + { + RTPrintf("Writing the updated signature database failed with %Rrc\n", rc); + rcExit = RTEXITCODE_FAILURE; + } + } + else + { + RTPrintf("Incomplete list of entries to add given\n"); + rcExit = RTEXITCODE_FAILURE; + } + } + } + else + { + RTPrintf("Loading the signature database failed with %Rrc\n", rc); + rcExit = RTEXITCODE_FAILURE; + } + + RTEfiSigDbDestroy(hEfiSigDb); + } + else + { + RTPrintf("Creating the signature database failed with %Rrc\n", rc); + rcExit = RTEXITCODE_FAILURE; + } + + RTVfsFileRelease(hVfsFile); + } + else + rcExit = RTEXITCODE_FAILURE; + + return rcExit; +} + + +/** + * Adds the given signature to the given database. + * + * @returns IPRT status code. + * @param hEfiSigDb The EFI signature database handle. + * @param pszSigPath The signature data path. + * @param pszSigType The signature type. + * @param pszUuidOwner The owner UUID. + */ +static int rtEfiSigDbAddSig(RTEFISIGDB hEfiSigDb, const char *pszSigPath, const char *pszSigType, const char *pszUuidOwner) +{ + RTEFISIGTYPE enmSigType = rtEfiSigDbGetTypeById(pszSigType); + if (enmSigType == RTEFISIGTYPE_INVALID) + return RTMsgErrorRc(VERR_INVALID_PARAMETER, "Signature type '%s' is unknown!", pszSigType); + + RTUUID UuidOwner; + int rc = RTUuidFromStr(&UuidOwner, pszUuidOwner); + if (RT_FAILURE(rc)) + return RTMsgErrorRc(VERR_INVALID_PARAMETER, "Owner UUID '%s' is malformed!", pszUuidOwner); + + RTVFSFILE hVfsFileSigData = NIL_RTVFSFILE; + rc = rtEfiSigDbOpen(pszSigPath, &hVfsFileSigData); + if (RT_FAILURE(rc)) + return RTMsgErrorRc(rc, "Opening '%s' failed: %Rrc", pszSigPath, rc); + + rc = RTEfiSigDbAddSignatureFromFile(hEfiSigDb, enmSigType, &UuidOwner, hVfsFileSigData); + RTVfsFileRelease(hVfsFileSigData); + if (RT_FAILURE(rc)) + return RTMsgErrorRc(rc, "Adding signature '%s' failed: %Rrc", pszSigPath, rc); + + return VINF_SUCCESS; +} + + +/** + * Sets the given attributes for the given EFI variable store variable. + * + * @returns IPRT status code. + * @param hVfsVarStore Handle of the EFI variable store VFS. + * @param pszVar The variable to set the attributes for. + * @param fAttr The attributes to set, see EFI_VAR_HEADER_ATTR_XXX. + */ +static int rtEfiSigDbSetVarAttr(RTVFS hVfsVarStore, const char *pszVar, uint32_t fAttr) +{ + char szVarPath[_1K]; + ssize_t cch = RTStrPrintf2(szVarPath, sizeof(szVarPath), "/raw/%s/attr", pszVar); + Assert(cch > 0); RT_NOREF(cch); + + RTVFSFILE hVfsFileAttr = NIL_RTVFSFILE; + int rc = RTVfsFileOpen(hVfsVarStore, szVarPath, + RTFILE_O_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, + &hVfsFileAttr); + if (RT_SUCCESS(rc)) + { + uint32_t fAttrLe = RT_H2LE_U32(fAttr); + rc = RTVfsFileWrite(hVfsFileAttr, &fAttrLe, sizeof(fAttrLe), NULL /*pcbWritten*/); + RTVfsFileRelease(hVfsFileAttr); + } + + return rc; +} + + +/** + * Adds the given variable to the variable store. + * + * @returns IPRT status code. + * @param hVfsVarStore Handle of the EFI variable store VFS. + * @param pGuid The EFI GUID of the variable. + * @param pszVar The variable name. + * @param fAttr Attributes for the variable. + * @param phVfsFile Where to return the VFS file handle to the created variable on success. + */ +static int rtEfiSigDbVarStoreAddVar(RTVFS hVfsVarStore, PCEFI_GUID pGuid, const char *pszVar, uint32_t fAttr, PRTVFSFILE phVfsFile) +{ + RTUUID UuidVar; + RTEfiGuidToUuid(&UuidVar, pGuid); + + char szVarPath[_1K]; + ssize_t cch = RTStrPrintf2(szVarPath, sizeof(szVarPath), "/by-uuid/%RTuuid/%s", &UuidVar, pszVar); + Assert(cch > 0); RT_NOREF(cch); + + int rc = RTVfsFileOpen(hVfsVarStore, szVarPath, + RTFILE_O_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, + phVfsFile); + if ( rc == VERR_PATH_NOT_FOUND + || rc == VERR_FILE_NOT_FOUND) + { + /* + * Try to create the owner GUID of the variable by creating the appropriate directory, + * ignore error if it exists already. + */ + RTVFSDIR hVfsDirRoot = NIL_RTVFSDIR; + rc = RTVfsOpenRoot(hVfsVarStore, &hVfsDirRoot); + if (RT_SUCCESS(rc)) + { + char szGuidPath[_1K]; + cch = RTStrPrintf2(szGuidPath, sizeof(szGuidPath), "by-uuid/%RTuuid", &UuidVar); + Assert(cch > 0); + + RTVFSDIR hVfsDirGuid = NIL_RTVFSDIR; + rc = RTVfsDirCreateDir(hVfsDirRoot, szGuidPath, 0755, 0 /*fFlags*/, &hVfsDirGuid); + if (RT_SUCCESS(rc)) + RTVfsDirRelease(hVfsDirGuid); + else if (rc == VERR_ALREADY_EXISTS) + rc = VINF_SUCCESS; + + RTVfsDirRelease(hVfsDirRoot); + } + else + rc = RTMsgErrorRc(rc, "Opening variable storage root directory failed: %Rrc", rc); + + if (RT_SUCCESS(rc)) + { + rc = RTVfsFileOpen(hVfsVarStore, szVarPath, + RTFILE_O_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_CREATE, + phVfsFile); + if (RT_SUCCESS(rc)) + rc = rtEfiSigDbSetVarAttr(hVfsVarStore, pszVar, fAttr); + } + + if (RT_FAILURE(rc)) + rc = RTMsgErrorRc(rc, "Creating the variable '%s' failed: %Rrc", pszVar, rc); + } + + return rc; +} + + +/** + * Creates the given variable and sets the data. + * + * @returns IPRT status code. + * @param hVfsVarStore Handle of the EFI variable store VFS. + * @param pGuid The EFI GUID of the variable. + * @param pszVar The variable name. + * @param fAttr Attributes for the variable. + * @param pvBuf The data to write. + * @param cbBuf Number of bytes of data. + */ +static int rtEfiSigDbVarStoreSetVar(RTVFS hVfsVarStore, PCEFI_GUID pGuid, const char *pszVar, uint32_t fAttr, + const void *pvBuf, size_t cbBuf) +{ + RTVFSFILE hVfsFileVar = NIL_RTVFSFILE; + int rc = rtEfiSigDbVarStoreAddVar(hVfsVarStore, pGuid, pszVar, fAttr, &hVfsFileVar); + if (RT_SUCCESS(rc)) + { + rc = RTVfsFileWrite(hVfsFileVar, pvBuf, cbBuf, NULL /*pcbWritten*/); + if (RT_FAILURE(rc)) + rc = RTMsgErrorRc(rc, "Writing variable '%s' failed: %Rrc", pszVar, rc); + RTVfsFileRelease(hVfsFileVar); + } + else + rc = RTMsgErrorRc(rc, "Creating variable '%s' failed: %Rrc", pszVar, rc); + + return rc; +} + + +/** + * Adds the given signature to the given signature database of the given EFI variable store. + * + * @returns IPRT status code. + * @param hVfsVarStore Handle of the EFI variable store VFS. + * @param pGuid The EFI GUID of the variable. + * @param pszDb The signature database to update. + * @param fWipeDbBefore Flag whether to wipe the database before adding the signature. + * @param cSigs Number of signatures following. + * @param ... A triple of signature path, signature type and owner uuid string pointers for each + * signature. + */ +static int rtEfiSigDbVarStoreAddToDb(RTVFS hVfsVarStore, PCEFI_GUID pGuid, const char *pszDb, bool fWipeDbBefore, uint32_t cSigs, + ... /*const char *pszSigPath, const char *pszSigType, const char *pszUuidOwner*/) +{ + RTVFSFILE hVfsFileSigDb = NIL_RTVFSFILE; + int rc = rtEfiSigDbVarStoreAddVar(hVfsVarStore, pGuid, pszDb, + EFI_VAR_HEADER_ATTR_NON_VOLATILE + | EFI_VAR_HEADER_ATTR_BOOTSERVICE_ACCESS + | EFI_VAR_HEADER_ATTR_RUNTIME_ACCESS + | EFI_AUTH_VAR_HEADER_ATTR_TIME_BASED_AUTH_WRITE_ACCESS, + &hVfsFileSigDb); + if (RT_SUCCESS(rc)) + { + RTEFISIGDB hEfiSigDb; + rc = RTEfiSigDbCreate(&hEfiSigDb); + if (RT_SUCCESS(rc)) + { + if (!fWipeDbBefore) + rc = RTEfiSigDbAddFromExistingDb(hEfiSigDb, hVfsFileSigDb); + if (RT_SUCCESS(rc)) + { + va_list VarArgs; + va_start(VarArgs, cSigs); + + while ( cSigs-- + && RT_SUCCESS(rc)) + { + const char *pszSigPath = va_arg(VarArgs, const char *); + const char *pszSigType = va_arg(VarArgs, const char *); + const char *pszUuidOwner = va_arg(VarArgs, const char *); + + rc = rtEfiSigDbAddSig(hEfiSigDb, pszSigPath, pszSigType, pszUuidOwner); + } + + va_end(VarArgs); + if (RT_SUCCESS(rc)) + { + rc = RTVfsFileSeek(hVfsFileSigDb, 0 /*offSeek*/, RTFILE_SEEK_BEGIN, NULL /*poffActual*/); + AssertRC(rc); + + rc = RTEfiSigDbWriteToFile(hEfiSigDb, hVfsFileSigDb); + if (RT_FAILURE(rc)) + rc = RTMsgErrorRc(rc, "Writing updated signature database failed: %Rrc", rc); + } + } + else + rc = RTMsgErrorRc(rc, "Loading signature database failed: %Rrc", rc); + + RTEfiSigDbDestroy(hEfiSigDb); + } + else + rc = RTMsgErrorRc(rc, "Creating signature database failed: %Rrc", rc); + + RTVfsFileRelease(hVfsFileSigDb); + } + else + rc = RTMsgErrorRc(rc, "Opening signature database '%s' failed: %Rrc", pszDb, rc); + + return rc; +} + + +/** + * Handles the 'initnvram' command. + * + * @returns Program exit code. + * @param pszArg0 The program name. + * @param cArgs The number of arguments to the 'add' command. + * @param papszArgs The argument vector, starting after 'add'. + */ +static RTEXITCODE rtEfiSgDbCmdInitNvram(const char *pszArg0, int cArgs, char **papszArgs) +{ + RT_NOREF(pszArg0); + RTERRINFOSTATIC ErrInfo; + + /* + * Parse the command line. + */ + static RTGETOPTDEF const s_aOptions[] = + { + { "--pk", 'p', RTGETOPT_REQ_STRING }, + { "--pk-owner", 'o', RTGETOPT_REQ_STRING }, + { "--kek", 'k', RTGETOPT_REQ_STRING }, + { "--kek-owner", 'w', RTGETOPT_REQ_STRING }, + { "--db", 'd', RTGETOPT_REQ_STRING }, + { "--secure-boot", 's', RTGETOPT_REQ_BOOL_ONOFF } + }; + + RTEXITCODE rcExit = RTEXITCODE_SUCCESS; + RTGETOPTSTATE State; + int rc = RTGetOptInit(&State, cArgs, papszArgs, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST); + if (RT_FAILURE(rc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc", rc); + + const char *pszNvram = NULL; + const char *pszPkPath = NULL; + const char *pszUuidPkOwner = NULL; + const char *pszKekPath = NULL; + const char *pszUuidKekOwner = NULL; + const char **papszDb = NULL; + bool fSecureBoot = true; + bool fSetSecureBoot = false; + uint32_t cDbEntries = 0; + uint32_t cDbEntriesMax = 0; + + RTGETOPTUNION ValueUnion; + int chOpt; + while ((chOpt = RTGetOpt(&State, &ValueUnion)) != 0) + { + switch (chOpt) + { + case 'p': + pszPkPath = ValueUnion.psz; + break; + case 'o': + pszUuidPkOwner = ValueUnion.psz; + break; + + case 'k': + pszKekPath = ValueUnion.psz; + break; + case 'w': + pszUuidKekOwner = ValueUnion.psz; + break; + + case 'd': + { + if (cDbEntries == cDbEntriesMax) + { + uint32_t cDbEntriesMaxNew = cDbEntriesMax + 10; + const char **papszDbNew = (const char **)RTMemRealloc(papszDb, cDbEntriesMaxNew * sizeof(const char *)); + if (!papszDbNew) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Out of memory allocating memory for '%s'", ValueUnion.psz); + + papszDb = papszDbNew; + cDbEntriesMax = cDbEntriesMaxNew; + } + + papszDb[cDbEntries++] = ValueUnion.psz; + break; + } + + case 's': + fSecureBoot = ValueUnion.f; + fSetSecureBoot = true; + break; + + case VINF_GETOPT_NOT_OPTION: + /* The first non-option is the NVRAM file. */ + if (!pszNvram) + pszNvram = ValueUnion.psz; + else + return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid option '%s'", ValueUnion.psz); + break; + + default: + return RTGetOptPrintError(chOpt, &ValueUnion); + } + } + + if (!pszNvram) + return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The NVRAM file path is missing"); + + if ( pszPkPath + && !pszUuidPkOwner) + return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The PK is missing the owner UUID"); + + if ( pszKekPath + && !pszUuidKekOwner) + return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The KEK is missing the owner UUID"); + + RTVFSFILE hVfsFileNvram = NIL_RTVFSFILE; + rc = RTVfsFileOpenNormal(pszNvram, RTFILE_O_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, + &hVfsFileNvram); + if (RT_SUCCESS(rc)) + { + RTVFS hVfsEfiVarStore = NIL_RTVFS; + rc = RTEfiVarStoreOpenAsVfs(hVfsFileNvram, 0 /*fMntFlags*/, 0 /*fVarStoreFlags*/, &hVfsEfiVarStore, RTErrInfoInitStatic(&ErrInfo)); + if (RT_SUCCESS(rc)) + { + EFI_GUID GuidSecurityDb = EFI_IMAGE_SECURITY_DATABASE_GUID; + EFI_GUID GuidGlobalVar = EFI_GLOBAL_VARIABLE_GUID; + + if (pszPkPath) + rc = rtEfiSigDbVarStoreAddToDb(hVfsEfiVarStore, &GuidGlobalVar, "PK", true /*fWipeDbBefore*/, 1 /*cSigs*/, pszPkPath, "x509", pszUuidPkOwner); + if ( RT_SUCCESS(rc) + && pszKekPath) + rc = rtEfiSigDbVarStoreAddToDb(hVfsEfiVarStore, &GuidGlobalVar ,"KEK", true /*fWipeDbBefore*/, 1 /*cSigs*/, pszKekPath, "x509", pszUuidKekOwner); + + if ( RT_SUCCESS(rc) + && cDbEntries) + { + /** @todo Optimize to avoid re-opening and re-parsing the database for every entry. */ + for (uint32_t i = 0; i < cDbEntries && RT_SUCCESS(rc); i++) + { + const char *pszDbEntry = papszDb[i]; + + const char *pszSigType = pszDbEntry; + const char *pszUuidOwner = strchr(pszSigType, ':'); + if (pszUuidOwner) + pszUuidOwner++; + const char *pszSigPath = pszUuidOwner ? strchr(pszUuidOwner, ':') : NULL; + if (pszSigPath) + pszSigPath++; + + if ( pszUuidOwner + && pszSigPath) + { + char *pszSigTypeFree = RTStrDupN(pszSigType, pszUuidOwner - pszSigType - 1); + char *pszUuidOwnerFree = RTStrDupN(pszUuidOwner, pszSigPath - pszUuidOwner - 1); + + if ( pszSigTypeFree + && pszUuidOwnerFree) + rc = rtEfiSigDbVarStoreAddToDb(hVfsEfiVarStore, &GuidSecurityDb, "db", + i == 0 ? true : false /*fWipeDbBefore*/, + 1 /*cSigs*/, + pszSigPath, pszSigTypeFree, pszUuidOwnerFree); + else + rc = RTMsgErrorRc(VERR_NO_MEMORY, "Out of memory!"); + + if (pszSigTypeFree) + RTStrFree(pszSigTypeFree); + if (pszUuidOwnerFree) + RTStrFree(pszUuidOwnerFree); + } + else + rc = RTMsgErrorRc(VERR_INVALID_PARAMETER, "DB entry '%s' is malformed!", pszDbEntry); + } + + if (RT_FAILURE(rc)) + rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Initializing the NVRAM '%s' failed: %Rrc", pszNvram, rc); + } + + if ( RT_SUCCESS(rc) + && fSetSecureBoot) + { + EFI_GUID GuidSecureBootEnable = EFI_SECURE_BOOT_ENABLE_DISABLE_GUID; + uint8_t bVar = fSecureBoot ? 0x1 : 0x0; + rtEfiSigDbVarStoreSetVar(hVfsEfiVarStore, &GuidSecureBootEnable, "SecureBootEnable", + EFI_VAR_HEADER_ATTR_NON_VOLATILE + | EFI_VAR_HEADER_ATTR_BOOTSERVICE_ACCESS + | EFI_VAR_HEADER_ATTR_RUNTIME_ACCESS, + &bVar, sizeof(bVar)); + } + + RTVfsRelease(hVfsEfiVarStore); + } + + RTVfsFileRelease(hVfsFileNvram); + } + + if (papszDb) + RTMemFree(papszDb); + return rcExit; +} + + +int main(int argc, char **argv) +{ + int rc = RTR3InitExe(argc, &argv, 0); + if (RT_FAILURE(rc)) + return RTMsgInitFailure(rc); + + /* + * Switch on the command. + */ + RTEXITCODE rcExit = RTEXITCODE_SYNTAX; + if (argc < 2) + rtEfiSigDbUsage(argv[0], NULL); + else if (!strcmp(argv[1], "list")) + rcExit = rtEfiSgDbCmdList(argv[0], argc - 2, argv + 2); + else if (!strcmp(argv[1], "add")) + rcExit = rtEfiSgDbCmdAdd(argv[0], argc - 2, argv + 2); + else if (!strcmp(argv[1], "initnvram")) + rcExit = rtEfiSgDbCmdInitNvram(argv[0], argc - 2, argv + 2); + else if ( !strcmp(argv[1], "-h") + || !strcmp(argv[1], "-?") + || !strcmp(argv[1], "--help")) + rcExit = rtEfiSigDbUsage(argv[0], NULL); + else if ( !strcmp(argv[1], "-V") + || !strcmp(argv[1], "--version")) + rcExit = rtEfiSigDbVersion(); + else + RTMsgError("Unknown command: '%s'", argv[1]); + + return rcExit; +} + |