VirtualBox

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

Last change on this file since 71877 was 71877, checked in by vboxsync, 7 years ago

RTSignTool: Added a --prepend flag to the add-nested-cat-signature and add-nested-exe-signature, also fixed handling of more than 1 nested signature. [bug/build fix]

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