summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/tools/RTSignTool.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/VBox/Runtime/tools/RTSignTool.cpp2729
1 files changed, 2729 insertions, 0 deletions
diff --git a/src/VBox/Runtime/tools/RTSignTool.cpp b/src/VBox/Runtime/tools/RTSignTool.cpp
new file mode 100644
index 00000000..14ee89fc
--- /dev/null
+++ b/src/VBox/Runtime/tools/RTSignTool.cpp
@@ -0,0 +1,2729 @@
+/* $Id: RTSignTool.cpp $ */
+/** @file
+ * IPRT - Signing Tool.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE 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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/assert.h>
+#include <iprt/buildconfig.h>
+#include <iprt/err.h>
+#include <iprt/getopt.h>
+#include <iprt/file.h>
+#include <iprt/initterm.h>
+#include <iprt/ldr.h>
+#include <iprt/message.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/uuid.h>
+#include <iprt/zero.h>
+#ifndef RT_OS_WINDOWS
+# include <iprt/formats/pecoff.h>
+#endif
+#include <iprt/crypto/applecodesign.h>
+#include <iprt/crypto/digest.h>
+#include <iprt/crypto/x509.h>
+#include <iprt/crypto/pkcs7.h>
+#include <iprt/crypto/store.h>
+#include <iprt/crypto/spc.h>
+#ifdef VBOX
+# include <VBox/sup.h> /* Certificates */
+#endif
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/windows.h>
+# include <ImageHlp.h>
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Help detail levels. */
+typedef enum RTSIGNTOOLHELP
+{
+ RTSIGNTOOLHELP_USAGE,
+ RTSIGNTOOLHELP_FULL
+} RTSIGNTOOLHELP;
+
+
+/**
+ * PKCS\#7 signature data.
+ */
+typedef struct SIGNTOOLPKCS7
+{
+ /** The raw signature. */
+ uint8_t *pbBuf;
+ /** Size of the raw signature. */
+ size_t cbBuf;
+ /** The filename. */
+ const char *pszFilename;
+ /** The outer content info wrapper. */
+ RTCRPKCS7CONTENTINFO ContentInfo;
+ /** Pointer to the decoded SignedData inside the ContentInfo member. */
+ PRTCRPKCS7SIGNEDDATA pSignedData;
+
+ /** Newly encoded raw signature.
+ * @sa SignToolPkcs7_Encode() */
+ uint8_t *pbNewBuf;
+ /** Size of newly encoded raw signature. */
+ size_t cbNewBuf;
+
+} SIGNTOOLPKCS7;
+typedef SIGNTOOLPKCS7 *PSIGNTOOLPKCS7;
+
+
+/**
+ * PKCS\#7 signature data for executable.
+ */
+typedef struct SIGNTOOLPKCS7EXE : public SIGNTOOLPKCS7
+{
+ /** The module handle. */
+ RTLDRMOD hLdrMod;
+} SIGNTOOLPKCS7EXE;
+typedef SIGNTOOLPKCS7EXE *PSIGNTOOLPKCS7EXE;
+
+
+/**
+ * Data for the show exe (signature) command.
+ */
+typedef struct SHOWEXEPKCS7 : public SIGNTOOLPKCS7EXE
+{
+ /** The verbosity. */
+ unsigned cVerbosity;
+ /** The prefix buffer. */
+ char szPrefix[256];
+ /** Temporary buffer. */
+ char szTmp[4096];
+} SHOWEXEPKCS7;
+typedef SHOWEXEPKCS7 *PSHOWEXEPKCS7;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static RTEXITCODE HandleHelp(int cArgs, char **papszArgs);
+static RTEXITCODE HelpHelp(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel);
+static RTEXITCODE HandleVersion(int cArgs, char **papszArgs);
+static int HandleShowExeWorkerPkcs7Display(PSHOWEXEPKCS7 pThis, PRTCRPKCS7SIGNEDDATA pSignedData, size_t offPrefix,
+ PCRTCRPKCS7CONTENTINFO pContentInfo);
+
+
+/**
+ * Deletes the structure.
+ *
+ * @param pThis The structure to initialize.
+ */
+static void SignToolPkcs7_Delete(PSIGNTOOLPKCS7 pThis)
+{
+ RTCrPkcs7ContentInfo_Delete(&pThis->ContentInfo);
+ pThis->pSignedData = NULL;
+ RTMemFree(pThis->pbBuf);
+ pThis->pbBuf = NULL;
+ pThis->cbBuf = 0;
+ RTMemFree(pThis->pbNewBuf);
+ pThis->pbNewBuf = NULL;
+ pThis->cbNewBuf = 0;
+}
+
+
+/**
+ * Deletes the structure.
+ *
+ * @param pThis The structure to initialize.
+ */
+static void SignToolPkcs7Exe_Delete(PSIGNTOOLPKCS7EXE pThis)
+{
+ if (pThis->hLdrMod != NIL_RTLDRMOD)
+ {
+ int rc2 = RTLdrClose(pThis->hLdrMod);
+ if (RT_FAILURE(rc2))
+ RTMsgError("RTLdrClose failed: %Rrc\n", rc2);
+ pThis->hLdrMod = NIL_RTLDRMOD;
+ }
+ SignToolPkcs7_Delete(pThis);
+}
+
+
+/**
+ * Decodes the PKCS #7 blob pointed to by pThis->pbBuf.
+ *
+ * @returns IPRT status code (error message already shown on failure).
+ * @param pThis The PKCS\#7 signature to decode.
+ * @param fCatalog Set if catalog file, clear if executable.
+ */
+static int SignToolPkcs7_Decode(PSIGNTOOLPKCS7 pThis, bool fCatalog)
+{
+ RTERRINFOSTATIC ErrInfo;
+ RTASN1CURSORPRIMARY PrimaryCursor;
+ RTAsn1CursorInitPrimary(&PrimaryCursor, pThis->pbBuf, (uint32_t)pThis->cbBuf, RTErrInfoInitStatic(&ErrInfo),
+ &g_RTAsn1DefaultAllocator, 0, "WinCert");
+
+ int rc = RTCrPkcs7ContentInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, &pThis->ContentInfo, "CI");
+ if (RT_SUCCESS(rc))
+ {
+ if (RTCrPkcs7ContentInfo_IsSignedData(&pThis->ContentInfo))
+ {
+ pThis->pSignedData = pThis->ContentInfo.u.pSignedData;
+
+ /*
+ * Decode the authenticode bits.
+ */
+ if (!strcmp(pThis->pSignedData->ContentInfo.ContentType.szObjId, RTCRSPCINDIRECTDATACONTENT_OID))
+ {
+ PRTCRSPCINDIRECTDATACONTENT pIndData = pThis->pSignedData->ContentInfo.u.pIndirectDataContent;
+ Assert(pIndData);
+
+ /*
+ * Check that things add up.
+ */
+ rc = RTCrPkcs7SignedData_CheckSanity(pThis->pSignedData,
+ RTCRPKCS7SIGNEDDATA_SANITY_F_AUTHENTICODE
+ | RTCRPKCS7SIGNEDDATA_SANITY_F_ONLY_KNOWN_HASH
+ | RTCRPKCS7SIGNEDDATA_SANITY_F_SIGNING_CERT_PRESENT,
+ RTErrInfoInitStatic(&ErrInfo), "SD");
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrSpcIndirectDataContent_CheckSanityEx(pIndData,
+ pThis->pSignedData,
+ RTCRSPCINDIRECTDATACONTENT_SANITY_F_ONLY_KNOWN_HASH,
+ RTErrInfoInitStatic(&ErrInfo));
+ if (RT_FAILURE(rc))
+ RTMsgError("SPC indirect data content sanity check failed for '%s': %Rrc - %s\n",
+ pThis->pszFilename, rc, ErrInfo.szMsg);
+ }
+ else
+ RTMsgError("PKCS#7 sanity check failed for '%s': %Rrc - %s\n", pThis->pszFilename, rc, ErrInfo.szMsg);
+ }
+ else if (!strcmp(pThis->pSignedData->ContentInfo.ContentType.szObjId, RTCR_PKCS7_DATA_OID))
+ { /* apple code signing */ }
+ else if (!fCatalog)
+ RTMsgError("Unexpected the signed content in '%s': %s (expected %s)", pThis->pszFilename,
+ pThis->pSignedData->ContentInfo.ContentType.szObjId, RTCRSPCINDIRECTDATACONTENT_OID);
+ }
+ else
+ rc = RTMsgErrorRc(VERR_CR_PKCS7_NOT_SIGNED_DATA,
+ "PKCS#7 content is inside '%s' is not 'signedData': %s\n",
+ pThis->pszFilename, pThis->ContentInfo.ContentType.szObjId);
+ }
+ else
+ RTMsgError("RTCrPkcs7ContentInfo_DecodeAsn1 failed on '%s': %Rrc - %s\n", pThis->pszFilename, rc, ErrInfo.szMsg);
+ return rc;
+}
+
+
+/**
+ * Reads and decodes PKCS\#7 signature from the given cat file.
+ *
+ * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error message
+ * on failure.
+ * @param pThis The structure to initialize.
+ * @param pszFilename The catalog (or any other DER PKCS\#7) filename.
+ * @param cVerbosity The verbosity.
+ */
+static RTEXITCODE SignToolPkcs7_InitFromFile(PSIGNTOOLPKCS7 pThis, const char *pszFilename, unsigned cVerbosity)
+{
+ /*
+ * Init the return structure.
+ */
+ RT_ZERO(*pThis);
+ pThis->pszFilename = pszFilename;
+
+ /*
+ * Lazy bird uses RTFileReadAll and duplicates the allocation.
+ */
+ void *pvFile;
+ int rc = RTFileReadAll(pszFilename, &pvFile, &pThis->cbBuf);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->pbBuf = (uint8_t *)RTMemDup(pvFile, pThis->cbBuf);
+ RTFileReadAllFree(pvFile, pThis->cbBuf);
+ if (pThis->pbBuf)
+ {
+ if (cVerbosity > 2)
+ RTPrintf("PKCS#7 signature: %u bytes\n", pThis->cbBuf);
+
+ /*
+ * Decode it.
+ */
+ rc = SignToolPkcs7_Decode(pThis, true /*fCatalog*/);
+ if (RT_SUCCESS(rc))
+ return RTEXITCODE_SUCCESS;
+ }
+ else
+ RTMsgError("Out of memory!");
+ }
+ else
+ RTMsgError("Error reading '%s' into memory: %Rrc", pszFilename, rc);
+
+ SignToolPkcs7_Delete(pThis);
+ return RTEXITCODE_FAILURE;
+}
+
+
+/**
+ * Encodes the signature into the SIGNTOOLPKCS7::pbNewBuf and
+ * SIGNTOOLPKCS7::cbNewBuf members.
+ *
+ * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error message
+ * on failure.
+ * @param pThis The signature to encode.
+ * @param cVerbosity The verbosity.
+ */
+static RTEXITCODE SignToolPkcs7_Encode(PSIGNTOOLPKCS7 pThis, unsigned cVerbosity)
+{
+ RTERRINFOSTATIC StaticErrInfo;
+ PRTASN1CORE pRoot = RTCrPkcs7ContentInfo_GetAsn1Core(&pThis->ContentInfo);
+ uint32_t cbEncoded;
+ int rc = RTAsn1EncodePrepare(pRoot, RTASN1ENCODE_F_DER, &cbEncoded, RTErrInfoInitStatic(&StaticErrInfo));
+ if (RT_SUCCESS(rc))
+ {
+ if (cVerbosity >= 4)
+ RTAsn1Dump(pRoot, 0, 0, RTStrmDumpPrintfV, g_pStdOut);
+
+ RTMemFree(pThis->pbNewBuf);
+ pThis->cbNewBuf = cbEncoded;
+ pThis->pbNewBuf = (uint8_t *)RTMemAllocZ(cbEncoded);
+ if (pThis->pbNewBuf)
+ {
+ rc = RTAsn1EncodeToBuffer(pRoot, RTASN1ENCODE_F_DER, pThis->pbNewBuf, pThis->cbNewBuf,
+ RTErrInfoInitStatic(&StaticErrInfo));
+ if (RT_SUCCESS(rc))
+ {
+ if (cVerbosity > 1)
+ RTMsgInfo("Encoded signature to %u bytes", cbEncoded);
+ return RTEXITCODE_SUCCESS;
+ }
+ RTMsgError("RTAsn1EncodeToBuffer failed: %Rrc", rc);
+
+ RTMemFree(pThis->pbNewBuf);
+ pThis->pbNewBuf = NULL;
+ }
+ else
+ RTMsgError("Failed to allocate %u bytes!", cbEncoded);
+ }
+ else
+ RTMsgError("RTAsn1EncodePrepare failed: %Rrc - %s", rc, StaticErrInfo.szMsg);
+ return RTEXITCODE_FAILURE;
+}
+
+
+/**
+ * Adds the @a pSrc signature as a nested signature.
+ *
+ * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error message
+ * on failure.
+ * @param pThis The signature to modify.
+ * @param pSrc The signature to add as nested.
+ * @param cVerbosity The verbosity.
+ * @param fPrepend Whether to prepend (true) or append (false) the
+ * source signature to the nested attribute.
+ */
+static RTEXITCODE SignToolPkcs7_AddNestedSignature(PSIGNTOOLPKCS7 pThis, PSIGNTOOLPKCS7 pSrc,
+ unsigned cVerbosity, bool fPrepend)
+{
+ PRTCRPKCS7SIGNERINFO pSignerInfo = pThis->pSignedData->SignerInfos.papItems[0];
+ int rc;
+
+ /*
+ * Deal with UnauthenticatedAttributes being absent before trying to append to the array.
+ */
+ if (pSignerInfo->UnauthenticatedAttributes.cItems == 0)
+ {
+ /* HACK ALERT! Invent ASN.1 setters/whatever for members to replace this mess. */
+
+ if (pSignerInfo->AuthenticatedAttributes.cItems == 0)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "No authenticated or unauthenticated attributes! Sorry, no can do.");
+
+ Assert(pSignerInfo->UnauthenticatedAttributes.SetCore.Asn1Core.uTag == 0);
+ rc = RTAsn1SetCore_Init(&pSignerInfo->UnauthenticatedAttributes.SetCore,
+ pSignerInfo->AuthenticatedAttributes.SetCore.Asn1Core.pOps);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTAsn1SetCore_Init failed: %Rrc", rc);
+ pSignerInfo->UnauthenticatedAttributes.SetCore.Asn1Core.uTag = 1;
+ pSignerInfo->UnauthenticatedAttributes.SetCore.Asn1Core.fClass = ASN1_TAGCLASS_CONTEXT | ASN1_TAGFLAG_CONSTRUCTED;
+ RTAsn1MemInitArrayAllocation(&pSignerInfo->UnauthenticatedAttributes.Allocation,
+ pSignerInfo->AuthenticatedAttributes.Allocation.pAllocator,
+ sizeof(**pSignerInfo->UnauthenticatedAttributes.papItems));
+ }
+
+ /*
+ * Find or add an unauthenticated attribute for nested signatures.
+ */
+ rc = VERR_NOT_FOUND;
+ PRTCRPKCS7ATTRIBUTE pAttr = NULL;
+ int32_t iPos = pSignerInfo->UnauthenticatedAttributes.cItems;
+ while (iPos-- > 0)
+ if (pSignerInfo->UnauthenticatedAttributes.papItems[iPos]->enmType == RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE)
+ {
+ pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[iPos];
+ rc = VINF_SUCCESS;
+ break;
+ }
+ if (iPos < 0)
+ {
+ iPos = RTCrPkcs7Attributes_Append(&pSignerInfo->UnauthenticatedAttributes);
+ if (iPos >= 0)
+ {
+ if (cVerbosity >= 3)
+ RTMsgInfo("Adding UnauthenticatedAttribute #%u...", iPos);
+ Assert((uint32_t)iPos < pSignerInfo->UnauthenticatedAttributes.cItems);
+
+ pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[iPos];
+ rc = RTAsn1ObjId_InitFromString(&pAttr->Type, RTCR_PKCS9_ID_MS_NESTED_SIGNATURE, pAttr->Allocation.pAllocator);
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo Generalize the Type + enmType DYN stuff and generate setters. */
+ Assert(pAttr->enmType == RTCRPKCS7ATTRIBUTETYPE_NOT_PRESENT);
+ Assert(pAttr->uValues.pContentInfos == NULL);
+ pAttr->enmType = RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE;
+ rc = RTAsn1MemAllocZ(&pAttr->Allocation, (void **)&pAttr->uValues.pContentInfos,
+ sizeof(*pAttr->uValues.pContentInfos));
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrPkcs7SetOfContentInfos_Init(pAttr->uValues.pContentInfos, pAttr->Allocation.pAllocator);
+ if (!RT_SUCCESS(rc))
+ RTMsgError("RTCrPkcs7ContentInfos_Init failed: %Rrc", rc);
+ }
+ else
+ RTMsgError("RTAsn1MemAllocZ failed: %Rrc", rc);
+ }
+ else
+ RTMsgError("RTAsn1ObjId_InitFromString failed: %Rrc", rc);
+ }
+ else
+ RTMsgError("RTCrPkcs7Attributes_Append failed: %Rrc", iPos);
+ }
+ else if (cVerbosity >= 2)
+ RTMsgInfo("Found UnauthenticatedAttribute #%u...", iPos);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Append/prepend the signature.
+ */
+ uint32_t iActualPos = UINT32_MAX;
+ iPos = fPrepend ? 0 : pAttr->uValues.pContentInfos->cItems;
+ rc = RTCrPkcs7SetOfContentInfos_InsertEx(pAttr->uValues.pContentInfos, iPos, &pSrc->ContentInfo,
+ pAttr->Allocation.pAllocator, &iActualPos);
+ if (RT_SUCCESS(rc))
+ {
+ if (cVerbosity > 0)
+ RTMsgInfo("Added nested signature (#%u)", iActualPos);
+ if (cVerbosity >= 3)
+ {
+ RTMsgInfo("SingerInfo dump after change:");
+ RTAsn1Dump(RTCrPkcs7SignerInfo_GetAsn1Core(pSignerInfo), 0, 2, RTStrmDumpPrintfV, g_pStdOut);
+ }
+ return RTEXITCODE_SUCCESS;
+ }
+
+ RTMsgError("RTCrPkcs7ContentInfos_InsertEx failed: %Rrc", rc);
+ }
+ return RTEXITCODE_FAILURE;
+}
+
+
+/**
+ * Writes the signature to the file.
+ *
+ * Caller must have called SignToolPkcs7_Encode() prior to this function.
+ *
+ * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error
+ * message on failure.
+ * @param pThis The file which to write.
+ * @param cVerbosity The verbosity.
+ */
+static RTEXITCODE SignToolPkcs7_WriteSignatureToFile(PSIGNTOOLPKCS7 pThis, const char *pszFilename, unsigned cVerbosity)
+{
+ AssertReturn(pThis->cbNewBuf && pThis->pbNewBuf, RTEXITCODE_FAILURE);
+
+ /*
+ * Open+truncate file, write new signature, close. Simple.
+ */
+ RTFILE hFile;
+ int rc = RTFileOpen(&hFile, pszFilename, RTFILE_O_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_TRUNCATE | RTFILE_O_DENY_WRITE);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileWrite(hFile, pThis->pbNewBuf, pThis->cbNewBuf, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileClose(hFile);
+ if (RT_SUCCESS(rc))
+ {
+ if (cVerbosity > 0)
+ RTMsgInfo("Wrote %u bytes to %s", pThis->cbNewBuf, pszFilename);
+ return RTEXITCODE_SUCCESS;
+ }
+
+ RTMsgError("RTFileClose failed on %s: %Rrc", pszFilename, rc);
+ }
+ else
+ RTMsgError("Write error on %s: %Rrc", pszFilename, rc);
+ }
+ else
+ RTMsgError("Failed to open %s for writing: %Rrc", pszFilename, rc);
+ return RTEXITCODE_FAILURE;
+}
+
+
+
+/**
+ * Worker for recursively searching for MS nested signatures and signer infos.
+ *
+ * @returns Pointer to the signer info corresponding to @a iSignature. NULL if
+ * not found.
+ * @param pSignedData The signature to search.
+ * @param piNextSignature Pointer to the variable keeping track of the next
+ * signature number.
+ * @param iReqSignature The request signature number.
+ * @param ppSignedData Where to return the signature data structure.
+ */
+static PRTCRPKCS7SIGNERINFO SignToolPkcs7_FindNestedSignatureByIndexWorker(PRTCRPKCS7SIGNEDDATA pSignedData,
+ uint32_t *piNextSignature,
+ uint32_t iReqSignature,
+ PRTCRPKCS7SIGNEDDATA *ppSignedData)
+{
+ for (uint32_t iSignerInfo = 0; iSignerInfo < pSignedData->SignerInfos.cItems; iSignerInfo++)
+ {
+ /* Match?*/
+ PRTCRPKCS7SIGNERINFO pSignerInfo = pSignedData->SignerInfos.papItems[iSignerInfo];
+ if (*piNextSignature == iReqSignature)
+ {
+ *ppSignedData = pSignedData;
+ return pSignerInfo;
+ }
+ *piNextSignature += 1;
+
+ /* Look for nested signatures. */
+ for (uint32_t iAttrib = 0; iAttrib < pSignerInfo->UnauthenticatedAttributes.cItems; iAttrib++)
+ if (pSignerInfo->UnauthenticatedAttributes.papItems[iAttrib]->enmType == RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE)
+ {
+ PRTCRPKCS7SETOFCONTENTINFOS pCntInfos;
+ pCntInfos = pSignerInfo->UnauthenticatedAttributes.papItems[iAttrib]->uValues.pContentInfos;
+ for (uint32_t iCntInfo = 0; iCntInfo < pCntInfos->cItems; iCntInfo++)
+ {
+ PRTCRPKCS7CONTENTINFO pCntInfo = pCntInfos->papItems[iCntInfo];
+ if (RTCrPkcs7ContentInfo_IsSignedData(pCntInfo))
+ {
+ PRTCRPKCS7SIGNERINFO pRet;
+ pRet = SignToolPkcs7_FindNestedSignatureByIndexWorker(pCntInfo->u.pSignedData, piNextSignature,
+ iReqSignature, ppSignedData);
+ if (pRet)
+ return pRet;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+
+/**
+ * Locates the given nested signature.
+ *
+ * @returns Pointer to the signer info corresponding to @a iSignature. NULL if
+ * not found.
+ * @param pThis The PKCS\#7 structure to search.
+ * @param iReqSignature The requested signature number.
+ * @param ppSignedData Where to return the pointer to the signed data that
+ * the returned signer info belongs to.
+ *
+ * @todo Move into SPC or PKCS\#7.
+ */
+static PRTCRPKCS7SIGNERINFO SignToolPkcs7_FindNestedSignatureByIndex(PSIGNTOOLPKCS7 pThis, uint32_t iReqSignature,
+ PRTCRPKCS7SIGNEDDATA *ppSignedData)
+{
+ uint32_t iNextSignature = 0;
+ return SignToolPkcs7_FindNestedSignatureByIndexWorker(pThis->pSignedData, &iNextSignature, iReqSignature, ppSignedData);
+}
+
+
+
+/**
+ * Reads and decodes PKCS\#7 signature from the given executable.
+ *
+ * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error message
+ * on failure.
+ * @param pThis The structure to initialize.
+ * @param pszFilename The executable filename.
+ * @param cVerbosity The verbosity.
+ * @param enmLdrArch For FAT binaries.
+ */
+static RTEXITCODE SignToolPkcs7Exe_InitFromFile(PSIGNTOOLPKCS7EXE pThis, const char *pszFilename,
+ unsigned cVerbosity, RTLDRARCH enmLdrArch = RTLDRARCH_WHATEVER)
+{
+ /*
+ * Init the return structure.
+ */
+ RT_ZERO(*pThis);
+ pThis->hLdrMod = NIL_RTLDRMOD;
+ pThis->pszFilename = pszFilename;
+
+ /*
+ * Open the image and check if it's signed.
+ */
+ int rc = RTLdrOpen(pszFilename, RTLDR_O_FOR_VALIDATION, enmLdrArch, &pThis->hLdrMod);
+ if (RT_SUCCESS(rc))
+ {
+ bool fIsSigned = false;
+ rc = RTLdrQueryProp(pThis->hLdrMod, RTLDRPROP_IS_SIGNED, &fIsSigned, sizeof(fIsSigned));
+ if (RT_SUCCESS(rc) && fIsSigned)
+ {
+ /*
+ * Query the PKCS#7 data (assuming M$ style signing) and hand it to a worker.
+ */
+ size_t cbActual = 0;
+#ifdef DEBUG
+ size_t cbBuf = 64;
+#else
+ size_t cbBuf = _512K;
+#endif
+ void *pvBuf = RTMemAllocZ(cbBuf);
+ if (pvBuf)
+ {
+ rc = RTLdrQueryPropEx(pThis->hLdrMod, RTLDRPROP_PKCS7_SIGNED_DATA, NULL /*pvBits*/, pvBuf, cbBuf, &cbActual);
+ if (rc == VERR_BUFFER_OVERFLOW)
+ {
+ RTMemFree(pvBuf);
+ cbBuf = cbActual;
+ pvBuf = RTMemAllocZ(cbActual);
+ if (pvBuf)
+ rc = RTLdrQueryPropEx(pThis->hLdrMod, RTLDRPROP_PKCS7_SIGNED_DATA, NULL /*pvBits*/,
+ pvBuf, cbBuf, &cbActual);
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ pThis->pbBuf = (uint8_t *)pvBuf;
+ pThis->cbBuf = cbActual;
+ if (RT_SUCCESS(rc))
+ {
+ if (cVerbosity > 2)
+ RTPrintf("PKCS#7 signature: %u bytes\n", cbActual);
+ if (cVerbosity > 3)
+ RTPrintf("%.*Rhxd\n", cbActual, pvBuf);
+
+ /*
+ * Decode it.
+ */
+ rc = SignToolPkcs7_Decode(pThis, false /*fCatalog*/);
+ if (RT_SUCCESS(rc))
+ return RTEXITCODE_SUCCESS;
+ }
+ else
+ RTMsgError("RTLdrQueryPropEx/RTLDRPROP_PKCS7_SIGNED_DATA failed on '%s': %Rrc\n", pszFilename, rc);
+ }
+ else if (RT_SUCCESS(rc))
+ RTMsgInfo("'%s': not signed\n", pszFilename);
+ else
+ RTMsgError("RTLdrQueryProp/RTLDRPROP_IS_SIGNED failed on '%s': %Rrc\n", pszFilename, rc);
+ }
+ else
+ RTMsgError("Error opening executable image '%s': %Rrc", pszFilename, rc);
+
+ SignToolPkcs7Exe_Delete(pThis);
+ return RTEXITCODE_FAILURE;
+}
+
+
+/**
+ * Calculates the checksum of an executable.
+ *
+ * @returns Success indicator (errors are reported)
+ * @param pThis The exe file to checksum.
+ * @param hFile The file handle.
+ * @param puCheckSum Where to return the checksum.
+ */
+static bool SignToolPkcs7Exe_CalcPeCheckSum(PSIGNTOOLPKCS7EXE pThis, RTFILE hFile, uint32_t *puCheckSum)
+{
+#ifdef RT_OS_WINDOWS
+ /*
+ * Try use IMAGEHLP!MapFileAndCheckSumW first.
+ */
+ PRTUTF16 pwszPath;
+ int rc = RTStrToUtf16(pThis->pszFilename, &pwszPath);
+ if (RT_SUCCESS(rc))
+ {
+ decltype(MapFileAndCheckSumW) *pfnMapFileAndCheckSumW;
+ pfnMapFileAndCheckSumW = (decltype(MapFileAndCheckSumW) *)RTLdrGetSystemSymbol("IMAGEHLP.DLL", "MapFileAndCheckSumW");
+ if (pfnMapFileAndCheckSumW)
+ {
+ DWORD uHeaderSum = UINT32_MAX;
+ DWORD uCheckSum = UINT32_MAX;
+ DWORD dwRc = pfnMapFileAndCheckSumW(pwszPath, &uHeaderSum, &uCheckSum);
+ if (dwRc == CHECKSUM_SUCCESS)
+ {
+ *puCheckSum = uCheckSum;
+ return true;
+ }
+ }
+ }
+#endif
+
+ RT_NOREF(pThis, hFile, puCheckSum);
+ RTMsgError("Implement check sum calcuation fallback!");
+ return false;
+}
+
+
+/**
+ * Writes the signature to the file.
+ *
+ * This has the side-effect of closing the hLdrMod member. So, it can only be
+ * called once!
+ *
+ * Caller must have called SignToolPkcs7_Encode() prior to this function.
+ *
+ * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error
+ * message on failure.
+ * @param pThis The file which to write.
+ * @param cVerbosity The verbosity.
+ */
+static RTEXITCODE SignToolPkcs7Exe_WriteSignatureToFile(PSIGNTOOLPKCS7EXE pThis, unsigned cVerbosity)
+{
+ AssertReturn(pThis->cbNewBuf && pThis->pbNewBuf, RTEXITCODE_FAILURE);
+
+ /*
+ * Get the file header offset and arch before closing the destination handle.
+ */
+ uint32_t offNtHdrs;
+ int rc = RTLdrQueryProp(pThis->hLdrMod, RTLDRPROP_FILE_OFF_HEADER, &offNtHdrs, sizeof(offNtHdrs));
+ if (RT_SUCCESS(rc))
+ {
+ RTLDRARCH enmLdrArch = RTLdrGetArch(pThis->hLdrMod);
+ if (enmLdrArch != RTLDRARCH_INVALID)
+ {
+ RTLdrClose(pThis->hLdrMod);
+ pThis->hLdrMod = NIL_RTLDRMOD;
+ unsigned cbNtHdrs = 0;
+ switch (enmLdrArch)
+ {
+ case RTLDRARCH_AMD64:
+ cbNtHdrs = sizeof(IMAGE_NT_HEADERS64);
+ break;
+ case RTLDRARCH_X86_32:
+ cbNtHdrs = sizeof(IMAGE_NT_HEADERS32);
+ break;
+ default:
+ RTMsgError("Unknown image arch: %d", enmLdrArch);
+ }
+ if (cbNtHdrs > 0)
+ {
+ if (cVerbosity > 0)
+ RTMsgInfo("offNtHdrs=%#x cbNtHdrs=%u\n", offNtHdrs, cbNtHdrs);
+
+ /*
+ * Open the executable file for writing.
+ */
+ RTFILE hFile;
+ rc = RTFileOpen(&hFile, pThis->pszFilename, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
+ if (RT_SUCCESS(rc))
+ {
+ /* Read the file header and locate the security directory entry. */
+ union
+ {
+ IMAGE_NT_HEADERS32 NtHdrs32;
+ IMAGE_NT_HEADERS64 NtHdrs64;
+ } uBuf;
+ PIMAGE_DATA_DIRECTORY pSecDir = cbNtHdrs == sizeof(IMAGE_NT_HEADERS64)
+ ? &uBuf.NtHdrs64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]
+ : &uBuf.NtHdrs32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
+
+ rc = RTFileReadAt(hFile, offNtHdrs, &uBuf, cbNtHdrs, NULL);
+ if ( RT_SUCCESS(rc)
+ && uBuf.NtHdrs32.Signature == IMAGE_NT_SIGNATURE)
+ {
+ /*
+ * Drop any old signature by truncating the file.
+ */
+ if ( pSecDir->Size > 8
+ && pSecDir->VirtualAddress > offNtHdrs + sizeof(IMAGE_NT_HEADERS32))
+ {
+ rc = RTFileSetSize(hFile, pSecDir->VirtualAddress);
+ if (RT_FAILURE(rc))
+ RTMsgError("Error truncating file to %#x bytes: %Rrc", pSecDir->VirtualAddress, rc);
+ }
+ else
+ rc = RTMsgErrorRc(VERR_BAD_EXE_FORMAT, "Bad security directory entry: VA=%#x Size=%#x",
+ pSecDir->VirtualAddress, pSecDir->Size);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Sector align the signature portion.
+ */
+ uint32_t const cbWinCert = RT_UOFFSETOF(WIN_CERTIFICATE, bCertificate);
+ uint64_t offCur = 0;
+ rc = RTFileGetSize(hFile, &offCur);
+ if ( RT_SUCCESS(rc)
+ && offCur < _2G)
+ {
+ if (offCur & 0x1ff)
+ {
+ uint32_t cbNeeded = 0x200 - ((uint32_t)offCur & 0x1ff);
+ rc = RTFileWriteAt(hFile, offCur, g_abRTZero4K, cbNeeded, NULL);
+ if (RT_SUCCESS(rc))
+ offCur += cbNeeded;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Write the header followed by the signature data.
+ */
+ uint32_t const cbZeroPad = (uint32_t)(RT_ALIGN_Z(pThis->cbNewBuf, 8) - pThis->cbNewBuf);
+ pSecDir->VirtualAddress = (uint32_t)offCur;
+ pSecDir->Size = cbWinCert + (uint32_t)pThis->cbNewBuf + cbZeroPad;
+ if (cVerbosity >= 2)
+ RTMsgInfo("Writing %u (%#x) bytes of signature at %#x (%u).\n",
+ pSecDir->Size, pSecDir->Size, pSecDir->VirtualAddress, pSecDir->VirtualAddress);
+
+ WIN_CERTIFICATE WinCert;
+ WinCert.dwLength = pSecDir->Size;
+ WinCert.wRevision = WIN_CERT_REVISION_2_0;
+ WinCert.wCertificateType = WIN_CERT_TYPE_PKCS_SIGNED_DATA;
+
+ rc = RTFileWriteAt(hFile, offCur, &WinCert, cbWinCert, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ offCur += cbWinCert;
+ rc = RTFileWriteAt(hFile, offCur, pThis->pbNewBuf, pThis->cbNewBuf, NULL);
+ }
+ if (RT_SUCCESS(rc) && cbZeroPad)
+ {
+ offCur += pThis->cbNewBuf;
+ rc = RTFileWriteAt(hFile, offCur, g_abRTZero4K, cbZeroPad, NULL);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Reset the checksum (sec dir updated already) and rewrite the header.
+ */
+ uBuf.NtHdrs32.OptionalHeader.CheckSum = 0;
+ offCur = offNtHdrs;
+ rc = RTFileWriteAt(hFile, offNtHdrs, &uBuf, cbNtHdrs, NULL);
+ if (RT_SUCCESS(rc))
+ rc = RTFileFlush(hFile);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Calc checksum and write out the header again.
+ */
+ uint32_t uCheckSum = UINT32_MAX;
+ if (SignToolPkcs7Exe_CalcPeCheckSum(pThis, hFile, &uCheckSum))
+ {
+ uBuf.NtHdrs32.OptionalHeader.CheckSum = uCheckSum;
+ rc = RTFileWriteAt(hFile, offNtHdrs, &uBuf, cbNtHdrs, NULL);
+ if (RT_SUCCESS(rc))
+ rc = RTFileFlush(hFile);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileClose(hFile);
+ if (RT_SUCCESS(rc))
+ return RTEXITCODE_SUCCESS;
+ RTMsgError("RTFileClose failed: %Rrc\n", rc);
+ return RTEXITCODE_FAILURE;
+ }
+ }
+ }
+ }
+ }
+ if (RT_FAILURE(rc))
+ RTMsgError("Write error at %#RX64: %Rrc", offCur, rc);
+ }
+ else if (RT_SUCCESS(rc))
+ RTMsgError("File to big: %'RU64 bytes", offCur);
+ else
+ RTMsgError("RTFileGetSize failed: %Rrc", rc);
+ }
+ }
+ else if (RT_SUCCESS(rc))
+ RTMsgError("Not NT executable header!");
+ else
+ RTMsgError("Error reading NT headers (%#x bytes) at %#x: %Rrc", cbNtHdrs, offNtHdrs, rc);
+ RTFileClose(hFile);
+ }
+ else
+ RTMsgError("Failed to open '%s' for writing: %Rrc", pThis->pszFilename, rc);
+ }
+ }
+ else
+ RTMsgError("RTLdrGetArch failed!");
+ }
+ else
+ RTMsgError("RTLdrQueryProp/RTLDRPROP_FILE_OFF_HEADER failed: %Rrc", rc);
+ return RTEXITCODE_FAILURE;
+}
+
+
+
+/*
+ * The 'extract-exe-signer-cert' command.
+ */
+static RTEXITCODE HelpExtractExeSignerCert(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
+{
+ RT_NOREF_PV(enmLevel);
+ RTStrmPrintf(pStrm, "extract-exe-signer-cert [--ber|--cer|--der] [--signature-index|-i <num>] [--exe|-e] <exe> [--output|-o] <outfile.cer>\n");
+ return RTEXITCODE_SUCCESS;
+}
+
+static RTEXITCODE HandleExtractExeSignerCert(int cArgs, char **papszArgs)
+{
+ /*
+ * Parse arguments.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--ber", 'b', RTGETOPT_REQ_NOTHING },
+ { "--cer", 'c', RTGETOPT_REQ_NOTHING },
+ { "--der", 'd', RTGETOPT_REQ_NOTHING },
+ { "--exe", 'e', RTGETOPT_REQ_STRING },
+ { "--output", 'o', RTGETOPT_REQ_STRING },
+ { "--signature-index", 'i', RTGETOPT_REQ_UINT32 },
+ };
+
+ const char *pszExe = NULL;
+ const char *pszOut = NULL;
+ RTLDRARCH enmLdrArch = RTLDRARCH_WHATEVER;
+ unsigned cVerbosity = 0;
+ uint32_t fCursorFlags = RTASN1CURSOR_FLAGS_DER;
+ uint32_t iSignature = 0;
+
+ RTGETOPTSTATE GetState;
+ int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ AssertRCReturn(rc, RTEXITCODE_FAILURE);
+ RTGETOPTUNION ValueUnion;
+ int ch;
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case 'e': pszExe = ValueUnion.psz; break;
+ case 'o': pszOut = ValueUnion.psz; break;
+ case 'b': fCursorFlags = 0; break;
+ case 'c': fCursorFlags = RTASN1CURSOR_FLAGS_CER; break;
+ case 'd': fCursorFlags = RTASN1CURSOR_FLAGS_DER; break;
+ case 'i': iSignature = ValueUnion.u32; break;
+ case 'V': return HandleVersion(cArgs, papszArgs);
+ case 'h': return HelpExtractExeSignerCert(g_pStdOut, RTSIGNTOOLHELP_FULL);
+
+ case VINF_GETOPT_NOT_OPTION:
+ if (!pszExe)
+ pszExe = ValueUnion.psz;
+ else if (!pszOut)
+ pszOut = ValueUnion.psz;
+ else
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many file arguments: %s", ValueUnion.psz);
+ break;
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+ if (!pszExe)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
+ if (!pszOut)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "No output file given.");
+ if (RTPathExists(pszOut))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "The output file '%s' exists.", pszOut);
+
+ /*
+ * Do it.
+ */
+ /* Read & decode the PKCS#7 signature. */
+ SIGNTOOLPKCS7EXE This;
+ RTEXITCODE rcExit = SignToolPkcs7Exe_InitFromFile(&This, pszExe, cVerbosity, enmLdrArch);
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ /* Find the signing certificate (ASSUMING that the certificate used is shipped in the set of certificates). */
+ PRTCRPKCS7SIGNEDDATA pSignedData;
+ PCRTCRPKCS7SIGNERINFO pSignerInfo = SignToolPkcs7_FindNestedSignatureByIndex(&This, iSignature, &pSignedData);
+ rcExit = RTEXITCODE_FAILURE;
+ if (pSignerInfo)
+ {
+ PCRTCRPKCS7ISSUERANDSERIALNUMBER pISN = &pSignedData->SignerInfos.papItems[0]->IssuerAndSerialNumber;
+ PCRTCRX509CERTIFICATE pCert;
+ pCert = RTCrPkcs7SetOfCerts_FindX509ByIssuerAndSerialNumber(&pSignedData->Certificates,
+ &pISN->Name, &pISN->SerialNumber);
+ if (pCert)
+ {
+ /*
+ * Write it out.
+ */
+ RTFILE hFile;
+ rc = RTFileOpen(&hFile, pszOut, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE);
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t cbCert = pCert->SeqCore.Asn1Core.cbHdr + pCert->SeqCore.Asn1Core.cb;
+ rc = RTFileWrite(hFile, pCert->SeqCore.Asn1Core.uData.pu8 - pCert->SeqCore.Asn1Core.cbHdr,
+ cbCert, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileClose(hFile);
+ if (RT_SUCCESS(rc))
+ {
+ hFile = NIL_RTFILE;
+ rcExit = RTEXITCODE_SUCCESS;
+ RTMsgInfo("Successfully wrote %u bytes to '%s'", cbCert, pszOut);
+ }
+ else
+ RTMsgError("RTFileClose failed: %Rrc", rc);
+ }
+ else
+ RTMsgError("RTFileWrite failed: %Rrc", rc);
+ RTFileClose(hFile);
+ }
+ else
+ RTMsgError("Error opening '%s' for writing: %Rrc", pszOut, rc);
+ }
+ else
+ RTMsgError("Certificate not found.");
+ }
+ else
+ RTMsgError("Could not locate signature #%u!", iSignature);
+
+ /* Delete the signature data. */
+ SignToolPkcs7Exe_Delete(&This);
+ }
+ return rcExit;
+}
+
+
+/*
+ * The 'add-nested-exe-signature' command.
+ */
+static RTEXITCODE HelpAddNestedExeSignature(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
+{
+ RT_NOREF_PV(enmLevel);
+ RTStrmPrintf(pStrm, "add-nested-exe-signature [-v|--verbose] [-d|--debug] [-p|--prepend] <destination-exe> <source-exe>\n");
+ if (enmLevel == RTSIGNTOOLHELP_FULL)
+ RTStrmPrintf(pStrm,
+ "\n"
+ "The --debug option allows the source-exe to be omitted in order to test the\n"
+ "encoding and PE file modification.\n"
+ "\n"
+ "The --prepend option puts the nested signature first rather than appending it\n"
+ "to the end of of the nested signature set. Windows reads nested signatures in\n"
+ "reverse order, so --prepend will logically putting it last.\n"
+ );
+ return RTEXITCODE_SUCCESS;
+}
+
+
+static RTEXITCODE HandleAddNestedExeSignature(int cArgs, char **papszArgs)
+{
+ /*
+ * Parse arguments.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--prepend", 'p', RTGETOPT_REQ_NOTHING },
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
+ { "--debug", 'd', RTGETOPT_REQ_NOTHING },
+ };
+
+ const char *pszDst = NULL;
+ const char *pszSrc = NULL;
+ unsigned cVerbosity = 0;
+ bool fDebug = false;
+ bool fPrepend = false;
+
+ RTGETOPTSTATE GetState;
+ int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ AssertRCReturn(rc, RTEXITCODE_FAILURE);
+ RTGETOPTUNION ValueUnion;
+ int ch;
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case 'v': cVerbosity++; break;
+ case 'd': fDebug = pszSrc == NULL; break;
+ case 'p': fPrepend = true; break;
+ case 'V': return HandleVersion(cArgs, papszArgs);
+ case 'h': return HelpAddNestedExeSignature(g_pStdOut, RTSIGNTOOLHELP_FULL);
+
+ case VINF_GETOPT_NOT_OPTION:
+ if (!pszDst)
+ pszDst = ValueUnion.psz;
+ else if (!pszSrc)
+ {
+ pszSrc = ValueUnion.psz;
+ fDebug = false;
+ }
+ else
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many file arguments: %s", ValueUnion.psz);
+ break;
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+ if (!pszDst)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "No destination executable given.");
+ if (!pszSrc && !fDebug)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "No source executable file given.");
+
+ /*
+ * Do it.
+ */
+ /* Read & decode the source PKCS#7 signature. */
+ SIGNTOOLPKCS7EXE Src;
+ RTEXITCODE rcExit = pszSrc ? SignToolPkcs7Exe_InitFromFile(&Src, pszSrc, cVerbosity) : RTEXITCODE_SUCCESS;
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ /* Ditto for the destination PKCS#7 signature. */
+ SIGNTOOLPKCS7EXE Dst;
+ rcExit = SignToolPkcs7Exe_InitFromFile(&Dst, pszDst, cVerbosity);
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ /* Do the signature manipulation. */
+ if (pszSrc)
+ rcExit = SignToolPkcs7_AddNestedSignature(&Dst, &Src, cVerbosity, fPrepend);
+ if (rcExit == RTEXITCODE_SUCCESS)
+ rcExit = SignToolPkcs7_Encode(&Dst, cVerbosity);
+
+ /* Update the destination executable file. */
+ if (rcExit == RTEXITCODE_SUCCESS)
+ rcExit = SignToolPkcs7Exe_WriteSignatureToFile(&Dst, cVerbosity);
+
+ SignToolPkcs7Exe_Delete(&Dst);
+ }
+ if (pszSrc)
+ SignToolPkcs7Exe_Delete(&Src);
+ }
+
+ return rcExit;
+}
+
+
+/*
+ * The 'add-nested-cat-signature' command.
+ */
+static RTEXITCODE HelpAddNestedCatSignature(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
+{
+ RT_NOREF_PV(enmLevel);
+ RTStrmPrintf(pStrm, "add-nested-cat-signature [-v|--verbose] [-d|--debug] [-p|--prepend] <destination-cat> <source-cat>\n");
+ if (enmLevel == RTSIGNTOOLHELP_FULL)
+ RTStrmPrintf(pStrm,
+ "\n"
+ "The --debug option allows the source-cat to be omitted in order to test the\n"
+ "ASN.1 re-encoding of the destination catalog file.\n"
+ "\n"
+ "The --prepend option puts the nested signature first rather than appending it\n"
+ "to the end of of the nested signature set. Windows reads nested signatures in\n"
+ "reverse order, so --prepend will logically putting it last.\n"
+ );
+ return RTEXITCODE_SUCCESS;
+}
+
+
+static RTEXITCODE HandleAddNestedCatSignature(int cArgs, char **papszArgs)
+{
+ /*
+ * Parse arguments.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--prepend", 'p', RTGETOPT_REQ_NOTHING },
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
+ { "--debug", 'd', RTGETOPT_REQ_NOTHING },
+ };
+
+ const char *pszDst = NULL;
+ const char *pszSrc = NULL;
+ unsigned cVerbosity = 0;
+ bool fDebug = false;
+ bool fPrepend = false;
+
+ RTGETOPTSTATE GetState;
+ int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ AssertRCReturn(rc, RTEXITCODE_FAILURE);
+ RTGETOPTUNION ValueUnion;
+ int ch;
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)))
+ {
+ switch (ch)
+ {
+ case 'v': cVerbosity++; break;
+ case 'd': fDebug = pszSrc == NULL; break;
+ case 'p': fPrepend = true; break;
+ case 'V': return HandleVersion(cArgs, papszArgs);
+ case 'h': return HelpAddNestedCatSignature(g_pStdOut, RTSIGNTOOLHELP_FULL);
+
+ case VINF_GETOPT_NOT_OPTION:
+ if (!pszDst)
+ pszDst = ValueUnion.psz;
+ else if (!pszSrc)
+ {
+ pszSrc = ValueUnion.psz;
+ fDebug = false;
+ }
+ else
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many file arguments: %s", ValueUnion.psz);
+ break;
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+ if (!pszDst)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "No destination catalog file given.");
+ if (!pszSrc && !fDebug)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "No source catalog file given.");
+
+ /*
+ * Do it.
+ */
+ /* Read & decode the source PKCS#7 signature. */
+ SIGNTOOLPKCS7 Src;
+ RTEXITCODE rcExit = pszSrc ? SignToolPkcs7_InitFromFile(&Src, pszSrc, cVerbosity) : RTEXITCODE_SUCCESS;
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ /* Ditto for the destination PKCS#7 signature. */
+ SIGNTOOLPKCS7EXE Dst;
+ rcExit = SignToolPkcs7_InitFromFile(&Dst, pszDst, cVerbosity);
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ /* Do the signature manipulation. */
+ if (pszSrc)
+ rcExit = SignToolPkcs7_AddNestedSignature(&Dst, &Src, cVerbosity, fPrepend);
+ if (rcExit == RTEXITCODE_SUCCESS)
+ rcExit = SignToolPkcs7_Encode(&Dst, cVerbosity);
+
+ /* Update the destination executable file. */
+ if (rcExit == RTEXITCODE_SUCCESS)
+ rcExit = SignToolPkcs7_WriteSignatureToFile(&Dst, pszDst, cVerbosity);
+
+ SignToolPkcs7_Delete(&Dst);
+ }
+ if (pszSrc)
+ SignToolPkcs7_Delete(&Src);
+ }
+
+ return rcExit;
+}
+
+#ifndef IPRT_IN_BUILD_TOOL
+
+/*
+ * The 'verify-exe' command.
+ */
+static RTEXITCODE HelpVerifyExe(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
+{
+ RT_NOREF_PV(enmLevel);
+ RTStrmPrintf(pStrm,
+ "verify-exe [--verbose|--quiet] [--kernel] [--root <root-cert.der>] [--additional <supp-cert.der>]\n"
+ " [--type <win|osx>] <exe1> [exe2 [..]]\n");
+ return RTEXITCODE_SUCCESS;
+}
+
+typedef struct VERIFYEXESTATE
+{
+ RTCRSTORE hRootStore;
+ RTCRSTORE hKernelRootStore;
+ RTCRSTORE hAdditionalStore;
+ bool fKernel;
+ int cVerbose;
+ enum { kSignType_Windows, kSignType_OSX } enmSignType;
+ uint64_t uTimestamp;
+ RTLDRARCH enmLdrArch;
+} VERIFYEXESTATE;
+
+# ifdef VBOX
+/** Certificate store load set.
+ * Declared outside HandleVerifyExe because of braindead gcc visibility crap. */
+struct STSTORESET
+{
+ RTCRSTORE hStore;
+ PCSUPTAENTRY paTAs;
+ unsigned cTAs;
+};
+# endif
+
+/**
+ * @callback_method_impl{FNRTCRPKCS7VERIFYCERTCALLBACK,
+ * Standard code signing. Use this for Microsoft SPC.}
+ */
+static DECLCALLBACK(int) VerifyExecCertVerifyCallback(PCRTCRX509CERTIFICATE pCert, RTCRX509CERTPATHS hCertPaths, uint32_t fFlags,
+ void *pvUser, PRTERRINFO pErrInfo)
+{
+ VERIFYEXESTATE *pState = (VERIFYEXESTATE *)pvUser;
+ uint32_t cPaths = hCertPaths != NIL_RTCRX509CERTPATHS ? RTCrX509CertPathsGetPathCount(hCertPaths) : 0;
+
+ /*
+ * Dump all the paths.
+ */
+ if (pState->cVerbose > 0)
+ {
+ for (uint32_t iPath = 0; iPath < cPaths; iPath++)
+ {
+ RTPrintf("---\n");
+ RTCrX509CertPathsDumpOne(hCertPaths, iPath, pState->cVerbose, RTStrmDumpPrintfV, g_pStdOut);
+ *pErrInfo->pszMsg = '\0';
+ }
+ RTPrintf("---\n");
+ }
+
+ /*
+ * Test signing certificates normally doesn't have all the necessary
+ * features required below. So, treat them as special cases.
+ */
+ if ( hCertPaths == NIL_RTCRX509CERTPATHS
+ && RTCrX509Name_Compare(&pCert->TbsCertificate.Issuer, &pCert->TbsCertificate.Subject) == 0)
+ {
+ RTMsgInfo("Test signed.\n");
+ return VINF_SUCCESS;
+ }
+
+ if (hCertPaths == NIL_RTCRX509CERTPATHS)
+ RTMsgInfo("Signed by trusted certificate.\n");
+
+ /*
+ * Standard code signing capabilites required.
+ */
+ int rc = RTCrPkcs7VerifyCertCallbackCodeSigning(pCert, hCertPaths, fFlags, NULL, pErrInfo);
+ if ( RT_SUCCESS(rc)
+ && (fFlags & RTCRPKCS7VCC_F_SIGNED_DATA))
+ {
+ /*
+ * If windows kernel signing, a valid certificate path must be anchored
+ * by the microsoft kernel signing root certificate. The only
+ * alternative is test signing.
+ */
+ if ( pState->fKernel
+ && hCertPaths != NIL_RTCRX509CERTPATHS
+ && pState->enmSignType == VERIFYEXESTATE::kSignType_Windows)
+ {
+ uint32_t cFound = 0;
+ uint32_t cValid = 0;
+ for (uint32_t iPath = 0; iPath < cPaths; iPath++)
+ {
+ bool fTrusted;
+ PCRTCRX509NAME pSubject;
+ PCRTCRX509SUBJECTPUBLICKEYINFO pPublicKeyInfo;
+ int rcVerify;
+ rc = RTCrX509CertPathsQueryPathInfo(hCertPaths, iPath, &fTrusted, NULL /*pcNodes*/, &pSubject, &pPublicKeyInfo,
+ NULL, NULL /*pCertCtx*/, &rcVerify);
+ AssertRCBreak(rc);
+
+ if (RT_SUCCESS(rcVerify))
+ {
+ Assert(fTrusted);
+ cValid++;
+
+ /* Search the kernel signing root store for a matching anchor. */
+ RTCRSTORECERTSEARCH Search;
+ rc = RTCrStoreCertFindBySubjectOrAltSubjectByRfc5280(pState->hKernelRootStore, pSubject, &Search);
+ AssertRCBreak(rc);
+ PCRTCRCERTCTX pCertCtx;
+ while ((pCertCtx = RTCrStoreCertSearchNext(pState->hKernelRootStore, &Search)) != NULL)
+ {
+ PCRTCRX509SUBJECTPUBLICKEYINFO pPubKeyInfo;
+ if (pCertCtx->pCert)
+ pPubKeyInfo = &pCertCtx->pCert->TbsCertificate.SubjectPublicKeyInfo;
+ else if (pCertCtx->pTaInfo)
+ pPubKeyInfo = &pCertCtx->pTaInfo->PubKey;
+ else
+ pPubKeyInfo = NULL;
+ if (RTCrX509SubjectPublicKeyInfo_Compare(pPubKeyInfo, pPublicKeyInfo) == 0)
+ cFound++;
+ RTCrCertCtxRelease(pCertCtx);
+ }
+
+ int rc2 = RTCrStoreCertSearchDestroy(pState->hKernelRootStore, &Search); AssertRC(rc2);
+ }
+ }
+ if (RT_SUCCESS(rc) && cFound == 0)
+ rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE, "Not valid kernel code signature.");
+ if (RT_SUCCESS(rc) && cValid != 2)
+ RTMsgWarning("%u valid paths, expected 2", cValid);
+ }
+ /*
+ * For Mac OS X signing, check for special developer ID attributes.
+ */
+ else if (pState->enmSignType == VERIFYEXESTATE::kSignType_OSX)
+ {
+ uint32_t cDevIdApp = 0;
+ uint32_t cDevIdKext = 0;
+ for (uint32_t i = 0; i < pCert->TbsCertificate.T3.Extensions.cItems; i++)
+ {
+ PCRTCRX509EXTENSION pExt = pCert->TbsCertificate.T3.Extensions.papItems[i];
+ if (RTAsn1ObjId_CompareWithString(&pExt->ExtnId, RTCR_APPLE_CS_DEVID_APPLICATION_OID) == 0)
+ {
+ cDevIdApp++;
+ if (!pExt->Critical.fValue)
+ rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
+ "Dev ID Application certificate extension is not flagged critical");
+ }
+ else if (RTAsn1ObjId_CompareWithString(&pExt->ExtnId, RTCR_APPLE_CS_DEVID_KEXT_OID) == 0)
+ {
+ cDevIdKext++;
+ if (!pExt->Critical.fValue)
+ rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
+ "Dev ID kext certificate extension is not flagged critical");
+ }
+ }
+ if (cDevIdApp == 0)
+ rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
+ "Certificate is missing the 'Dev ID Application' extension");
+ if (cDevIdKext == 0 && pState->fKernel)
+ rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
+ "Certificate is missing the 'Dev ID kext' extension");
+ }
+ }
+
+ return rc;
+}
+
+
+/** @callback_method_impl{FNRTLDRVALIDATESIGNEDDATA} */
+static DECLCALLBACK(int) VerifyExeCallback(RTLDRMOD hLdrMod, RTLDRSIGNATURETYPE enmSignature,
+ void const *pvSignature, size_t cbSignature,
+ void const *pvExternalData, size_t cbExternalData,
+ PRTERRINFO pErrInfo, void *pvUser)
+{
+ VERIFYEXESTATE *pState = (VERIFYEXESTATE *)pvUser;
+ RT_NOREF_PV(hLdrMod); RT_NOREF_PV(cbSignature);
+
+ switch (enmSignature)
+ {
+ case RTLDRSIGNATURETYPE_PKCS7_SIGNED_DATA:
+ {
+ PCRTCRPKCS7CONTENTINFO pContentInfo = (PCRTCRPKCS7CONTENTINFO)pvSignature;
+
+ RTTIMESPEC ValidationTime;
+ RTTimeSpecSetSeconds(&ValidationTime, pState->uTimestamp);
+
+ /*
+ * Dump the signed data if so requested.
+ */
+ if (pState->cVerbose)
+ RTAsn1Dump(&pContentInfo->SeqCore.Asn1Core, 0, 0, RTStrmDumpPrintfV, g_pStdOut);
+
+
+ /*
+ * Do the actual verification. Will have to modify this so it takes
+ * the authenticode policies into account.
+ */
+ if (pvExternalData)
+ return RTCrPkcs7VerifySignedDataWithExternalData(pContentInfo,
+ RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE_SIGNING_TIME_ONLY
+ | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_SIGNING_TIME_IF_PRESENT
+ | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_MS_TIMESTAMP_IF_PRESENT,
+ pState->hAdditionalStore, pState->hRootStore, &ValidationTime,
+ VerifyExecCertVerifyCallback, pState,
+ pvExternalData, cbExternalData, pErrInfo);
+ return RTCrPkcs7VerifySignedData(pContentInfo,
+ RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE_SIGNING_TIME_ONLY
+ | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_SIGNING_TIME_IF_PRESENT
+ | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_MS_TIMESTAMP_IF_PRESENT,
+ pState->hAdditionalStore, pState->hRootStore, &ValidationTime,
+ VerifyExecCertVerifyCallback, pState, pErrInfo);
+ }
+
+ default:
+ return RTErrInfoSetF(pErrInfo, VERR_NOT_SUPPORTED, "Unsupported signature type: %d", enmSignature);
+ }
+}
+
+/**
+ * Worker for HandleVerifyExe.
+ */
+static RTEXITCODE HandleVerifyExeWorker(VERIFYEXESTATE *pState, const char *pszFilename, PRTERRINFOSTATIC pStaticErrInfo)
+{
+ /*
+ * Open the executable image and verify it.
+ */
+ RTLDRMOD hLdrMod;
+ int rc = RTLdrOpen(pszFilename, RTLDR_O_FOR_VALIDATION, pState->enmLdrArch, &hLdrMod);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error opening executable image '%s': %Rrc", pszFilename, rc);
+
+
+ RTTIMESPEC Now;
+ bool fTriedNow = false;
+ rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &pState->uTimestamp, sizeof(pState->uTimestamp));
+ if (rc == VERR_NOT_FOUND)
+ {
+ fTriedNow = true;
+ pState->uTimestamp = RTTimeSpecGetSeconds(RTTimeNow(&Now));
+ rc = VINF_SUCCESS;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrVerifySignature(hLdrMod, VerifyExeCallback, pState, RTErrInfoInitStatic(pStaticErrInfo));
+ if (RT_SUCCESS(rc))
+ RTMsgInfo("'%s' is valid.\n", pszFilename);
+ else if (rc == VERR_CR_X509_CPV_NOT_VALID_AT_TIME && !fTriedNow)
+ {
+ pState->uTimestamp = RTTimeSpecGetSeconds(RTTimeNow(&Now));
+ rc = RTLdrVerifySignature(hLdrMod, VerifyExeCallback, pState, RTErrInfoInitStatic(pStaticErrInfo));
+ if (RT_SUCCESS(rc))
+ RTMsgInfo("'%s' is valid now, but not at link time.\n", pszFilename);
+ }
+ if (RT_FAILURE(rc))
+ RTMsgError("RTLdrVerifySignature failed on '%s': %Rrc - %s\n", pszFilename, rc, pStaticErrInfo->szMsg);
+ }
+ else
+ RTMsgError("RTLdrQueryProp/RTLDRPROP_TIMESTAMP_SECONDS failed on '%s': %Rrc\n", pszFilename, rc);
+
+ int rc2 = RTLdrClose(hLdrMod);
+ if (RT_FAILURE(rc2))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTLdrClose failed: %Rrc\n", rc2);
+ if (RT_FAILURE(rc))
+ return rc != VERR_LDRVI_NOT_SIGNED ? RTEXITCODE_FAILURE : RTEXITCODE_SKIPPED;
+
+ return RTEXITCODE_SUCCESS;
+}
+
+
+static RTEXITCODE HandleVerifyExe(int cArgs, char **papszArgs)
+{
+ RTERRINFOSTATIC StaticErrInfo;
+
+ /* Note! This code does not try to clean up the crypto stores on failure.
+ This is intentional as the code is only expected to be used in a
+ one-command-per-process environment where we do exit() upon
+ returning from this function. */
+
+ /*
+ * Parse arguments.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--kernel", 'k', RTGETOPT_REQ_NOTHING },
+ { "--root", 'r', RTGETOPT_REQ_STRING },
+ { "--additional", 'a', RTGETOPT_REQ_STRING },
+ { "--add", 'a', RTGETOPT_REQ_STRING },
+ { "--type", 't', RTGETOPT_REQ_STRING },
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
+ { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
+ };
+
+ VERIFYEXESTATE State =
+ {
+ NIL_RTCRSTORE, NIL_RTCRSTORE, NIL_RTCRSTORE, false, false,
+ VERIFYEXESTATE::kSignType_Windows, 0, RTLDRARCH_WHATEVER
+ };
+ int rc = RTCrStoreCreateInMem(&State.hRootStore, 0);
+ if (RT_SUCCESS(rc))
+ rc = RTCrStoreCreateInMem(&State.hKernelRootStore, 0);
+ if (RT_SUCCESS(rc))
+ rc = RTCrStoreCreateInMem(&State.hAdditionalStore, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error creating in-memory certificate store: %Rrc", rc);
+
+ RTGETOPTSTATE GetState;
+ rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ AssertRCReturn(rc, RTEXITCODE_FAILURE);
+ RTGETOPTUNION ValueUnion;
+ int ch;
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)) && ch != VINF_GETOPT_NOT_OPTION)
+ {
+ switch (ch)
+ {
+ case 'r': case 'a':
+ rc = RTCrStoreCertAddFromFile(ch == 'r' ? State.hRootStore : State.hAdditionalStore,
+ RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR,
+ ValueUnion.psz, RTErrInfoInitStatic(&StaticErrInfo));
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error loading certificate '%s': %Rrc - %s",
+ ValueUnion.psz, rc, StaticErrInfo.szMsg);
+ if (RTErrInfoIsSet(&StaticErrInfo.Core))
+ RTMsgWarning("Warnings loading certificate '%s': %s", ValueUnion.psz, StaticErrInfo.szMsg);
+ break;
+
+ case 't':
+ if (!strcmp(ValueUnion.psz, "win") || !strcmp(ValueUnion.psz, "windows"))
+ State.enmSignType = VERIFYEXESTATE::kSignType_Windows;
+ else if (!strcmp(ValueUnion.psz, "osx") || !strcmp(ValueUnion.psz, "apple"))
+ State.enmSignType = VERIFYEXESTATE::kSignType_OSX;
+ else
+ return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown signing type: '%s'", ValueUnion.psz);
+ break;
+
+ case 'k': State.fKernel = true; break;
+ case 'v': State.cVerbose++; break;
+ case 'q': State.cVerbose = 0; break;
+ case 'V': return HandleVersion(cArgs, papszArgs);
+ case 'h': return HelpVerifyExe(g_pStdOut, RTSIGNTOOLHELP_FULL);
+ default: return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+ if (ch != VINF_GETOPT_NOT_OPTION)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
+
+ /*
+ * Populate the certificate stores according to the signing type.
+ */
+# ifdef VBOX
+ unsigned cSets = 0;
+ struct STSTORESET aSets[6];
+ switch (State.enmSignType)
+ {
+ case VERIFYEXESTATE::kSignType_Windows:
+ aSets[cSets].hStore = State.hRootStore;
+ aSets[cSets].paTAs = g_aSUPTimestampTAs;
+ aSets[cSets].cTAs = g_cSUPTimestampTAs;
+ cSets++;
+ aSets[cSets].hStore = State.hRootStore;
+ aSets[cSets].paTAs = g_aSUPSpcRootTAs;
+ aSets[cSets].cTAs = g_cSUPSpcRootTAs;
+ cSets++;
+ aSets[cSets].hStore = State.hRootStore;
+ aSets[cSets].paTAs = g_aSUPNtKernelRootTAs;
+ aSets[cSets].cTAs = g_cSUPNtKernelRootTAs;
+ cSets++;
+ aSets[cSets].hStore = State.hKernelRootStore;
+ aSets[cSets].paTAs = g_aSUPNtKernelRootTAs;
+ aSets[cSets].cTAs = g_cSUPNtKernelRootTAs;
+ cSets++;
+ break;
+
+ case VERIFYEXESTATE::kSignType_OSX:
+ aSets[cSets].hStore = State.hRootStore;
+ aSets[cSets].paTAs = g_aSUPAppleRootTAs;
+ aSets[cSets].cTAs = g_cSUPAppleRootTAs;
+ cSets++;
+ break;
+ }
+ for (unsigned i = 0; i < cSets; i++)
+ for (unsigned j = 0; j < aSets[i].cTAs; j++)
+ {
+ rc = RTCrStoreCertAddEncoded(aSets[i].hStore, RTCRCERTCTX_F_ENC_TAF_DER, aSets[i].paTAs[j].pch,
+ aSets[i].paTAs[j].cb, RTErrInfoInitStatic(&StaticErrInfo));
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTCrStoreCertAddEncoded failed (%u/%u): %s",
+ i, j, StaticErrInfo.szMsg);
+ }
+# endif /* VBOX */
+
+ /*
+ * Do it.
+ */
+ RTEXITCODE rcExit;
+ for (;;)
+ {
+ rcExit = HandleVerifyExeWorker(&State, ValueUnion.psz, &StaticErrInfo);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ break;
+
+ /*
+ * Next file
+ */
+ ch = RTGetOpt(&GetState, &ValueUnion);
+ if (ch == 0)
+ break;
+ if (ch != VINF_GETOPT_NOT_OPTION)
+ {
+ rcExit = RTGetOptPrintError(ch, &ValueUnion);
+ break;
+ }
+ }
+
+ /*
+ * Clean up.
+ */
+ uint32_t cRefs;
+ cRefs = RTCrStoreRelease(State.hRootStore); Assert(cRefs == 0);
+ cRefs = RTCrStoreRelease(State.hKernelRootStore); Assert(cRefs == 0);
+ cRefs = RTCrStoreRelease(State.hAdditionalStore); Assert(cRefs == 0);
+
+ return rcExit;
+}
+
+#endif /* !IPRT_IN_BUILD_TOOL */
+
+/*
+ * common code for show-exe and show-cat:
+ */
+
+/**
+ * Display an object ID.
+ *
+ * @returns IPRT status code.
+ * @param pThis The show exe instance data.
+ * @param pObjId The object ID to display.
+ * @param pszLabel The field label (prefixed by szPrefix).
+ * @param pszPost What to print after the ID (typically newline).
+ */
+static void HandleShowExeWorkerDisplayObjId(PSHOWEXEPKCS7 pThis, PCRTASN1OBJID pObjId, const char *pszLabel, const char *pszPost)
+{
+ int rc = RTAsn1QueryObjIdName(pObjId, pThis->szTmp, sizeof(pThis->szTmp));
+ if (RT_SUCCESS(rc))
+ {
+ if (pThis->cVerbosity > 1)
+ RTPrintf("%s%s%s (%s)%s", pThis->szPrefix, pszLabel, pThis->szTmp, pObjId->szObjId, pszPost);
+ else
+ RTPrintf("%s%s%s%s", pThis->szPrefix, pszLabel, pThis->szTmp, pszPost);
+ }
+ else
+ RTPrintf("%s%s%s%s", pThis->szPrefix, pszLabel, pObjId->szObjId, pszPost);
+}
+
+
+/**
+ * Display an object ID, without prefix and label
+ *
+ * @returns IPRT status code.
+ * @param pThis The show exe instance data.
+ * @param pObjId The object ID to display.
+ * @param pszPost What to print after the ID (typically newline).
+ */
+static void HandleShowExeWorkerDisplayObjIdSimple(PSHOWEXEPKCS7 pThis, PCRTASN1OBJID pObjId, const char *pszPost)
+{
+ int rc = RTAsn1QueryObjIdName(pObjId, pThis->szTmp, sizeof(pThis->szTmp));
+ if (RT_SUCCESS(rc))
+ {
+ if (pThis->cVerbosity > 1)
+ RTPrintf("%s (%s)%s", pThis->szTmp, pObjId->szObjId, pszPost);
+ else
+ RTPrintf("%s%s", pThis->szTmp, pszPost);
+ }
+ else
+ RTPrintf("%s%s", pObjId->szObjId, pszPost);
+}
+
+
+/**
+ * Display a signer info attribute.
+ *
+ * @returns IPRT status code.
+ * @param pThis The show exe instance data.
+ * @param offPrefix The current prefix offset.
+ * @param pAttr The attribute to display.
+ */
+static int HandleShowExeWorkerPkcs7DisplayAttrib(PSHOWEXEPKCS7 pThis, size_t offPrefix, PCRTCRPKCS7ATTRIBUTE pAttr)
+{
+ HandleShowExeWorkerDisplayObjId(pThis, &pAttr->Type, "", ":\n");
+
+ int rc = VINF_SUCCESS;
+ switch (pAttr->enmType)
+ {
+ case RTCRPKCS7ATTRIBUTETYPE_UNKNOWN:
+ if (pAttr->uValues.pCores->cItems <= 1)
+ RTPrintf("%s %u bytes\n", pThis->szPrefix,pAttr->uValues.pCores->SetCore.Asn1Core.cb);
+ else
+ RTPrintf("%s %u bytes divided by %u items\n", pThis->szPrefix, pAttr->uValues.pCores->SetCore.Asn1Core.cb, pAttr->uValues.pCores->cItems);
+ break;
+
+ /* Object IDs, use pObjIds. */
+ case RTCRPKCS7ATTRIBUTETYPE_OBJ_IDS:
+ if (pAttr->uValues.pObjIds->cItems != 1)
+ RTPrintf("%s%u object IDs:", pThis->szPrefix, pAttr->uValues.pObjIds->cItems);
+ for (unsigned i = 0; i < pAttr->uValues.pObjIds->cItems; i++)
+ {
+ if (pAttr->uValues.pObjIds->cItems == 1)
+ RTPrintf("%s ", pThis->szPrefix);
+ else
+ RTPrintf("%s ObjId[%u]: ", pThis->szPrefix, i);
+ HandleShowExeWorkerDisplayObjIdSimple(pThis, pAttr->uValues.pObjIds->papItems[i], "\n");
+ }
+ break;
+
+ /* Sequence of object IDs, use pObjIdSeqs. */
+ case RTCRPKCS7ATTRIBUTETYPE_MS_STATEMENT_TYPE:
+ if (pAttr->uValues.pObjIdSeqs->cItems != 1)
+ RTPrintf("%s%u object IDs:", pThis->szPrefix, pAttr->uValues.pObjIdSeqs->cItems);
+ for (unsigned i = 0; i < pAttr->uValues.pObjIdSeqs->cItems; i++)
+ {
+ uint32_t const cObjIds = pAttr->uValues.pObjIdSeqs->papItems[i]->cItems;
+ for (unsigned j = 0; j < cObjIds; j++)
+ {
+ if (pAttr->uValues.pObjIdSeqs->cItems == 1)
+ RTPrintf("%s ", pThis->szPrefix);
+ else
+ RTPrintf("%s ObjIdSeq[%u]: ", pThis->szPrefix, i);
+ if (cObjIds != 1)
+ RTPrintf(" ObjId[%u]: ", j);
+ HandleShowExeWorkerDisplayObjIdSimple(pThis, pAttr->uValues.pObjIdSeqs->papItems[i]->papItems[i], "\n");
+ }
+ }
+ break;
+
+ /* Octet strings, use pOctetStrings. */
+ case RTCRPKCS7ATTRIBUTETYPE_OCTET_STRINGS:
+ if (pAttr->uValues.pOctetStrings->cItems != 1)
+ RTPrintf("%s%u octet strings:", pThis->szPrefix, pAttr->uValues.pOctetStrings->cItems);
+ for (unsigned i = 0; i < pAttr->uValues.pOctetStrings->cItems; i++)
+ {
+ PCRTASN1OCTETSTRING pOctetString = pAttr->uValues.pOctetStrings->papItems[i];
+ uint32_t cbContent = pOctetString->Asn1Core.cb;
+ if (cbContent > 0 && (cbContent <= 128 || pThis->cVerbosity >= 2))
+ {
+ uint8_t const *pbContent = pOctetString->Asn1Core.uData.pu8;
+ uint32_t off = 0;
+ while (off < cbContent)
+ {
+ uint32_t cbNow = RT_MIN(cbContent - off, 16);
+ if (pAttr->uValues.pOctetStrings->cItems == 1)
+ RTPrintf("%s %#06x: %.*Rhxs\n", pThis->szPrefix, off, cbNow, &pbContent[off]);
+ else
+ RTPrintf("%s OctetString[%u]: %#06x: %.*Rhxs\n", pThis->szPrefix, i, off, cbNow, &pbContent[off]);
+ off += cbNow;
+ }
+ }
+ else
+ RTPrintf("%s: OctetString[%u]: %u bytes\n", pThis->szPrefix, i, pOctetString->Asn1Core.cb);
+ }
+ break;
+
+ /* Counter signatures (PKCS \#9), use pCounterSignatures. */
+ case RTCRPKCS7ATTRIBUTETYPE_COUNTER_SIGNATURES:
+ RTPrintf("%sTODO: RTCRPKCS7ATTRIBUTETYPE_COUNTER_SIGNATURES! %u bytes\n",
+ pThis->szPrefix, pAttr->uValues.pCounterSignatures->SetCore.Asn1Core.cb);
+ break;
+
+ /* Signing time (PKCS \#9), use pSigningTime. */
+ case RTCRPKCS7ATTRIBUTETYPE_SIGNING_TIME:
+ for (uint32_t i = 0; i < pAttr->uValues.pSigningTime->cItems; i++)
+ {
+ PCRTASN1TIME pTime = pAttr->uValues.pSigningTime->papItems[i];
+ char szTS[RTTIME_STR_LEN];
+ RTTimeToString(&pTime->Time, szTS, sizeof(szTS));
+ if (pAttr->uValues.pSigningTime->cItems == 1)
+ RTPrintf("%s %s (%.*s)\n", pThis->szPrefix, szTS, pTime->Asn1Core.cb, pTime->Asn1Core.uData.pch);
+ else
+ RTPrintf("%s #%u: %s (%.*s)\n", pThis->szPrefix, i, szTS, pTime->Asn1Core.cb, pTime->Asn1Core.uData.pch);
+ }
+ break;
+
+ /* Microsoft timestamp info (RFC-3161) signed data, use pContentInfo. */
+ case RTCRPKCS7ATTRIBUTETYPE_MS_TIMESTAMP:
+ case RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE:
+ if (pAttr->uValues.pContentInfos->cItems > 1)
+ RTPrintf("%s%u nested signatures, %u bytes in total\n", pThis->szPrefix,
+ pAttr->uValues.pContentInfos->cItems, pAttr->uValues.pContentInfos->SetCore.Asn1Core.cb);
+ for (unsigned i = 0; i < pAttr->uValues.pContentInfos->cItems; i++)
+ {
+ size_t offPrefix2 = offPrefix;
+ if (pAttr->uValues.pContentInfos->cItems > 1)
+ offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, "NestedSig[%u]: ", i);
+ else
+ offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, " ");
+ // offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, "NestedSig: ", i);
+ PCRTCRPKCS7CONTENTINFO pContentInfo = pAttr->uValues.pContentInfos->papItems[i];
+ int rc2;
+ if (RTCrPkcs7ContentInfo_IsSignedData(pContentInfo))
+ rc2 = HandleShowExeWorkerPkcs7Display(pThis, pContentInfo->u.pSignedData, offPrefix2, pContentInfo);
+ else
+ rc2 = RTMsgErrorRc(VERR_ASN1_UNEXPECTED_OBJ_ID, "%sPKCS#7 content in nested signature is not 'signedData': %s",
+ pThis->szPrefix, pContentInfo->ContentType.szObjId);
+ if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+ rc = rc2;
+ }
+ break;
+
+ case RTCRPKCS7ATTRIBUTETYPE_APPLE_MULTI_CD_PLIST:
+ if (pAttr->uValues.pContentInfos->cItems != 1)
+ RTPrintf("%s%u plists, expected only 1.\n", pThis->szPrefix, pAttr->uValues.pOctetStrings->cItems);
+ for (unsigned i = 0; i < pAttr->uValues.pOctetStrings->cItems; i++)
+ {
+ PCRTASN1OCTETSTRING pOctetString = pAttr->uValues.pOctetStrings->papItems[i];
+ size_t cbContent = pOctetString->Asn1Core.cb;
+ char const *pchContent = pOctetString->Asn1Core.uData.pch;
+ rc = RTStrValidateEncodingEx(pchContent, cbContent, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
+ if (RT_SUCCESS(rc))
+ {
+ while (cbContent > 0)
+ {
+ const char *pchNewLine = (const char *)memchr(pchContent, '\n', cbContent);
+ size_t cchToWrite = pchNewLine ? pchNewLine - pchContent : cbContent;
+ if (pAttr->uValues.pOctetStrings->cItems == 1)
+ RTPrintf("%s %.*s\n", pThis->szPrefix, cchToWrite, pchContent);
+ else
+ RTPrintf("%s plist[%u]: %.*s\n", pThis->szPrefix, i, cchToWrite, pchContent);
+ if (!pchNewLine)
+ break;
+ pchContent = pchNewLine + 1;
+ cbContent -= cchToWrite + 1;
+ }
+ }
+ else
+ {
+ if (pAttr->uValues.pContentInfos->cItems != 1)
+ RTPrintf("%s: plist[%u]: Invalid UTF-8: %Rrc\n", pThis->szPrefix, i, rc);
+ else
+ RTPrintf("%s: Invalid UTF-8: %Rrc\n", pThis->szPrefix, rc);
+ for (uint32_t off = 0; off < cbContent; off += 16)
+ {
+ size_t cbNow = RT_MIN(cbContent - off, 16);
+ if (pAttr->uValues.pOctetStrings->cItems == 1)
+ RTPrintf("%s %#06x: %.*Rhxs\n", pThis->szPrefix, off, cbNow, &pchContent[off]);
+ else
+ RTPrintf("%s plist[%u]: %#06x: %.*Rhxs\n", pThis->szPrefix, i, off, cbNow, &pchContent[off]);
+ }
+ }
+ }
+ break;
+
+ case RTCRPKCS7ATTRIBUTETYPE_INVALID:
+ RTPrintf("%sINVALID!\n", pThis->szPrefix);
+ break;
+ case RTCRPKCS7ATTRIBUTETYPE_NOT_PRESENT:
+ RTPrintf("%sNOT PRESENT!\n", pThis->szPrefix);
+ break;
+ default:
+ RTPrintf("%senmType=%d!\n", pThis->szPrefix, pAttr->enmType);
+ break;
+ }
+ return rc;
+}
+
+
+/**
+ * Displays a Microsoft SPC indirect data structure.
+ *
+ * @returns IPRT status code.
+ * @param pThis The show exe instance data.
+ * @param offPrefix The current prefix offset.
+ * @param pIndData The indirect data to display.
+ */
+static int HandleShowExeWorkerPkcs7DisplaySpcIdirectDataContent(PSHOWEXEPKCS7 pThis, size_t offPrefix,
+ PCRTCRSPCINDIRECTDATACONTENT pIndData)
+{
+ /*
+ * The image hash.
+ */
+ RTDIGESTTYPE const enmDigestType = RTCrX509AlgorithmIdentifier_QueryDigestType(&pIndData->DigestInfo.DigestAlgorithm);
+ const char *pszDigestType = RTCrDigestTypeToName(enmDigestType);
+ RTPrintf("%s Digest Type: %s", pThis->szPrefix, pszDigestType);
+ if (pThis->cVerbosity > 1)
+ RTPrintf(" (%s)\n", pIndData->DigestInfo.DigestAlgorithm.Algorithm.szObjId);
+ else
+ RTPrintf("\n");
+ RTPrintf("%s Digest: %.*Rhxs\n",
+ pThis->szPrefix, pIndData->DigestInfo.Digest.Asn1Core.cb, pIndData->DigestInfo.Digest.Asn1Core.uData.pu8);
+
+ /*
+ * The data/file/url.
+ */
+ switch (pIndData->Data.enmType)
+ {
+ case RTCRSPCAAOVTYPE_PE_IMAGE_DATA:
+ {
+ RTPrintf("%s Data Type: PE Image Data\n", pThis->szPrefix);
+ PRTCRSPCPEIMAGEDATA pPeImage = pIndData->Data.uValue.pPeImage;
+ /** @todo display "Flags". */
+
+ switch (pPeImage->T0.File.enmChoice)
+ {
+ case RTCRSPCLINKCHOICE_MONIKER:
+ {
+ PRTCRSPCSERIALIZEDOBJECT pMoniker = pPeImage->T0.File.u.pMoniker;
+ if (RTCrSpcSerializedObject_IsPresent(pMoniker))
+ {
+ if (RTUuidCompareStr(pMoniker->Uuid.Asn1Core.uData.pUuid, RTCRSPCSERIALIZEDOBJECT_UUID_STR) == 0)
+ {
+ RTPrintf("%s Moniker: SpcSerializedObject (%RTuuid)\n",
+ pThis->szPrefix, pMoniker->Uuid.Asn1Core.uData.pUuid);
+
+ PCRTCRSPCSERIALIZEDOBJECTATTRIBUTES pData = pMoniker->u.pData;
+ if (pData)
+ for (uint32_t i = 0; i < pData->cItems; i++)
+ {
+ RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix,
+ "MonikerAttrib[%u]: ", i);
+
+ switch (pData->papItems[i]->enmType)
+ {
+ case RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V2:
+ case RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V1:
+ {
+ PCRTCRSPCSERIALIZEDPAGEHASHES pPgHashes = pData->papItems[i]->u.pPageHashes;
+ uint32_t const cbHash = pData->papItems[i]->enmType
+ == RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V1
+ ? 160/8 /*SHA-1*/ : 256/8 /*SHA-256*/;
+ uint32_t const cPages = pPgHashes->RawData.Asn1Core.cb / (cbHash + sizeof(uint32_t));
+
+ RTPrintf("%sPage Hashes version %u - %u pages (%u bytes total)\n", pThis->szPrefix,
+ pData->papItems[i]->enmType
+ == RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V1 ? 1 : 2,
+ cPages, pPgHashes->RawData.Asn1Core.cb);
+ if (pThis->cVerbosity > 0)
+ {
+ PCRTCRSPCPEIMAGEPAGEHASHES pPg = pPgHashes->pData;
+ for (unsigned iPg = 0; iPg < cPages; iPg++)
+ {
+ uint32_t offHash = 0;
+ do
+ {
+ if (offHash == 0)
+ RTPrintf("%.*s Page#%04u/%#08x: ",
+ offPrefix, pThis->szPrefix, iPg, pPg->Generic.offFile);
+ else
+ RTPrintf("%.*s ", offPrefix, pThis->szPrefix);
+ uint32_t cbLeft = cbHash - offHash;
+ if (cbLeft > 24)
+ cbLeft = 16;
+ RTPrintf("%.*Rhxs\n", cbLeft, &pPg->Generic.abHash[offHash]);
+ offHash += cbLeft;
+ } while (offHash < cbHash);
+ pPg = (PCRTCRSPCPEIMAGEPAGEHASHES)&pPg->Generic.abHash[cbHash];
+ }
+
+ if (pThis->cVerbosity > 3)
+ RTPrintf("%.*Rhxd\n",
+ pPgHashes->RawData.Asn1Core.cb,
+ pPgHashes->RawData.Asn1Core.uData.pu8);
+ }
+ break;
+ }
+
+ case RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_UNKNOWN:
+ HandleShowExeWorkerDisplayObjIdSimple(pThis, &pData->papItems[i]->Type, "\n");
+ break;
+ case RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_NOT_PRESENT:
+ RTPrintf("%sNot present!\n", pThis->szPrefix);
+ break;
+ default:
+ RTPrintf("%senmType=%d!\n", pThis->szPrefix, pData->papItems[i]->enmType);
+ break;
+ }
+ pThis->szPrefix[offPrefix] = '\0';
+ }
+ else
+ RTPrintf("%s pData is NULL!\n", pThis->szPrefix);
+ }
+ else
+ RTPrintf("%s Moniker: Unknown UUID: %RTuuid\n",
+ pThis->szPrefix, pMoniker->Uuid.Asn1Core.uData.pUuid);
+ }
+ else
+ RTPrintf("%s Moniker: not present\n", pThis->szPrefix);
+ break;
+ }
+
+ case RTCRSPCLINKCHOICE_URL:
+ {
+ const char *pszUrl = NULL;
+ int rc = pPeImage->T0.File.u.pUrl
+ ? RTAsn1String_QueryUtf8(pPeImage->T0.File.u.pUrl, &pszUrl, NULL)
+ : VERR_NOT_FOUND;
+ if (RT_SUCCESS(rc))
+ RTPrintf("%s URL: '%s'\n", pThis->szPrefix, pszUrl);
+ else
+ RTPrintf("%s URL: rc=%Rrc\n", pThis->szPrefix, rc);
+ break;
+ }
+
+ case RTCRSPCLINKCHOICE_FILE:
+ {
+ const char *pszFile = NULL;
+ int rc = pPeImage->T0.File.u.pT2 && pPeImage->T0.File.u.pT2->File.u.pAscii
+ ? RTAsn1String_QueryUtf8(pPeImage->T0.File.u.pT2->File.u.pAscii, &pszFile, NULL)
+ : VERR_NOT_FOUND;
+ if (RT_SUCCESS(rc))
+ RTPrintf("%s File: '%s'\n", pThis->szPrefix, pszFile);
+ else
+ RTPrintf("%s File: rc=%Rrc\n", pThis->szPrefix, rc);
+ break;
+ }
+
+ case RTCRSPCLINKCHOICE_NOT_PRESENT:
+ RTPrintf("%s File not present!\n", pThis->szPrefix);
+ break;
+ default:
+ RTPrintf("%s enmChoice=%d!\n", pThis->szPrefix, pPeImage->T0.File.enmChoice);
+ break;
+ }
+ break;
+ }
+
+ case RTCRSPCAAOVTYPE_UNKNOWN:
+ HandleShowExeWorkerDisplayObjId(pThis, &pIndData->Data.Type, " Data Type: ", "\n");
+ break;
+ case RTCRSPCAAOVTYPE_NOT_PRESENT:
+ RTPrintf("%s Data Type: Not present!\n", pThis->szPrefix);
+ break;
+ default:
+ RTPrintf("%s Data Type: enmType=%d!\n", pThis->szPrefix, pIndData->Data.enmType);
+ break;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Display an PKCS#7 signed data instance.
+ *
+ * @returns IPRT status code.
+ * @param pThis The show exe instance data.
+ * @param pSignedData The signed data to display.
+ * @param offPrefix The current prefix offset.
+ * @param pContentInfo The content info structure (for the size).
+ */
+static int HandleShowExeWorkerPkcs7Display(PSHOWEXEPKCS7 pThis, PRTCRPKCS7SIGNEDDATA pSignedData, size_t offPrefix,
+ PCRTCRPKCS7CONTENTINFO pContentInfo)
+{
+ pThis->szPrefix[offPrefix] = '\0';
+ RTPrintf("%sPKCS#7 signature: %u (%#x) bytes\n", pThis->szPrefix,
+ RTASN1CORE_GET_RAW_ASN1_SIZE(&pContentInfo->SeqCore.Asn1Core),
+ RTASN1CORE_GET_RAW_ASN1_SIZE(&pContentInfo->SeqCore.Asn1Core));
+
+ /*
+ * Display list of signing algorithms.
+ */
+ RTPrintf("%sDigestAlgorithms: ", pThis->szPrefix);
+ if (pSignedData->DigestAlgorithms.cItems == 0)
+ RTPrintf("none");
+ for (unsigned i = 0; i < pSignedData->DigestAlgorithms.cItems; i++)
+ {
+ PCRTCRX509ALGORITHMIDENTIFIER pAlgoId = pSignedData->DigestAlgorithms.papItems[i];
+ const char *pszDigestType = RTCrDigestTypeToName(RTCrX509AlgorithmIdentifier_QueryDigestType(pAlgoId));
+ if (!pszDigestType)
+ pszDigestType = pAlgoId->Algorithm.szObjId;
+ RTPrintf(i == 0 ? "%s" : ", %s", pszDigestType);
+ if (pThis->cVerbosity > 1)
+ RTPrintf(" (%s)", pAlgoId->Algorithm.szObjId);
+ }
+ RTPrintf("\n");
+
+ /*
+ * Display the signed data content.
+ */
+ if (RTAsn1ObjId_CompareWithString(&pSignedData->ContentInfo.ContentType, RTCRSPCINDIRECTDATACONTENT_OID) == 0)
+ {
+ RTPrintf("%s ContentType: SpcIndirectDataContent (" RTCRSPCINDIRECTDATACONTENT_OID ")\n", pThis->szPrefix);
+ size_t offPrefix2 = RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, " SPC Ind Data: ");
+ HandleShowExeWorkerPkcs7DisplaySpcIdirectDataContent(pThis, offPrefix2 + offPrefix,
+ pSignedData->ContentInfo.u.pIndirectDataContent);
+ pThis->szPrefix[offPrefix] = '\0';
+ }
+ else
+ HandleShowExeWorkerDisplayObjId(pThis, &pSignedData->ContentInfo.ContentType, " ContentType: ", " - not implemented.\n");
+
+ /*
+ * Display certificates (Certificates).
+ */
+ if (pSignedData->Certificates.cItems > 0)
+ {
+ RTPrintf("%s Certificates: %u\n", pThis->szPrefix, pSignedData->Certificates.cItems);
+ if (pThis->cVerbosity >= 2)
+ {
+ for (uint32_t i = 0; i < pSignedData->Certificates.cItems; i++)
+ {
+ if (i != 0)
+ RTPrintf("\n");
+ RTPrintf("%s Certificate #%u:\n", pThis->szPrefix, i);
+ RTAsn1Dump(RTCrPkcs7Cert_GetAsn1Core(pSignedData->Certificates.papItems[i]), 0,
+ ((uint32_t)offPrefix + 9) / 2, RTStrmDumpPrintfV, g_pStdOut);
+ }
+ }
+ /** @todo display certificates properly. */
+ }
+
+ if (pSignedData->Crls.cb > 0)
+ RTPrintf("%s CRLs: %u bytes\n", pThis->szPrefix, pSignedData->Crls.cb);
+
+ /*
+ * Show signatures (SignerInfos).
+ */
+ unsigned const cSigInfos = pSignedData->SignerInfos.cItems;
+ if (cSigInfos != 1)
+ RTPrintf("%s SignerInfos: %u signers\n", pThis->szPrefix, cSigInfos);
+ else
+ RTPrintf("%s SignerInfos:\n", pThis->szPrefix);
+ for (unsigned i = 0; i < cSigInfos; i++)
+ {
+ PRTCRPKCS7SIGNERINFO pSigInfo = pSignedData->SignerInfos.papItems[i];
+ size_t offPrefix2 = offPrefix;
+ if (cSigInfos != 1)
+ offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, "SignerInfo[%u]: ", i);
+
+ int rc = RTAsn1Integer_ToString(&pSigInfo->IssuerAndSerialNumber.SerialNumber,
+ pThis->szTmp, sizeof(pThis->szTmp), 0 /*fFlags*/, NULL);
+ if (RT_FAILURE(rc))
+ RTStrPrintf(pThis->szTmp, sizeof(pThis->szTmp), "%Rrc", rc);
+ RTPrintf("%s Serial No: %s\n", pThis->szPrefix, pThis->szTmp);
+
+ rc = RTCrX509Name_FormatAsString(&pSigInfo->IssuerAndSerialNumber.Name, pThis->szTmp, sizeof(pThis->szTmp), NULL);
+ if (RT_FAILURE(rc))
+ RTStrPrintf(pThis->szTmp, sizeof(pThis->szTmp), "%Rrc", rc);
+ RTPrintf("%s Issuer: %s\n", pThis->szPrefix, pThis->szTmp);
+
+ const char *pszType = RTCrDigestTypeToName(RTCrX509AlgorithmIdentifier_QueryDigestType(&pSigInfo->DigestAlgorithm));
+ if (!pszType)
+ pszType = pSigInfo->DigestAlgorithm.Algorithm.szObjId;
+ RTPrintf("%s Digest Algorithm: %s", pThis->szPrefix, pszType);
+ if (pThis->cVerbosity > 1)
+ RTPrintf(" (%s)\n", pSigInfo->DigestAlgorithm.Algorithm.szObjId);
+ else
+ RTPrintf("\n");
+
+ HandleShowExeWorkerDisplayObjId(pThis, &pSigInfo->DigestEncryptionAlgorithm.Algorithm,
+ "Digest Encryption Algorithm: ", "\n");
+
+ if (pSigInfo->AuthenticatedAttributes.cItems == 0)
+ RTPrintf("%s Authenticated Attributes: none\n", pThis->szPrefix);
+ else
+ {
+ RTPrintf("%s Authenticated Attributes: %u item%s\n", pThis->szPrefix,
+ pSigInfo->AuthenticatedAttributes.cItems, pSigInfo->AuthenticatedAttributes.cItems > 1 ? "s" : "");
+ for (unsigned j = 0; j < pSigInfo->AuthenticatedAttributes.cItems; j++)
+ {
+ PRTCRPKCS7ATTRIBUTE pAttr = pSigInfo->AuthenticatedAttributes.papItems[j];
+ size_t offPrefix3 = offPrefix2 + RTStrPrintf(&pThis->szPrefix[offPrefix2], sizeof(pThis->szPrefix) - offPrefix2,
+ " AuthAttrib[%u]: ", j);
+ HandleShowExeWorkerPkcs7DisplayAttrib(pThis, offPrefix3, pAttr);
+ }
+ pThis->szPrefix[offPrefix2] = '\0';
+ }
+
+ if (pSigInfo->UnauthenticatedAttributes.cItems == 0)
+ RTPrintf("%s Unauthenticated Attributes: none\n", pThis->szPrefix);
+ else
+ {
+ RTPrintf("%s Unauthenticated Attributes: %u item%s\n", pThis->szPrefix,
+ pSigInfo->UnauthenticatedAttributes.cItems, pSigInfo->UnauthenticatedAttributes.cItems > 1 ? "s" : "");
+ for (unsigned j = 0; j < pSigInfo->UnauthenticatedAttributes.cItems; j++)
+ {
+ PRTCRPKCS7ATTRIBUTE pAttr = pSigInfo->UnauthenticatedAttributes.papItems[j];
+ size_t offPrefix3 = offPrefix2 + RTStrPrintf(&pThis->szPrefix[offPrefix2], sizeof(pThis->szPrefix) - offPrefix2,
+ " UnauthAttrib[%u]: ", j);
+ HandleShowExeWorkerPkcs7DisplayAttrib(pThis, offPrefix3, pAttr);
+ }
+ pThis->szPrefix[offPrefix2] = '\0';
+ }
+
+ /** @todo show the encrypted stuff (EncryptedDigest)? */
+ }
+ pThis->szPrefix[offPrefix] = '\0';
+
+ return VINF_SUCCESS;
+}
+
+
+/*
+ * The 'show-exe' command.
+ */
+static RTEXITCODE HelpShowExe(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
+{
+ RT_NOREF_PV(enmLevel);
+ RTStrmPrintf(pStrm,
+ "show-exe [--verbose|-v] [--quiet|-q] <exe1> [exe2 [..]]\n");
+ return RTEXITCODE_SUCCESS;
+}
+
+
+static RTEXITCODE HandleShowExe(int cArgs, char **papszArgs)
+{
+ /*
+ * Parse arguments.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
+ { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
+ };
+
+ unsigned cVerbosity = 0;
+ RTLDRARCH enmLdrArch = RTLDRARCH_WHATEVER;
+
+ RTGETOPTSTATE GetState;
+ int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ AssertRCReturn(rc, RTEXITCODE_FAILURE);
+ RTGETOPTUNION ValueUnion;
+ int ch;
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)) && ch != VINF_GETOPT_NOT_OPTION)
+ {
+ switch (ch)
+ {
+ case 'v': cVerbosity++; break;
+ case 'q': cVerbosity = 0; break;
+ case 'V': return HandleVersion(cArgs, papszArgs);
+ case 'h': return HelpShowExe(g_pStdOut, RTSIGNTOOLHELP_FULL);
+ default: return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+ if (ch != VINF_GETOPT_NOT_OPTION)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
+
+ /*
+ * Do it.
+ */
+ unsigned iFile = 0;
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+ do
+ {
+ RTPrintf(iFile == 0 ? "%s:\n" : "\n%s:\n", ValueUnion.psz);
+
+ SHOWEXEPKCS7 This;
+ RT_ZERO(This);
+ This.cVerbosity = cVerbosity;
+
+ RTEXITCODE rcExitThis = SignToolPkcs7Exe_InitFromFile(&This, ValueUnion.psz, cVerbosity, enmLdrArch);
+ if (rcExitThis == RTEXITCODE_SUCCESS)
+ {
+ rc = HandleShowExeWorkerPkcs7Display(&This, This.pSignedData, 0, &This.ContentInfo);
+ if (RT_FAILURE(rc))
+ rcExit = RTEXITCODE_FAILURE;
+ SignToolPkcs7Exe_Delete(&This);
+ }
+ if (rcExitThis != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
+ rcExit = rcExitThis;
+
+ iFile++;
+ } while ((ch = RTGetOpt(&GetState, &ValueUnion)) == VINF_GETOPT_NOT_OPTION);
+ if (ch != 0)
+ return RTGetOptPrintError(ch, &ValueUnion);
+
+ return rcExit;
+}
+
+
+/*
+ * The 'show-cat' command.
+ */
+static RTEXITCODE HelpShowCat(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
+{
+ RT_NOREF_PV(enmLevel);
+ RTStrmPrintf(pStrm,
+ "show-cat [--verbose|-v] [--quiet|-q] <cat1> [cat2 [..]]\n");
+ return RTEXITCODE_SUCCESS;
+}
+
+
+static RTEXITCODE HandleShowCat(int cArgs, char **papszArgs)
+{
+ /*
+ * Parse arguments.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
+ { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
+ };
+
+ unsigned cVerbosity = 0;
+
+ RTGETOPTSTATE GetState;
+ int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ AssertRCReturn(rc, RTEXITCODE_FAILURE);
+ RTGETOPTUNION ValueUnion;
+ int ch;
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)) && ch != VINF_GETOPT_NOT_OPTION)
+ {
+ switch (ch)
+ {
+ case 'v': cVerbosity++; break;
+ case 'q': cVerbosity = 0; break;
+ case 'V': return HandleVersion(cArgs, papszArgs);
+ case 'h': return HelpShowCat(g_pStdOut, RTSIGNTOOLHELP_FULL);
+ default: return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+ if (ch != VINF_GETOPT_NOT_OPTION)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
+
+ /*
+ * Do it.
+ */
+ unsigned iFile = 0;
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+ do
+ {
+ RTPrintf(iFile == 0 ? "%s:\n" : "\n%s:\n", ValueUnion.psz);
+
+ SHOWEXEPKCS7 This;
+ RT_ZERO(This);
+ This.cVerbosity = cVerbosity;
+
+ RTEXITCODE rcExitThis = SignToolPkcs7_InitFromFile(&This, ValueUnion.psz, cVerbosity);
+ if (rcExitThis == RTEXITCODE_SUCCESS)
+ {
+ This.hLdrMod = NIL_RTLDRMOD;
+
+ rc = HandleShowExeWorkerPkcs7Display(&This, This.pSignedData, 0, &This.ContentInfo);
+ if (RT_FAILURE(rc))
+ rcExit = RTEXITCODE_FAILURE;
+ SignToolPkcs7Exe_Delete(&This);
+ }
+ if (rcExitThis != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
+ rcExit = rcExitThis;
+
+ iFile++;
+ } while ((ch = RTGetOpt(&GetState, &ValueUnion)) == VINF_GETOPT_NOT_OPTION);
+ if (ch != 0)
+ return RTGetOptPrintError(ch, &ValueUnion);
+
+ return rcExit;
+}
+
+
+/*
+ * The 'make-tainfo' command.
+ */
+static RTEXITCODE HelpMakeTaInfo(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
+{
+ RT_NOREF_PV(enmLevel);
+ RTStrmPrintf(pStrm,
+ "make-tainfo [--verbose|--quiet] [--cert <cert.der>] [-o|--output] <tainfo.der>\n");
+ return RTEXITCODE_SUCCESS;
+}
+
+
+typedef struct MAKETAINFOSTATE
+{
+ int cVerbose;
+ const char *pszCert;
+ const char *pszOutput;
+} MAKETAINFOSTATE;
+
+
+/** @callback_method_impl{FNRTASN1ENCODEWRITER} */
+static DECLCALLBACK(int) handleMakeTaInfoWriter(const void *pvBuf, size_t cbToWrite, void *pvUser, PRTERRINFO pErrInfo)
+{
+ RT_NOREF_PV(pErrInfo);
+ return RTStrmWrite((PRTSTREAM)pvUser, pvBuf, cbToWrite);
+}
+
+
+static RTEXITCODE HandleMakeTaInfo(int cArgs, char **papszArgs)
+{
+ /*
+ * Parse arguments.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--cert", 'c', RTGETOPT_REQ_STRING },
+ { "--output", 'o', RTGETOPT_REQ_STRING },
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
+ { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
+ };
+
+ MAKETAINFOSTATE State = { 0, NULL, NULL };
+
+ RTGETOPTSTATE GetState;
+ int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ AssertRCReturn(rc, RTEXITCODE_FAILURE);
+ RTGETOPTUNION ValueUnion;
+ int ch;
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (ch)
+ {
+ case 'c':
+ if (State.pszCert)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "The --cert option can only be used once.");
+ State.pszCert = ValueUnion.psz;
+ break;
+
+ case 'o':
+ case VINF_GETOPT_NOT_OPTION:
+ if (State.pszOutput)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Multiple output files specified.");
+ State.pszOutput = ValueUnion.psz;
+ break;
+
+ case 'v': State.cVerbose++; break;
+ case 'q': State.cVerbose = 0; break;
+ case 'V': return HandleVersion(cArgs, papszArgs);
+ case 'h': return HelpMakeTaInfo(g_pStdOut, RTSIGNTOOLHELP_FULL);
+ default: return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+ if (!State.pszCert)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "No input certificate was specified.");
+ if (!State.pszOutput)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "No output file was specified.");
+
+ /*
+ * Read the certificate.
+ */
+ RTERRINFOSTATIC StaticErrInfo;
+ RTCRX509CERTIFICATE Certificate;
+ rc = RTCrX509Certificate_ReadFromFile(&Certificate, State.pszCert, 0, &g_RTAsn1DefaultAllocator,
+ RTErrInfoInitStatic(&StaticErrInfo));
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error reading certificate from %s: %Rrc - %s",
+ State.pszCert, rc, StaticErrInfo.szMsg);
+ /*
+ * Construct the trust anchor information.
+ */
+ RTCRTAFTRUSTANCHORINFO TrustAnchor;
+ rc = RTCrTafTrustAnchorInfo_Init(&TrustAnchor, &g_RTAsn1DefaultAllocator);
+ if (RT_SUCCESS(rc))
+ {
+ /* Public key. */
+ Assert(RTCrX509SubjectPublicKeyInfo_IsPresent(&TrustAnchor.PubKey));
+ RTCrX509SubjectPublicKeyInfo_Delete(&TrustAnchor.PubKey);
+ rc = RTCrX509SubjectPublicKeyInfo_Clone(&TrustAnchor.PubKey, &Certificate.TbsCertificate.SubjectPublicKeyInfo,
+ &g_RTAsn1DefaultAllocator);
+ if (RT_FAILURE(rc))
+ RTMsgError("RTCrX509SubjectPublicKeyInfo_Clone failed: %Rrc", rc);
+ RTAsn1Core_ResetImplict(RTCrX509SubjectPublicKeyInfo_GetAsn1Core(&TrustAnchor.PubKey)); /* temporary hack. */
+
+ /* Key Identifier. */
+ PCRTASN1OCTETSTRING pKeyIdentifier = NULL;
+ if (Certificate.TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_SUBJECT_KEY_IDENTIFIER)
+ pKeyIdentifier = Certificate.TbsCertificate.T3.pSubjectKeyIdentifier;
+ else if ( (Certificate.TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_AUTHORITY_KEY_IDENTIFIER)
+ && RTCrX509Certificate_IsSelfSigned(&Certificate)
+ && RTAsn1OctetString_IsPresent(&Certificate.TbsCertificate.T3.pAuthorityKeyIdentifier->KeyIdentifier) )
+ pKeyIdentifier = &Certificate.TbsCertificate.T3.pAuthorityKeyIdentifier->KeyIdentifier;
+ else if ( (Certificate.TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_OLD_AUTHORITY_KEY_IDENTIFIER)
+ && RTCrX509Certificate_IsSelfSigned(&Certificate)
+ && RTAsn1OctetString_IsPresent(&Certificate.TbsCertificate.T3.pOldAuthorityKeyIdentifier->KeyIdentifier) )
+ pKeyIdentifier = &Certificate.TbsCertificate.T3.pOldAuthorityKeyIdentifier->KeyIdentifier;
+ if (pKeyIdentifier && pKeyIdentifier->Asn1Core.cb > 0)
+ {
+ Assert(RTAsn1OctetString_IsPresent(&TrustAnchor.KeyIdentifier));
+ RTAsn1OctetString_Delete(&TrustAnchor.KeyIdentifier);
+ rc = RTAsn1OctetString_Clone(&TrustAnchor.KeyIdentifier, pKeyIdentifier, &g_RTAsn1DefaultAllocator);
+ if (RT_FAILURE(rc))
+ RTMsgError("RTAsn1OctetString_Clone failed: %Rrc", rc);
+ RTAsn1Core_ResetImplict(RTAsn1OctetString_GetAsn1Core(&TrustAnchor.KeyIdentifier)); /* temporary hack. */
+ }
+ else
+ RTMsgWarning("No key identifier found or has zero length.");
+
+ /* Subject */
+ if (RT_SUCCESS(rc))
+ {
+ Assert(!RTCrTafCertPathControls_IsPresent(&TrustAnchor.CertPath));
+ rc = RTCrTafCertPathControls_Init(&TrustAnchor.CertPath, &g_RTAsn1DefaultAllocator);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(RTCrX509Name_IsPresent(&TrustAnchor.CertPath.TaName));
+ RTCrX509Name_Delete(&TrustAnchor.CertPath.TaName);
+ rc = RTCrX509Name_Clone(&TrustAnchor.CertPath.TaName, &Certificate.TbsCertificate.Subject,
+ &g_RTAsn1DefaultAllocator);
+ if (RT_SUCCESS(rc))
+ {
+ RTAsn1Core_ResetImplict(RTCrX509Name_GetAsn1Core(&TrustAnchor.CertPath.TaName)); /* temporary hack. */
+ rc = RTCrX509Name_RecodeAsUtf8(&TrustAnchor.CertPath.TaName, &g_RTAsn1DefaultAllocator);
+ if (RT_FAILURE(rc))
+ RTMsgError("RTCrX509Name_RecodeAsUtf8 failed: %Rrc", rc);
+ }
+ else
+ RTMsgError("RTCrX509Name_Clone failed: %Rrc", rc);
+ }
+ else
+ RTMsgError("RTCrTafCertPathControls_Init failed: %Rrc", rc);
+ }
+
+ /* Check that what we've constructed makes some sense. */
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrTafTrustAnchorInfo_CheckSanity(&TrustAnchor, 0, RTErrInfoInitStatic(&StaticErrInfo), "TAI");
+ if (RT_FAILURE(rc))
+ RTMsgError("RTCrTafTrustAnchorInfo_CheckSanity failed: %Rrc - %s", rc, StaticErrInfo.szMsg);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Encode it and write it to the output file.
+ */
+ uint32_t cbEncoded;
+ rc = RTAsn1EncodePrepare(RTCrTafTrustAnchorInfo_GetAsn1Core(&TrustAnchor), RTASN1ENCODE_F_DER, &cbEncoded,
+ RTErrInfoInitStatic(&StaticErrInfo));
+ if (RT_SUCCESS(rc))
+ {
+ if (State.cVerbose >= 1)
+ RTAsn1Dump(RTCrTafTrustAnchorInfo_GetAsn1Core(&TrustAnchor), 0, 0, RTStrmDumpPrintfV, g_pStdOut);
+
+ PRTSTREAM pStrm;
+ rc = RTStrmOpen(State.pszOutput, "wb", &pStrm);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTAsn1EncodeWrite(RTCrTafTrustAnchorInfo_GetAsn1Core(&TrustAnchor), RTASN1ENCODE_F_DER,
+ handleMakeTaInfoWriter, pStrm, RTErrInfoInitStatic(&StaticErrInfo));
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTStrmClose(pStrm);
+ if (RT_SUCCESS(rc))
+ RTMsgInfo("Successfully wrote TrustedAnchorInfo to '%s'.", State.pszOutput);
+ else
+ RTMsgError("RTStrmClose failed: %Rrc", rc);
+ }
+ else
+ {
+ RTMsgError("RTAsn1EncodeWrite failed: %Rrc - %s", rc, StaticErrInfo.szMsg);
+ RTStrmClose(pStrm);
+ }
+ }
+ else
+ RTMsgError("Error opening '%s' for writing: %Rrcs", State.pszOutput, rc);
+ }
+ else
+ RTMsgError("RTAsn1EncodePrepare failed: %Rrc - %s", rc, StaticErrInfo.szMsg);
+ }
+
+ RTCrTafTrustAnchorInfo_Delete(&TrustAnchor);
+ }
+ else
+ RTMsgError("RTCrTafTrustAnchorInfo_Init failed: %Rrc", rc);
+
+ RTCrX509Certificate_Delete(&Certificate);
+ return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+
+/*
+ * The 'version' command.
+ */
+static RTEXITCODE HelpVersion(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
+{
+ RT_NOREF_PV(enmLevel);
+ RTStrmPrintf(pStrm, "version\n");
+ return RTEXITCODE_SUCCESS;
+}
+
+static RTEXITCODE HandleVersion(int cArgs, char **papszArgs)
+{
+ RT_NOREF_PV(cArgs); RT_NOREF_PV(papszArgs);
+#ifndef IN_BLD_PROG /* RTBldCfgVersion or RTBldCfgRevision in build time IPRT lib. */
+ RTPrintf("%s\n", RTBldCfgVersion());
+ return RTEXITCODE_SUCCESS;
+#else
+ return RTEXITCODE_FAILURE;
+#endif
+}
+
+
+
+/**
+ * Command mapping.
+ */
+static struct
+{
+ /** The command. */
+ const char *pszCmd;
+ /**
+ * Handle the command.
+ * @returns Program exit code.
+ * @param cArgs Number of arguments.
+ * @param papszArgs The argument vector, starting with the command name.
+ */
+ RTEXITCODE (*pfnHandler)(int cArgs, char **papszArgs);
+ /**
+ * Produce help.
+ * @returns RTEXITCODE_SUCCESS to simplify handling '--help' in the handler.
+ * @param pStrm Where to send help text.
+ * @param enmLevel The level of the help information.
+ */
+ RTEXITCODE (*pfnHelp)(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel);
+}
+/** Mapping commands to handler and helper functions. */
+const g_aCommands[] =
+{
+ { "extract-exe-signer-cert", HandleExtractExeSignerCert, HelpExtractExeSignerCert },
+ { "add-nested-exe-signature", HandleAddNestedExeSignature, HelpAddNestedExeSignature },
+ { "add-nested-cat-signature", HandleAddNestedCatSignature, HelpAddNestedCatSignature },
+#ifndef IPRT_IN_BUILD_TOOL
+ { "verify-exe", HandleVerifyExe, HelpVerifyExe },
+#endif
+ { "show-exe", HandleShowExe, HelpShowExe },
+ { "show-cat", HandleShowCat, HelpShowCat },
+ { "make-tainfo", HandleMakeTaInfo, HelpMakeTaInfo },
+ { "help", HandleHelp, HelpHelp },
+ { "--help", HandleHelp, NULL },
+ { "-h", HandleHelp, NULL },
+ { "version", HandleVersion, HelpVersion },
+ { "--version", HandleVersion, NULL },
+ { "-V", HandleVersion, NULL },
+};
+
+
+/*
+ * The 'help' command.
+ */
+static RTEXITCODE HelpHelp(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
+{
+ RT_NOREF_PV(enmLevel);
+ RTStrmPrintf(pStrm, "help [cmd-patterns]\n");
+ return RTEXITCODE_SUCCESS;
+}
+
+static RTEXITCODE HandleHelp(int cArgs, char **papszArgs)
+{
+ RTSIGNTOOLHELP enmLevel = cArgs <= 1 ? RTSIGNTOOLHELP_USAGE : RTSIGNTOOLHELP_FULL;
+ uint32_t cShowed = 0;
+ for (uint32_t iCmd = 0; iCmd < RT_ELEMENTS(g_aCommands); iCmd++)
+ {
+ if (g_aCommands[iCmd].pfnHelp)
+ {
+ bool fShow = false;
+ if (cArgs <= 1)
+ fShow = true;
+ else
+ {
+ for (int iArg = 1; iArg < cArgs; iArg++)
+ if (RTStrSimplePatternMultiMatch(papszArgs[iArg], RTSTR_MAX, g_aCommands[iCmd].pszCmd, RTSTR_MAX, NULL))
+ {
+ fShow = true;
+ break;
+ }
+ }
+ if (fShow)
+ {
+ g_aCommands[iCmd].pfnHelp(g_pStdOut, enmLevel);
+ cShowed++;
+ }
+ }
+ }
+ return cShowed ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+
+
+int main(int argc, char **argv)
+{
+ int rc = RTR3InitExe(argc, &argv, 0);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ /*
+ * Parse global arguments.
+ */
+ int iArg = 1;
+ /* none presently. */
+
+ /*
+ * Command dispatcher.
+ */
+ if (iArg < argc)
+ {
+ const char *pszCmd = argv[iArg];
+ uint32_t i = RT_ELEMENTS(g_aCommands);
+ while (i-- > 0)
+ if (!strcmp(g_aCommands[i].pszCmd, pszCmd))
+ return g_aCommands[i].pfnHandler(argc - iArg, &argv[iArg]);
+ RTMsgError("Unknown command '%s'.", pszCmd);
+ }
+ else
+ RTMsgError("No command given. (try --help)");
+
+ return RTEXITCODE_SYNTAX;
+}
+