VirtualBox

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

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

(C) year

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