VirtualBox

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

Last change on this file since 74760 was 74760, checked in by vboxsync, 6 years ago

IPRT/ldr/asn1/pkcs7: Ironed out issues in decoding indefinite ASN.1 length records and successfully verified the first Mach-O signature. bugref:9232

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