VirtualBox

source: vbox/trunk/src/VBox/Runtime/tools/RTSignTool.cpp@ 86556

Last change on this file since 86556 was 86549, checked in by vboxsync, 4 years ago

SUPHardNt,IPRT: If there are nested signatures (i.e. more than one signature), don't get grumpy if there are time or cert path issues with some of them, as long as one or more checks out perfectly. (Mind, all the signature data must check out, it's just the cert path or signing time we're relaxing here.) ticketref:19743 bugref:3103

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 113.2 KB
Line 
1/* $Id: RTSignTool.cpp 86549 2020-10-12 23:59:53Z vboxsync $ */
2/** @file
3 * IPRT - Signing Tool.
4 */
5
6/*
7 * Copyright (C) 2006-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/assert.h>
32#include <iprt/buildconfig.h>
33#include <iprt/err.h>
34#include <iprt/getopt.h>
35#include <iprt/file.h>
36#include <iprt/initterm.h>
37#include <iprt/ldr.h>
38#include <iprt/message.h>
39#include <iprt/mem.h>
40#include <iprt/path.h>
41#include <iprt/stream.h>
42#include <iprt/string.h>
43#include <iprt/uuid.h>
44#include <iprt/zero.h>
45#ifndef RT_OS_WINDOWS
46# include <iprt/formats/pecoff.h>
47#endif
48#include <iprt/crypto/applecodesign.h>
49#include <iprt/crypto/digest.h>
50#include <iprt/crypto/x509.h>
51#include <iprt/crypto/pkcs7.h>
52#include <iprt/crypto/store.h>
53#include <iprt/crypto/spc.h>
54#ifdef VBOX
55# include <VBox/sup.h> /* Certificates */
56#endif
57#ifdef RT_OS_WINDOWS
58# include <iprt/win/windows.h>
59# include <iprt/win/imagehlp.h>
60#endif
61
62
63/*********************************************************************************************************************************
64* Structures and Typedefs *
65*********************************************************************************************************************************/
66/** Help detail levels. */
67typedef enum RTSIGNTOOLHELP
68{
69 RTSIGNTOOLHELP_USAGE,
70 RTSIGNTOOLHELP_FULL
71} RTSIGNTOOLHELP;
72
73
74/**
75 * PKCS\#7 signature data.
76 */
77typedef struct SIGNTOOLPKCS7
78{
79 /** The raw signature. */
80 uint8_t *pbBuf;
81 /** Size of the raw signature. */
82 size_t cbBuf;
83 /** The filename. */
84 const char *pszFilename;
85 /** The outer content info wrapper. */
86 RTCRPKCS7CONTENTINFO ContentInfo;
87 /** Pointer to the decoded SignedData inside the ContentInfo member. */
88 PRTCRPKCS7SIGNEDDATA pSignedData;
89
90 /** Newly encoded raw signature.
91 * @sa SignToolPkcs7_Encode() */
92 uint8_t *pbNewBuf;
93 /** Size of newly encoded raw signature. */
94 size_t cbNewBuf;
95
96} SIGNTOOLPKCS7;
97typedef SIGNTOOLPKCS7 *PSIGNTOOLPKCS7;
98
99
100/**
101 * PKCS\#7 signature data for executable.
102 */
103typedef struct SIGNTOOLPKCS7EXE : public SIGNTOOLPKCS7
104{
105 /** The module handle. */
106 RTLDRMOD hLdrMod;
107} SIGNTOOLPKCS7EXE;
108typedef SIGNTOOLPKCS7EXE *PSIGNTOOLPKCS7EXE;
109
110
111/**
112 * Data for the show exe (signature) command.
113 */
114typedef struct SHOWEXEPKCS7 : public SIGNTOOLPKCS7EXE
115{
116 /** The verbosity. */
117 unsigned cVerbosity;
118 /** The prefix buffer. */
119 char szPrefix[256];
120 /** Temporary buffer. */
121 char szTmp[4096];
122} SHOWEXEPKCS7;
123typedef SHOWEXEPKCS7 *PSHOWEXEPKCS7;
124
125
126/*********************************************************************************************************************************
127* Internal Functions *
128*********************************************************************************************************************************/
129static RTEXITCODE HandleHelp(int cArgs, char **papszArgs);
130static RTEXITCODE HelpHelp(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel);
131static RTEXITCODE HandleVersion(int cArgs, char **papszArgs);
132static int HandleShowExeWorkerPkcs7Display(PSHOWEXEPKCS7 pThis, PRTCRPKCS7SIGNEDDATA pSignedData, size_t offPrefix,
133 PCRTCRPKCS7CONTENTINFO pContentInfo);
134
135
136/**
137 * Deletes the structure.
138 *
139 * @param pThis The structure to initialize.
140 */
141static void SignToolPkcs7_Delete(PSIGNTOOLPKCS7 pThis)
142{
143 RTCrPkcs7ContentInfo_Delete(&pThis->ContentInfo);
144 pThis->pSignedData = NULL;
145 RTMemFree(pThis->pbBuf);
146 pThis->pbBuf = NULL;
147 pThis->cbBuf = 0;
148 RTMemFree(pThis->pbNewBuf);
149 pThis->pbNewBuf = NULL;
150 pThis->cbNewBuf = 0;
151}
152
153
154/**
155 * Deletes the structure.
156 *
157 * @param pThis The structure to initialize.
158 */
159static void SignToolPkcs7Exe_Delete(PSIGNTOOLPKCS7EXE pThis)
160{
161 if (pThis->hLdrMod != NIL_RTLDRMOD)
162 {
163 int rc2 = RTLdrClose(pThis->hLdrMod);
164 if (RT_FAILURE(rc2))
165 RTMsgError("RTLdrClose failed: %Rrc\n", rc2);
166 pThis->hLdrMod = NIL_RTLDRMOD;
167 }
168 SignToolPkcs7_Delete(pThis);
169}
170
171
172/**
173 * Decodes the PKCS #7 blob pointed to by pThis->pbBuf.
174 *
175 * @returns IPRT status code (error message already shown on failure).
176 * @param pThis The PKCS\#7 signature to decode.
177 * @param fCatalog Set if catalog file, clear if executable.
178 */
179static int SignToolPkcs7_Decode(PSIGNTOOLPKCS7 pThis, bool fCatalog)
180{
181 RTERRINFOSTATIC ErrInfo;
182 RTASN1CURSORPRIMARY PrimaryCursor;
183 RTAsn1CursorInitPrimary(&PrimaryCursor, pThis->pbBuf, (uint32_t)pThis->cbBuf, RTErrInfoInitStatic(&ErrInfo),
184 &g_RTAsn1DefaultAllocator, 0, "WinCert");
185
186 int rc = RTCrPkcs7ContentInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, &pThis->ContentInfo, "CI");
187 if (RT_SUCCESS(rc))
188 {
189 if (RTCrPkcs7ContentInfo_IsSignedData(&pThis->ContentInfo))
190 {
191 pThis->pSignedData = pThis->ContentInfo.u.pSignedData;
192
193 /*
194 * Decode the authenticode bits.
195 */
196 if (!strcmp(pThis->pSignedData->ContentInfo.ContentType.szObjId, RTCRSPCINDIRECTDATACONTENT_OID))
197 {
198 PRTCRSPCINDIRECTDATACONTENT pIndData = pThis->pSignedData->ContentInfo.u.pIndirectDataContent;
199 Assert(pIndData);
200
201 /*
202 * Check that things add up.
203 */
204 rc = RTCrPkcs7SignedData_CheckSanity(pThis->pSignedData,
205 RTCRPKCS7SIGNEDDATA_SANITY_F_AUTHENTICODE
206 | RTCRPKCS7SIGNEDDATA_SANITY_F_ONLY_KNOWN_HASH
207 | RTCRPKCS7SIGNEDDATA_SANITY_F_SIGNING_CERT_PRESENT,
208 RTErrInfoInitStatic(&ErrInfo), "SD");
209 if (RT_SUCCESS(rc))
210 {
211 rc = RTCrSpcIndirectDataContent_CheckSanityEx(pIndData,
212 pThis->pSignedData,
213 RTCRSPCINDIRECTDATACONTENT_SANITY_F_ONLY_KNOWN_HASH,
214 RTErrInfoInitStatic(&ErrInfo));
215 if (RT_FAILURE(rc))
216 RTMsgError("SPC indirect data content sanity check failed for '%s': %Rrc - %s\n",
217 pThis->pszFilename, rc, ErrInfo.szMsg);
218 }
219 else
220 RTMsgError("PKCS#7 sanity check failed for '%s': %Rrc - %s\n", pThis->pszFilename, rc, ErrInfo.szMsg);
221 }
222 else if (!strcmp(pThis->pSignedData->ContentInfo.ContentType.szObjId, RTCR_PKCS7_DATA_OID))
223 { /* apple code signing */ }
224 else if (!fCatalog)
225 RTMsgError("Unexpected the signed content in '%s': %s (expected %s)", pThis->pszFilename,
226 pThis->pSignedData->ContentInfo.ContentType.szObjId, RTCRSPCINDIRECTDATACONTENT_OID);
227 }
228 else
229 rc = RTMsgErrorRc(VERR_CR_PKCS7_NOT_SIGNED_DATA,
230 "PKCS#7 content is inside '%s' is not 'signedData': %s\n",
231 pThis->pszFilename, pThis->ContentInfo.ContentType.szObjId);
232 }
233 else
234 RTMsgError("RTCrPkcs7ContentInfo_DecodeAsn1 failed on '%s': %Rrc - %s\n", pThis->pszFilename, rc, ErrInfo.szMsg);
235 return rc;
236}
237
238
239/**
240 * Reads and decodes PKCS\#7 signature from the given cat file.
241 *
242 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error message
243 * on failure.
244 * @param pThis The structure to initialize.
245 * @param pszFilename The catalog (or any other DER PKCS\#7) filename.
246 * @param cVerbosity The verbosity.
247 */
248static RTEXITCODE SignToolPkcs7_InitFromFile(PSIGNTOOLPKCS7 pThis, const char *pszFilename, unsigned cVerbosity)
249{
250 /*
251 * Init the return structure.
252 */
253 RT_ZERO(*pThis);
254 pThis->pszFilename = pszFilename;
255
256 /*
257 * Lazy bird uses RTFileReadAll and duplicates the allocation.
258 */
259 void *pvFile;
260 int rc = RTFileReadAll(pszFilename, &pvFile, &pThis->cbBuf);
261 if (RT_SUCCESS(rc))
262 {
263 pThis->pbBuf = (uint8_t *)RTMemDup(pvFile, pThis->cbBuf);
264 RTFileReadAllFree(pvFile, pThis->cbBuf);
265 if (pThis->pbBuf)
266 {
267 if (cVerbosity > 2)
268 RTPrintf("PKCS#7 signature: %u bytes\n", pThis->cbBuf);
269
270 /*
271 * Decode it.
272 */
273 rc = SignToolPkcs7_Decode(pThis, true /*fCatalog*/);
274 if (RT_SUCCESS(rc))
275 return RTEXITCODE_SUCCESS;
276 }
277 else
278 RTMsgError("Out of memory!");
279 }
280 else
281 RTMsgError("Error reading '%s' into memory: %Rrc", pszFilename, rc);
282
283 SignToolPkcs7_Delete(pThis);
284 return RTEXITCODE_FAILURE;
285}
286
287
288/**
289 * Encodes the signature into the SIGNTOOLPKCS7::pbNewBuf and
290 * SIGNTOOLPKCS7::cbNewBuf members.
291 *
292 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error message
293 * on failure.
294 * @param pThis The signature to encode.
295 * @param cVerbosity The verbosity.
296 */
297static RTEXITCODE SignToolPkcs7_Encode(PSIGNTOOLPKCS7 pThis, unsigned cVerbosity)
298{
299 RTERRINFOSTATIC StaticErrInfo;
300 PRTASN1CORE pRoot = RTCrPkcs7ContentInfo_GetAsn1Core(&pThis->ContentInfo);
301 uint32_t cbEncoded;
302 int rc = RTAsn1EncodePrepare(pRoot, RTASN1ENCODE_F_DER, &cbEncoded, RTErrInfoInitStatic(&StaticErrInfo));
303 if (RT_SUCCESS(rc))
304 {
305 if (cVerbosity >= 4)
306 RTAsn1Dump(pRoot, 0, 0, RTStrmDumpPrintfV, g_pStdOut);
307
308 RTMemFree(pThis->pbNewBuf);
309 pThis->cbNewBuf = cbEncoded;
310 pThis->pbNewBuf = (uint8_t *)RTMemAllocZ(cbEncoded);
311 if (pThis->pbNewBuf)
312 {
313 rc = RTAsn1EncodeToBuffer(pRoot, RTASN1ENCODE_F_DER, pThis->pbNewBuf, pThis->cbNewBuf,
314 RTErrInfoInitStatic(&StaticErrInfo));
315 if (RT_SUCCESS(rc))
316 {
317 if (cVerbosity > 1)
318 RTMsgInfo("Encoded signature to %u bytes", cbEncoded);
319 return RTEXITCODE_SUCCESS;
320 }
321 RTMsgError("RTAsn1EncodeToBuffer failed: %Rrc", rc);
322
323 RTMemFree(pThis->pbNewBuf);
324 pThis->pbNewBuf = NULL;
325 }
326 else
327 RTMsgError("Failed to allocate %u bytes!", cbEncoded);
328 }
329 else
330 RTMsgError("RTAsn1EncodePrepare failed: %Rrc - %s", rc, StaticErrInfo.szMsg);
331 return RTEXITCODE_FAILURE;
332}
333
334
335/**
336 * Adds the @a pSrc signature as a nested signature.
337 *
338 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error message
339 * on failure.
340 * @param pThis The signature to modify.
341 * @param pSrc The signature to add as nested.
342 * @param cVerbosity The verbosity.
343 * @param fPrepend Whether to prepend (true) or append (false) the
344 * source signature to the nested attribute.
345 */
346static RTEXITCODE SignToolPkcs7_AddNestedSignature(PSIGNTOOLPKCS7 pThis, PSIGNTOOLPKCS7 pSrc,
347 unsigned cVerbosity, bool fPrepend)
348{
349 PRTCRPKCS7SIGNERINFO pSignerInfo = pThis->pSignedData->SignerInfos.papItems[0];
350 int rc;
351
352 /*
353 * Deal with UnauthenticatedAttributes being absent before trying to append to the array.
354 */
355 if (pSignerInfo->UnauthenticatedAttributes.cItems == 0)
356 {
357 /* HACK ALERT! Invent ASN.1 setters/whatever for members to replace this mess. */
358
359 if (pSignerInfo->AuthenticatedAttributes.cItems == 0)
360 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No authenticated or unauthenticated attributes! Sorry, no can do.");
361
362 Assert(pSignerInfo->UnauthenticatedAttributes.SetCore.Asn1Core.uTag == 0);
363 rc = RTAsn1SetCore_Init(&pSignerInfo->UnauthenticatedAttributes.SetCore,
364 pSignerInfo->AuthenticatedAttributes.SetCore.Asn1Core.pOps);
365 if (RT_FAILURE(rc))
366 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTAsn1SetCore_Init failed: %Rrc", rc);
367 pSignerInfo->UnauthenticatedAttributes.SetCore.Asn1Core.uTag = 1;
368 pSignerInfo->UnauthenticatedAttributes.SetCore.Asn1Core.fClass = ASN1_TAGCLASS_CONTEXT | ASN1_TAGFLAG_CONSTRUCTED;
369 RTAsn1MemInitArrayAllocation(&pSignerInfo->UnauthenticatedAttributes.Allocation,
370 pSignerInfo->AuthenticatedAttributes.Allocation.pAllocator,
371 sizeof(**pSignerInfo->UnauthenticatedAttributes.papItems));
372 }
373
374 /*
375 * Find or add an unauthenticated attribute for nested signatures.
376 */
377 rc = VERR_NOT_FOUND;
378 PRTCRPKCS7ATTRIBUTE pAttr = NULL;
379 int32_t iPos = pSignerInfo->UnauthenticatedAttributes.cItems;
380 while (iPos-- > 0)
381 if (pSignerInfo->UnauthenticatedAttributes.papItems[iPos]->enmType == RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE)
382 {
383 pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[iPos];
384 rc = VINF_SUCCESS;
385 break;
386 }
387 if (iPos < 0)
388 {
389 iPos = RTCrPkcs7Attributes_Append(&pSignerInfo->UnauthenticatedAttributes);
390 if (iPos >= 0)
391 {
392 if (cVerbosity >= 3)
393 RTMsgInfo("Adding UnauthenticatedAttribute #%u...", iPos);
394 Assert((uint32_t)iPos < pSignerInfo->UnauthenticatedAttributes.cItems);
395
396 pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[iPos];
397 rc = RTAsn1ObjId_InitFromString(&pAttr->Type, RTCR_PKCS9_ID_MS_NESTED_SIGNATURE, pAttr->Allocation.pAllocator);
398 if (RT_SUCCESS(rc))
399 {
400 /** @todo Generalize the Type + enmType DYN stuff and generate setters. */
401 Assert(pAttr->enmType == RTCRPKCS7ATTRIBUTETYPE_NOT_PRESENT);
402 Assert(pAttr->uValues.pContentInfos == NULL);
403 pAttr->enmType = RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE;
404 rc = RTAsn1MemAllocZ(&pAttr->Allocation, (void **)&pAttr->uValues.pContentInfos,
405 sizeof(*pAttr->uValues.pContentInfos));
406 if (RT_SUCCESS(rc))
407 {
408 rc = RTCrPkcs7SetOfContentInfos_Init(pAttr->uValues.pContentInfos, pAttr->Allocation.pAllocator);
409 if (!RT_SUCCESS(rc))
410 RTMsgError("RTCrPkcs7ContentInfos_Init failed: %Rrc", rc);
411 }
412 else
413 RTMsgError("RTAsn1MemAllocZ failed: %Rrc", rc);
414 }
415 else
416 RTMsgError("RTAsn1ObjId_InitFromString failed: %Rrc", rc);
417 }
418 else
419 RTMsgError("RTCrPkcs7Attributes_Append failed: %Rrc", iPos);
420 }
421 else if (cVerbosity >= 2)
422 RTMsgInfo("Found UnauthenticatedAttribute #%u...", iPos);
423 if (RT_SUCCESS(rc))
424 {
425 /*
426 * Append/prepend the signature.
427 */
428 uint32_t iActualPos = UINT32_MAX;
429 iPos = fPrepend ? 0 : pAttr->uValues.pContentInfos->cItems;
430 rc = RTCrPkcs7SetOfContentInfos_InsertEx(pAttr->uValues.pContentInfos, iPos, &pSrc->ContentInfo,
431 pAttr->Allocation.pAllocator, &iActualPos);
432 if (RT_SUCCESS(rc))
433 {
434 if (cVerbosity > 0)
435 RTMsgInfo("Added nested signature (#%u)", iActualPos);
436 if (cVerbosity >= 3)
437 {
438 RTMsgInfo("SingerInfo dump after change:");
439 RTAsn1Dump(RTCrPkcs7SignerInfo_GetAsn1Core(pSignerInfo), 0, 2, RTStrmDumpPrintfV, g_pStdOut);
440 }
441 return RTEXITCODE_SUCCESS;
442 }
443
444 RTMsgError("RTCrPkcs7ContentInfos_InsertEx failed: %Rrc", rc);
445 }
446 return RTEXITCODE_FAILURE;
447}
448
449
450/**
451 * Writes the signature to the file.
452 *
453 * Caller must have called SignToolPkcs7_Encode() prior to this function.
454 *
455 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error
456 * message on failure.
457 * @param pThis The file which to write.
458 * @param cVerbosity The verbosity.
459 */
460static RTEXITCODE SignToolPkcs7_WriteSignatureToFile(PSIGNTOOLPKCS7 pThis, const char *pszFilename, unsigned cVerbosity)
461{
462 AssertReturn(pThis->cbNewBuf && pThis->pbNewBuf, RTEXITCODE_FAILURE);
463
464 /*
465 * Open+truncate file, write new signature, close. Simple.
466 */
467 RTFILE hFile;
468 int rc = RTFileOpen(&hFile, pszFilename, RTFILE_O_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_TRUNCATE | RTFILE_O_DENY_WRITE);
469 if (RT_SUCCESS(rc))
470 {
471 rc = RTFileWrite(hFile, pThis->pbNewBuf, pThis->cbNewBuf, NULL);
472 if (RT_SUCCESS(rc))
473 {
474 rc = RTFileClose(hFile);
475 if (RT_SUCCESS(rc))
476 {
477 if (cVerbosity > 0)
478 RTMsgInfo("Wrote %u bytes to %s", pThis->cbNewBuf, pszFilename);
479 return RTEXITCODE_SUCCESS;
480 }
481
482 RTMsgError("RTFileClose failed on %s: %Rrc", pszFilename, rc);
483 }
484 else
485 RTMsgError("Write error on %s: %Rrc", pszFilename, rc);
486 }
487 else
488 RTMsgError("Failed to open %s for writing: %Rrc", pszFilename, rc);
489 return RTEXITCODE_FAILURE;
490}
491
492
493
494/**
495 * Worker for recursively searching for MS nested signatures and signer infos.
496 *
497 * @returns Pointer to the signer info corresponding to @a iSignature. NULL if
498 * not found.
499 * @param pSignedData The signature to search.
500 * @param piNextSignature Pointer to the variable keeping track of the next
501 * signature number.
502 * @param iReqSignature The request signature number.
503 * @param ppSignedData Where to return the signature data structure.
504 */
505static PRTCRPKCS7SIGNERINFO SignToolPkcs7_FindNestedSignatureByIndexWorker(PRTCRPKCS7SIGNEDDATA pSignedData,
506 uint32_t *piNextSignature,
507 uint32_t iReqSignature,
508 PRTCRPKCS7SIGNEDDATA *ppSignedData)
509{
510 for (uint32_t iSignerInfo = 0; iSignerInfo < pSignedData->SignerInfos.cItems; iSignerInfo++)
511 {
512 /* Match?*/
513 PRTCRPKCS7SIGNERINFO pSignerInfo = pSignedData->SignerInfos.papItems[iSignerInfo];
514 if (*piNextSignature == iReqSignature)
515 {
516 *ppSignedData = pSignedData;
517 return pSignerInfo;
518 }
519 *piNextSignature += 1;
520
521 /* Look for nested signatures. */
522 for (uint32_t iAttrib = 0; iAttrib < pSignerInfo->UnauthenticatedAttributes.cItems; iAttrib++)
523 if (pSignerInfo->UnauthenticatedAttributes.papItems[iAttrib]->enmType == RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE)
524 {
525 PRTCRPKCS7SETOFCONTENTINFOS pCntInfos;
526 pCntInfos = pSignerInfo->UnauthenticatedAttributes.papItems[iAttrib]->uValues.pContentInfos;
527 for (uint32_t iCntInfo = 0; iCntInfo < pCntInfos->cItems; iCntInfo++)
528 {
529 PRTCRPKCS7CONTENTINFO pCntInfo = pCntInfos->papItems[iCntInfo];
530 if (RTCrPkcs7ContentInfo_IsSignedData(pCntInfo))
531 {
532 PRTCRPKCS7SIGNERINFO pRet;
533 pRet = SignToolPkcs7_FindNestedSignatureByIndexWorker(pCntInfo->u.pSignedData, piNextSignature,
534 iReqSignature, ppSignedData);
535 if (pRet)
536 return pRet;
537 }
538 }
539 }
540 }
541 return NULL;
542}
543
544
545/**
546 * Locates the given nested signature.
547 *
548 * @returns Pointer to the signer info corresponding to @a iSignature. NULL if
549 * not found.
550 * @param pThis The PKCS\#7 structure to search.
551 * @param iReqSignature The requested signature number.
552 * @param ppSignedData Where to return the pointer to the signed data that
553 * the returned signer info belongs to.
554 *
555 * @todo Move into SPC or PKCS\#7.
556 */
557static PRTCRPKCS7SIGNERINFO SignToolPkcs7_FindNestedSignatureByIndex(PSIGNTOOLPKCS7 pThis, uint32_t iReqSignature,
558 PRTCRPKCS7SIGNEDDATA *ppSignedData)
559{
560 uint32_t iNextSignature = 0;
561 return SignToolPkcs7_FindNestedSignatureByIndexWorker(pThis->pSignedData, &iNextSignature, iReqSignature, ppSignedData);
562}
563
564
565
566/**
567 * Reads and decodes PKCS\#7 signature from the given executable.
568 *
569 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error message
570 * on failure.
571 * @param pThis The structure to initialize.
572 * @param pszFilename The executable filename.
573 * @param cVerbosity The verbosity.
574 * @param enmLdrArch For FAT binaries.
575 */
576static RTEXITCODE SignToolPkcs7Exe_InitFromFile(PSIGNTOOLPKCS7EXE pThis, const char *pszFilename,
577 unsigned cVerbosity, RTLDRARCH enmLdrArch = RTLDRARCH_WHATEVER)
578{
579 /*
580 * Init the return structure.
581 */
582 RT_ZERO(*pThis);
583 pThis->hLdrMod = NIL_RTLDRMOD;
584 pThis->pszFilename = pszFilename;
585
586 /*
587 * Open the image and check if it's signed.
588 */
589 int rc = RTLdrOpen(pszFilename, RTLDR_O_FOR_VALIDATION, enmLdrArch, &pThis->hLdrMod);
590 if (RT_SUCCESS(rc))
591 {
592 bool fIsSigned = false;
593 rc = RTLdrQueryProp(pThis->hLdrMod, RTLDRPROP_IS_SIGNED, &fIsSigned, sizeof(fIsSigned));
594 if (RT_SUCCESS(rc) && fIsSigned)
595 {
596 /*
597 * Query the PKCS#7 data (assuming M$ style signing) and hand it to a worker.
598 */
599 size_t cbActual = 0;
600#ifdef DEBUG
601 size_t cbBuf = 64;
602#else
603 size_t cbBuf = _512K;
604#endif
605 void *pvBuf = RTMemAllocZ(cbBuf);
606 if (pvBuf)
607 {
608 rc = RTLdrQueryPropEx(pThis->hLdrMod, RTLDRPROP_PKCS7_SIGNED_DATA, NULL /*pvBits*/, pvBuf, cbBuf, &cbActual);
609 if (rc == VERR_BUFFER_OVERFLOW)
610 {
611 RTMemFree(pvBuf);
612 cbBuf = cbActual;
613 pvBuf = RTMemAllocZ(cbActual);
614 if (pvBuf)
615 rc = RTLdrQueryPropEx(pThis->hLdrMod, RTLDRPROP_PKCS7_SIGNED_DATA, NULL /*pvBits*/,
616 pvBuf, cbBuf, &cbActual);
617 else
618 rc = VERR_NO_MEMORY;
619 }
620 }
621 else
622 rc = VERR_NO_MEMORY;
623
624 pThis->pbBuf = (uint8_t *)pvBuf;
625 pThis->cbBuf = cbActual;
626 if (RT_SUCCESS(rc))
627 {
628 if (cVerbosity > 2)
629 RTPrintf("PKCS#7 signature: %u bytes\n", cbActual);
630 if (cVerbosity > 3)
631 RTPrintf("%.*Rhxd\n", cbActual, pvBuf);
632
633 /*
634 * Decode it.
635 */
636 rc = SignToolPkcs7_Decode(pThis, false /*fCatalog*/);
637 if (RT_SUCCESS(rc))
638 return RTEXITCODE_SUCCESS;
639 }
640 else
641 RTMsgError("RTLdrQueryPropEx/RTLDRPROP_PKCS7_SIGNED_DATA failed on '%s': %Rrc\n", pszFilename, rc);
642 }
643 else if (RT_SUCCESS(rc))
644 RTMsgInfo("'%s': not signed\n", pszFilename);
645 else
646 RTMsgError("RTLdrQueryProp/RTLDRPROP_IS_SIGNED failed on '%s': %Rrc\n", pszFilename, rc);
647 }
648 else
649 RTMsgError("Error opening executable image '%s': %Rrc", pszFilename, rc);
650
651 SignToolPkcs7Exe_Delete(pThis);
652 return RTEXITCODE_FAILURE;
653}
654
655
656/**
657 * Calculates the checksum of an executable.
658 *
659 * @returns Success indicator (errors are reported)
660 * @param pThis The exe file to checksum.
661 * @param hFile The file handle.
662 * @param puCheckSum Where to return the checksum.
663 */
664static bool SignToolPkcs7Exe_CalcPeCheckSum(PSIGNTOOLPKCS7EXE pThis, RTFILE hFile, uint32_t *puCheckSum)
665{
666#ifdef RT_OS_WINDOWS
667 /*
668 * Try use IMAGEHLP!MapFileAndCheckSumW first.
669 */
670 PRTUTF16 pwszPath;
671 int rc = RTStrToUtf16(pThis->pszFilename, &pwszPath);
672 if (RT_SUCCESS(rc))
673 {
674 decltype(MapFileAndCheckSumW) *pfnMapFileAndCheckSumW;
675 pfnMapFileAndCheckSumW = (decltype(MapFileAndCheckSumW) *)RTLdrGetSystemSymbol("IMAGEHLP.DLL", "MapFileAndCheckSumW");
676 if (pfnMapFileAndCheckSumW)
677 {
678 DWORD uHeaderSum = UINT32_MAX;
679 DWORD uCheckSum = UINT32_MAX;
680 DWORD dwRc = pfnMapFileAndCheckSumW(pwszPath, &uHeaderSum, &uCheckSum);
681 if (dwRc == CHECKSUM_SUCCESS)
682 {
683 *puCheckSum = uCheckSum;
684 return true;
685 }
686 }
687 }
688#endif
689
690 RT_NOREF(pThis, hFile, puCheckSum);
691 RTMsgError("Implement check sum calcuation fallback!");
692 return false;
693}
694
695
696/**
697 * Writes the signature to the file.
698 *
699 * This has the side-effect of closing the hLdrMod member. So, it can only be
700 * called once!
701 *
702 * Caller must have called SignToolPkcs7_Encode() prior to this function.
703 *
704 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error
705 * message on failure.
706 * @param pThis The file which to write.
707 * @param cVerbosity The verbosity.
708 */
709static RTEXITCODE SignToolPkcs7Exe_WriteSignatureToFile(PSIGNTOOLPKCS7EXE pThis, unsigned cVerbosity)
710{
711 AssertReturn(pThis->cbNewBuf && pThis->pbNewBuf, RTEXITCODE_FAILURE);
712
713 /*
714 * Get the file header offset and arch before closing the destination handle.
715 */
716 uint32_t offNtHdrs;
717 int rc = RTLdrQueryProp(pThis->hLdrMod, RTLDRPROP_FILE_OFF_HEADER, &offNtHdrs, sizeof(offNtHdrs));
718 if (RT_SUCCESS(rc))
719 {
720 RTLDRARCH enmLdrArch = RTLdrGetArch(pThis->hLdrMod);
721 if (enmLdrArch != RTLDRARCH_INVALID)
722 {
723 RTLdrClose(pThis->hLdrMod);
724 pThis->hLdrMod = NIL_RTLDRMOD;
725 unsigned cbNtHdrs = 0;
726 switch (enmLdrArch)
727 {
728 case RTLDRARCH_AMD64:
729 cbNtHdrs = sizeof(IMAGE_NT_HEADERS64);
730 break;
731 case RTLDRARCH_X86_32:
732 cbNtHdrs = sizeof(IMAGE_NT_HEADERS32);
733 break;
734 default:
735 RTMsgError("Unknown image arch: %d", enmLdrArch);
736 }
737 if (cbNtHdrs > 0)
738 {
739 if (cVerbosity > 0)
740 RTMsgInfo("offNtHdrs=%#x cbNtHdrs=%u\n", offNtHdrs, cbNtHdrs);
741
742 /*
743 * Open the executable file for writing.
744 */
745 RTFILE hFile;
746 rc = RTFileOpen(&hFile, pThis->pszFilename, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
747 if (RT_SUCCESS(rc))
748 {
749 /* Read the file header and locate the security directory entry. */
750 union
751 {
752 IMAGE_NT_HEADERS32 NtHdrs32;
753 IMAGE_NT_HEADERS64 NtHdrs64;
754 } uBuf;
755 PIMAGE_DATA_DIRECTORY pSecDir = cbNtHdrs == sizeof(IMAGE_NT_HEADERS64)
756 ? &uBuf.NtHdrs64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]
757 : &uBuf.NtHdrs32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
758
759 rc = RTFileReadAt(hFile, offNtHdrs, &uBuf, cbNtHdrs, NULL);
760 if ( RT_SUCCESS(rc)
761 && uBuf.NtHdrs32.Signature == IMAGE_NT_SIGNATURE)
762 {
763 /*
764 * Drop any old signature by truncating the file.
765 */
766 if ( pSecDir->Size > 8
767 && pSecDir->VirtualAddress > offNtHdrs + sizeof(IMAGE_NT_HEADERS32))
768 {
769 rc = RTFileSetSize(hFile, pSecDir->VirtualAddress);
770 if (RT_FAILURE(rc))
771 RTMsgError("Error truncating file to %#x bytes: %Rrc", pSecDir->VirtualAddress, rc);
772 }
773 else
774 rc = RTMsgErrorRc(VERR_BAD_EXE_FORMAT, "Bad security directory entry: VA=%#x Size=%#x",
775 pSecDir->VirtualAddress, pSecDir->Size);
776 if (RT_SUCCESS(rc))
777 {
778 /*
779 * Sector align the signature portion.
780 */
781 uint32_t const cbWinCert = RT_UOFFSETOF(WIN_CERTIFICATE, bCertificate);
782 uint64_t offCur = 0;
783 rc = RTFileQuerySize(hFile, &offCur);
784 if ( RT_SUCCESS(rc)
785 && offCur < _2G)
786 {
787 if (offCur & 0x1ff)
788 {
789 uint32_t cbNeeded = 0x200 - ((uint32_t)offCur & 0x1ff);
790 rc = RTFileWriteAt(hFile, offCur, g_abRTZero4K, cbNeeded, NULL);
791 if (RT_SUCCESS(rc))
792 offCur += cbNeeded;
793 }
794 if (RT_SUCCESS(rc))
795 {
796 /*
797 * Write the header followed by the signature data.
798 */
799 uint32_t const cbZeroPad = (uint32_t)(RT_ALIGN_Z(pThis->cbNewBuf, 8) - pThis->cbNewBuf);
800 pSecDir->VirtualAddress = (uint32_t)offCur;
801 pSecDir->Size = cbWinCert + (uint32_t)pThis->cbNewBuf + cbZeroPad;
802 if (cVerbosity >= 2)
803 RTMsgInfo("Writing %u (%#x) bytes of signature at %#x (%u).\n",
804 pSecDir->Size, pSecDir->Size, pSecDir->VirtualAddress, pSecDir->VirtualAddress);
805
806 WIN_CERTIFICATE WinCert;
807 WinCert.dwLength = pSecDir->Size;
808 WinCert.wRevision = WIN_CERT_REVISION_2_0;
809 WinCert.wCertificateType = WIN_CERT_TYPE_PKCS_SIGNED_DATA;
810
811 rc = RTFileWriteAt(hFile, offCur, &WinCert, cbWinCert, NULL);
812 if (RT_SUCCESS(rc))
813 {
814 offCur += cbWinCert;
815 rc = RTFileWriteAt(hFile, offCur, pThis->pbNewBuf, pThis->cbNewBuf, NULL);
816 }
817 if (RT_SUCCESS(rc) && cbZeroPad)
818 {
819 offCur += pThis->cbNewBuf;
820 rc = RTFileWriteAt(hFile, offCur, g_abRTZero4K, cbZeroPad, NULL);
821 }
822 if (RT_SUCCESS(rc))
823 {
824 /*
825 * Reset the checksum (sec dir updated already) and rewrite the header.
826 */
827 uBuf.NtHdrs32.OptionalHeader.CheckSum = 0;
828 offCur = offNtHdrs;
829 rc = RTFileWriteAt(hFile, offNtHdrs, &uBuf, cbNtHdrs, NULL);
830 if (RT_SUCCESS(rc))
831 rc = RTFileFlush(hFile);
832 if (RT_SUCCESS(rc))
833 {
834 /*
835 * Calc checksum and write out the header again.
836 */
837 uint32_t uCheckSum = UINT32_MAX;
838 if (SignToolPkcs7Exe_CalcPeCheckSum(pThis, hFile, &uCheckSum))
839 {
840 uBuf.NtHdrs32.OptionalHeader.CheckSum = uCheckSum;
841 rc = RTFileWriteAt(hFile, offNtHdrs, &uBuf, cbNtHdrs, NULL);
842 if (RT_SUCCESS(rc))
843 rc = RTFileFlush(hFile);
844 if (RT_SUCCESS(rc))
845 {
846 rc = RTFileClose(hFile);
847 if (RT_SUCCESS(rc))
848 return RTEXITCODE_SUCCESS;
849 RTMsgError("RTFileClose failed: %Rrc\n", rc);
850 return RTEXITCODE_FAILURE;
851 }
852 }
853 }
854 }
855 }
856 if (RT_FAILURE(rc))
857 RTMsgError("Write error at %#RX64: %Rrc", offCur, rc);
858 }
859 else if (RT_SUCCESS(rc))
860 RTMsgError("File to big: %'RU64 bytes", offCur);
861 else
862 RTMsgError("RTFileQuerySize failed: %Rrc", rc);
863 }
864 }
865 else if (RT_SUCCESS(rc))
866 RTMsgError("Not NT executable header!");
867 else
868 RTMsgError("Error reading NT headers (%#x bytes) at %#x: %Rrc", cbNtHdrs, offNtHdrs, rc);
869 RTFileClose(hFile);
870 }
871 else
872 RTMsgError("Failed to open '%s' for writing: %Rrc", pThis->pszFilename, rc);
873 }
874 }
875 else
876 RTMsgError("RTLdrGetArch failed!");
877 }
878 else
879 RTMsgError("RTLdrQueryProp/RTLDRPROP_FILE_OFF_HEADER failed: %Rrc", rc);
880 return RTEXITCODE_FAILURE;
881}
882
883
884
885/*
886 * The 'extract-exe-signer-cert' command.
887 */
888static RTEXITCODE HelpExtractExeSignerCert(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
889{
890 RT_NOREF_PV(enmLevel);
891 RTStrmPrintf(pStrm, "extract-exe-signer-cert [--ber|--cer|--der] [--signature-index|-i <num>] [--exe|-e] <exe> [--output|-o] <outfile.cer>\n");
892 return RTEXITCODE_SUCCESS;
893}
894
895static RTEXITCODE HandleExtractExeSignerCert(int cArgs, char **papszArgs)
896{
897 /*
898 * Parse arguments.
899 */
900 static const RTGETOPTDEF s_aOptions[] =
901 {
902 { "--ber", 'b', RTGETOPT_REQ_NOTHING },
903 { "--cer", 'c', RTGETOPT_REQ_NOTHING },
904 { "--der", 'd', RTGETOPT_REQ_NOTHING },
905 { "--exe", 'e', RTGETOPT_REQ_STRING },
906 { "--output", 'o', RTGETOPT_REQ_STRING },
907 { "--signature-index", 'i', RTGETOPT_REQ_UINT32 },
908 };
909
910 const char *pszExe = NULL;
911 const char *pszOut = NULL;
912 RTLDRARCH enmLdrArch = RTLDRARCH_WHATEVER;
913 unsigned cVerbosity = 0;
914 uint32_t fCursorFlags = RTASN1CURSOR_FLAGS_DER;
915 uint32_t iSignature = 0;
916
917 RTGETOPTSTATE GetState;
918 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
919 AssertRCReturn(rc, RTEXITCODE_FAILURE);
920 RTGETOPTUNION ValueUnion;
921 int ch;
922 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
923 {
924 switch (ch)
925 {
926 case 'e': pszExe = ValueUnion.psz; break;
927 case 'o': pszOut = ValueUnion.psz; break;
928 case 'b': fCursorFlags = 0; break;
929 case 'c': fCursorFlags = RTASN1CURSOR_FLAGS_CER; break;
930 case 'd': fCursorFlags = RTASN1CURSOR_FLAGS_DER; break;
931 case 'i': iSignature = ValueUnion.u32; break;
932 case 'V': return HandleVersion(cArgs, papszArgs);
933 case 'h': return HelpExtractExeSignerCert(g_pStdOut, RTSIGNTOOLHELP_FULL);
934
935 case VINF_GETOPT_NOT_OPTION:
936 if (!pszExe)
937 pszExe = ValueUnion.psz;
938 else if (!pszOut)
939 pszOut = ValueUnion.psz;
940 else
941 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many file arguments: %s", ValueUnion.psz);
942 break;
943
944 default:
945 return RTGetOptPrintError(ch, &ValueUnion);
946 }
947 }
948 if (!pszExe)
949 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
950 if (!pszOut)
951 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No output file given.");
952 if (RTPathExists(pszOut))
953 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The output file '%s' exists.", pszOut);
954
955 /*
956 * Do it.
957 */
958 /* Read & decode the PKCS#7 signature. */
959 SIGNTOOLPKCS7EXE This;
960 RTEXITCODE rcExit = SignToolPkcs7Exe_InitFromFile(&This, pszExe, cVerbosity, enmLdrArch);
961 if (rcExit == RTEXITCODE_SUCCESS)
962 {
963 /* Find the signing certificate (ASSUMING that the certificate used is shipped in the set of certificates). */
964 PRTCRPKCS7SIGNEDDATA pSignedData;
965 PCRTCRPKCS7SIGNERINFO pSignerInfo = SignToolPkcs7_FindNestedSignatureByIndex(&This, iSignature, &pSignedData);
966 rcExit = RTEXITCODE_FAILURE;
967 if (pSignerInfo)
968 {
969 PCRTCRPKCS7ISSUERANDSERIALNUMBER pISN = &pSignedData->SignerInfos.papItems[0]->IssuerAndSerialNumber;
970 PCRTCRX509CERTIFICATE pCert;
971 pCert = RTCrPkcs7SetOfCerts_FindX509ByIssuerAndSerialNumber(&pSignedData->Certificates,
972 &pISN->Name, &pISN->SerialNumber);
973 if (pCert)
974 {
975 /*
976 * Write it out.
977 */
978 RTFILE hFile;
979 rc = RTFileOpen(&hFile, pszOut, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE);
980 if (RT_SUCCESS(rc))
981 {
982 uint32_t cbCert = pCert->SeqCore.Asn1Core.cbHdr + pCert->SeqCore.Asn1Core.cb;
983 rc = RTFileWrite(hFile, pCert->SeqCore.Asn1Core.uData.pu8 - pCert->SeqCore.Asn1Core.cbHdr,
984 cbCert, NULL);
985 if (RT_SUCCESS(rc))
986 {
987 rc = RTFileClose(hFile);
988 if (RT_SUCCESS(rc))
989 {
990 hFile = NIL_RTFILE;
991 rcExit = RTEXITCODE_SUCCESS;
992 RTMsgInfo("Successfully wrote %u bytes to '%s'", cbCert, pszOut);
993 }
994 else
995 RTMsgError("RTFileClose failed: %Rrc", rc);
996 }
997 else
998 RTMsgError("RTFileWrite failed: %Rrc", rc);
999 RTFileClose(hFile);
1000 }
1001 else
1002 RTMsgError("Error opening '%s' for writing: %Rrc", pszOut, rc);
1003 }
1004 else
1005 RTMsgError("Certificate not found.");
1006 }
1007 else
1008 RTMsgError("Could not locate signature #%u!", iSignature);
1009
1010 /* Delete the signature data. */
1011 SignToolPkcs7Exe_Delete(&This);
1012 }
1013 return rcExit;
1014}
1015
1016
1017/*
1018 * The 'add-nested-exe-signature' command.
1019 */
1020static RTEXITCODE HelpAddNestedExeSignature(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
1021{
1022 RT_NOREF_PV(enmLevel);
1023 RTStrmPrintf(pStrm, "add-nested-exe-signature [-v|--verbose] [-d|--debug] [-p|--prepend] <destination-exe> <source-exe>\n");
1024 if (enmLevel == RTSIGNTOOLHELP_FULL)
1025 RTStrmPrintf(pStrm,
1026 "\n"
1027 "The --debug option allows the source-exe to be omitted in order to test the\n"
1028 "encoding and PE file modification.\n"
1029 "\n"
1030 "The --prepend option puts the nested signature first rather than appending it\n"
1031 "to the end of of the nested signature set. Windows reads nested signatures in\n"
1032 "reverse order, so --prepend will logically putting it last.\n"
1033 );
1034 return RTEXITCODE_SUCCESS;
1035}
1036
1037
1038static RTEXITCODE HandleAddNestedExeSignature(int cArgs, char **papszArgs)
1039{
1040 /*
1041 * Parse arguments.
1042 */
1043 static const RTGETOPTDEF s_aOptions[] =
1044 {
1045 { "--prepend", 'p', RTGETOPT_REQ_NOTHING },
1046 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
1047 { "--debug", 'd', RTGETOPT_REQ_NOTHING },
1048 };
1049
1050 const char *pszDst = NULL;
1051 const char *pszSrc = NULL;
1052 unsigned cVerbosity = 0;
1053 bool fDebug = false;
1054 bool fPrepend = false;
1055
1056 RTGETOPTSTATE GetState;
1057 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1058 AssertRCReturn(rc, RTEXITCODE_FAILURE);
1059 RTGETOPTUNION ValueUnion;
1060 int ch;
1061 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1062 {
1063 switch (ch)
1064 {
1065 case 'v': cVerbosity++; break;
1066 case 'd': fDebug = pszSrc == NULL; break;
1067 case 'p': fPrepend = true; break;
1068 case 'V': return HandleVersion(cArgs, papszArgs);
1069 case 'h': return HelpAddNestedExeSignature(g_pStdOut, RTSIGNTOOLHELP_FULL);
1070
1071 case VINF_GETOPT_NOT_OPTION:
1072 if (!pszDst)
1073 pszDst = ValueUnion.psz;
1074 else if (!pszSrc)
1075 {
1076 pszSrc = ValueUnion.psz;
1077 fDebug = false;
1078 }
1079 else
1080 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many file arguments: %s", ValueUnion.psz);
1081 break;
1082
1083 default:
1084 return RTGetOptPrintError(ch, &ValueUnion);
1085 }
1086 }
1087 if (!pszDst)
1088 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No destination executable given.");
1089 if (!pszSrc && !fDebug)
1090 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No source executable file given.");
1091
1092 /*
1093 * Do it.
1094 */
1095 /* Read & decode the source PKCS#7 signature. */
1096 SIGNTOOLPKCS7EXE Src;
1097 RTEXITCODE rcExit = pszSrc ? SignToolPkcs7Exe_InitFromFile(&Src, pszSrc, cVerbosity) : RTEXITCODE_SUCCESS;
1098 if (rcExit == RTEXITCODE_SUCCESS)
1099 {
1100 /* Ditto for the destination PKCS#7 signature. */
1101 SIGNTOOLPKCS7EXE Dst;
1102 rcExit = SignToolPkcs7Exe_InitFromFile(&Dst, pszDst, cVerbosity);
1103 if (rcExit == RTEXITCODE_SUCCESS)
1104 {
1105 /* Do the signature manipulation. */
1106 if (pszSrc)
1107 rcExit = SignToolPkcs7_AddNestedSignature(&Dst, &Src, cVerbosity, fPrepend);
1108 if (rcExit == RTEXITCODE_SUCCESS)
1109 rcExit = SignToolPkcs7_Encode(&Dst, cVerbosity);
1110
1111 /* Update the destination executable file. */
1112 if (rcExit == RTEXITCODE_SUCCESS)
1113 rcExit = SignToolPkcs7Exe_WriteSignatureToFile(&Dst, cVerbosity);
1114
1115 SignToolPkcs7Exe_Delete(&Dst);
1116 }
1117 if (pszSrc)
1118 SignToolPkcs7Exe_Delete(&Src);
1119 }
1120
1121 return rcExit;
1122}
1123
1124
1125/*
1126 * The 'add-nested-cat-signature' command.
1127 */
1128static RTEXITCODE HelpAddNestedCatSignature(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
1129{
1130 RT_NOREF_PV(enmLevel);
1131 RTStrmPrintf(pStrm, "add-nested-cat-signature [-v|--verbose] [-d|--debug] [-p|--prepend] <destination-cat> <source-cat>\n");
1132 if (enmLevel == RTSIGNTOOLHELP_FULL)
1133 RTStrmPrintf(pStrm,
1134 "\n"
1135 "The --debug option allows the source-cat to be omitted in order to test the\n"
1136 "ASN.1 re-encoding of the destination catalog file.\n"
1137 "\n"
1138 "The --prepend option puts the nested signature first rather than appending it\n"
1139 "to the end of of the nested signature set. Windows reads nested signatures in\n"
1140 "reverse order, so --prepend will logically putting it last.\n"
1141 );
1142 return RTEXITCODE_SUCCESS;
1143}
1144
1145
1146static RTEXITCODE HandleAddNestedCatSignature(int cArgs, char **papszArgs)
1147{
1148 /*
1149 * Parse arguments.
1150 */
1151 static const RTGETOPTDEF s_aOptions[] =
1152 {
1153 { "--prepend", 'p', RTGETOPT_REQ_NOTHING },
1154 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
1155 { "--debug", 'd', RTGETOPT_REQ_NOTHING },
1156 };
1157
1158 const char *pszDst = NULL;
1159 const char *pszSrc = NULL;
1160 unsigned cVerbosity = 0;
1161 bool fDebug = false;
1162 bool fPrepend = false;
1163
1164 RTGETOPTSTATE GetState;
1165 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1166 AssertRCReturn(rc, RTEXITCODE_FAILURE);
1167 RTGETOPTUNION ValueUnion;
1168 int ch;
1169 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1170 {
1171 switch (ch)
1172 {
1173 case 'v': cVerbosity++; break;
1174 case 'd': fDebug = pszSrc == NULL; break;
1175 case 'p': fPrepend = true; break;
1176 case 'V': return HandleVersion(cArgs, papszArgs);
1177 case 'h': return HelpAddNestedCatSignature(g_pStdOut, RTSIGNTOOLHELP_FULL);
1178
1179 case VINF_GETOPT_NOT_OPTION:
1180 if (!pszDst)
1181 pszDst = ValueUnion.psz;
1182 else if (!pszSrc)
1183 {
1184 pszSrc = ValueUnion.psz;
1185 fDebug = false;
1186 }
1187 else
1188 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many file arguments: %s", ValueUnion.psz);
1189 break;
1190
1191 default:
1192 return RTGetOptPrintError(ch, &ValueUnion);
1193 }
1194 }
1195 if (!pszDst)
1196 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No destination catalog file given.");
1197 if (!pszSrc && !fDebug)
1198 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No source catalog file given.");
1199
1200 /*
1201 * Do it.
1202 */
1203 /* Read & decode the source PKCS#7 signature. */
1204 SIGNTOOLPKCS7 Src;
1205 RTEXITCODE rcExit = pszSrc ? SignToolPkcs7_InitFromFile(&Src, pszSrc, cVerbosity) : RTEXITCODE_SUCCESS;
1206 if (rcExit == RTEXITCODE_SUCCESS)
1207 {
1208 /* Ditto for the destination PKCS#7 signature. */
1209 SIGNTOOLPKCS7EXE Dst;
1210 rcExit = SignToolPkcs7_InitFromFile(&Dst, pszDst, cVerbosity);
1211 if (rcExit == RTEXITCODE_SUCCESS)
1212 {
1213 /* Do the signature manipulation. */
1214 if (pszSrc)
1215 rcExit = SignToolPkcs7_AddNestedSignature(&Dst, &Src, cVerbosity, fPrepend);
1216 if (rcExit == RTEXITCODE_SUCCESS)
1217 rcExit = SignToolPkcs7_Encode(&Dst, cVerbosity);
1218
1219 /* Update the destination executable file. */
1220 if (rcExit == RTEXITCODE_SUCCESS)
1221 rcExit = SignToolPkcs7_WriteSignatureToFile(&Dst, pszDst, cVerbosity);
1222
1223 SignToolPkcs7_Delete(&Dst);
1224 }
1225 if (pszSrc)
1226 SignToolPkcs7_Delete(&Src);
1227 }
1228
1229 return rcExit;
1230}
1231
1232#ifndef IPRT_IN_BUILD_TOOL
1233
1234/*
1235 * The 'verify-exe' command.
1236 */
1237static RTEXITCODE HelpVerifyExe(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
1238{
1239 RT_NOREF_PV(enmLevel);
1240 RTStrmPrintf(pStrm,
1241 "verify-exe [--verbose|--quiet] [--kernel] [--root <root-cert.der>] [--additional <supp-cert.der>]\n"
1242 " [--type <win|osx>] <exe1> [exe2 [..]]\n");
1243 return RTEXITCODE_SUCCESS;
1244}
1245
1246typedef struct VERIFYEXESTATE
1247{
1248 RTCRSTORE hRootStore;
1249 RTCRSTORE hKernelRootStore;
1250 RTCRSTORE hAdditionalStore;
1251 bool fKernel;
1252 int cVerbose;
1253 enum { kSignType_Windows, kSignType_OSX } enmSignType;
1254 RTLDRARCH enmLdrArch;
1255 uint32_t cBad;
1256 uint32_t cOkay;
1257 const char *pszFilename;
1258} VERIFYEXESTATE;
1259
1260# ifdef VBOX
1261/** Certificate store load set.
1262 * Declared outside HandleVerifyExe because of braindead gcc visibility crap. */
1263struct STSTORESET
1264{
1265 RTCRSTORE hStore;
1266 PCSUPTAENTRY paTAs;
1267 unsigned cTAs;
1268};
1269# endif
1270
1271/**
1272 * @callback_method_impl{FNRTCRPKCS7VERIFYCERTCALLBACK,
1273 * Standard code signing. Use this for Microsoft SPC.}
1274 */
1275static DECLCALLBACK(int) VerifyExecCertVerifyCallback(PCRTCRX509CERTIFICATE pCert, RTCRX509CERTPATHS hCertPaths, uint32_t fFlags,
1276 void *pvUser, PRTERRINFO pErrInfo)
1277{
1278 VERIFYEXESTATE *pState = (VERIFYEXESTATE *)pvUser;
1279 uint32_t cPaths = RTCrX509CertPathsGetPathCount(hCertPaths);
1280
1281 /*
1282 * Dump all the paths.
1283 */
1284 if (pState->cVerbose > 0)
1285 {
1286 for (uint32_t iPath = 0; iPath < cPaths; iPath++)
1287 {
1288 RTPrintf("---\n");
1289 RTCrX509CertPathsDumpOne(hCertPaths, iPath, pState->cVerbose, RTStrmDumpPrintfV, g_pStdOut);
1290 *pErrInfo->pszMsg = '\0';
1291 }
1292 RTPrintf("---\n");
1293 }
1294
1295 /*
1296 * Test signing certificates normally doesn't have all the necessary
1297 * features required below. So, treat them as special cases.
1298 */
1299 if ( hCertPaths == NIL_RTCRX509CERTPATHS
1300 && RTCrX509Name_Compare(&pCert->TbsCertificate.Issuer, &pCert->TbsCertificate.Subject) == 0)
1301 {
1302 RTMsgInfo("Test signed.\n");
1303 return VINF_SUCCESS;
1304 }
1305
1306 if (hCertPaths == NIL_RTCRX509CERTPATHS)
1307 RTMsgInfo("Signed by trusted certificate.\n");
1308
1309 /*
1310 * Standard code signing capabilites required.
1311 */
1312 int rc = RTCrPkcs7VerifyCertCallbackCodeSigning(pCert, hCertPaths, fFlags, NULL, pErrInfo);
1313 if ( RT_SUCCESS(rc)
1314 && (fFlags & RTCRPKCS7VCC_F_SIGNED_DATA))
1315 {
1316 /*
1317 * If windows kernel signing, a valid certificate path must be anchored
1318 * by the microsoft kernel signing root certificate. The only
1319 * alternative is test signing.
1320 */
1321 if ( pState->fKernel
1322 && hCertPaths != NIL_RTCRX509CERTPATHS
1323 && pState->enmSignType == VERIFYEXESTATE::kSignType_Windows)
1324 {
1325 uint32_t cFound = 0;
1326 uint32_t cValid = 0;
1327 for (uint32_t iPath = 0; iPath < cPaths; iPath++)
1328 {
1329 bool fTrusted;
1330 PCRTCRX509NAME pSubject;
1331 PCRTCRX509SUBJECTPUBLICKEYINFO pPublicKeyInfo;
1332 int rcVerify;
1333 rc = RTCrX509CertPathsQueryPathInfo(hCertPaths, iPath, &fTrusted, NULL /*pcNodes*/, &pSubject, &pPublicKeyInfo,
1334 NULL, NULL /*pCertCtx*/, &rcVerify);
1335 AssertRCBreak(rc);
1336
1337 if (RT_SUCCESS(rcVerify))
1338 {
1339 Assert(fTrusted);
1340 cValid++;
1341
1342 /* Search the kernel signing root store for a matching anchor. */
1343 RTCRSTORECERTSEARCH Search;
1344 rc = RTCrStoreCertFindBySubjectOrAltSubjectByRfc5280(pState->hKernelRootStore, pSubject, &Search);
1345 AssertRCBreak(rc);
1346 PCRTCRCERTCTX pCertCtx;
1347 while ((pCertCtx = RTCrStoreCertSearchNext(pState->hKernelRootStore, &Search)) != NULL)
1348 {
1349 PCRTCRX509SUBJECTPUBLICKEYINFO pPubKeyInfo;
1350 if (pCertCtx->pCert)
1351 pPubKeyInfo = &pCertCtx->pCert->TbsCertificate.SubjectPublicKeyInfo;
1352 else if (pCertCtx->pTaInfo)
1353 pPubKeyInfo = &pCertCtx->pTaInfo->PubKey;
1354 else
1355 pPubKeyInfo = NULL;
1356 if (RTCrX509SubjectPublicKeyInfo_Compare(pPubKeyInfo, pPublicKeyInfo) == 0)
1357 cFound++;
1358 RTCrCertCtxRelease(pCertCtx);
1359 }
1360
1361 int rc2 = RTCrStoreCertSearchDestroy(pState->hKernelRootStore, &Search); AssertRC(rc2);
1362 }
1363 }
1364 if (RT_SUCCESS(rc) && cFound == 0)
1365 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE, "Not valid kernel code signature.");
1366 if (RT_SUCCESS(rc) && cValid != 2)
1367 RTMsgWarning("%u valid paths, expected 2", cValid);
1368 }
1369 /*
1370 * For Mac OS X signing, check for special developer ID attributes.
1371 */
1372 else if (pState->enmSignType == VERIFYEXESTATE::kSignType_OSX)
1373 {
1374 uint32_t cDevIdApp = 0;
1375 uint32_t cDevIdKext = 0;
1376 for (uint32_t i = 0; i < pCert->TbsCertificate.T3.Extensions.cItems; i++)
1377 {
1378 PCRTCRX509EXTENSION pExt = pCert->TbsCertificate.T3.Extensions.papItems[i];
1379 if (RTAsn1ObjId_CompareWithString(&pExt->ExtnId, RTCR_APPLE_CS_DEVID_APPLICATION_OID) == 0)
1380 {
1381 cDevIdApp++;
1382 if (!pExt->Critical.fValue)
1383 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
1384 "Dev ID Application certificate extension is not flagged critical");
1385 }
1386 else if (RTAsn1ObjId_CompareWithString(&pExt->ExtnId, RTCR_APPLE_CS_DEVID_KEXT_OID) == 0)
1387 {
1388 cDevIdKext++;
1389 if (!pExt->Critical.fValue)
1390 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
1391 "Dev ID kext certificate extension is not flagged critical");
1392 }
1393 }
1394 if (cDevIdApp == 0)
1395 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
1396 "Certificate is missing the 'Dev ID Application' extension");
1397 if (cDevIdKext == 0 && pState->fKernel)
1398 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
1399 "Certificate is missing the 'Dev ID kext' extension");
1400 }
1401 }
1402
1403 return rc;
1404}
1405
1406/** @callback_method_impl{FNRTLDRVALIDATESIGNEDDATA} */
1407static DECLCALLBACK(int) VerifyExeCallback(RTLDRMOD hLdrMod, PCRTLDRSIGNATUREINFO pInfo, PRTERRINFO pErrInfo, void *pvUser)
1408{
1409 VERIFYEXESTATE *pState = (VERIFYEXESTATE *)pvUser;
1410 RT_NOREF_PV(hLdrMod);
1411
1412 switch (pInfo->enmType)
1413 {
1414 case RTLDRSIGNATURETYPE_PKCS7_SIGNED_DATA:
1415 {
1416 PCRTCRPKCS7CONTENTINFO pContentInfo = (PCRTCRPKCS7CONTENTINFO)pInfo->pvSignature;
1417
1418 /*
1419 * Dump the signed data if so requested and it's the first one, assuming that
1420 * additional signatures in contained wihtin the same ContentInfo structure.
1421 */
1422 if (pState->cVerbose && pInfo->iSignature == 0)
1423 RTAsn1Dump(&pContentInfo->SeqCore.Asn1Core, 0, 0, RTStrmDumpPrintfV, g_pStdOut);
1424
1425 /*
1426 * We'll try different alternative timestamps here.
1427 */
1428 struct { RTTIMESPEC TimeSpec; const char *pszDesc; } aTimes[2];
1429 unsigned cTimes = 0;
1430
1431 /* Linking timestamp: */
1432 uint64_t uLinkingTime = 0;
1433 int rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &uLinkingTime, sizeof(uLinkingTime));
1434 if (RT_SUCCESS(rc))
1435 {
1436 RTTimeSpecSetSeconds(&aTimes[0].TimeSpec, uLinkingTime);
1437 aTimes[0].pszDesc = "at link time";
1438 cTimes++;
1439 }
1440 else if (rc != VERR_NOT_FOUND)
1441 RTMsgError("RTLdrQueryProp/RTLDRPROP_TIMESTAMP_SECONDS failed on '%s': %Rrc\n", pState->pszFilename, rc);
1442
1443 /* Now: */
1444 RTTimeNow(&aTimes[cTimes].TimeSpec);
1445 aTimes[cTimes].pszDesc = "now";
1446 cTimes++;
1447
1448 /*
1449 * Do the actual verification.
1450 */
1451 for (unsigned iTime = 0; iTime < cTimes; iTime++)
1452 {
1453 if (pInfo->pvExternalData)
1454 rc = RTCrPkcs7VerifySignedDataWithExternalData(pContentInfo,
1455 RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE_SIGNING_TIME_ONLY
1456 | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_SIGNING_TIME_IF_PRESENT
1457 | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_MS_TIMESTAMP_IF_PRESENT
1458 | RTCRPKCS7VERIFY_SD_F_CHECK_TRUST_ANCHORS,
1459 pState->hAdditionalStore, pState->hRootStore,
1460 &aTimes[iTime].TimeSpec,
1461 VerifyExecCertVerifyCallback, pState,
1462 pInfo->pvExternalData, pInfo->cbExternalData, pErrInfo);
1463 else
1464 rc = RTCrPkcs7VerifySignedData(pContentInfo,
1465 RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE_SIGNING_TIME_ONLY
1466 | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_SIGNING_TIME_IF_PRESENT
1467 | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_MS_TIMESTAMP_IF_PRESENT
1468 | RTCRPKCS7VERIFY_SD_F_CHECK_TRUST_ANCHORS,
1469 pState->hAdditionalStore, pState->hRootStore,
1470 &aTimes[iTime].TimeSpec,
1471 VerifyExecCertVerifyCallback, pState, pErrInfo);
1472 if (RT_SUCCESS(rc))
1473 {
1474 Assert(rc == VINF_SUCCESS);
1475 if (pInfo->cSignatures == 1)
1476 RTMsgInfo("'%s' is valid %s.\n", pState->pszFilename, aTimes[iTime].pszDesc);
1477 else
1478 RTMsgInfo("'%s' signature #%u is valid %s.\n",
1479 pState->pszFilename, pInfo->iSignature + 1, aTimes[iTime].pszDesc);
1480 pState->cOkay++;
1481 return VINF_SUCCESS;
1482 }
1483 if (rc != VERR_CR_X509_CPV_NOT_VALID_AT_TIME)
1484 {
1485 if (pInfo->cSignatures == 1)
1486 RTMsgError("%s: Failed to verify signature: %Rrc%#RTeim\n", pState->pszFilename, rc, pErrInfo);
1487 else
1488 RTMsgError("%s: Failed to verify signature #%u: %Rrc%#RTeim\n",
1489 pState->pszFilename, pInfo->iSignature + 1, rc, pErrInfo);
1490 pState->cBad++;
1491 return VINF_SUCCESS;
1492 }
1493 }
1494
1495 if (pInfo->cSignatures == 1)
1496 RTMsgError("%s: Signature is not valid at present or link time.\n", pState->pszFilename);
1497 else
1498 RTMsgError("%s: Signature #%u is not valid at present or link time.\n",
1499 pState->pszFilename, pInfo->iSignature + 1);
1500 pState->cBad++;
1501 return VINF_SUCCESS;
1502 }
1503
1504 default:
1505 return RTErrInfoSetF(pErrInfo, VERR_NOT_SUPPORTED, "Unsupported signature type: %d", pInfo->enmType);
1506 }
1507}
1508
1509/**
1510 * Worker for HandleVerifyExe.
1511 */
1512static RTEXITCODE HandleVerifyExeWorker(VERIFYEXESTATE *pState, const char *pszFilename, PRTERRINFOSTATIC pStaticErrInfo)
1513{
1514 /*
1515 * Open the executable image and verify it.
1516 */
1517 RTLDRMOD hLdrMod;
1518 int rc = RTLdrOpen(pszFilename, RTLDR_O_FOR_VALIDATION, pState->enmLdrArch, &hLdrMod);
1519 if (RT_FAILURE(rc))
1520 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error opening executable image '%s': %Rrc", pszFilename, rc);
1521
1522 /* Reset the state. */
1523 pState->cBad = 0;
1524 pState->cOkay = 0;
1525 pState->pszFilename = pszFilename;
1526
1527 rc = RTLdrVerifySignature(hLdrMod, VerifyExeCallback, pState, RTErrInfoInitStatic(pStaticErrInfo));
1528 if (RT_FAILURE(rc))
1529 RTMsgError("RTLdrVerifySignature failed on '%s': %Rrc - %s\n", pszFilename, rc, pStaticErrInfo->szMsg);
1530
1531 int rc2 = RTLdrClose(hLdrMod);
1532 if (RT_FAILURE(rc2))
1533 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTLdrClose failed: %Rrc\n", rc2);
1534 if (RT_FAILURE(rc))
1535 return rc != VERR_LDRVI_NOT_SIGNED ? RTEXITCODE_FAILURE : RTEXITCODE_SKIPPED;
1536
1537 return pState->cOkay > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1538}
1539
1540
1541static RTEXITCODE HandleVerifyExe(int cArgs, char **papszArgs)
1542{
1543 RTERRINFOSTATIC StaticErrInfo;
1544
1545 /* Note! This code does not try to clean up the crypto stores on failure.
1546 This is intentional as the code is only expected to be used in a
1547 one-command-per-process environment where we do exit() upon
1548 returning from this function. */
1549
1550 /*
1551 * Parse arguments.
1552 */
1553 static const RTGETOPTDEF s_aOptions[] =
1554 {
1555 { "--kernel", 'k', RTGETOPT_REQ_NOTHING },
1556 { "--root", 'r', RTGETOPT_REQ_STRING },
1557 { "--additional", 'a', RTGETOPT_REQ_STRING },
1558 { "--add", 'a', RTGETOPT_REQ_STRING },
1559 { "--type", 't', RTGETOPT_REQ_STRING },
1560 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
1561 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
1562 };
1563
1564 VERIFYEXESTATE State =
1565 {
1566 NIL_RTCRSTORE, NIL_RTCRSTORE, NIL_RTCRSTORE, false, 0,
1567 VERIFYEXESTATE::kSignType_Windows, RTLDRARCH_WHATEVER,
1568 0, 0, NULL
1569 };
1570 int rc = RTCrStoreCreateInMem(&State.hRootStore, 0);
1571 if (RT_SUCCESS(rc))
1572 rc = RTCrStoreCreateInMem(&State.hKernelRootStore, 0);
1573 if (RT_SUCCESS(rc))
1574 rc = RTCrStoreCreateInMem(&State.hAdditionalStore, 0);
1575 if (RT_FAILURE(rc))
1576 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error creating in-memory certificate store: %Rrc", rc);
1577
1578 RTGETOPTSTATE GetState;
1579 rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1580 AssertRCReturn(rc, RTEXITCODE_FAILURE);
1581 RTGETOPTUNION ValueUnion;
1582 int ch;
1583 while ((ch = RTGetOpt(&GetState, &ValueUnion)) && ch != VINF_GETOPT_NOT_OPTION)
1584 {
1585 switch (ch)
1586 {
1587 case 'r': case 'a':
1588 rc = RTCrStoreCertAddFromFile(ch == 'r' ? State.hRootStore : State.hAdditionalStore,
1589 RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR,
1590 ValueUnion.psz, RTErrInfoInitStatic(&StaticErrInfo));
1591 if (RT_FAILURE(rc))
1592 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error loading certificate '%s': %Rrc - %s",
1593 ValueUnion.psz, rc, StaticErrInfo.szMsg);
1594 if (RTErrInfoIsSet(&StaticErrInfo.Core))
1595 RTMsgWarning("Warnings loading certificate '%s': %s", ValueUnion.psz, StaticErrInfo.szMsg);
1596 break;
1597
1598 case 't':
1599 if (!strcmp(ValueUnion.psz, "win") || !strcmp(ValueUnion.psz, "windows"))
1600 State.enmSignType = VERIFYEXESTATE::kSignType_Windows;
1601 else if (!strcmp(ValueUnion.psz, "osx") || !strcmp(ValueUnion.psz, "apple"))
1602 State.enmSignType = VERIFYEXESTATE::kSignType_OSX;
1603 else
1604 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown signing type: '%s'", ValueUnion.psz);
1605 break;
1606
1607 case 'k': State.fKernel = true; break;
1608 case 'v': State.cVerbose++; break;
1609 case 'q': State.cVerbose = 0; break;
1610 case 'V': return HandleVersion(cArgs, papszArgs);
1611 case 'h': return HelpVerifyExe(g_pStdOut, RTSIGNTOOLHELP_FULL);
1612 default: return RTGetOptPrintError(ch, &ValueUnion);
1613 }
1614 }
1615 if (ch != VINF_GETOPT_NOT_OPTION)
1616 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
1617
1618 /*
1619 * Populate the certificate stores according to the signing type.
1620 */
1621# ifdef VBOX
1622 unsigned cSets = 0;
1623 struct STSTORESET aSets[6];
1624 switch (State.enmSignType)
1625 {
1626 case VERIFYEXESTATE::kSignType_Windows:
1627 aSets[cSets].hStore = State.hRootStore;
1628 aSets[cSets].paTAs = g_aSUPTimestampTAs;
1629 aSets[cSets].cTAs = g_cSUPTimestampTAs;
1630 cSets++;
1631 aSets[cSets].hStore = State.hRootStore;
1632 aSets[cSets].paTAs = g_aSUPSpcRootTAs;
1633 aSets[cSets].cTAs = g_cSUPSpcRootTAs;
1634 cSets++;
1635 aSets[cSets].hStore = State.hRootStore;
1636 aSets[cSets].paTAs = g_aSUPNtKernelRootTAs;
1637 aSets[cSets].cTAs = g_cSUPNtKernelRootTAs;
1638 cSets++;
1639 aSets[cSets].hStore = State.hKernelRootStore;
1640 aSets[cSets].paTAs = g_aSUPNtKernelRootTAs;
1641 aSets[cSets].cTAs = g_cSUPNtKernelRootTAs;
1642 cSets++;
1643 break;
1644
1645 case VERIFYEXESTATE::kSignType_OSX:
1646 aSets[cSets].hStore = State.hRootStore;
1647 aSets[cSets].paTAs = g_aSUPAppleRootTAs;
1648 aSets[cSets].cTAs = g_cSUPAppleRootTAs;
1649 cSets++;
1650 break;
1651 }
1652 for (unsigned i = 0; i < cSets; i++)
1653 for (unsigned j = 0; j < aSets[i].cTAs; j++)
1654 {
1655 rc = RTCrStoreCertAddEncoded(aSets[i].hStore, RTCRCERTCTX_F_ENC_TAF_DER, aSets[i].paTAs[j].pch,
1656 aSets[i].paTAs[j].cb, RTErrInfoInitStatic(&StaticErrInfo));
1657 if (RT_FAILURE(rc))
1658 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTCrStoreCertAddEncoded failed (%u/%u): %s",
1659 i, j, StaticErrInfo.szMsg);
1660 }
1661# endif /* VBOX */
1662
1663 /*
1664 * Do it.
1665 */
1666 RTEXITCODE rcExit;
1667 for (;;)
1668 {
1669 rcExit = HandleVerifyExeWorker(&State, ValueUnion.psz, &StaticErrInfo);
1670 if (rcExit != RTEXITCODE_SUCCESS)
1671 break;
1672
1673 /*
1674 * Next file
1675 */
1676 ch = RTGetOpt(&GetState, &ValueUnion);
1677 if (ch == 0)
1678 break;
1679 if (ch != VINF_GETOPT_NOT_OPTION)
1680 {
1681 rcExit = RTGetOptPrintError(ch, &ValueUnion);
1682 break;
1683 }
1684 }
1685
1686 /*
1687 * Clean up.
1688 */
1689 uint32_t cRefs;
1690 cRefs = RTCrStoreRelease(State.hRootStore); Assert(cRefs == 0);
1691 cRefs = RTCrStoreRelease(State.hKernelRootStore); Assert(cRefs == 0);
1692 cRefs = RTCrStoreRelease(State.hAdditionalStore); Assert(cRefs == 0);
1693
1694 return rcExit;
1695}
1696
1697#endif /* !IPRT_IN_BUILD_TOOL */
1698
1699/*
1700 * common code for show-exe and show-cat:
1701 */
1702
1703/**
1704 * Display an object ID.
1705 *
1706 * @returns IPRT status code.
1707 * @param pThis The show exe instance data.
1708 * @param pObjId The object ID to display.
1709 * @param pszLabel The field label (prefixed by szPrefix).
1710 * @param pszPost What to print after the ID (typically newline).
1711 */
1712static void HandleShowExeWorkerDisplayObjId(PSHOWEXEPKCS7 pThis, PCRTASN1OBJID pObjId, const char *pszLabel, const char *pszPost)
1713{
1714 int rc = RTAsn1QueryObjIdName(pObjId, pThis->szTmp, sizeof(pThis->szTmp));
1715 if (RT_SUCCESS(rc))
1716 {
1717 if (pThis->cVerbosity > 1)
1718 RTPrintf("%s%s%s (%s)%s", pThis->szPrefix, pszLabel, pThis->szTmp, pObjId->szObjId, pszPost);
1719 else
1720 RTPrintf("%s%s%s%s", pThis->szPrefix, pszLabel, pThis->szTmp, pszPost);
1721 }
1722 else
1723 RTPrintf("%s%s%s%s", pThis->szPrefix, pszLabel, pObjId->szObjId, pszPost);
1724}
1725
1726
1727/**
1728 * Display an object ID, without prefix and label
1729 *
1730 * @returns IPRT status code.
1731 * @param pThis The show exe instance data.
1732 * @param pObjId The object ID to display.
1733 * @param pszPost What to print after the ID (typically newline).
1734 */
1735static void HandleShowExeWorkerDisplayObjIdSimple(PSHOWEXEPKCS7 pThis, PCRTASN1OBJID pObjId, const char *pszPost)
1736{
1737 int rc = RTAsn1QueryObjIdName(pObjId, pThis->szTmp, sizeof(pThis->szTmp));
1738 if (RT_SUCCESS(rc))
1739 {
1740 if (pThis->cVerbosity > 1)
1741 RTPrintf("%s (%s)%s", pThis->szTmp, pObjId->szObjId, pszPost);
1742 else
1743 RTPrintf("%s%s", pThis->szTmp, pszPost);
1744 }
1745 else
1746 RTPrintf("%s%s", pObjId->szObjId, pszPost);
1747}
1748
1749
1750/**
1751 * Display a signer info attribute.
1752 *
1753 * @returns IPRT status code.
1754 * @param pThis The show exe instance data.
1755 * @param offPrefix The current prefix offset.
1756 * @param pAttr The attribute to display.
1757 */
1758static int HandleShowExeWorkerPkcs7DisplayAttrib(PSHOWEXEPKCS7 pThis, size_t offPrefix, PCRTCRPKCS7ATTRIBUTE pAttr)
1759{
1760 HandleShowExeWorkerDisplayObjId(pThis, &pAttr->Type, "", ":\n");
1761
1762 int rc = VINF_SUCCESS;
1763 switch (pAttr->enmType)
1764 {
1765 case RTCRPKCS7ATTRIBUTETYPE_UNKNOWN:
1766 if (pAttr->uValues.pCores->cItems <= 1)
1767 RTPrintf("%s %u bytes\n", pThis->szPrefix,pAttr->uValues.pCores->SetCore.Asn1Core.cb);
1768 else
1769 RTPrintf("%s %u bytes divided by %u items\n", pThis->szPrefix, pAttr->uValues.pCores->SetCore.Asn1Core.cb, pAttr->uValues.pCores->cItems);
1770 break;
1771
1772 /* Object IDs, use pObjIds. */
1773 case RTCRPKCS7ATTRIBUTETYPE_OBJ_IDS:
1774 if (pAttr->uValues.pObjIds->cItems != 1)
1775 RTPrintf("%s%u object IDs:", pThis->szPrefix, pAttr->uValues.pObjIds->cItems);
1776 for (unsigned i = 0; i < pAttr->uValues.pObjIds->cItems; i++)
1777 {
1778 if (pAttr->uValues.pObjIds->cItems == 1)
1779 RTPrintf("%s ", pThis->szPrefix);
1780 else
1781 RTPrintf("%s ObjId[%u]: ", pThis->szPrefix, i);
1782 HandleShowExeWorkerDisplayObjIdSimple(pThis, pAttr->uValues.pObjIds->papItems[i], "\n");
1783 }
1784 break;
1785
1786 /* Sequence of object IDs, use pObjIdSeqs. */
1787 case RTCRPKCS7ATTRIBUTETYPE_MS_STATEMENT_TYPE:
1788 if (pAttr->uValues.pObjIdSeqs->cItems != 1)
1789 RTPrintf("%s%u object IDs:", pThis->szPrefix, pAttr->uValues.pObjIdSeqs->cItems);
1790 for (unsigned i = 0; i < pAttr->uValues.pObjIdSeqs->cItems; i++)
1791 {
1792 uint32_t const cObjIds = pAttr->uValues.pObjIdSeqs->papItems[i]->cItems;
1793 for (unsigned j = 0; j < cObjIds; j++)
1794 {
1795 if (pAttr->uValues.pObjIdSeqs->cItems == 1)
1796 RTPrintf("%s ", pThis->szPrefix);
1797 else
1798 RTPrintf("%s ObjIdSeq[%u]: ", pThis->szPrefix, i);
1799 if (cObjIds != 1)
1800 RTPrintf(" ObjId[%u]: ", j);
1801 HandleShowExeWorkerDisplayObjIdSimple(pThis, pAttr->uValues.pObjIdSeqs->papItems[i]->papItems[i], "\n");
1802 }
1803 }
1804 break;
1805
1806 /* Octet strings, use pOctetStrings. */
1807 case RTCRPKCS7ATTRIBUTETYPE_OCTET_STRINGS:
1808 if (pAttr->uValues.pOctetStrings->cItems != 1)
1809 RTPrintf("%s%u octet strings:", pThis->szPrefix, pAttr->uValues.pOctetStrings->cItems);
1810 for (unsigned i = 0; i < pAttr->uValues.pOctetStrings->cItems; i++)
1811 {
1812 PCRTASN1OCTETSTRING pOctetString = pAttr->uValues.pOctetStrings->papItems[i];
1813 uint32_t cbContent = pOctetString->Asn1Core.cb;
1814 if (cbContent > 0 && (cbContent <= 128 || pThis->cVerbosity >= 2))
1815 {
1816 uint8_t const *pbContent = pOctetString->Asn1Core.uData.pu8;
1817 uint32_t off = 0;
1818 while (off < cbContent)
1819 {
1820 uint32_t cbNow = RT_MIN(cbContent - off, 16);
1821 if (pAttr->uValues.pOctetStrings->cItems == 1)
1822 RTPrintf("%s %#06x: %.*Rhxs\n", pThis->szPrefix, off, cbNow, &pbContent[off]);
1823 else
1824 RTPrintf("%s OctetString[%u]: %#06x: %.*Rhxs\n", pThis->szPrefix, i, off, cbNow, &pbContent[off]);
1825 off += cbNow;
1826 }
1827 }
1828 else
1829 RTPrintf("%s: OctetString[%u]: %u bytes\n", pThis->szPrefix, i, pOctetString->Asn1Core.cb);
1830 }
1831 break;
1832
1833 /* Counter signatures (PKCS \#9), use pCounterSignatures. */
1834 case RTCRPKCS7ATTRIBUTETYPE_COUNTER_SIGNATURES:
1835 RTPrintf("%sTODO: RTCRPKCS7ATTRIBUTETYPE_COUNTER_SIGNATURES! %u bytes\n",
1836 pThis->szPrefix, pAttr->uValues.pCounterSignatures->SetCore.Asn1Core.cb);
1837 break;
1838
1839 /* Signing time (PKCS \#9), use pSigningTime. */
1840 case RTCRPKCS7ATTRIBUTETYPE_SIGNING_TIME:
1841 for (uint32_t i = 0; i < pAttr->uValues.pSigningTime->cItems; i++)
1842 {
1843 PCRTASN1TIME pTime = pAttr->uValues.pSigningTime->papItems[i];
1844 char szTS[RTTIME_STR_LEN];
1845 RTTimeToString(&pTime->Time, szTS, sizeof(szTS));
1846 if (pAttr->uValues.pSigningTime->cItems == 1)
1847 RTPrintf("%s %s (%.*s)\n", pThis->szPrefix, szTS, pTime->Asn1Core.cb, pTime->Asn1Core.uData.pch);
1848 else
1849 RTPrintf("%s #%u: %s (%.*s)\n", pThis->szPrefix, i, szTS, pTime->Asn1Core.cb, pTime->Asn1Core.uData.pch);
1850 }
1851 break;
1852
1853 /* Microsoft timestamp info (RFC-3161) signed data, use pContentInfo. */
1854 case RTCRPKCS7ATTRIBUTETYPE_MS_TIMESTAMP:
1855 case RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE:
1856 if (pAttr->uValues.pContentInfos->cItems > 1)
1857 RTPrintf("%s%u nested signatures, %u bytes in total\n", pThis->szPrefix,
1858 pAttr->uValues.pContentInfos->cItems, pAttr->uValues.pContentInfos->SetCore.Asn1Core.cb);
1859 for (unsigned i = 0; i < pAttr->uValues.pContentInfos->cItems; i++)
1860 {
1861 size_t offPrefix2 = offPrefix;
1862 if (pAttr->uValues.pContentInfos->cItems > 1)
1863 offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, "NestedSig[%u]: ", i);
1864 else
1865 offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, " ");
1866 // offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, "NestedSig: ", i);
1867 PCRTCRPKCS7CONTENTINFO pContentInfo = pAttr->uValues.pContentInfos->papItems[i];
1868 int rc2;
1869 if (RTCrPkcs7ContentInfo_IsSignedData(pContentInfo))
1870 rc2 = HandleShowExeWorkerPkcs7Display(pThis, pContentInfo->u.pSignedData, offPrefix2, pContentInfo);
1871 else
1872 rc2 = RTMsgErrorRc(VERR_ASN1_UNEXPECTED_OBJ_ID, "%sPKCS#7 content in nested signature is not 'signedData': %s",
1873 pThis->szPrefix, pContentInfo->ContentType.szObjId);
1874 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
1875 rc = rc2;
1876 }
1877 break;
1878
1879 case RTCRPKCS7ATTRIBUTETYPE_APPLE_MULTI_CD_PLIST:
1880 if (pAttr->uValues.pContentInfos->cItems != 1)
1881 RTPrintf("%s%u plists, expected only 1.\n", pThis->szPrefix, pAttr->uValues.pOctetStrings->cItems);
1882 for (unsigned i = 0; i < pAttr->uValues.pOctetStrings->cItems; i++)
1883 {
1884 PCRTASN1OCTETSTRING pOctetString = pAttr->uValues.pOctetStrings->papItems[i];
1885 size_t cbContent = pOctetString->Asn1Core.cb;
1886 char const *pchContent = pOctetString->Asn1Core.uData.pch;
1887 rc = RTStrValidateEncodingEx(pchContent, cbContent, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
1888 if (RT_SUCCESS(rc))
1889 {
1890 while (cbContent > 0)
1891 {
1892 const char *pchNewLine = (const char *)memchr(pchContent, '\n', cbContent);
1893 size_t cchToWrite = pchNewLine ? pchNewLine - pchContent : cbContent;
1894 if (pAttr->uValues.pOctetStrings->cItems == 1)
1895 RTPrintf("%s %.*s\n", pThis->szPrefix, cchToWrite, pchContent);
1896 else
1897 RTPrintf("%s plist[%u]: %.*s\n", pThis->szPrefix, i, cchToWrite, pchContent);
1898 if (!pchNewLine)
1899 break;
1900 pchContent = pchNewLine + 1;
1901 cbContent -= cchToWrite + 1;
1902 }
1903 }
1904 else
1905 {
1906 if (pAttr->uValues.pContentInfos->cItems != 1)
1907 RTPrintf("%s: plist[%u]: Invalid UTF-8: %Rrc\n", pThis->szPrefix, i, rc);
1908 else
1909 RTPrintf("%s: Invalid UTF-8: %Rrc\n", pThis->szPrefix, rc);
1910 for (uint32_t off = 0; off < cbContent; off += 16)
1911 {
1912 size_t cbNow = RT_MIN(cbContent - off, 16);
1913 if (pAttr->uValues.pOctetStrings->cItems == 1)
1914 RTPrintf("%s %#06x: %.*Rhxs\n", pThis->szPrefix, off, cbNow, &pchContent[off]);
1915 else
1916 RTPrintf("%s plist[%u]: %#06x: %.*Rhxs\n", pThis->szPrefix, i, off, cbNow, &pchContent[off]);
1917 }
1918 }
1919 }
1920 break;
1921
1922 case RTCRPKCS7ATTRIBUTETYPE_INVALID:
1923 RTPrintf("%sINVALID!\n", pThis->szPrefix);
1924 break;
1925 case RTCRPKCS7ATTRIBUTETYPE_NOT_PRESENT:
1926 RTPrintf("%sNOT PRESENT!\n", pThis->szPrefix);
1927 break;
1928 default:
1929 RTPrintf("%senmType=%d!\n", pThis->szPrefix, pAttr->enmType);
1930 break;
1931 }
1932 return rc;
1933}
1934
1935
1936/**
1937 * Displays a Microsoft SPC indirect data structure.
1938 *
1939 * @returns IPRT status code.
1940 * @param pThis The show exe instance data.
1941 * @param offPrefix The current prefix offset.
1942 * @param pIndData The indirect data to display.
1943 */
1944static int HandleShowExeWorkerPkcs7DisplaySpcIdirectDataContent(PSHOWEXEPKCS7 pThis, size_t offPrefix,
1945 PCRTCRSPCINDIRECTDATACONTENT pIndData)
1946{
1947 /*
1948 * The image hash.
1949 */
1950 RTDIGESTTYPE const enmDigestType = RTCrX509AlgorithmIdentifier_QueryDigestType(&pIndData->DigestInfo.DigestAlgorithm);
1951 const char *pszDigestType = RTCrDigestTypeToName(enmDigestType);
1952 RTPrintf("%s Digest Type: %s", pThis->szPrefix, pszDigestType);
1953 if (pThis->cVerbosity > 1)
1954 RTPrintf(" (%s)\n", pIndData->DigestInfo.DigestAlgorithm.Algorithm.szObjId);
1955 else
1956 RTPrintf("\n");
1957 RTPrintf("%s Digest: %.*Rhxs\n",
1958 pThis->szPrefix, pIndData->DigestInfo.Digest.Asn1Core.cb, pIndData->DigestInfo.Digest.Asn1Core.uData.pu8);
1959
1960 /*
1961 * The data/file/url.
1962 */
1963 switch (pIndData->Data.enmType)
1964 {
1965 case RTCRSPCAAOVTYPE_PE_IMAGE_DATA:
1966 {
1967 RTPrintf("%s Data Type: PE Image Data\n", pThis->szPrefix);
1968 PRTCRSPCPEIMAGEDATA pPeImage = pIndData->Data.uValue.pPeImage;
1969 /** @todo display "Flags". */
1970
1971 switch (pPeImage->T0.File.enmChoice)
1972 {
1973 case RTCRSPCLINKCHOICE_MONIKER:
1974 {
1975 PRTCRSPCSERIALIZEDOBJECT pMoniker = pPeImage->T0.File.u.pMoniker;
1976 if (RTCrSpcSerializedObject_IsPresent(pMoniker))
1977 {
1978 if (RTUuidCompareStr(pMoniker->Uuid.Asn1Core.uData.pUuid, RTCRSPCSERIALIZEDOBJECT_UUID_STR) == 0)
1979 {
1980 RTPrintf("%s Moniker: SpcSerializedObject (%RTuuid)\n",
1981 pThis->szPrefix, pMoniker->Uuid.Asn1Core.uData.pUuid);
1982
1983 PCRTCRSPCSERIALIZEDOBJECTATTRIBUTES pData = pMoniker->u.pData;
1984 if (pData)
1985 for (uint32_t i = 0; i < pData->cItems; i++)
1986 {
1987 RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix,
1988 "MonikerAttrib[%u]: ", i);
1989
1990 switch (pData->papItems[i]->enmType)
1991 {
1992 case RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V2:
1993 case RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V1:
1994 {
1995 PCRTCRSPCSERIALIZEDPAGEHASHES pPgHashes = pData->papItems[i]->u.pPageHashes;
1996 uint32_t const cbHash = pData->papItems[i]->enmType
1997 == RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V1
1998 ? 160/8 /*SHA-1*/ : 256/8 /*SHA-256*/;
1999 uint32_t const cPages = pPgHashes->RawData.Asn1Core.cb / (cbHash + sizeof(uint32_t));
2000
2001 RTPrintf("%sPage Hashes version %u - %u pages (%u bytes total)\n", pThis->szPrefix,
2002 pData->papItems[i]->enmType
2003 == RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V1 ? 1 : 2,
2004 cPages, pPgHashes->RawData.Asn1Core.cb);
2005 if (pThis->cVerbosity > 0)
2006 {
2007 PCRTCRSPCPEIMAGEPAGEHASHES pPg = pPgHashes->pData;
2008 for (unsigned iPg = 0; iPg < cPages; iPg++)
2009 {
2010 uint32_t offHash = 0;
2011 do
2012 {
2013 if (offHash == 0)
2014 RTPrintf("%.*s Page#%04u/%#08x: ",
2015 offPrefix, pThis->szPrefix, iPg, pPg->Generic.offFile);
2016 else
2017 RTPrintf("%.*s ", offPrefix, pThis->szPrefix);
2018 uint32_t cbLeft = cbHash - offHash;
2019 if (cbLeft > 24)
2020 cbLeft = 16;
2021 RTPrintf("%.*Rhxs\n", cbLeft, &pPg->Generic.abHash[offHash]);
2022 offHash += cbLeft;
2023 } while (offHash < cbHash);
2024 pPg = (PCRTCRSPCPEIMAGEPAGEHASHES)&pPg->Generic.abHash[cbHash];
2025 }
2026
2027 if (pThis->cVerbosity > 3)
2028 RTPrintf("%.*Rhxd\n",
2029 pPgHashes->RawData.Asn1Core.cb,
2030 pPgHashes->RawData.Asn1Core.uData.pu8);
2031 }
2032 break;
2033 }
2034
2035 case RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_UNKNOWN:
2036 HandleShowExeWorkerDisplayObjIdSimple(pThis, &pData->papItems[i]->Type, "\n");
2037 break;
2038 case RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_NOT_PRESENT:
2039 RTPrintf("%sNot present!\n", pThis->szPrefix);
2040 break;
2041 default:
2042 RTPrintf("%senmType=%d!\n", pThis->szPrefix, pData->papItems[i]->enmType);
2043 break;
2044 }
2045 pThis->szPrefix[offPrefix] = '\0';
2046 }
2047 else
2048 RTPrintf("%s pData is NULL!\n", pThis->szPrefix);
2049 }
2050 else
2051 RTPrintf("%s Moniker: Unknown UUID: %RTuuid\n",
2052 pThis->szPrefix, pMoniker->Uuid.Asn1Core.uData.pUuid);
2053 }
2054 else
2055 RTPrintf("%s Moniker: not present\n", pThis->szPrefix);
2056 break;
2057 }
2058
2059 case RTCRSPCLINKCHOICE_URL:
2060 {
2061 const char *pszUrl = NULL;
2062 int rc = pPeImage->T0.File.u.pUrl
2063 ? RTAsn1String_QueryUtf8(pPeImage->T0.File.u.pUrl, &pszUrl, NULL)
2064 : VERR_NOT_FOUND;
2065 if (RT_SUCCESS(rc))
2066 RTPrintf("%s URL: '%s'\n", pThis->szPrefix, pszUrl);
2067 else
2068 RTPrintf("%s URL: rc=%Rrc\n", pThis->szPrefix, rc);
2069 break;
2070 }
2071
2072 case RTCRSPCLINKCHOICE_FILE:
2073 {
2074 const char *pszFile = NULL;
2075 int rc = pPeImage->T0.File.u.pT2 && pPeImage->T0.File.u.pT2->File.u.pAscii
2076 ? RTAsn1String_QueryUtf8(pPeImage->T0.File.u.pT2->File.u.pAscii, &pszFile, NULL)
2077 : VERR_NOT_FOUND;
2078 if (RT_SUCCESS(rc))
2079 RTPrintf("%s File: '%s'\n", pThis->szPrefix, pszFile);
2080 else
2081 RTPrintf("%s File: rc=%Rrc\n", pThis->szPrefix, rc);
2082 break;
2083 }
2084
2085 case RTCRSPCLINKCHOICE_NOT_PRESENT:
2086 RTPrintf("%s File not present!\n", pThis->szPrefix);
2087 break;
2088 default:
2089 RTPrintf("%s enmChoice=%d!\n", pThis->szPrefix, pPeImage->T0.File.enmChoice);
2090 break;
2091 }
2092 break;
2093 }
2094
2095 case RTCRSPCAAOVTYPE_UNKNOWN:
2096 HandleShowExeWorkerDisplayObjId(pThis, &pIndData->Data.Type, " Data Type: ", "\n");
2097 break;
2098 case RTCRSPCAAOVTYPE_NOT_PRESENT:
2099 RTPrintf("%s Data Type: Not present!\n", pThis->szPrefix);
2100 break;
2101 default:
2102 RTPrintf("%s Data Type: enmType=%d!\n", pThis->szPrefix, pIndData->Data.enmType);
2103 break;
2104 }
2105
2106 return VINF_SUCCESS;
2107}
2108
2109
2110/**
2111 * Display an PKCS#7 signed data instance.
2112 *
2113 * @returns IPRT status code.
2114 * @param pThis The show exe instance data.
2115 * @param pSignedData The signed data to display.
2116 * @param offPrefix The current prefix offset.
2117 * @param pContentInfo The content info structure (for the size).
2118 */
2119static int HandleShowExeWorkerPkcs7Display(PSHOWEXEPKCS7 pThis, PRTCRPKCS7SIGNEDDATA pSignedData, size_t offPrefix,
2120 PCRTCRPKCS7CONTENTINFO pContentInfo)
2121{
2122 pThis->szPrefix[offPrefix] = '\0';
2123 RTPrintf("%sPKCS#7 signature: %u (%#x) bytes\n", pThis->szPrefix,
2124 RTASN1CORE_GET_RAW_ASN1_SIZE(&pContentInfo->SeqCore.Asn1Core),
2125 RTASN1CORE_GET_RAW_ASN1_SIZE(&pContentInfo->SeqCore.Asn1Core));
2126
2127 /*
2128 * Display list of signing algorithms.
2129 */
2130 RTPrintf("%sDigestAlgorithms: ", pThis->szPrefix);
2131 if (pSignedData->DigestAlgorithms.cItems == 0)
2132 RTPrintf("none");
2133 for (unsigned i = 0; i < pSignedData->DigestAlgorithms.cItems; i++)
2134 {
2135 PCRTCRX509ALGORITHMIDENTIFIER pAlgoId = pSignedData->DigestAlgorithms.papItems[i];
2136 const char *pszDigestType = RTCrDigestTypeToName(RTCrX509AlgorithmIdentifier_QueryDigestType(pAlgoId));
2137 if (!pszDigestType)
2138 pszDigestType = pAlgoId->Algorithm.szObjId;
2139 RTPrintf(i == 0 ? "%s" : ", %s", pszDigestType);
2140 if (pThis->cVerbosity > 1)
2141 RTPrintf(" (%s)", pAlgoId->Algorithm.szObjId);
2142 }
2143 RTPrintf("\n");
2144
2145 /*
2146 * Display the signed data content.
2147 */
2148 if (RTAsn1ObjId_CompareWithString(&pSignedData->ContentInfo.ContentType, RTCRSPCINDIRECTDATACONTENT_OID) == 0)
2149 {
2150 RTPrintf("%s ContentType: SpcIndirectDataContent (" RTCRSPCINDIRECTDATACONTENT_OID ")\n", pThis->szPrefix);
2151 size_t offPrefix2 = RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, " SPC Ind Data: ");
2152 HandleShowExeWorkerPkcs7DisplaySpcIdirectDataContent(pThis, offPrefix2 + offPrefix,
2153 pSignedData->ContentInfo.u.pIndirectDataContent);
2154 pThis->szPrefix[offPrefix] = '\0';
2155 }
2156 else
2157 HandleShowExeWorkerDisplayObjId(pThis, &pSignedData->ContentInfo.ContentType, " ContentType: ", " - not implemented.\n");
2158
2159 /*
2160 * Display certificates (Certificates).
2161 */
2162 if (pSignedData->Certificates.cItems > 0)
2163 {
2164 RTPrintf("%s Certificates: %u\n", pThis->szPrefix, pSignedData->Certificates.cItems);
2165 if (pThis->cVerbosity >= 2)
2166 {
2167 for (uint32_t i = 0; i < pSignedData->Certificates.cItems; i++)
2168 {
2169 if (i != 0)
2170 RTPrintf("\n");
2171 RTPrintf("%s Certificate #%u:\n", pThis->szPrefix, i);
2172 RTAsn1Dump(RTCrPkcs7Cert_GetAsn1Core(pSignedData->Certificates.papItems[i]), 0,
2173 ((uint32_t)offPrefix + 9) / 2, RTStrmDumpPrintfV, g_pStdOut);
2174 }
2175 }
2176 /** @todo display certificates properly. */
2177 }
2178
2179 if (pSignedData->Crls.cb > 0)
2180 RTPrintf("%s CRLs: %u bytes\n", pThis->szPrefix, pSignedData->Crls.cb);
2181
2182 /*
2183 * Show signatures (SignerInfos).
2184 */
2185 unsigned const cSigInfos = pSignedData->SignerInfos.cItems;
2186 if (cSigInfos != 1)
2187 RTPrintf("%s SignerInfos: %u signers\n", pThis->szPrefix, cSigInfos);
2188 else
2189 RTPrintf("%s SignerInfos:\n", pThis->szPrefix);
2190 for (unsigned i = 0; i < cSigInfos; i++)
2191 {
2192 PRTCRPKCS7SIGNERINFO pSigInfo = pSignedData->SignerInfos.papItems[i];
2193 size_t offPrefix2 = offPrefix;
2194 if (cSigInfos != 1)
2195 offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, "SignerInfo[%u]: ", i);
2196
2197 int rc = RTAsn1Integer_ToString(&pSigInfo->IssuerAndSerialNumber.SerialNumber,
2198 pThis->szTmp, sizeof(pThis->szTmp), 0 /*fFlags*/, NULL);
2199 if (RT_FAILURE(rc))
2200 RTStrPrintf(pThis->szTmp, sizeof(pThis->szTmp), "%Rrc", rc);
2201 RTPrintf("%s Serial No: %s\n", pThis->szPrefix, pThis->szTmp);
2202
2203 rc = RTCrX509Name_FormatAsString(&pSigInfo->IssuerAndSerialNumber.Name, pThis->szTmp, sizeof(pThis->szTmp), NULL);
2204 if (RT_FAILURE(rc))
2205 RTStrPrintf(pThis->szTmp, sizeof(pThis->szTmp), "%Rrc", rc);
2206 RTPrintf("%s Issuer: %s\n", pThis->szPrefix, pThis->szTmp);
2207
2208 const char *pszType = RTCrDigestTypeToName(RTCrX509AlgorithmIdentifier_QueryDigestType(&pSigInfo->DigestAlgorithm));
2209 if (!pszType)
2210 pszType = pSigInfo->DigestAlgorithm.Algorithm.szObjId;
2211 RTPrintf("%s Digest Algorithm: %s", pThis->szPrefix, pszType);
2212 if (pThis->cVerbosity > 1)
2213 RTPrintf(" (%s)\n", pSigInfo->DigestAlgorithm.Algorithm.szObjId);
2214 else
2215 RTPrintf("\n");
2216
2217 HandleShowExeWorkerDisplayObjId(pThis, &pSigInfo->DigestEncryptionAlgorithm.Algorithm,
2218 "Digest Encryption Algorithm: ", "\n");
2219
2220 if (pSigInfo->AuthenticatedAttributes.cItems == 0)
2221 RTPrintf("%s Authenticated Attributes: none\n", pThis->szPrefix);
2222 else
2223 {
2224 RTPrintf("%s Authenticated Attributes: %u item%s\n", pThis->szPrefix,
2225 pSigInfo->AuthenticatedAttributes.cItems, pSigInfo->AuthenticatedAttributes.cItems > 1 ? "s" : "");
2226 for (unsigned j = 0; j < pSigInfo->AuthenticatedAttributes.cItems; j++)
2227 {
2228 PRTCRPKCS7ATTRIBUTE pAttr = pSigInfo->AuthenticatedAttributes.papItems[j];
2229 size_t offPrefix3 = offPrefix2 + RTStrPrintf(&pThis->szPrefix[offPrefix2], sizeof(pThis->szPrefix) - offPrefix2,
2230 " AuthAttrib[%u]: ", j);
2231 HandleShowExeWorkerPkcs7DisplayAttrib(pThis, offPrefix3, pAttr);
2232 }
2233 pThis->szPrefix[offPrefix2] = '\0';
2234 }
2235
2236 if (pSigInfo->UnauthenticatedAttributes.cItems == 0)
2237 RTPrintf("%s Unauthenticated Attributes: none\n", pThis->szPrefix);
2238 else
2239 {
2240 RTPrintf("%s Unauthenticated Attributes: %u item%s\n", pThis->szPrefix,
2241 pSigInfo->UnauthenticatedAttributes.cItems, pSigInfo->UnauthenticatedAttributes.cItems > 1 ? "s" : "");
2242 for (unsigned j = 0; j < pSigInfo->UnauthenticatedAttributes.cItems; j++)
2243 {
2244 PRTCRPKCS7ATTRIBUTE pAttr = pSigInfo->UnauthenticatedAttributes.papItems[j];
2245 size_t offPrefix3 = offPrefix2 + RTStrPrintf(&pThis->szPrefix[offPrefix2], sizeof(pThis->szPrefix) - offPrefix2,
2246 " UnauthAttrib[%u]: ", j);
2247 HandleShowExeWorkerPkcs7DisplayAttrib(pThis, offPrefix3, pAttr);
2248 }
2249 pThis->szPrefix[offPrefix2] = '\0';
2250 }
2251
2252 /** @todo show the encrypted stuff (EncryptedDigest)? */
2253 }
2254 pThis->szPrefix[offPrefix] = '\0';
2255
2256 return VINF_SUCCESS;
2257}
2258
2259
2260/*
2261 * The 'show-exe' command.
2262 */
2263static RTEXITCODE HelpShowExe(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
2264{
2265 RT_NOREF_PV(enmLevel);
2266 RTStrmPrintf(pStrm,
2267 "show-exe [--verbose|-v] [--quiet|-q] <exe1> [exe2 [..]]\n");
2268 return RTEXITCODE_SUCCESS;
2269}
2270
2271
2272static RTEXITCODE HandleShowExe(int cArgs, char **papszArgs)
2273{
2274 /*
2275 * Parse arguments.
2276 */
2277 static const RTGETOPTDEF s_aOptions[] =
2278 {
2279 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
2280 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
2281 };
2282
2283 unsigned cVerbosity = 0;
2284 RTLDRARCH enmLdrArch = RTLDRARCH_WHATEVER;
2285
2286 RTGETOPTSTATE GetState;
2287 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
2288 AssertRCReturn(rc, RTEXITCODE_FAILURE);
2289 RTGETOPTUNION ValueUnion;
2290 int ch;
2291 while ((ch = RTGetOpt(&GetState, &ValueUnion)) && ch != VINF_GETOPT_NOT_OPTION)
2292 {
2293 switch (ch)
2294 {
2295 case 'v': cVerbosity++; break;
2296 case 'q': cVerbosity = 0; break;
2297 case 'V': return HandleVersion(cArgs, papszArgs);
2298 case 'h': return HelpShowExe(g_pStdOut, RTSIGNTOOLHELP_FULL);
2299 default: return RTGetOptPrintError(ch, &ValueUnion);
2300 }
2301 }
2302 if (ch != VINF_GETOPT_NOT_OPTION)
2303 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
2304
2305 /*
2306 * Do it.
2307 */
2308 unsigned iFile = 0;
2309 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
2310 do
2311 {
2312 RTPrintf(iFile == 0 ? "%s:\n" : "\n%s:\n", ValueUnion.psz);
2313
2314 SHOWEXEPKCS7 This;
2315 RT_ZERO(This);
2316 This.cVerbosity = cVerbosity;
2317
2318 RTEXITCODE rcExitThis = SignToolPkcs7Exe_InitFromFile(&This, ValueUnion.psz, cVerbosity, enmLdrArch);
2319 if (rcExitThis == RTEXITCODE_SUCCESS)
2320 {
2321 rc = HandleShowExeWorkerPkcs7Display(&This, This.pSignedData, 0, &This.ContentInfo);
2322 if (RT_FAILURE(rc))
2323 rcExit = RTEXITCODE_FAILURE;
2324 SignToolPkcs7Exe_Delete(&This);
2325 }
2326 if (rcExitThis != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
2327 rcExit = rcExitThis;
2328
2329 iFile++;
2330 } while ((ch = RTGetOpt(&GetState, &ValueUnion)) == VINF_GETOPT_NOT_OPTION);
2331 if (ch != 0)
2332 return RTGetOptPrintError(ch, &ValueUnion);
2333
2334 return rcExit;
2335}
2336
2337
2338/*
2339 * The 'show-cat' command.
2340 */
2341static RTEXITCODE HelpShowCat(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
2342{
2343 RT_NOREF_PV(enmLevel);
2344 RTStrmPrintf(pStrm,
2345 "show-cat [--verbose|-v] [--quiet|-q] <cat1> [cat2 [..]]\n");
2346 return RTEXITCODE_SUCCESS;
2347}
2348
2349
2350static RTEXITCODE HandleShowCat(int cArgs, char **papszArgs)
2351{
2352 /*
2353 * Parse arguments.
2354 */
2355 static const RTGETOPTDEF s_aOptions[] =
2356 {
2357 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
2358 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
2359 };
2360
2361 unsigned cVerbosity = 0;
2362
2363 RTGETOPTSTATE GetState;
2364 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
2365 AssertRCReturn(rc, RTEXITCODE_FAILURE);
2366 RTGETOPTUNION ValueUnion;
2367 int ch;
2368 while ((ch = RTGetOpt(&GetState, &ValueUnion)) && ch != VINF_GETOPT_NOT_OPTION)
2369 {
2370 switch (ch)
2371 {
2372 case 'v': cVerbosity++; break;
2373 case 'q': cVerbosity = 0; break;
2374 case 'V': return HandleVersion(cArgs, papszArgs);
2375 case 'h': return HelpShowCat(g_pStdOut, RTSIGNTOOLHELP_FULL);
2376 default: return RTGetOptPrintError(ch, &ValueUnion);
2377 }
2378 }
2379 if (ch != VINF_GETOPT_NOT_OPTION)
2380 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
2381
2382 /*
2383 * Do it.
2384 */
2385 unsigned iFile = 0;
2386 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
2387 do
2388 {
2389 RTPrintf(iFile == 0 ? "%s:\n" : "\n%s:\n", ValueUnion.psz);
2390
2391 SHOWEXEPKCS7 This;
2392 RT_ZERO(This);
2393 This.cVerbosity = cVerbosity;
2394
2395 RTEXITCODE rcExitThis = SignToolPkcs7_InitFromFile(&This, ValueUnion.psz, cVerbosity);
2396 if (rcExitThis == RTEXITCODE_SUCCESS)
2397 {
2398 This.hLdrMod = NIL_RTLDRMOD;
2399
2400 rc = HandleShowExeWorkerPkcs7Display(&This, This.pSignedData, 0, &This.ContentInfo);
2401 if (RT_FAILURE(rc))
2402 rcExit = RTEXITCODE_FAILURE;
2403 SignToolPkcs7Exe_Delete(&This);
2404 }
2405 if (rcExitThis != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
2406 rcExit = rcExitThis;
2407
2408 iFile++;
2409 } while ((ch = RTGetOpt(&GetState, &ValueUnion)) == VINF_GETOPT_NOT_OPTION);
2410 if (ch != 0)
2411 return RTGetOptPrintError(ch, &ValueUnion);
2412
2413 return rcExit;
2414}
2415
2416
2417/*
2418 * The 'make-tainfo' command.
2419 */
2420static RTEXITCODE HelpMakeTaInfo(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
2421{
2422 RT_NOREF_PV(enmLevel);
2423 RTStrmPrintf(pStrm,
2424 "make-tainfo [--verbose|--quiet] [--cert <cert.der>] [-o|--output] <tainfo.der>\n");
2425 return RTEXITCODE_SUCCESS;
2426}
2427
2428
2429typedef struct MAKETAINFOSTATE
2430{
2431 int cVerbose;
2432 const char *pszCert;
2433 const char *pszOutput;
2434} MAKETAINFOSTATE;
2435
2436
2437/** @callback_method_impl{FNRTASN1ENCODEWRITER} */
2438static DECLCALLBACK(int) handleMakeTaInfoWriter(const void *pvBuf, size_t cbToWrite, void *pvUser, PRTERRINFO pErrInfo)
2439{
2440 RT_NOREF_PV(pErrInfo);
2441 return RTStrmWrite((PRTSTREAM)pvUser, pvBuf, cbToWrite);
2442}
2443
2444
2445static RTEXITCODE HandleMakeTaInfo(int cArgs, char **papszArgs)
2446{
2447 /*
2448 * Parse arguments.
2449 */
2450 static const RTGETOPTDEF s_aOptions[] =
2451 {
2452 { "--cert", 'c', RTGETOPT_REQ_STRING },
2453 { "--output", 'o', RTGETOPT_REQ_STRING },
2454 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
2455 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
2456 };
2457
2458 MAKETAINFOSTATE State = { 0, NULL, NULL };
2459
2460 RTGETOPTSTATE GetState;
2461 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
2462 AssertRCReturn(rc, RTEXITCODE_FAILURE);
2463 RTGETOPTUNION ValueUnion;
2464 int ch;
2465 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
2466 {
2467 switch (ch)
2468 {
2469 case 'c':
2470 if (State.pszCert)
2471 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The --cert option can only be used once.");
2472 State.pszCert = ValueUnion.psz;
2473 break;
2474
2475 case 'o':
2476 case VINF_GETOPT_NOT_OPTION:
2477 if (State.pszOutput)
2478 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Multiple output files specified.");
2479 State.pszOutput = ValueUnion.psz;
2480 break;
2481
2482 case 'v': State.cVerbose++; break;
2483 case 'q': State.cVerbose = 0; break;
2484 case 'V': return HandleVersion(cArgs, papszArgs);
2485 case 'h': return HelpMakeTaInfo(g_pStdOut, RTSIGNTOOLHELP_FULL);
2486 default: return RTGetOptPrintError(ch, &ValueUnion);
2487 }
2488 }
2489 if (!State.pszCert)
2490 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No input certificate was specified.");
2491 if (!State.pszOutput)
2492 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No output file was specified.");
2493
2494 /*
2495 * Read the certificate.
2496 */
2497 RTERRINFOSTATIC StaticErrInfo;
2498 RTCRX509CERTIFICATE Certificate;
2499 rc = RTCrX509Certificate_ReadFromFile(&Certificate, State.pszCert, 0, &g_RTAsn1DefaultAllocator,
2500 RTErrInfoInitStatic(&StaticErrInfo));
2501 if (RT_FAILURE(rc))
2502 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error reading certificate from %s: %Rrc - %s",
2503 State.pszCert, rc, StaticErrInfo.szMsg);
2504 /*
2505 * Construct the trust anchor information.
2506 */
2507 RTCRTAFTRUSTANCHORINFO TrustAnchor;
2508 rc = RTCrTafTrustAnchorInfo_Init(&TrustAnchor, &g_RTAsn1DefaultAllocator);
2509 if (RT_SUCCESS(rc))
2510 {
2511 /* Public key. */
2512 Assert(RTCrX509SubjectPublicKeyInfo_IsPresent(&TrustAnchor.PubKey));
2513 RTCrX509SubjectPublicKeyInfo_Delete(&TrustAnchor.PubKey);
2514 rc = RTCrX509SubjectPublicKeyInfo_Clone(&TrustAnchor.PubKey, &Certificate.TbsCertificate.SubjectPublicKeyInfo,
2515 &g_RTAsn1DefaultAllocator);
2516 if (RT_FAILURE(rc))
2517 RTMsgError("RTCrX509SubjectPublicKeyInfo_Clone failed: %Rrc", rc);
2518 RTAsn1Core_ResetImplict(RTCrX509SubjectPublicKeyInfo_GetAsn1Core(&TrustAnchor.PubKey)); /* temporary hack. */
2519
2520 /* Key Identifier. */
2521 PCRTASN1OCTETSTRING pKeyIdentifier = NULL;
2522 if (Certificate.TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_SUBJECT_KEY_IDENTIFIER)
2523 pKeyIdentifier = Certificate.TbsCertificate.T3.pSubjectKeyIdentifier;
2524 else if ( (Certificate.TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_AUTHORITY_KEY_IDENTIFIER)
2525 && RTCrX509Certificate_IsSelfSigned(&Certificate)
2526 && RTAsn1OctetString_IsPresent(&Certificate.TbsCertificate.T3.pAuthorityKeyIdentifier->KeyIdentifier) )
2527 pKeyIdentifier = &Certificate.TbsCertificate.T3.pAuthorityKeyIdentifier->KeyIdentifier;
2528 else if ( (Certificate.TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_OLD_AUTHORITY_KEY_IDENTIFIER)
2529 && RTCrX509Certificate_IsSelfSigned(&Certificate)
2530 && RTAsn1OctetString_IsPresent(&Certificate.TbsCertificate.T3.pOldAuthorityKeyIdentifier->KeyIdentifier) )
2531 pKeyIdentifier = &Certificate.TbsCertificate.T3.pOldAuthorityKeyIdentifier->KeyIdentifier;
2532 if (pKeyIdentifier && pKeyIdentifier->Asn1Core.cb > 0)
2533 {
2534 Assert(RTAsn1OctetString_IsPresent(&TrustAnchor.KeyIdentifier));
2535 RTAsn1OctetString_Delete(&TrustAnchor.KeyIdentifier);
2536 rc = RTAsn1OctetString_Clone(&TrustAnchor.KeyIdentifier, pKeyIdentifier, &g_RTAsn1DefaultAllocator);
2537 if (RT_FAILURE(rc))
2538 RTMsgError("RTAsn1OctetString_Clone failed: %Rrc", rc);
2539 RTAsn1Core_ResetImplict(RTAsn1OctetString_GetAsn1Core(&TrustAnchor.KeyIdentifier)); /* temporary hack. */
2540 }
2541 else
2542 RTMsgWarning("No key identifier found or has zero length.");
2543
2544 /* Subject */
2545 if (RT_SUCCESS(rc))
2546 {
2547 Assert(!RTCrTafCertPathControls_IsPresent(&TrustAnchor.CertPath));
2548 rc = RTCrTafCertPathControls_Init(&TrustAnchor.CertPath, &g_RTAsn1DefaultAllocator);
2549 if (RT_SUCCESS(rc))
2550 {
2551 Assert(RTCrX509Name_IsPresent(&TrustAnchor.CertPath.TaName));
2552 RTCrX509Name_Delete(&TrustAnchor.CertPath.TaName);
2553 rc = RTCrX509Name_Clone(&TrustAnchor.CertPath.TaName, &Certificate.TbsCertificate.Subject,
2554 &g_RTAsn1DefaultAllocator);
2555 if (RT_SUCCESS(rc))
2556 {
2557 RTAsn1Core_ResetImplict(RTCrX509Name_GetAsn1Core(&TrustAnchor.CertPath.TaName)); /* temporary hack. */
2558 rc = RTCrX509Name_RecodeAsUtf8(&TrustAnchor.CertPath.TaName, &g_RTAsn1DefaultAllocator);
2559 if (RT_FAILURE(rc))
2560 RTMsgError("RTCrX509Name_RecodeAsUtf8 failed: %Rrc", rc);
2561 }
2562 else
2563 RTMsgError("RTCrX509Name_Clone failed: %Rrc", rc);
2564 }
2565 else
2566 RTMsgError("RTCrTafCertPathControls_Init failed: %Rrc", rc);
2567 }
2568
2569 /* Check that what we've constructed makes some sense. */
2570 if (RT_SUCCESS(rc))
2571 {
2572 rc = RTCrTafTrustAnchorInfo_CheckSanity(&TrustAnchor, 0, RTErrInfoInitStatic(&StaticErrInfo), "TAI");
2573 if (RT_FAILURE(rc))
2574 RTMsgError("RTCrTafTrustAnchorInfo_CheckSanity failed: %Rrc - %s", rc, StaticErrInfo.szMsg);
2575 }
2576
2577 if (RT_SUCCESS(rc))
2578 {
2579 /*
2580 * Encode it and write it to the output file.
2581 */
2582 uint32_t cbEncoded;
2583 rc = RTAsn1EncodePrepare(RTCrTafTrustAnchorInfo_GetAsn1Core(&TrustAnchor), RTASN1ENCODE_F_DER, &cbEncoded,
2584 RTErrInfoInitStatic(&StaticErrInfo));
2585 if (RT_SUCCESS(rc))
2586 {
2587 if (State.cVerbose >= 1)
2588 RTAsn1Dump(RTCrTafTrustAnchorInfo_GetAsn1Core(&TrustAnchor), 0, 0, RTStrmDumpPrintfV, g_pStdOut);
2589
2590 PRTSTREAM pStrm;
2591 rc = RTStrmOpen(State.pszOutput, "wb", &pStrm);
2592 if (RT_SUCCESS(rc))
2593 {
2594 rc = RTAsn1EncodeWrite(RTCrTafTrustAnchorInfo_GetAsn1Core(&TrustAnchor), RTASN1ENCODE_F_DER,
2595 handleMakeTaInfoWriter, pStrm, RTErrInfoInitStatic(&StaticErrInfo));
2596 if (RT_SUCCESS(rc))
2597 {
2598 rc = RTStrmClose(pStrm);
2599 if (RT_SUCCESS(rc))
2600 RTMsgInfo("Successfully wrote TrustedAnchorInfo to '%s'.", State.pszOutput);
2601 else
2602 RTMsgError("RTStrmClose failed: %Rrc", rc);
2603 }
2604 else
2605 {
2606 RTMsgError("RTAsn1EncodeWrite failed: %Rrc - %s", rc, StaticErrInfo.szMsg);
2607 RTStrmClose(pStrm);
2608 }
2609 }
2610 else
2611 RTMsgError("Error opening '%s' for writing: %Rrcs", State.pszOutput, rc);
2612 }
2613 else
2614 RTMsgError("RTAsn1EncodePrepare failed: %Rrc - %s", rc, StaticErrInfo.szMsg);
2615 }
2616
2617 RTCrTafTrustAnchorInfo_Delete(&TrustAnchor);
2618 }
2619 else
2620 RTMsgError("RTCrTafTrustAnchorInfo_Init failed: %Rrc", rc);
2621
2622 RTCrX509Certificate_Delete(&Certificate);
2623 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2624}
2625
2626
2627
2628/*
2629 * The 'version' command.
2630 */
2631static RTEXITCODE HelpVersion(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
2632{
2633 RT_NOREF_PV(enmLevel);
2634 RTStrmPrintf(pStrm, "version\n");
2635 return RTEXITCODE_SUCCESS;
2636}
2637
2638static RTEXITCODE HandleVersion(int cArgs, char **papszArgs)
2639{
2640 RT_NOREF_PV(cArgs); RT_NOREF_PV(papszArgs);
2641#ifndef IN_BLD_PROG /* RTBldCfgVersion or RTBldCfgRevision in build time IPRT lib. */
2642 RTPrintf("%s\n", RTBldCfgVersion());
2643 return RTEXITCODE_SUCCESS;
2644#else
2645 return RTEXITCODE_FAILURE;
2646#endif
2647}
2648
2649
2650
2651/**
2652 * Command mapping.
2653 */
2654static struct
2655{
2656 /** The command. */
2657 const char *pszCmd;
2658 /**
2659 * Handle the command.
2660 * @returns Program exit code.
2661 * @param cArgs Number of arguments.
2662 * @param papszArgs The argument vector, starting with the command name.
2663 */
2664 RTEXITCODE (*pfnHandler)(int cArgs, char **papszArgs);
2665 /**
2666 * Produce help.
2667 * @returns RTEXITCODE_SUCCESS to simplify handling '--help' in the handler.
2668 * @param pStrm Where to send help text.
2669 * @param enmLevel The level of the help information.
2670 */
2671 RTEXITCODE (*pfnHelp)(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel);
2672}
2673/** Mapping commands to handler and helper functions. */
2674const g_aCommands[] =
2675{
2676 { "extract-exe-signer-cert", HandleExtractExeSignerCert, HelpExtractExeSignerCert },
2677 { "add-nested-exe-signature", HandleAddNestedExeSignature, HelpAddNestedExeSignature },
2678 { "add-nested-cat-signature", HandleAddNestedCatSignature, HelpAddNestedCatSignature },
2679#ifndef IPRT_IN_BUILD_TOOL
2680 { "verify-exe", HandleVerifyExe, HelpVerifyExe },
2681#endif
2682 { "show-exe", HandleShowExe, HelpShowExe },
2683 { "show-cat", HandleShowCat, HelpShowCat },
2684 { "make-tainfo", HandleMakeTaInfo, HelpMakeTaInfo },
2685 { "help", HandleHelp, HelpHelp },
2686 { "--help", HandleHelp, NULL },
2687 { "-h", HandleHelp, NULL },
2688 { "version", HandleVersion, HelpVersion },
2689 { "--version", HandleVersion, NULL },
2690 { "-V", HandleVersion, NULL },
2691};
2692
2693
2694/*
2695 * The 'help' command.
2696 */
2697static RTEXITCODE HelpHelp(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
2698{
2699 RT_NOREF_PV(enmLevel);
2700 RTStrmPrintf(pStrm, "help [cmd-patterns]\n");
2701 return RTEXITCODE_SUCCESS;
2702}
2703
2704static RTEXITCODE HandleHelp(int cArgs, char **papszArgs)
2705{
2706 RTSIGNTOOLHELP enmLevel = cArgs <= 1 ? RTSIGNTOOLHELP_USAGE : RTSIGNTOOLHELP_FULL;
2707 uint32_t cShowed = 0;
2708 for (uint32_t iCmd = 0; iCmd < RT_ELEMENTS(g_aCommands); iCmd++)
2709 {
2710 if (g_aCommands[iCmd].pfnHelp)
2711 {
2712 bool fShow = false;
2713 if (cArgs <= 1)
2714 fShow = true;
2715 else
2716 {
2717 for (int iArg = 1; iArg < cArgs; iArg++)
2718 if (RTStrSimplePatternMultiMatch(papszArgs[iArg], RTSTR_MAX, g_aCommands[iCmd].pszCmd, RTSTR_MAX, NULL))
2719 {
2720 fShow = true;
2721 break;
2722 }
2723 }
2724 if (fShow)
2725 {
2726 g_aCommands[iCmd].pfnHelp(g_pStdOut, enmLevel);
2727 cShowed++;
2728 }
2729 }
2730 }
2731 return cShowed ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2732}
2733
2734
2735
2736int main(int argc, char **argv)
2737{
2738 int rc = RTR3InitExe(argc, &argv, 0);
2739 if (RT_FAILURE(rc))
2740 return RTMsgInitFailure(rc);
2741
2742 /*
2743 * Parse global arguments.
2744 */
2745 int iArg = 1;
2746 /* none presently. */
2747
2748 /*
2749 * Command dispatcher.
2750 */
2751 if (iArg < argc)
2752 {
2753 const char *pszCmd = argv[iArg];
2754 uint32_t i = RT_ELEMENTS(g_aCommands);
2755 while (i-- > 0)
2756 if (!strcmp(g_aCommands[i].pszCmd, pszCmd))
2757 return g_aCommands[i].pfnHandler(argc - iArg, &argv[iArg]);
2758 RTMsgError("Unknown command '%s'.", pszCmd);
2759 }
2760 else
2761 RTMsgError("No command given. (try --help)");
2762
2763 return RTEXITCODE_SYNTAX;
2764}
2765
Note: See TracBrowser for help on using the repository browser.

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