VirtualBox

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

Last change on this file since 93115 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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