VirtualBox

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

Last change on this file since 95583 was 95583, checked in by vboxsync, 2 years ago

RTSignTool: Some showexe work. bugref:8691

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