VirtualBox

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

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

RTSignTool,IPRT: Working on signing executable images. [build fix] bugref:8691

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 171.0 KB
Line 
1/* $Id: RTSignTool.cpp 95615 2022-07-13 01:25:01Z 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/ctype.h>
34#include <iprt/err.h>
35#include <iprt/getopt.h>
36#include <iprt/file.h>
37#include <iprt/initterm.h>
38#include <iprt/ldr.h>
39#include <iprt/message.h>
40#include <iprt/mem.h>
41#include <iprt/path.h>
42#include <iprt/stream.h>
43#include <iprt/string.h>
44#include <iprt/uuid.h>
45#include <iprt/zero.h>
46#ifndef RT_OS_WINDOWS
47# include <iprt/formats/pecoff.h>
48#endif
49#include <iprt/crypto/applecodesign.h>
50#include <iprt/crypto/digest.h>
51#include <iprt/crypto/key.h>
52#include <iprt/crypto/x509.h>
53#include <iprt/crypto/pkcs7.h>
54#include <iprt/crypto/store.h>
55#include <iprt/crypto/spc.h>
56#ifdef VBOX
57# include <VBox/sup.h> /* Certificates */
58#endif
59#ifdef RT_OS_WINDOWS
60# include <iprt/win/windows.h>
61# include <iprt/win/imagehlp.h>
62#endif
63
64
65/*********************************************************************************************************************************
66* Defined Constants And Macros *
67*********************************************************************************************************************************/
68#define OPT_HASH_PAGES 1000
69#define OPT_NO_HASH_PAGES 1001
70#define OPT_CERT_FILE 1002
71#define OPT_KEY_FILE 1003
72#define OPT_ADD_CERT 1004
73
74#define OPT_TIMESTAMP_CERT_FILE 1010
75#define OPT_TIMESTAMP_KEY_FILE 1011
76#define OPT_TIMESTAMP_TYPE 1012
77#define OPT_TIMESTAMP_OVERRIDE 1016
78
79
80/*********************************************************************************************************************************
81* Structures and Typedefs *
82*********************************************************************************************************************************/
83/** Help detail levels. */
84typedef enum RTSIGNTOOLHELP
85{
86 RTSIGNTOOLHELP_USAGE,
87 RTSIGNTOOLHELP_FULL
88} RTSIGNTOOLHELP;
89
90
91/**
92 * PKCS\#7 signature data.
93 */
94typedef struct SIGNTOOLPKCS7
95{
96 /** The raw signature. */
97 uint8_t *pbBuf;
98 /** Size of the raw signature. */
99 size_t cbBuf;
100 /** The filename. */
101 const char *pszFilename;
102 /** The outer content info wrapper. */
103 RTCRPKCS7CONTENTINFO ContentInfo;
104 /** Pointer to the decoded SignedData inside the ContentInfo member. */
105 PRTCRPKCS7SIGNEDDATA pSignedData;
106
107 /** Newly encoded raw signature.
108 * @sa SignToolPkcs7_Encode() */
109 uint8_t *pbNewBuf;
110 /** Size of newly encoded raw signature. */
111 size_t cbNewBuf;
112
113} SIGNTOOLPKCS7;
114typedef SIGNTOOLPKCS7 *PSIGNTOOLPKCS7;
115
116
117/**
118 * PKCS\#7 signature data for executable.
119 */
120typedef struct SIGNTOOLPKCS7EXE : public SIGNTOOLPKCS7
121{
122 /** The module handle. */
123 RTLDRMOD hLdrMod;
124} SIGNTOOLPKCS7EXE;
125typedef SIGNTOOLPKCS7EXE *PSIGNTOOLPKCS7EXE;
126
127
128/**
129 * Data for the show exe (signature) command.
130 */
131typedef struct SHOWEXEPKCS7 : public SIGNTOOLPKCS7EXE
132{
133 /** The verbosity. */
134 unsigned cVerbosity;
135 /** The prefix buffer. */
136 char szPrefix[256];
137 /** Temporary buffer. */
138 char szTmp[4096];
139} SHOWEXEPKCS7;
140typedef SHOWEXEPKCS7 *PSHOWEXEPKCS7;
141
142
143/**
144 * Certificate w/ public key + private key pair for signing.
145 */
146typedef struct SIGNTOOLKEYPAIR
147{
148 RTCRX509CERTIFICATE Cert;
149 PCRTCRX509CERTIFICATE pCertificate;
150 RTCRKEY hPrivateKey;
151
152 SIGNTOOLKEYPAIR()
153 : pCertificate(NULL)
154 , hPrivateKey(NIL_RTCRKEY)
155 {
156 RT_ZERO(Cert);
157 }
158
159 ~SIGNTOOLKEYPAIR()
160 {
161 if (hPrivateKey != NIL_RTCRKEY)
162 {
163 RTCrKeyRelease(hPrivateKey);
164 hPrivateKey = NIL_RTCRKEY;
165 }
166 if (pCertificate == &Cert)
167 {
168 RTCrX509Certificate_Delete(&Cert);
169 pCertificate = NULL;
170 }
171 }
172
173 bool isComplete(void) const
174 {
175 return pCertificate && hPrivateKey != NIL_RTCRKEY;
176 }
177
178 bool isNull(void) const
179 {
180 return pCertificate == NULL && hPrivateKey == NIL_RTCRKEY;
181 }
182} SIGNTOOLKEYPAIR;
183
184
185/*********************************************************************************************************************************
186* Internal Functions *
187*********************************************************************************************************************************/
188static RTEXITCODE HandleHelp(int cArgs, char **papszArgs);
189static RTEXITCODE HelpHelp(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel);
190static RTEXITCODE HandleVersion(int cArgs, char **papszArgs);
191static int HandleShowExeWorkerPkcs7DisplaySignerInfo(PSHOWEXEPKCS7 pThis, size_t offPrefix, PCRTCRPKCS7SIGNERINFO pSignerInfo);
192static int HandleShowExeWorkerPkcs7Display(PSHOWEXEPKCS7 pThis, PRTCRPKCS7SIGNEDDATA pSignedData, size_t offPrefix,
193 PCRTCRPKCS7CONTENTINFO pContentInfo);
194
195
196/**
197 * Deletes the structure.
198 *
199 * @param pThis The structure to initialize.
200 */
201static void SignToolPkcs7_Delete(PSIGNTOOLPKCS7 pThis)
202{
203 RTCrPkcs7ContentInfo_Delete(&pThis->ContentInfo);
204 pThis->pSignedData = NULL;
205 RTMemFree(pThis->pbBuf);
206 pThis->pbBuf = NULL;
207 pThis->cbBuf = 0;
208 RTMemFree(pThis->pbNewBuf);
209 pThis->pbNewBuf = NULL;
210 pThis->cbNewBuf = 0;
211}
212
213
214/**
215 * Deletes the structure.
216 *
217 * @param pThis The structure to initialize.
218 */
219static void SignToolPkcs7Exe_Delete(PSIGNTOOLPKCS7EXE pThis)
220{
221 if (pThis->hLdrMod != NIL_RTLDRMOD)
222 {
223 int rc2 = RTLdrClose(pThis->hLdrMod);
224 if (RT_FAILURE(rc2))
225 RTMsgError("RTLdrClose failed: %Rrc\n", rc2);
226 pThis->hLdrMod = NIL_RTLDRMOD;
227 }
228 SignToolPkcs7_Delete(pThis);
229}
230
231
232/**
233 * Decodes the PKCS #7 blob pointed to by pThis->pbBuf.
234 *
235 * @returns IPRT status code (error message already shown on failure).
236 * @param pThis The PKCS\#7 signature to decode.
237 * @param fCatalog Set if catalog file, clear if executable.
238 */
239static int SignToolPkcs7_Decode(PSIGNTOOLPKCS7 pThis, bool fCatalog)
240{
241 RTERRINFOSTATIC ErrInfo;
242 RTASN1CURSORPRIMARY PrimaryCursor;
243 RTAsn1CursorInitPrimary(&PrimaryCursor, pThis->pbBuf, (uint32_t)pThis->cbBuf, RTErrInfoInitStatic(&ErrInfo),
244 &g_RTAsn1DefaultAllocator, 0, "WinCert");
245
246 int rc = RTCrPkcs7ContentInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, &pThis->ContentInfo, "CI");
247 if (RT_SUCCESS(rc))
248 {
249 if (RTCrPkcs7ContentInfo_IsSignedData(&pThis->ContentInfo))
250 {
251 pThis->pSignedData = pThis->ContentInfo.u.pSignedData;
252
253 /*
254 * Decode the authenticode bits.
255 */
256 if (!strcmp(pThis->pSignedData->ContentInfo.ContentType.szObjId, RTCRSPCINDIRECTDATACONTENT_OID))
257 {
258 PRTCRSPCINDIRECTDATACONTENT pIndData = pThis->pSignedData->ContentInfo.u.pIndirectDataContent;
259 Assert(pIndData);
260
261 /*
262 * Check that things add up.
263 */
264 rc = RTCrPkcs7SignedData_CheckSanity(pThis->pSignedData,
265 RTCRPKCS7SIGNEDDATA_SANITY_F_AUTHENTICODE
266 | RTCRPKCS7SIGNEDDATA_SANITY_F_ONLY_KNOWN_HASH
267 | RTCRPKCS7SIGNEDDATA_SANITY_F_SIGNING_CERT_PRESENT,
268 RTErrInfoInitStatic(&ErrInfo), "SD");
269 if (RT_SUCCESS(rc))
270 {
271 rc = RTCrSpcIndirectDataContent_CheckSanityEx(pIndData,
272 pThis->pSignedData,
273 RTCRSPCINDIRECTDATACONTENT_SANITY_F_ONLY_KNOWN_HASH,
274 RTErrInfoInitStatic(&ErrInfo));
275 if (RT_FAILURE(rc))
276 RTMsgError("SPC indirect data content sanity check failed for '%s': %Rrc - %s\n",
277 pThis->pszFilename, rc, ErrInfo.szMsg);
278 }
279 else
280 RTMsgError("PKCS#7 sanity check failed for '%s': %Rrc - %s\n", pThis->pszFilename, rc, ErrInfo.szMsg);
281 }
282 else if (!strcmp(pThis->pSignedData->ContentInfo.ContentType.szObjId, RTCR_PKCS7_DATA_OID))
283 { /* apple code signing */ }
284 else if (!fCatalog)
285 RTMsgError("Unexpected the signed content in '%s': %s (expected %s)", pThis->pszFilename,
286 pThis->pSignedData->ContentInfo.ContentType.szObjId, RTCRSPCINDIRECTDATACONTENT_OID);
287 }
288 else
289 rc = RTMsgErrorRc(VERR_CR_PKCS7_NOT_SIGNED_DATA,
290 "PKCS#7 content is inside '%s' is not 'signedData': %s\n",
291 pThis->pszFilename, pThis->ContentInfo.ContentType.szObjId);
292 }
293 else
294 RTMsgError("RTCrPkcs7ContentInfo_DecodeAsn1 failed on '%s': %Rrc - %s\n", pThis->pszFilename, rc, ErrInfo.szMsg);
295 return rc;
296}
297
298
299/**
300 * Reads and decodes PKCS\#7 signature from the given cat file.
301 *
302 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error message
303 * on failure.
304 * @param pThis The structure to initialize.
305 * @param pszFilename The catalog (or any other DER PKCS\#7) filename.
306 * @param cVerbosity The verbosity.
307 */
308static RTEXITCODE SignToolPkcs7_InitFromFile(PSIGNTOOLPKCS7 pThis, const char *pszFilename, unsigned cVerbosity)
309{
310 /*
311 * Init the return structure.
312 */
313 RT_ZERO(*pThis);
314 pThis->pszFilename = pszFilename;
315
316 /*
317 * Lazy bird uses RTFileReadAll and duplicates the allocation.
318 */
319 void *pvFile;
320 int rc = RTFileReadAll(pszFilename, &pvFile, &pThis->cbBuf);
321 if (RT_SUCCESS(rc))
322 {
323 pThis->pbBuf = (uint8_t *)RTMemDup(pvFile, pThis->cbBuf);
324 RTFileReadAllFree(pvFile, pThis->cbBuf);
325 if (pThis->pbBuf)
326 {
327 if (cVerbosity > 2)
328 RTPrintf("PKCS#7 signature: %u bytes\n", pThis->cbBuf);
329
330 /*
331 * Decode it.
332 */
333 rc = SignToolPkcs7_Decode(pThis, true /*fCatalog*/);
334 if (RT_SUCCESS(rc))
335 return RTEXITCODE_SUCCESS;
336 }
337 else
338 RTMsgError("Out of memory!");
339 }
340 else
341 RTMsgError("Error reading '%s' into memory: %Rrc", pszFilename, rc);
342
343 SignToolPkcs7_Delete(pThis);
344 return RTEXITCODE_FAILURE;
345}
346
347
348/**
349 * Encodes the signature into the SIGNTOOLPKCS7::pbNewBuf and
350 * SIGNTOOLPKCS7::cbNewBuf members.
351 *
352 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error message
353 * on failure.
354 * @param pThis The signature to encode.
355 * @param cVerbosity The verbosity.
356 */
357static RTEXITCODE SignToolPkcs7_Encode(PSIGNTOOLPKCS7 pThis, unsigned cVerbosity)
358{
359 RTERRINFOSTATIC StaticErrInfo;
360 PRTASN1CORE pRoot = RTCrPkcs7ContentInfo_GetAsn1Core(&pThis->ContentInfo);
361 uint32_t cbEncoded;
362 int rc = RTAsn1EncodePrepare(pRoot, RTASN1ENCODE_F_DER, &cbEncoded, RTErrInfoInitStatic(&StaticErrInfo));
363 if (RT_SUCCESS(rc))
364 {
365 if (cVerbosity >= 4)
366 RTAsn1Dump(pRoot, 0, 0, RTStrmDumpPrintfV, g_pStdOut);
367
368 RTMemFree(pThis->pbNewBuf);
369 pThis->cbNewBuf = cbEncoded;
370 pThis->pbNewBuf = (uint8_t *)RTMemAllocZ(cbEncoded);
371 if (pThis->pbNewBuf)
372 {
373 rc = RTAsn1EncodeToBuffer(pRoot, RTASN1ENCODE_F_DER, pThis->pbNewBuf, pThis->cbNewBuf,
374 RTErrInfoInitStatic(&StaticErrInfo));
375 if (RT_SUCCESS(rc))
376 {
377 if (cVerbosity > 1)
378 RTMsgInfo("Encoded signature to %u bytes", cbEncoded);
379 return RTEXITCODE_SUCCESS;
380 }
381 RTMsgError("RTAsn1EncodeToBuffer failed: %Rrc", rc);
382
383 RTMemFree(pThis->pbNewBuf);
384 pThis->pbNewBuf = NULL;
385 }
386 else
387 RTMsgError("Failed to allocate %u bytes!", cbEncoded);
388 }
389 else
390 RTMsgError("RTAsn1EncodePrepare failed: %Rrc - %s", rc, StaticErrInfo.szMsg);
391 return RTEXITCODE_FAILURE;
392}
393
394
395/**
396 * Helper that makes sure the UnauthenticatedAttributes are present in the given
397 * SignerInfo structure.
398 *
399 * Call this before trying to modify the array.
400 *
401 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error already
402 * displayed on failure.
403 * @param pSignerInfo The SignerInfo structure in question.
404 */
405static RTEXITCODE SignToolPkcs7_EnsureUnauthenticatedAttributesPresent(PRTCRPKCS7SIGNERINFO pSignerInfo)
406{
407 if (pSignerInfo->UnauthenticatedAttributes.cItems == 0)
408 {
409 /* HACK ALERT! Invent ASN.1 setters/whatever for members to replace this mess. */
410
411 if (pSignerInfo->AuthenticatedAttributes.cItems == 0)
412 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No authenticated or unauthenticated attributes! Sorry, no can do.");
413
414 Assert(pSignerInfo->UnauthenticatedAttributes.SetCore.Asn1Core.uTag == 0);
415 int rc = RTAsn1SetCore_Init(&pSignerInfo->UnauthenticatedAttributes.SetCore,
416 pSignerInfo->AuthenticatedAttributes.SetCore.Asn1Core.pOps);
417 if (RT_FAILURE(rc))
418 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTAsn1SetCore_Init failed: %Rrc", rc);
419 pSignerInfo->UnauthenticatedAttributes.SetCore.Asn1Core.uTag = 1;
420 pSignerInfo->UnauthenticatedAttributes.SetCore.Asn1Core.fClass = ASN1_TAGCLASS_CONTEXT | ASN1_TAGFLAG_CONSTRUCTED;
421 RTAsn1MemInitArrayAllocation(&pSignerInfo->UnauthenticatedAttributes.Allocation,
422 pSignerInfo->AuthenticatedAttributes.Allocation.pAllocator,
423 sizeof(**pSignerInfo->UnauthenticatedAttributes.papItems));
424 }
425 return RTEXITCODE_SUCCESS;
426}
427
428
429/**
430 * Adds the @a pSrc signature as a nested signature.
431 *
432 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error message
433 * on failure.
434 * @param pThis The signature to modify.
435 * @param pSrc The signature to add as nested.
436 * @param cVerbosity The verbosity.
437 * @param fPrepend Whether to prepend (true) or append (false) the
438 * source signature to the nested attribute.
439 */
440static RTEXITCODE SignToolPkcs7_AddNestedSignature(PSIGNTOOLPKCS7 pThis, PSIGNTOOLPKCS7 pSrc,
441 unsigned cVerbosity, bool fPrepend)
442{
443 PRTCRPKCS7SIGNERINFO pSignerInfo = pThis->pSignedData->SignerInfos.papItems[0];
444
445 /*
446 * Deal with UnauthenticatedAttributes being absent before trying to append to the array.
447 */
448 RTEXITCODE rcExit = SignToolPkcs7_EnsureUnauthenticatedAttributesPresent(pSignerInfo);
449 if (rcExit != RTEXITCODE_SUCCESS)
450 return rcExit;
451
452 /*
453 * Find or add an unauthenticated attribute for nested signatures.
454 */
455 int rc = VERR_NOT_FOUND;
456 PRTCRPKCS7ATTRIBUTE pAttr = NULL;
457 int32_t iPos = pSignerInfo->UnauthenticatedAttributes.cItems;
458 while (iPos-- > 0)
459 if (pSignerInfo->UnauthenticatedAttributes.papItems[iPos]->enmType == RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE)
460 {
461 pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[iPos];
462 rc = VINF_SUCCESS;
463 break;
464 }
465 if (iPos < 0)
466 {
467 iPos = RTCrPkcs7Attributes_Append(&pSignerInfo->UnauthenticatedAttributes);
468 if (iPos >= 0)
469 {
470 if (cVerbosity >= 3)
471 RTMsgInfo("Adding UnauthenticatedAttribute #%u...", iPos);
472 Assert((uint32_t)iPos < pSignerInfo->UnauthenticatedAttributes.cItems);
473
474 pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[iPos];
475 rc = RTAsn1ObjId_InitFromString(&pAttr->Type, RTCR_PKCS9_ID_MS_NESTED_SIGNATURE, pAttr->Allocation.pAllocator);
476 if (RT_SUCCESS(rc))
477 {
478 /** @todo Generalize the Type + enmType DYN stuff and generate setters. */
479 Assert(pAttr->enmType == RTCRPKCS7ATTRIBUTETYPE_NOT_PRESENT);
480 Assert(pAttr->uValues.pContentInfos == NULL);
481 pAttr->enmType = RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE;
482 rc = RTAsn1MemAllocZ(&pAttr->Allocation, (void **)&pAttr->uValues.pContentInfos,
483 sizeof(*pAttr->uValues.pContentInfos));
484 if (RT_SUCCESS(rc))
485 {
486 rc = RTCrPkcs7SetOfContentInfos_Init(pAttr->uValues.pContentInfos, pAttr->Allocation.pAllocator);
487 if (!RT_SUCCESS(rc))
488 RTMsgError("RTCrPkcs7ContentInfos_Init failed: %Rrc", rc);
489 }
490 else
491 RTMsgError("RTAsn1MemAllocZ failed: %Rrc", rc);
492 }
493 else
494 RTMsgError("RTAsn1ObjId_InitFromString failed: %Rrc", rc);
495 }
496 else
497 RTMsgError("RTCrPkcs7Attributes_Append failed: %Rrc", iPos);
498 }
499 else if (cVerbosity >= 2)
500 RTMsgInfo("Found UnauthenticatedAttribute #%u...", iPos);
501 if (RT_SUCCESS(rc))
502 {
503 /*
504 * Append/prepend the signature.
505 */
506 uint32_t iActualPos = UINT32_MAX;
507 iPos = fPrepend ? 0 : pAttr->uValues.pContentInfos->cItems;
508 rc = RTCrPkcs7SetOfContentInfos_InsertEx(pAttr->uValues.pContentInfos, iPos, &pSrc->ContentInfo,
509 pAttr->Allocation.pAllocator, &iActualPos);
510 if (RT_SUCCESS(rc))
511 {
512 if (cVerbosity > 0)
513 RTMsgInfo("Added nested signature (#%u)", iActualPos);
514 if (cVerbosity >= 3)
515 {
516 RTMsgInfo("SingerInfo dump after change:");
517 RTAsn1Dump(RTCrPkcs7SignerInfo_GetAsn1Core(pSignerInfo), 0, 2, RTStrmDumpPrintfV, g_pStdOut);
518 }
519 return RTEXITCODE_SUCCESS;
520 }
521
522 RTMsgError("RTCrPkcs7ContentInfos_InsertEx failed: %Rrc", rc);
523 }
524 return RTEXITCODE_FAILURE;
525}
526
527
528/**
529 * Writes the signature to the file.
530 *
531 * Caller must have called SignToolPkcs7_Encode() prior to this function.
532 *
533 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error
534 * message on failure.
535 * @param pThis The file which to write.
536 * @param cVerbosity The verbosity.
537 */
538static RTEXITCODE SignToolPkcs7_WriteSignatureToFile(PSIGNTOOLPKCS7 pThis, const char *pszFilename, unsigned cVerbosity)
539{
540 AssertReturn(pThis->cbNewBuf && pThis->pbNewBuf, RTEXITCODE_FAILURE);
541
542 /*
543 * Open+truncate file, write new signature, close. Simple.
544 */
545 RTFILE hFile;
546 int rc = RTFileOpen(&hFile, pszFilename, RTFILE_O_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_TRUNCATE | RTFILE_O_DENY_WRITE);
547 if (RT_SUCCESS(rc))
548 {
549 rc = RTFileWrite(hFile, pThis->pbNewBuf, pThis->cbNewBuf, NULL);
550 if (RT_SUCCESS(rc))
551 {
552 rc = RTFileClose(hFile);
553 if (RT_SUCCESS(rc))
554 {
555 if (cVerbosity > 0)
556 RTMsgInfo("Wrote %u bytes to %s", pThis->cbNewBuf, pszFilename);
557 return RTEXITCODE_SUCCESS;
558 }
559
560 RTMsgError("RTFileClose failed on %s: %Rrc", pszFilename, rc);
561 }
562 else
563 RTMsgError("Write error on %s: %Rrc", pszFilename, rc);
564 }
565 else
566 RTMsgError("Failed to open %s for writing: %Rrc", pszFilename, rc);
567 return RTEXITCODE_FAILURE;
568}
569
570
571
572/**
573 * Worker for recursively searching for MS nested signatures and signer infos.
574 *
575 * @returns Pointer to the signer info corresponding to @a iReqSignature. NULL
576 * if not found.
577 * @param pSignedData The signature to search.
578 * @param piNextSignature Pointer to the variable keeping track of the next
579 * signature number.
580 * @param iReqSignature The request signature number.
581 * @param ppSignedData Where to return the signature data structure.
582 * Optional.
583 */
584static PRTCRPKCS7SIGNERINFO SignToolPkcs7_FindNestedSignatureByIndexWorker(PRTCRPKCS7SIGNEDDATA pSignedData,
585 uint32_t *piNextSignature,
586 uint32_t iReqSignature,
587 PRTCRPKCS7SIGNEDDATA *ppSignedData)
588{
589 for (uint32_t iSignerInfo = 0; iSignerInfo < pSignedData->SignerInfos.cItems; iSignerInfo++)
590 {
591 /* Match?*/
592 PRTCRPKCS7SIGNERINFO pSignerInfo = pSignedData->SignerInfos.papItems[iSignerInfo];
593 if (*piNextSignature == iReqSignature)
594 {
595 if (ppSignedData)
596 *ppSignedData = pSignedData;
597 return pSignerInfo;
598 }
599 *piNextSignature += 1;
600
601 /* Look for nested signatures. */
602 for (uint32_t iAttrib = 0; iAttrib < pSignerInfo->UnauthenticatedAttributes.cItems; iAttrib++)
603 if (pSignerInfo->UnauthenticatedAttributes.papItems[iAttrib]->enmType == RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE)
604 {
605 PRTCRPKCS7SETOFCONTENTINFOS pCntInfos;
606 pCntInfos = pSignerInfo->UnauthenticatedAttributes.papItems[iAttrib]->uValues.pContentInfos;
607 for (uint32_t iCntInfo = 0; iCntInfo < pCntInfos->cItems; iCntInfo++)
608 {
609 PRTCRPKCS7CONTENTINFO pCntInfo = pCntInfos->papItems[iCntInfo];
610 if (RTCrPkcs7ContentInfo_IsSignedData(pCntInfo))
611 {
612 PRTCRPKCS7SIGNERINFO pRet;
613 pRet = SignToolPkcs7_FindNestedSignatureByIndexWorker(pCntInfo->u.pSignedData, piNextSignature,
614 iReqSignature, ppSignedData);
615 if (pRet)
616 return pRet;
617 }
618 }
619 }
620 }
621 return NULL;
622}
623
624
625/**
626 * Locates the given nested signature.
627 *
628 * @returns Pointer to the signer info corresponding to @a iReqSignature. NULL
629 * if not found.
630 * @param pThis The PKCS\#7 structure to search.
631 * @param iReqSignature The requested signature number.
632 * @param ppSignedData Where to return the pointer to the signed data that
633 * the returned signer info belongs to.
634 *
635 * @todo Move into SPC or PKCS\#7.
636 */
637static PRTCRPKCS7SIGNERINFO SignToolPkcs7_FindNestedSignatureByIndex(PSIGNTOOLPKCS7 pThis, uint32_t iReqSignature,
638 PRTCRPKCS7SIGNEDDATA *ppSignedData)
639{
640 uint32_t iNextSignature = 0;
641 return SignToolPkcs7_FindNestedSignatureByIndexWorker(pThis->pSignedData, &iNextSignature, iReqSignature, ppSignedData);
642}
643
644
645
646/**
647 * Reads and decodes PKCS\#7 signature from the given executable.
648 *
649 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error message
650 * on failure.
651 * @param pThis The structure to initialize.
652 * @param pszFilename The executable filename.
653 * @param cVerbosity The verbosity.
654 * @param enmLdrArch For FAT binaries.
655 */
656static RTEXITCODE SignToolPkcs7Exe_InitFromFile(PSIGNTOOLPKCS7EXE pThis, const char *pszFilename,
657 unsigned cVerbosity, RTLDRARCH enmLdrArch = RTLDRARCH_WHATEVER)
658{
659 /*
660 * Init the return structure.
661 */
662 RT_ZERO(*pThis);
663 pThis->hLdrMod = NIL_RTLDRMOD;
664 pThis->pszFilename = pszFilename;
665
666 /*
667 * Open the image and check if it's signed.
668 */
669 int rc = RTLdrOpen(pszFilename, RTLDR_O_FOR_VALIDATION, enmLdrArch, &pThis->hLdrMod);
670 if (RT_SUCCESS(rc))
671 {
672 bool fIsSigned = false;
673 rc = RTLdrQueryProp(pThis->hLdrMod, RTLDRPROP_IS_SIGNED, &fIsSigned, sizeof(fIsSigned));
674 if (RT_SUCCESS(rc) && fIsSigned)
675 {
676 /*
677 * Query the PKCS#7 data (assuming M$ style signing) and hand it to a worker.
678 */
679 size_t cbActual = 0;
680#ifdef DEBUG
681 size_t cbBuf = 64;
682#else
683 size_t cbBuf = _512K;
684#endif
685 void *pvBuf = RTMemAllocZ(cbBuf);
686 if (pvBuf)
687 {
688 rc = RTLdrQueryPropEx(pThis->hLdrMod, RTLDRPROP_PKCS7_SIGNED_DATA, NULL /*pvBits*/, pvBuf, cbBuf, &cbActual);
689 if (rc == VERR_BUFFER_OVERFLOW)
690 {
691 RTMemFree(pvBuf);
692 cbBuf = cbActual;
693 pvBuf = RTMemAllocZ(cbActual);
694 if (pvBuf)
695 rc = RTLdrQueryPropEx(pThis->hLdrMod, RTLDRPROP_PKCS7_SIGNED_DATA, NULL /*pvBits*/,
696 pvBuf, cbBuf, &cbActual);
697 else
698 rc = VERR_NO_MEMORY;
699 }
700 }
701 else
702 rc = VERR_NO_MEMORY;
703
704 pThis->pbBuf = (uint8_t *)pvBuf;
705 pThis->cbBuf = cbActual;
706 if (RT_SUCCESS(rc))
707 {
708 if (cVerbosity > 2)
709 RTPrintf("PKCS#7 signature: %u bytes\n", cbActual);
710 if (cVerbosity > 3)
711 RTPrintf("%.*Rhxd\n", cbActual, pvBuf);
712
713 /*
714 * Decode it.
715 */
716 rc = SignToolPkcs7_Decode(pThis, false /*fCatalog*/);
717 if (RT_SUCCESS(rc))
718 return RTEXITCODE_SUCCESS;
719 }
720 else
721 RTMsgError("RTLdrQueryPropEx/RTLDRPROP_PKCS7_SIGNED_DATA failed on '%s': %Rrc\n", pszFilename, rc);
722 }
723 else if (RT_SUCCESS(rc))
724 RTMsgInfo("'%s': not signed\n", pszFilename);
725 else
726 RTMsgError("RTLdrQueryProp/RTLDRPROP_IS_SIGNED failed on '%s': %Rrc\n", pszFilename, rc);
727 }
728 else
729 RTMsgError("Error opening executable image '%s': %Rrc", pszFilename, rc);
730
731 SignToolPkcs7Exe_Delete(pThis);
732 return RTEXITCODE_FAILURE;
733}
734
735
736/**
737 * Calculates the checksum of an executable.
738 *
739 * @returns Success indicator (errors are reported)
740 * @param pThis The exe file to checksum.
741 * @param hFile The file handle.
742 * @param puCheckSum Where to return the checksum.
743 */
744static bool SignToolPkcs7Exe_CalcPeCheckSum(PSIGNTOOLPKCS7EXE pThis, RTFILE hFile, uint32_t *puCheckSum)
745{
746#ifdef RT_OS_WINDOWS
747 /*
748 * Try use IMAGEHLP!MapFileAndCheckSumW first.
749 */
750 PRTUTF16 pwszPath;
751 int rc = RTStrToUtf16(pThis->pszFilename, &pwszPath);
752 if (RT_SUCCESS(rc))
753 {
754 decltype(MapFileAndCheckSumW) *pfnMapFileAndCheckSumW;
755 pfnMapFileAndCheckSumW = (decltype(MapFileAndCheckSumW) *)RTLdrGetSystemSymbol("IMAGEHLP.DLL", "MapFileAndCheckSumW");
756 if (pfnMapFileAndCheckSumW)
757 {
758 DWORD uHeaderSum = UINT32_MAX;
759 DWORD uCheckSum = UINT32_MAX;
760 DWORD dwRc = pfnMapFileAndCheckSumW(pwszPath, &uHeaderSum, &uCheckSum);
761 if (dwRc == CHECKSUM_SUCCESS)
762 {
763 *puCheckSum = uCheckSum;
764 return true;
765 }
766 }
767 }
768#endif
769
770 RT_NOREF(pThis, hFile, puCheckSum);
771 RTMsgError("Implement check sum calcuation fallback!");
772 return false;
773}
774
775
776/**
777 * Writes the signature to the file.
778 *
779 * This has the side-effect of closing the hLdrMod member. So, it can only be
780 * called once!
781 *
782 * Caller must have called SignToolPkcs7_Encode() prior to this function.
783 *
784 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error
785 * message on failure.
786 * @param pThis The file which to write.
787 * @param cVerbosity The verbosity.
788 */
789static RTEXITCODE SignToolPkcs7Exe_WriteSignatureToFile(PSIGNTOOLPKCS7EXE pThis, unsigned cVerbosity)
790{
791 AssertReturn(pThis->cbNewBuf && pThis->pbNewBuf, RTEXITCODE_FAILURE);
792
793 /*
794 * Get the file header offset and arch before closing the destination handle.
795 */
796 uint32_t offNtHdrs;
797 int rc = RTLdrQueryProp(pThis->hLdrMod, RTLDRPROP_FILE_OFF_HEADER, &offNtHdrs, sizeof(offNtHdrs));
798 if (RT_SUCCESS(rc))
799 {
800 RTLDRARCH enmLdrArch = RTLdrGetArch(pThis->hLdrMod);
801 if (enmLdrArch != RTLDRARCH_INVALID)
802 {
803 RTLdrClose(pThis->hLdrMod);
804 pThis->hLdrMod = NIL_RTLDRMOD;
805 unsigned cbNtHdrs = 0;
806 switch (enmLdrArch)
807 {
808 case RTLDRARCH_AMD64:
809 cbNtHdrs = sizeof(IMAGE_NT_HEADERS64);
810 break;
811 case RTLDRARCH_X86_32:
812 cbNtHdrs = sizeof(IMAGE_NT_HEADERS32);
813 break;
814 default:
815 RTMsgError("Unknown image arch: %d", enmLdrArch);
816 }
817 if (cbNtHdrs > 0)
818 {
819 if (cVerbosity > 0)
820 RTMsgInfo("offNtHdrs=%#x cbNtHdrs=%u\n", offNtHdrs, cbNtHdrs);
821
822 /*
823 * Open the executable file for writing.
824 */
825 RTFILE hFile;
826 rc = RTFileOpen(&hFile, pThis->pszFilename, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
827 if (RT_SUCCESS(rc))
828 {
829 /* Read the file header and locate the security directory entry. */
830 union
831 {
832 IMAGE_NT_HEADERS32 NtHdrs32;
833 IMAGE_NT_HEADERS64 NtHdrs64;
834 } uBuf;
835 PIMAGE_DATA_DIRECTORY pSecDir = cbNtHdrs == sizeof(IMAGE_NT_HEADERS64)
836 ? &uBuf.NtHdrs64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]
837 : &uBuf.NtHdrs32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
838
839 rc = RTFileReadAt(hFile, offNtHdrs, &uBuf, cbNtHdrs, NULL);
840 if ( RT_SUCCESS(rc)
841 && uBuf.NtHdrs32.Signature == IMAGE_NT_SIGNATURE)
842 {
843 /*
844 * Drop any old signature by truncating the file.
845 */
846 if ( pSecDir->Size > 8
847 && pSecDir->VirtualAddress > offNtHdrs + sizeof(IMAGE_NT_HEADERS32))
848 {
849 rc = RTFileSetSize(hFile, pSecDir->VirtualAddress);
850 if (RT_FAILURE(rc))
851 RTMsgError("Error truncating file to %#x bytes: %Rrc", pSecDir->VirtualAddress, rc);
852 }
853 else
854 rc = RTMsgErrorRc(VERR_BAD_EXE_FORMAT, "Bad security directory entry: VA=%#x Size=%#x",
855 pSecDir->VirtualAddress, pSecDir->Size);
856 if (RT_SUCCESS(rc))
857 {
858 /*
859 * Sector align the signature portion.
860 */
861 uint32_t const cbWinCert = RT_UOFFSETOF(WIN_CERTIFICATE, bCertificate);
862 uint64_t offCur = 0;
863 rc = RTFileQuerySize(hFile, &offCur);
864 if ( RT_SUCCESS(rc)
865 && offCur < _2G)
866 {
867 if (offCur & 0x1ff)
868 {
869 uint32_t cbNeeded = 0x200 - ((uint32_t)offCur & 0x1ff);
870 rc = RTFileWriteAt(hFile, offCur, g_abRTZero4K, cbNeeded, NULL);
871 if (RT_SUCCESS(rc))
872 offCur += cbNeeded;
873 }
874 if (RT_SUCCESS(rc))
875 {
876 /*
877 * Write the header followed by the signature data.
878 */
879 uint32_t const cbZeroPad = (uint32_t)(RT_ALIGN_Z(pThis->cbNewBuf, 8) - pThis->cbNewBuf);
880 pSecDir->VirtualAddress = (uint32_t)offCur;
881 pSecDir->Size = cbWinCert + (uint32_t)pThis->cbNewBuf + cbZeroPad;
882 if (cVerbosity >= 2)
883 RTMsgInfo("Writing %u (%#x) bytes of signature at %#x (%u).\n",
884 pSecDir->Size, pSecDir->Size, pSecDir->VirtualAddress, pSecDir->VirtualAddress);
885
886 WIN_CERTIFICATE WinCert;
887 WinCert.dwLength = pSecDir->Size;
888 WinCert.wRevision = WIN_CERT_REVISION_2_0;
889 WinCert.wCertificateType = WIN_CERT_TYPE_PKCS_SIGNED_DATA;
890
891 rc = RTFileWriteAt(hFile, offCur, &WinCert, cbWinCert, NULL);
892 if (RT_SUCCESS(rc))
893 {
894 offCur += cbWinCert;
895 rc = RTFileWriteAt(hFile, offCur, pThis->pbNewBuf, pThis->cbNewBuf, NULL);
896 }
897 if (RT_SUCCESS(rc) && cbZeroPad)
898 {
899 offCur += pThis->cbNewBuf;
900 rc = RTFileWriteAt(hFile, offCur, g_abRTZero4K, cbZeroPad, NULL);
901 }
902 if (RT_SUCCESS(rc))
903 {
904 /*
905 * Reset the checksum (sec dir updated already) and rewrite the header.
906 */
907 uBuf.NtHdrs32.OptionalHeader.CheckSum = 0;
908 offCur = offNtHdrs;
909 rc = RTFileWriteAt(hFile, offNtHdrs, &uBuf, cbNtHdrs, NULL);
910 if (RT_SUCCESS(rc))
911 rc = RTFileFlush(hFile);
912 if (RT_SUCCESS(rc))
913 {
914 /*
915 * Calc checksum and write out the header again.
916 */
917 uint32_t uCheckSum = UINT32_MAX;
918 if (SignToolPkcs7Exe_CalcPeCheckSum(pThis, hFile, &uCheckSum))
919 {
920 uBuf.NtHdrs32.OptionalHeader.CheckSum = uCheckSum;
921 rc = RTFileWriteAt(hFile, offNtHdrs, &uBuf, cbNtHdrs, NULL);
922 if (RT_SUCCESS(rc))
923 rc = RTFileFlush(hFile);
924 if (RT_SUCCESS(rc))
925 {
926 rc = RTFileClose(hFile);
927 if (RT_SUCCESS(rc))
928 return RTEXITCODE_SUCCESS;
929 RTMsgError("RTFileClose failed: %Rrc\n", rc);
930 return RTEXITCODE_FAILURE;
931 }
932 }
933 }
934 }
935 }
936 if (RT_FAILURE(rc))
937 RTMsgError("Write error at %#RX64: %Rrc", offCur, rc);
938 }
939 else if (RT_SUCCESS(rc))
940 RTMsgError("File to big: %'RU64 bytes", offCur);
941 else
942 RTMsgError("RTFileQuerySize failed: %Rrc", rc);
943 }
944 }
945 else if (RT_SUCCESS(rc))
946 RTMsgError("Not NT executable header!");
947 else
948 RTMsgError("Error reading NT headers (%#x bytes) at %#x: %Rrc", cbNtHdrs, offNtHdrs, rc);
949 RTFileClose(hFile);
950 }
951 else
952 RTMsgError("Failed to open '%s' for writing: %Rrc", pThis->pszFilename, rc);
953 }
954 }
955 else
956 RTMsgError("RTLdrGetArch failed!");
957 }
958 else
959 RTMsgError("RTLdrQueryProp/RTLDRPROP_FILE_OFF_HEADER failed: %Rrc", rc);
960 return RTEXITCODE_FAILURE;
961}
962
963#ifndef IPRT_IN_BUILD_TOOL
964
965static RTEXITCODE SignToolPkcs7_AuthAttribsAddSigningTime(PRTCRPKCS7ATTRIBUTES pAuthAttribs, RTTIMESPEC SigningTime)
966{
967 /*
968 * Signing time. For the old-style timestamps, Symantec used ASN.1 UTC TIME.
969 * start -vv vv=ASN1_TAG_UTC_TIME
970 * 00000187d6a65fd0/23b0: 0d 01 09 05 31 0f 17 0d-31 36 31 30 30 35 30 37 ....1...16100507
971 * 00000187d6a65fe0/23c0: 35 30 33 30 5a 30 23 06-09 2a 86 48 86 f7 0d 01 5030Z0#..*.H....
972 * ^^- end 2016-10-05T07:50:30.000000000Z (161005075030Z)
973 */
974 int32_t iPos = RTCrPkcs7Attributes_Append(pAuthAttribs);
975 if (iPos < 0)
976 return RTMsgErrorExitFailure("RTCrPkcs7Attributes_Append failed: %Rrc", iPos);
977
978 /* Create the attrib and its sub-set of timestamps. */
979 PRTCRPKCS7ATTRIBUTE pAttr = pAuthAttribs->papItems[iPos];
980 int rc = RTAsn1ObjId_InitFromString(&pAttr->Type, RTCR_PKCS9_ID_SIGNING_TIME_OID, pAttr->Allocation.pAllocator);
981 if (RT_FAILURE(rc))
982 return RTMsgErrorExitFailure("RTAsn1ObjId_InitFromString/RTCR_PKCS9_ID_SIGNING_TIME_OID failed: %Rrc", rc);
983
984 /** @todo Generalize the Type + enmType DYN stuff and generate setters. */
985 Assert(pAttr->enmType == RTCRPKCS7ATTRIBUTETYPE_NOT_PRESENT);
986 Assert(pAttr->uValues.pContentInfos == NULL);
987 pAttr->enmType = RTCRPKCS7ATTRIBUTETYPE_SIGNING_TIME;
988 rc = RTAsn1MemAllocZ(&pAttr->Allocation, (void **)&pAttr->uValues.pSigningTime, sizeof(*pAttr->uValues.pSigningTime));
989 if (RT_FAILURE(rc))
990 return RTMsgErrorExitFailure("RTAsn1MemAllocZ failed: %Rrc", rc);
991
992 rc = RTAsn1SetOfTimes_Init(pAttr->uValues.pSigningTime, pAttr->Allocation.pAllocator);
993 if (RT_FAILURE(rc))
994 return RTMsgErrorExitFailure("RTAsn1SetOfTimes_Init failed: %Rrc", rc);
995
996 /* Create the timestamp. */
997 iPos = RTAsn1SetOfTimes_Append(pAttr->uValues.pSigningTime);
998 if (iPos < 0)
999 return RTMsgErrorExitFailure("RTAsn1SetOfTimes_Append failed: %Rrc", iPos);
1000
1001 PRTASN1TIME pTime = pAttr->uValues.pSigningTime->papItems[iPos];
1002 rc = RTAsn1Time_SetTimeSpec(pTime, pAttr->Allocation.pAllocator, &SigningTime);
1003 if (RT_FAILURE(rc))
1004 return RTMsgErrorExitFailure("RTAsn1Time_SetTimeSpec failed: %Rrc", rc);
1005
1006 return RTEXITCODE_SUCCESS;
1007}
1008
1009static RTEXITCODE SignToolPkcs7_AuthAttribsAddObjIdSeqsEmpty(PRTCRPKCS7ATTRIBUTES pAuthAttribs, const char *pszAttrId)
1010{
1011 int32_t iPos = RTCrPkcs7Attributes_Append(pAuthAttribs);
1012 if (iPos < 0)
1013 return RTMsgErrorExitFailure("RTCrPkcs7Attributes_Append failed: %Rrc", iPos);
1014
1015 /* Create the attrib and its sub-set of timestamps. */
1016 PRTCRPKCS7ATTRIBUTE pAttr = pAuthAttribs->papItems[iPos];
1017 int rc = RTAsn1ObjId_InitFromString(&pAttr->Type, pszAttrId, pAttr->Allocation.pAllocator);
1018 if (RT_FAILURE(rc))
1019 return RTMsgErrorExitFailure("RTAsn1ObjId_InitFromString/%s failed: %Rrc", pszAttrId, rc);
1020
1021 /** @todo Generalize the Type + enmType DYN stuff and generate setters. */
1022 Assert(pAttr->enmType == RTCRPKCS7ATTRIBUTETYPE_NOT_PRESENT);
1023 Assert(pAttr->uValues.pContentInfos == NULL);
1024 pAttr->enmType = RTCRPKCS7ATTRIBUTETYPE_MS_STATEMENT_TYPE;
1025 rc = RTAsn1MemAllocZ(&pAttr->Allocation, (void **)&pAttr->uValues.pObjIdSeqs, sizeof(*pAttr->uValues.pObjIdSeqs));
1026 if (RT_FAILURE(rc))
1027 return RTMsgErrorExitFailure("RTAsn1MemAllocZ failed: %Rrc", rc);
1028
1029 rc = RTAsn1SetOfObjIdSeqs_Init(pAttr->uValues.pObjIdSeqs, pAttr->Allocation.pAllocator);
1030 if (RT_FAILURE(rc))
1031 return RTMsgErrorExitFailure("RTAsn1SetOfObjIdSeqs_Init failed: %Rrc", rc);
1032
1033 return RTEXITCODE_SUCCESS;
1034}
1035
1036static RTEXITCODE SignToolPkcs7_AuthAttribsAddObjIdSeqsValue(PRTCRPKCS7ATTRIBUTES pAuthAttribs, const char *pszAttrId,
1037 const char *pszValueId)
1038{
1039 RTEXITCODE rcExit = SignToolPkcs7_AuthAttribsAddObjIdSeqsEmpty(pAuthAttribs, pszAttrId);
1040 if (rcExit != RTEXITCODE_SUCCESS)
1041 return rcExit;
1042
1043 /* Add attribute value entry. */
1044 PRTCRPKCS7ATTRIBUTE pAttr = pAuthAttribs->papItems[pAuthAttribs->cItems - 1];
1045 int32_t iPos = RTAsn1SetOfObjIdSeqs_Append(pAttr->uValues.pObjIdSeqs);
1046 if (iPos < 0)
1047 return RTMsgErrorExitFailure("RTAsn1SetOfObjIdSeqs_Append failed: %Rrc", iPos);
1048 PRTASN1SEQOFOBJIDS pSeqObjIds = pAttr->uValues.pObjIdSeqs->papItems[iPos];
1049
1050 /* Add a object id to the value. */
1051 RTASN1OBJID ObjIdValue;
1052 int rc = RTAsn1ObjId_InitFromString(&ObjIdValue, pszValueId, &g_RTAsn1DefaultAllocator);
1053 if (RT_FAILURE(rc))
1054 return RTMsgErrorExitFailure("RTAsn1ObjId_InitFromString/%s failed: %Rrc", pszAttrId, rc);
1055
1056 rc = RTAsn1SeqOfObjIds_InsertEx(pSeqObjIds, 0 /*iPos*/, &ObjIdValue, &g_RTAsn1DefaultAllocator, NULL);
1057 RTAsn1ObjId_Delete(&ObjIdValue);
1058 if (RT_FAILURE(rc))
1059 return RTMsgErrorExitFailure("RTAsn1SeqOfObjIds_InsertEx failed: %Rrc", iPos);
1060
1061 return RTEXITCODE_SUCCESS;
1062}
1063
1064static RTEXITCODE SignToolPkcs7_AddAuthAttribsForTimestamp(PRTCRPKCS7ATTRIBUTES pAuthAttribs, bool fTimestampTypeOld,
1065 RTTIMESPEC SigningTime, PCRTCRX509CERTIFICATE pTimestampCert)
1066{
1067 /*
1068 * Add signing time.
1069 */
1070 RTEXITCODE rcExit = SignToolPkcs7_AuthAttribsAddSigningTime(pAuthAttribs, SigningTime);
1071 if (rcExit != RTEXITCODE_SUCCESS)
1072 return rcExit;
1073
1074 /*
1075 * More later if we want to support fTimestampTypeOld = false perhaps?
1076 */
1077 Assert(fTimestampTypeOld);
1078 RT_NOREF(fTimestampTypeOld, pTimestampCert);
1079
1080 return RTEXITCODE_SUCCESS;
1081}
1082
1083static RTEXITCODE SignToolPkcs7_AddAuthAttribsForImageSignature(PRTCRPKCS7ATTRIBUTES pAuthAttribs, RTTIMESPEC SigningTime)
1084{
1085 /*
1086 * Add SpcOpusInfo. No attribute values.
1087 * SEQ start -vv vv- Type ObjId
1088 * 1c60: 0e 03 02 1a 05 00 a0 70-30 10 06 0a 2b 06 01 04 .......p0...+...
1089 * 1c70: 01 82 37 02 01 0c 31 02-30 00 30 19 06 09 2a 86 ..7...1.0.0...*.
1090 * Set Of -^^ ^^- Empty Sequence.
1091 */
1092 RTEXITCODE rcExit = SignToolPkcs7_AuthAttribsAddObjIdSeqsEmpty(pAuthAttribs, RTCR_PKCS9_ID_MS_SP_OPUS_INFO);
1093 if (rcExit != RTEXITCODE_SUCCESS)
1094 return rcExit;
1095
1096 /*
1097 * Add ContentType = Ms-SpcIndirectDataContext?
1098 * SEQ start -vv vv- Type ObjId
1099 * 1c70: 01 82 37 02 01 0c 31 02-30 00 30 19 06 09 2a 86 ..7...1.0.0...*.
1100 * 1c80: 48 86 f7 0d 01 09 03 31-0c 06 0a 2b 06 01 04 01 H......1...+....
1101 * 1c90: 82 37 02 01 04 ^^- ^^- ObjId
1102 * ^- Set Of
1103 */
1104
1105 /*
1106 * Add Ms-SpcStatementType = Ms-SpcIndividualCodeSigning.
1107 * SEQ start -vv vv- Type ObjId
1108 * 1c90: 82 37 02 01 04 30 1c 06-0a 2b 06 01 04 01 82 37 .7...0...+.....7
1109 * 1ca0: 02 01 0b 31 0e 30 0c 06-0a 2b 06 01 04 01 82 37 ...1.0...+.....7
1110 * 1cb0: 02 01 15 ^^ ^^ ^^- ObjId
1111 * Set Of -^^ ^^- Sequence Of
1112 */
1113 rcExit = SignToolPkcs7_AuthAttribsAddObjIdSeqsValue(pAuthAttribs, RTCR_PKCS9_ID_MS_STATEMENT_TYPE,
1114 RTCRSPC_STMT_TYPE_INDIVIDUAL_CODE_SIGNING);
1115 if (rcExit != RTEXITCODE_SUCCESS)
1116 return rcExit;
1117
1118 /*
1119 * Add signing time. We add this, even if signtool.exe, since OpenSSL will always do it otherwise.
1120 */
1121 rcExit = SignToolPkcs7_AuthAttribsAddSigningTime(pAuthAttribs, SigningTime);
1122 if (rcExit != RTEXITCODE_SUCCESS)
1123 return rcExit;
1124
1125 /** @todo more? Some certificate stuff? */
1126
1127 return RTEXITCODE_SUCCESS;
1128}
1129
1130
1131static RTEXITCODE SignToolPkcs7_PrependCounterSignature(PRTCRPKCS7SIGNERINFO pSignerInfo,
1132 PCRTCRPKCS7SIGNERINFO pCounterSignerInfo, unsigned cVerbosity)
1133{
1134 /* Make sure the UnauthenticatedAttributes member is there. */
1135 RTEXITCODE rcExit = SignToolPkcs7_EnsureUnauthenticatedAttributesPresent(pSignerInfo);
1136 if (rcExit != RTEXITCODE_SUCCESS)
1137 return rcExit;
1138
1139 /* Append an entry to UnauthenticatedAttributes. */
1140 uint32_t iPos;
1141 int rc = RTCrPkcs7Attributes_InsertEx(&pSignerInfo->UnauthenticatedAttributes, 0 /*iPosition*/, NULL /*pToClone*/,
1142 &g_RTAsn1DefaultAllocator, &iPos);
1143 if (RT_FAILURE(rc))
1144 return RTMsgErrorExitFailure("RTCrPkcs7Attributes_Append failed: %Rrc", rc);
1145 Assert(iPos < pSignerInfo->UnauthenticatedAttributes.cItems); Assert(iPos == 0);
1146 PRTCRPKCS7ATTRIBUTE pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[iPos];
1147
1148 if (cVerbosity >= 2)
1149 RTMsgInfo("Adding UnauthenticatedAttribute #%u...", iPos);
1150
1151 /* Create the attrib and its sub-set of counter signatures. */
1152 rc = RTAsn1ObjId_InitFromString(&pAttr->Type, RTCR_PKCS9_ID_COUNTER_SIGNATURE_OID, pAttr->Allocation.pAllocator);
1153 if (RT_FAILURE(rc))
1154 return RTMsgErrorExitFailure("RTAsn1ObjId_InitFromString failed: %Rrc", rc);
1155
1156 /** @todo Generalize the Type + enmType DYN stuff and generate setters. */
1157 Assert(pAttr->enmType == RTCRPKCS7ATTRIBUTETYPE_NOT_PRESENT);
1158 Assert(pAttr->uValues.pContentInfos == NULL);
1159 pAttr->enmType = RTCRPKCS7ATTRIBUTETYPE_COUNTER_SIGNATURES;
1160 rc = RTAsn1MemAllocZ(&pAttr->Allocation, (void **)&pAttr->uValues.pCounterSignatures,
1161 sizeof(*pAttr->uValues.pCounterSignatures));
1162 if (RT_FAILURE(rc))
1163 return RTMsgErrorExitFailure("RTAsn1MemAllocZ failed: %Rrc", rc);
1164
1165 rc = RTCrPkcs7SignerInfos_Init(pAttr->uValues.pCounterSignatures, pAttr->Allocation.pAllocator);
1166 if (RT_FAILURE(rc))
1167 return RTMsgErrorExitFailure("RTCrPkcs7SignerInfos_Init failed: %Rrc", rc);
1168
1169 /* Insert the counter signature. */
1170 rc = RTCrPkcs7SignerInfos_InsertEx(pAttr->uValues.pCounterSignatures, 0 /*iPosition*/, pCounterSignerInfo,
1171 pAttr->Allocation.pAllocator, NULL);
1172 if (RT_FAILURE(rc))
1173 return RTMsgErrorExitFailure("RTCrPkcs7SignerInfos_InsertEx failed: %Rrc", rc);
1174
1175 return RTEXITCODE_SUCCESS;
1176}
1177
1178
1179static RTEXITCODE SignToolPkcs7_AppendCertificate(PRTCRPKCS7SIGNEDDATA pSignedData, PCRTCRX509CERTIFICATE pCertToAppend)
1180{
1181 if (pSignedData->Certificates.cItems == 0 && !RTCrPkcs7SetOfCerts_IsPresent(&pSignedData->Certificates))
1182 return RTMsgErrorExitFailure("PKCS#7 signature includes no certificates! Didn't expect that");
1183
1184 /* Already there? */
1185 PCRTCRX509CERTIFICATE pExisting
1186 = RTCrPkcs7SetOfCerts_FindX509ByIssuerAndSerialNumber(&pSignedData->Certificates, &pCertToAppend->TbsCertificate.Issuer,
1187 &pCertToAppend->TbsCertificate.SerialNumber);
1188 if (!pExisting || RTCrX509Certificate_Compare(pExisting, pCertToAppend) != 0)
1189 {
1190 /* Append a RTCRPKCS7CERT entry. */
1191 //int32_t iPos = RTCrPkcs7SetOfCerts_Append(&pSignedData->Certificates);
1192 //if (iPos < 0)
1193 // return RTMsgErrorExitFailure("RTCrPkcs7SetOfCerts_Append failed: %Rrc", iPos);
1194 uint32_t iPos;
1195 int rc = RTCrPkcs7SetOfCerts_InsertEx(&pSignedData->Certificates, 0 /*iPosition*/, NULL /*pToClone*/,
1196 &g_RTAsn1DefaultAllocator, &iPos);
1197 if (RT_FAILURE(rc))
1198 return RTMsgErrorExitFailure("RTCrPkcs7SetOfCerts_Append failed: %Rrc", rc);
1199
1200 PRTCRPKCS7CERT pCertEntry = pSignedData->Certificates.papItems[iPos];
1201
1202 /** @todo Generalize the Type + enmType DYN stuff and generate setters. */
1203 Assert(pCertEntry->enmChoice == RTCRPKCS7CERTCHOICE_INVALID);
1204 Assert(pCertEntry->u.pX509Cert == NULL);
1205 pCertEntry->enmChoice = RTCRPKCS7CERTCHOICE_X509;
1206 rc = RTAsn1MemAllocZ(&pCertEntry->Allocation, (void **)&pCertEntry->u.pX509Cert, sizeof(*pCertEntry->u.pX509Cert));
1207 if (RT_FAILURE(rc))
1208 return RTMsgErrorExitFailure("RTAsn1MemAllocZ failed: %Rrc", rc);
1209
1210 /* Copy over the certificate we wish to append. */
1211 rc = RTCrX509Certificate_Clone(pCertEntry->u.pX509Cert, pCertToAppend, pCertEntry->Allocation.pAllocator);
1212 if (RT_FAILURE(rc))
1213 return RTMsgErrorExitFailure("RTCrX509Certificate_Clone failed: %Rrc", rc);
1214 }
1215 return RTEXITCODE_SUCCESS;
1216}
1217
1218
1219static RTEXITCODE Pkcs7SignStuff(const char *pszWhat, const void *pvToDataToSign, size_t cbToDataToSign,
1220 PCRTCRPKCS7ATTRIBUTES pAuthAttribs, RTCRSTORE hAdditionalCerts, uint32_t fExtraFlags,
1221 RTDIGESTTYPE enmDigestType, SIGNTOOLKEYPAIR *pCertKeyPair,
1222 unsigned cVerbosity, void **ppvSigned, size_t *pcbSigned,
1223 PRTCRPKCS7CONTENTINFO pContentInfo, PRTCRPKCS7SIGNEDDATA *ppSignedData)
1224{
1225 *ppvSigned = NULL;
1226 if (pcbSigned)
1227 *pcbSigned = 0;
1228 if (ppSignedData)
1229 *ppSignedData = NULL;
1230
1231 /* Figure out how large the signature will be. */
1232 RTERRINFOSTATIC ErrInfo;
1233 size_t cbSigned = 1024;
1234 int rc = RTCrPkcs7SimpleSignSignedData(RTCRPKCS7SIGN_SD_F_NO_SMIME_CAP | fExtraFlags,
1235 pCertKeyPair->pCertificate, pCertKeyPair->hPrivateKey, pvToDataToSign, cbToDataToSign,
1236 enmDigestType, hAdditionalCerts, pAuthAttribs,
1237 NULL, &cbSigned, RTErrInfoInitStatic(&ErrInfo));
1238 if (rc != VERR_BUFFER_OVERFLOW)
1239 return RTMsgErrorExitFailure("RTCrPkcs7SimpleSignSignedData failed: %Rrc%#RTeim", rc, &ErrInfo.Core);
1240
1241 /* Allocate memory for it and do the actual signing. */
1242 void *pvSigned = RTMemAllocZ(cbSigned);
1243 if (!pvSigned)
1244 return RTMsgErrorExitFailure("Failed to allocate %#zx bytes for %s signature", cbSigned, pszWhat);
1245 rc = RTCrPkcs7SimpleSignSignedData(RTCRPKCS7SIGN_SD_F_NO_SMIME_CAP | fExtraFlags,
1246 pCertKeyPair->pCertificate, pCertKeyPair->hPrivateKey, pvToDataToSign, cbToDataToSign,
1247 enmDigestType, hAdditionalCerts, pAuthAttribs,
1248 pvSigned, &cbSigned, RTErrInfoInitStatic(&ErrInfo));
1249 if (RT_SUCCESS(rc))
1250 {
1251 if (cVerbosity > 2)
1252 RTMsgInfo("%s signature: %#zx bytes\n%.*Rhxd\n", pszWhat, cbSigned, cbSigned, pvSigned);
1253
1254 /*
1255 * Decode the signature and check that it is SignedData.
1256 */
1257 RTASN1CURSORPRIMARY PrimaryCursor;
1258 RTAsn1CursorInitPrimary(&PrimaryCursor, pvSigned, (uint32_t)cbSigned, RTErrInfoInitStatic(&ErrInfo),
1259 &g_RTAsn1DefaultAllocator, 0, pszWhat);
1260 rc = RTCrPkcs7ContentInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, pContentInfo, "CI");
1261 if (RT_SUCCESS(rc))
1262 {
1263 if (RTCrPkcs7ContentInfo_IsSignedData(pContentInfo))
1264 {
1265 *ppvSigned = pvSigned;
1266 if (pcbSigned)
1267 *pcbSigned = cbSigned;
1268 if (ppSignedData)
1269 *ppSignedData = pContentInfo->u.pSignedData;
1270
1271 if (cVerbosity)
1272 {
1273 SHOWEXEPKCS7 ShowExe;
1274 RT_ZERO(ShowExe);
1275 ShowExe.cVerbosity = cVerbosity;
1276 HandleShowExeWorkerPkcs7Display(&ShowExe, pContentInfo->u.pSignedData, 0, pContentInfo);
1277 }
1278 return RTEXITCODE_SUCCESS;
1279 }
1280
1281 RTMsgError("RTCrPkcs7SimpleSignSignedData did not create SignedData: %s", pContentInfo->ContentType.szObjId);
1282 }
1283 else
1284 RTMsgError("RTCrPkcs7ContentInfo_DecodeAsn1 failed: %Rrc%#RTeim", rc, &ErrInfo.Core);
1285 RTCrPkcs7ContentInfo_Delete(pContentInfo);
1286 }
1287 RTMemFree(pvSigned);
1288 return RTEXITCODE_FAILURE;
1289}
1290
1291static RTEXITCODE SignToolPkcs7_AddTimestampSignatureEx(PRTCRPKCS7SIGNERINFO pSignerInfo, PRTCRPKCS7SIGNEDDATA pSignedData,
1292 unsigned cVerbosity, bool fReplaceExisting, bool fTimestampTypeOld,
1293 RTTIMESPEC SigningTime, SIGNTOOLKEYPAIR *pTimestampPair)
1294{
1295 AssertReturn(fTimestampTypeOld, RTMsgErrorExitFailure("New style signatures not supported yet"));
1296
1297 /*
1298 * Create a set of attributes we need to include in the AuthenticatedAttributes
1299 * of the timestamp signature.
1300 */
1301 RTCRPKCS7ATTRIBUTES AuthAttribs;
1302 int rc = RTCrPkcs7Attributes_Init(&AuthAttribs, &g_RTAsn1DefaultAllocator);
1303 if (RT_FAILURE(rc))
1304 return RTMsgErrorExitFailure("RTCrPkcs7SetOfAttributes_Init failed: %Rrc", rc);
1305
1306 RTEXITCODE rcExit = SignToolPkcs7_AddAuthAttribsForTimestamp(&AuthAttribs, fTimestampTypeOld, SigningTime,
1307 pTimestampPair->pCertificate);
1308 if (rcExit == RTEXITCODE_SUCCESS)
1309 {
1310 /*
1311 * Now create a PKCS#7 signature of the encrypted signature from the selected signer info.
1312 */
1313 void *pvSigned = NULL;
1314 PRTCRPKCS7SIGNEDDATA pTsSignedData = NULL;
1315 RTCRPKCS7CONTENTINFO TsContentInfo;
1316 rcExit = Pkcs7SignStuff("timestamp", pSignerInfo->EncryptedDigest.Asn1Core.uData.pv,
1317 pSignerInfo->EncryptedDigest.Asn1Core.cb, &AuthAttribs, NIL_RTCRSTORE /*hAdditionalCerts*/,
1318 RTCRPKCS7SIGN_SD_F_DEATCHED, RTDIGESTTYPE_SHA1, pTimestampPair, cVerbosity,
1319 &pvSigned, NULL /*pcbSigned*/, &TsContentInfo, &pTsSignedData);
1320 if (rcExit == RTEXITCODE_SUCCESS)
1321 {
1322
1323 /*
1324 * If we're replacing existing timestamp signatures, remove old ones now.
1325 */
1326 if ( fReplaceExisting
1327 && RTCrPkcs7Attributes_IsPresent(&pSignerInfo->UnauthenticatedAttributes))
1328 {
1329 uint32_t iItem = pSignerInfo->UnauthenticatedAttributes.cItems;
1330 while (iItem-- > 0)
1331 {
1332 PRTCRPKCS7ATTRIBUTE pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[iItem];
1333 if (pAttr->enmType == RTCRPKCS7ATTRIBUTETYPE_COUNTER_SIGNATURES) /* ASSUMES all counter sigs are timstamps */
1334 {
1335 if (cVerbosity > 1)
1336 RTMsgInfo("Removing counter signature in attribute #%u\n", iItem);
1337 rc = RTCrPkcs7Attributes_Erase(&pSignerInfo->UnauthenticatedAttributes, iItem);
1338 if (RT_FAILURE(rc))
1339 rcExit = RTMsgErrorExitFailure("RTCrPkcs7Attributes_Erase failed on #%u: %Rrc", iItem, rc);
1340 }
1341 }
1342 }
1343
1344 /*
1345 * Add the new one.
1346 */
1347 if (rcExit == RTEXITCODE_SUCCESS)
1348 rcExit = SignToolPkcs7_PrependCounterSignature(pSignerInfo, pTsSignedData->SignerInfos.papItems[0], cVerbosity);
1349
1350 /*
1351 * Make sure the signing certificate is included.
1352 */
1353 if (rcExit == RTEXITCODE_SUCCESS)
1354 rcExit = SignToolPkcs7_AppendCertificate(pSignedData, pTimestampPair->pCertificate);
1355
1356 /*
1357 * Clean up.
1358 */
1359 RTCrPkcs7ContentInfo_Delete(&TsContentInfo);
1360 RTMemFree(pvSigned);
1361 }
1362 }
1363 RTCrPkcs7Attributes_Delete(&AuthAttribs);
1364 return rcExit;
1365}
1366
1367static RTEXITCODE SignToolPkcs7_AddTimestampSignature(SIGNTOOLPKCS7EXE *pThis, unsigned cVerbosity, unsigned iSignature,
1368 bool fReplaceExisting, bool fTimestampTypeOld, RTTIMESPEC SigningTime,
1369 SIGNTOOLKEYPAIR *pTimestampPair)
1370{
1371 AssertReturn(fTimestampTypeOld, RTMsgErrorExitFailure("New style signatures not supported yet"));
1372
1373 /*
1374 * Locate the signature specified by iSignature and add a timestamp to it.
1375 */
1376 PRTCRPKCS7SIGNEDDATA pSignedData = NULL;
1377 PRTCRPKCS7SIGNERINFO pSignerInfo = SignToolPkcs7_FindNestedSignatureByIndex(pThis, iSignature, &pSignedData);
1378 if (!pSignerInfo)
1379 return RTMsgErrorExitFailure("No signature #%u in %s", iSignature, pThis->pszFilename);
1380
1381 return SignToolPkcs7_AddTimestampSignatureEx(pSignerInfo, pSignedData, cVerbosity, fReplaceExisting, fTimestampTypeOld,
1382 SigningTime, pTimestampPair);
1383}
1384
1385static RTEXITCODE SignToolPkcs7_SignSpcIndData(SIGNTOOLPKCS7EXE *pThis, RTCRSPCINDIRECTDATACONTENT *pSpcIndData,
1386 unsigned cVerbosity, bool fReplaceExisting, SIGNTOOLKEYPAIR *pSigningCertKey,
1387 RTCRSTORE hAddCerts, bool fTimestampTypeOld, RTTIMESPEC SigningTime,
1388 SIGNTOOLKEYPAIR *pTimestampCertKey)
1389{
1390 /*
1391 * Encode it.
1392 */
1393 RTERRINFOSTATIC ErrInfo;
1394 PRTASN1CORE pSpcRoot = RTCrSpcIndirectDataContent_GetAsn1Core(pSpcIndData);
1395 uint32_t cbSpcEncoded = 0;
1396 int rc = RTAsn1EncodePrepare(pSpcRoot, RTASN1ENCODE_F_DER, &cbSpcEncoded, RTErrInfoInitStatic(&ErrInfo));
1397 if (RT_FAILURE(rc))
1398 return RTMsgErrorExitFailure("RTAsn1EncodePrepare failed: %Rrc%RTeim", rc, &ErrInfo.Core);
1399
1400 if (cVerbosity >= 4)
1401 RTAsn1Dump(pSpcRoot, 0, 0, RTStrmDumpPrintfV, g_pStdOut);
1402
1403 void *pvSpcEncoded = (uint8_t *)RTMemTmpAllocZ(cbSpcEncoded);
1404 if (!pvSpcEncoded)
1405 return RTMsgErrorExitFailure("Failed to allocate %#z bytes for SpcIndirectData", cbSpcEncoded);
1406
1407 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
1408 rc = RTAsn1EncodeToBuffer(pSpcRoot, RTASN1ENCODE_F_DER, pvSpcEncoded, cbSpcEncoded, RTErrInfoInitStatic(&ErrInfo));
1409 if (RT_SUCCESS(rc))
1410 {
1411 /*
1412 * Create additional authenticated attributes.
1413 */
1414 RTCRPKCS7ATTRIBUTES AuthAttribs;
1415 rc = RTCrPkcs7Attributes_Init(&AuthAttribs, &g_RTAsn1DefaultAllocator);
1416 if (RT_SUCCESS(rc))
1417 {
1418 rcExit = SignToolPkcs7_AddAuthAttribsForImageSignature(&AuthAttribs, SigningTime);
1419 if (rcExit == RTEXITCODE_SUCCESS)
1420 {
1421 /*
1422 * Ditch the old signature if so desired.
1423 */
1424 if (fReplaceExisting && pThis->pSignedData)
1425 {
1426 RTCrPkcs7ContentInfo_Delete(&pThis->ContentInfo);
1427 pThis->pSignedData = NULL;
1428 RTMemFree(pThis->pbBuf);
1429 pThis->pbBuf = NULL;
1430 pThis->cbBuf = 0;
1431 }
1432
1433 /*
1434 * Do the actual signing.
1435 */
1436 SIGNTOOLPKCS7 Src = { NULL, 0, NULL };
1437 PSIGNTOOLPKCS7 pSigDst = !pThis->pSignedData ? pThis : &Src;
1438 rcExit = Pkcs7SignStuff("image", pvSpcEncoded, cbSpcEncoded, &AuthAttribs, hAddCerts, 0 /*fExtraFlags*/,
1439 RTDIGESTTYPE_SHA1 /** @todo*/, pSigningCertKey, cVerbosity,
1440 (void **)&pSigDst->pbBuf, &pSigDst->cbBuf, &pSigDst->ContentInfo, &pSigDst->pSignedData);
1441 if (rcExit == RTEXITCODE_SUCCESS)
1442 {
1443 /*
1444 * Add a timestamp signature if requested.
1445 */
1446 if (pTimestampCertKey->isComplete())
1447 rcExit = SignToolPkcs7_AddTimestampSignatureEx(pSigDst->pSignedData->SignerInfos.papItems[0],
1448 pSigDst->pSignedData,
1449 cVerbosity, false /*fReplaceExisting*/,
1450 fTimestampTypeOld, SigningTime, pTimestampCertKey);
1451
1452 /*
1453 * Append the signature to the existing one, if that's what we're doing.
1454 */
1455 if (rcExit == RTEXITCODE_SUCCESS && pSigDst == &Src)
1456 rcExit = SignToolPkcs7_AddNestedSignature(pThis, &Src, cVerbosity, true /*fPrepend*/); /** @todo prepend/append option */
1457
1458 /* cleanup */
1459 if (pSigDst == &Src)
1460 SignToolPkcs7_Delete(&Src);
1461 }
1462 }
1463 RTCrPkcs7Attributes_Delete(&AuthAttribs);
1464 }
1465 else
1466 RTMsgError("RTCrPkcs7SetOfAttributes_Init failed: %Rrc", rc);
1467 }
1468 else
1469 RTMsgError("RTAsn1EncodeToBuffer failed: %Rrc", rc);
1470 RTMemTmpFree(pvSpcEncoded);
1471 return rcExit;
1472}
1473
1474static RTEXITCODE SignToolPkcs7_SpcCompleteWithoutPageHashes(SIGNTOOLPKCS7EXE *pThis, RTCRSPCINDIRECTDATACONTENT *pSpcIndData)
1475{
1476 RT_NOREF(pThis);
1477 PRTCRSPCPEIMAGEDATA pPeImage = pSpcIndData->Data.uValue.pPeImage;
1478 Assert(pPeImage);
1479 //pPeImage->Flags
1480 RT_NOREF(pPeImage)
1481
1482 return RTEXITCODE_SUCCESS;
1483}
1484
1485static RTEXITCODE SignToolPkcs7_SpcAddImagePageHashes(SIGNTOOLPKCS7EXE *pThis, RTCRSPCINDIRECTDATACONTENT *pSpcIndData,
1486 RTDIGESTTYPE enmSigType)
1487{
1488 RT_NOREF(pThis, pSpcIndData, enmSigType);
1489 return RTEXITCODE_SUCCESS;
1490}
1491
1492static RTEXITCODE SignToolPkcs7_SpcAddImageHash(SIGNTOOLPKCS7EXE *pThis, RTCRSPCINDIRECTDATACONTENT *pSpcIndData,
1493 RTDIGESTTYPE enmSigType)
1494{
1495 uint32_t const cbHash = RTCrDigestTypeToHashSize(enmSigType);
1496 const char * const pszAlgId = RTCrDigestTypeToAlgorithmOid(enmSigType);
1497
1498 /*
1499 * Ask the loader for the hash.
1500 */
1501 uint8_t abHash[RTSHA512_HASH_SIZE];
1502 int rc = RTLdrHashImage(pThis->hLdrMod, enmSigType, abHash, sizeof(abHash));
1503 if (RT_FAILURE(rc))
1504 return RTMsgErrorExitFailure("RTLdrHashImage/%s failed: %Rrc", RTCrDigestTypeToName(enmSigType), rc);
1505
1506 /*
1507 * Set it.
1508 */
1509 /** @todo no setter, this should be okay, though... */
1510 rc = RTAsn1ObjId_InitFromString(&pSpcIndData->DigestInfo.DigestAlgorithm.Algorithm, pszAlgId, &g_RTAsn1DefaultAllocator);
1511 if (RT_FAILURE(rc))
1512 return RTMsgErrorExitFailure("RTAsn1ObjId_InitFromString/%s failed: %Rrc", pszAlgId, rc);
1513
1514 rc = RTAsn1ContentDup(&pSpcIndData->DigestInfo.Digest.Asn1Core, abHash, cbHash, &g_RTAsn1DefaultAllocator);
1515 if (RT_FAILURE(rc))
1516 return RTMsgErrorExitFailure("RTAsn1ContentDup/%#x failed: %Rrc", cbHash, rc);
1517
1518 return RTEXITCODE_SUCCESS;
1519}
1520
1521
1522static RTEXITCODE SignToolPkcs7_AddOrReplaceSignature(SIGNTOOLPKCS7EXE *pThis, unsigned cVerbosity, RTDIGESTTYPE enmSigType,
1523 bool fReplaceExisting, bool fHashPages, SIGNTOOLKEYPAIR *pSigningCertKey,
1524 RTCRSTORE hAddCerts, bool fTimestampTypeOld,
1525 RTTIMESPEC SigningTime, SIGNTOOLKEYPAIR *pTimestampCertKey)
1526{
1527 AssertReturn(fTimestampTypeOld || pTimestampCertKey->isNull(),
1528 RTMsgErrorExitFailure("New style signatures not supported yet"));
1529
1530 /*
1531 * We must construct the data to be backed into the PKCS#7 signature
1532 * and signed.
1533 */
1534 RTCRSPCINDIRECTDATACONTENT SpcIndData;
1535 int rc = RTCrSpcIndirectDataContent_Init(&SpcIndData, &g_RTAsn1DefaultAllocator);
1536 if (RT_FAILURE(rc))
1537 return RTMsgErrorExitFailure("RTCrSpcIndirectDataContent_Init failed: %Rrc", rc);
1538
1539 /* Set the data to PE image. */
1540 /** @todo Generalize the Type + enmType DYN stuff and generate setters. */
1541 Assert(SpcIndData.Data.enmType == RTCRSPCAAOVTYPE_NOT_PRESENT);
1542 Assert(SpcIndData.Data.uValue.pPeImage == NULL);
1543 SpcIndData.Data.enmType = RTCRSPCAAOVTYPE_PE_IMAGE_DATA;
1544 rc = RTAsn1MemAllocZ(&SpcIndData.Data.Allocation, (void **)&SpcIndData.Data.uValue.pPeImage,
1545 sizeof(*SpcIndData.Data.uValue.pPeImage));
1546 RTEXITCODE rcExit;
1547 if (RT_SUCCESS(rc))
1548 {
1549 rc = RTCrSpcPeImageData_Init(SpcIndData.Data.uValue.pPeImage, SpcIndData.Data.Allocation.pAllocator);
1550 if (RT_SUCCESS(rc))
1551 {
1552 /* Add the hashes. */
1553 rcExit = SignToolPkcs7_SpcAddImageHash(pThis, &SpcIndData, enmSigType);
1554 if (rcExit == RTEXITCODE_SUCCESS)
1555 {
1556 if (fHashPages)
1557 rcExit = SignToolPkcs7_SpcAddImagePageHashes(pThis, &SpcIndData, enmSigType);
1558 else
1559 rcExit = SignToolPkcs7_SpcCompleteWithoutPageHashes(pThis, &SpcIndData);
1560
1561 /*
1562 * Encode and sign the SPC data, timestamp it, and line it up for adding to the executable.
1563 */
1564 if (rcExit == RTEXITCODE_SUCCESS)
1565 rcExit = SignToolPkcs7_SignSpcIndData(pThis, &SpcIndData, cVerbosity, fReplaceExisting, pSigningCertKey,
1566 hAddCerts, fTimestampTypeOld, SigningTime, pTimestampCertKey);
1567 }
1568 }
1569 else
1570 rcExit = RTMsgErrorExitFailure("RTCrPkcs7SignerInfos_Init failed: %Rrc", rc);
1571 }
1572 else
1573 rcExit = RTMsgErrorExitFailure("RTAsn1MemAllocZ failed for RTCRSPCPEIMAGEDATA: %Rrc", rc);
1574
1575 RTCrSpcIndirectDataContent_Delete(&SpcIndData);
1576 return rcExit;
1577}
1578
1579#endif /* !IPRT_IN_BUILD_TOOL */
1580
1581
1582/*********************************************************************************************************************************
1583* The 'extract-exe-signer-cert' command. *
1584*********************************************************************************************************************************/
1585
1586static RTEXITCODE HelpExtractExeSignerCert(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
1587{
1588 RT_NOREF_PV(enmLevel);
1589 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
1590 "extract-exe-signer-cert [--ber|--cer|--der] [--signature-index|-i <num>] [--exe|-e] <exe> [--output|-o] <outfile.cer>\n");
1591 return RTEXITCODE_SUCCESS;
1592}
1593
1594static RTEXITCODE HandleExtractExeSignerCert(int cArgs, char **papszArgs)
1595{
1596 /*
1597 * Parse arguments.
1598 */
1599 static const RTGETOPTDEF s_aOptions[] =
1600 {
1601 { "--ber", 'b', RTGETOPT_REQ_NOTHING },
1602 { "--cer", 'c', RTGETOPT_REQ_NOTHING },
1603 { "--der", 'd', RTGETOPT_REQ_NOTHING },
1604 { "--exe", 'e', RTGETOPT_REQ_STRING },
1605 { "--output", 'o', RTGETOPT_REQ_STRING },
1606 { "--signature-index", 'i', RTGETOPT_REQ_UINT32 },
1607 };
1608
1609 const char *pszExe = NULL;
1610 const char *pszOut = NULL;
1611 RTLDRARCH enmLdrArch = RTLDRARCH_WHATEVER;
1612 unsigned cVerbosity = 0;
1613 uint32_t fCursorFlags = RTASN1CURSOR_FLAGS_DER;
1614 uint32_t iSignature = 0;
1615
1616 RTGETOPTSTATE GetState;
1617 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1618 AssertRCReturn(rc, RTEXITCODE_FAILURE);
1619 RTGETOPTUNION ValueUnion;
1620 int ch;
1621 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1622 {
1623 switch (ch)
1624 {
1625 case 'e': pszExe = ValueUnion.psz; break;
1626 case 'o': pszOut = ValueUnion.psz; break;
1627 case 'b': fCursorFlags = 0; break;
1628 case 'c': fCursorFlags = RTASN1CURSOR_FLAGS_CER; break;
1629 case 'd': fCursorFlags = RTASN1CURSOR_FLAGS_DER; break;
1630 case 'i': iSignature = ValueUnion.u32; break;
1631 case 'V': return HandleVersion(cArgs, papszArgs);
1632 case 'h': return HelpExtractExeSignerCert(g_pStdOut, RTSIGNTOOLHELP_FULL);
1633
1634 case VINF_GETOPT_NOT_OPTION:
1635 if (!pszExe)
1636 pszExe = ValueUnion.psz;
1637 else if (!pszOut)
1638 pszOut = ValueUnion.psz;
1639 else
1640 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many file arguments: %s", ValueUnion.psz);
1641 break;
1642
1643 default:
1644 return RTGetOptPrintError(ch, &ValueUnion);
1645 }
1646 }
1647 if (!pszExe)
1648 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
1649 if (!pszOut)
1650 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No output file given.");
1651 if (RTPathExists(pszOut))
1652 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The output file '%s' exists.", pszOut);
1653
1654 /*
1655 * Do it.
1656 */
1657 /* Read & decode the PKCS#7 signature. */
1658 SIGNTOOLPKCS7EXE This;
1659 RTEXITCODE rcExit = SignToolPkcs7Exe_InitFromFile(&This, pszExe, cVerbosity, enmLdrArch);
1660 if (rcExit == RTEXITCODE_SUCCESS)
1661 {
1662 /* Find the signing certificate (ASSUMING that the certificate used is shipped in the set of certificates). */
1663 PRTCRPKCS7SIGNEDDATA pSignedData;
1664 PCRTCRPKCS7SIGNERINFO pSignerInfo = SignToolPkcs7_FindNestedSignatureByIndex(&This, iSignature, &pSignedData);
1665 rcExit = RTEXITCODE_FAILURE;
1666 if (pSignerInfo)
1667 {
1668 PCRTCRPKCS7ISSUERANDSERIALNUMBER pISN = &pSignedData->SignerInfos.papItems[0]->IssuerAndSerialNumber;
1669 PCRTCRX509CERTIFICATE pCert;
1670 pCert = RTCrPkcs7SetOfCerts_FindX509ByIssuerAndSerialNumber(&pSignedData->Certificates,
1671 &pISN->Name, &pISN->SerialNumber);
1672 if (pCert)
1673 {
1674 /*
1675 * Write it out.
1676 */
1677 RTFILE hFile;
1678 rc = RTFileOpen(&hFile, pszOut, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE);
1679 if (RT_SUCCESS(rc))
1680 {
1681 uint32_t cbCert = pCert->SeqCore.Asn1Core.cbHdr + pCert->SeqCore.Asn1Core.cb;
1682 rc = RTFileWrite(hFile, pCert->SeqCore.Asn1Core.uData.pu8 - pCert->SeqCore.Asn1Core.cbHdr,
1683 cbCert, NULL);
1684 if (RT_SUCCESS(rc))
1685 {
1686 rc = RTFileClose(hFile);
1687 if (RT_SUCCESS(rc))
1688 {
1689 hFile = NIL_RTFILE;
1690 rcExit = RTEXITCODE_SUCCESS;
1691 RTMsgInfo("Successfully wrote %u bytes to '%s'", cbCert, pszOut);
1692 }
1693 else
1694 RTMsgError("RTFileClose failed: %Rrc", rc);
1695 }
1696 else
1697 RTMsgError("RTFileWrite failed: %Rrc", rc);
1698 RTFileClose(hFile);
1699 }
1700 else
1701 RTMsgError("Error opening '%s' for writing: %Rrc", pszOut, rc);
1702 }
1703 else
1704 RTMsgError("Certificate not found.");
1705 }
1706 else
1707 RTMsgError("Could not locate signature #%u!", iSignature);
1708
1709 /* Delete the signature data. */
1710 SignToolPkcs7Exe_Delete(&This);
1711 }
1712 return rcExit;
1713}
1714
1715
1716/*********************************************************************************************************************************
1717* The 'add-nested-exe-signature' command. *
1718*********************************************************************************************************************************/
1719
1720static RTEXITCODE HelpAddNestedExeSignature(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
1721{
1722 RT_NOREF_PV(enmLevel);
1723 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
1724 "add-nested-exe-signature [-v|--verbose] [-d|--debug] [-p|--prepend] <destination-exe> <source-exe>\n");
1725 if (enmLevel == RTSIGNTOOLHELP_FULL)
1726 RTStrmWrappedPrintf(pStrm, 0,
1727 "\n"
1728 "The --debug option allows the source-exe to be omitted in order to test the "
1729 "encoding and PE file modification.\n"
1730 "\n"
1731 "The --prepend option puts the nested signature first rather than appending it "
1732 "to the end of of the nested signature set. Windows reads nested signatures in "
1733 "reverse order, so --prepend will logically putting it last.\n");
1734 return RTEXITCODE_SUCCESS;
1735}
1736
1737
1738static RTEXITCODE HandleAddNestedExeSignature(int cArgs, char **papszArgs)
1739{
1740 /*
1741 * Parse arguments.
1742 */
1743 static const RTGETOPTDEF s_aOptions[] =
1744 {
1745 { "--prepend", 'p', RTGETOPT_REQ_NOTHING },
1746 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
1747 { "--debug", 'd', RTGETOPT_REQ_NOTHING },
1748 };
1749
1750 const char *pszDst = NULL;
1751 const char *pszSrc = NULL;
1752 unsigned cVerbosity = 0;
1753 bool fDebug = false;
1754 bool fPrepend = false;
1755
1756 RTGETOPTSTATE GetState;
1757 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1758 AssertRCReturn(rc, RTEXITCODE_FAILURE);
1759 RTGETOPTUNION ValueUnion;
1760 int ch;
1761 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1762 {
1763 switch (ch)
1764 {
1765 case 'v': cVerbosity++; break;
1766 case 'd': fDebug = pszSrc == NULL; break;
1767 case 'p': fPrepend = true; break;
1768 case 'V': return HandleVersion(cArgs, papszArgs);
1769 case 'h': return HelpAddNestedExeSignature(g_pStdOut, RTSIGNTOOLHELP_FULL);
1770
1771 case VINF_GETOPT_NOT_OPTION:
1772 if (!pszDst)
1773 pszDst = ValueUnion.psz;
1774 else if (!pszSrc)
1775 {
1776 pszSrc = ValueUnion.psz;
1777 fDebug = false;
1778 }
1779 else
1780 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many file arguments: %s", ValueUnion.psz);
1781 break;
1782
1783 default:
1784 return RTGetOptPrintError(ch, &ValueUnion);
1785 }
1786 }
1787 if (!pszDst)
1788 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No destination executable given.");
1789 if (!pszSrc && !fDebug)
1790 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No source executable file given.");
1791
1792 /*
1793 * Do it.
1794 */
1795 /* Read & decode the source PKCS#7 signature. */
1796 SIGNTOOLPKCS7EXE Src;
1797 RTEXITCODE rcExit = pszSrc ? SignToolPkcs7Exe_InitFromFile(&Src, pszSrc, cVerbosity) : RTEXITCODE_SUCCESS;
1798 if (rcExit == RTEXITCODE_SUCCESS)
1799 {
1800 /* Ditto for the destination PKCS#7 signature. */
1801 SIGNTOOLPKCS7EXE Dst;
1802 rcExit = SignToolPkcs7Exe_InitFromFile(&Dst, pszDst, cVerbosity);
1803 if (rcExit == RTEXITCODE_SUCCESS)
1804 {
1805 /* Do the signature manipulation. */
1806 if (pszSrc)
1807 rcExit = SignToolPkcs7_AddNestedSignature(&Dst, &Src, cVerbosity, fPrepend);
1808 if (rcExit == RTEXITCODE_SUCCESS)
1809 rcExit = SignToolPkcs7_Encode(&Dst, cVerbosity);
1810
1811 /* Update the destination executable file. */
1812 if (rcExit == RTEXITCODE_SUCCESS)
1813 rcExit = SignToolPkcs7Exe_WriteSignatureToFile(&Dst, cVerbosity);
1814
1815 SignToolPkcs7Exe_Delete(&Dst);
1816 }
1817 if (pszSrc)
1818 SignToolPkcs7Exe_Delete(&Src);
1819 }
1820
1821 return rcExit;
1822}
1823
1824
1825/*********************************************************************************************************************************
1826* The 'add-nested-cat-signature' command. *
1827*********************************************************************************************************************************/
1828
1829static RTEXITCODE HelpAddNestedCatSignature(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
1830{
1831 RT_NOREF_PV(enmLevel);
1832 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
1833 "add-nested-cat-signature [-v|--verbose] [-d|--debug] [-p|--prepend] <destination-cat> <source-cat>\n");
1834 if (enmLevel == RTSIGNTOOLHELP_FULL)
1835 RTStrmWrappedPrintf(pStrm, 0,
1836 "\n"
1837 "The --debug option allows the source-cat to be omitted in order to test the "
1838 "ASN.1 re-encoding of the destination catalog file.\n"
1839 "\n"
1840 "The --prepend option puts the nested signature first rather than appending it "
1841 "to the end of of the nested signature set. Windows reads nested signatures in "
1842 "reverse order, so --prepend will logically putting it last.\n");
1843 return RTEXITCODE_SUCCESS;
1844}
1845
1846
1847static RTEXITCODE HandleAddNestedCatSignature(int cArgs, char **papszArgs)
1848{
1849 /*
1850 * Parse arguments.
1851 */
1852 static const RTGETOPTDEF s_aOptions[] =
1853 {
1854 { "--prepend", 'p', RTGETOPT_REQ_NOTHING },
1855 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
1856 { "--debug", 'd', RTGETOPT_REQ_NOTHING },
1857 };
1858
1859 const char *pszDst = NULL;
1860 const char *pszSrc = NULL;
1861 unsigned cVerbosity = 0;
1862 bool fDebug = false;
1863 bool fPrepend = false;
1864
1865 RTGETOPTSTATE GetState;
1866 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1867 AssertRCReturn(rc, RTEXITCODE_FAILURE);
1868 RTGETOPTUNION ValueUnion;
1869 int ch;
1870 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1871 {
1872 switch (ch)
1873 {
1874 case 'v': cVerbosity++; break;
1875 case 'd': fDebug = pszSrc == NULL; break;
1876 case 'p': fPrepend = true; break;
1877 case 'V': return HandleVersion(cArgs, papszArgs);
1878 case 'h': return HelpAddNestedCatSignature(g_pStdOut, RTSIGNTOOLHELP_FULL);
1879
1880 case VINF_GETOPT_NOT_OPTION:
1881 if (!pszDst)
1882 pszDst = ValueUnion.psz;
1883 else if (!pszSrc)
1884 {
1885 pszSrc = ValueUnion.psz;
1886 fDebug = false;
1887 }
1888 else
1889 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many file arguments: %s", ValueUnion.psz);
1890 break;
1891
1892 default:
1893 return RTGetOptPrintError(ch, &ValueUnion);
1894 }
1895 }
1896 if (!pszDst)
1897 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No destination catalog file given.");
1898 if (!pszSrc && !fDebug)
1899 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No source catalog file given.");
1900
1901 /*
1902 * Do it.
1903 */
1904 /* Read & decode the source PKCS#7 signature. */
1905 SIGNTOOLPKCS7 Src;
1906 RTEXITCODE rcExit = pszSrc ? SignToolPkcs7_InitFromFile(&Src, pszSrc, cVerbosity) : RTEXITCODE_SUCCESS;
1907 if (rcExit == RTEXITCODE_SUCCESS)
1908 {
1909 /* Ditto for the destination PKCS#7 signature. */
1910 SIGNTOOLPKCS7EXE Dst;
1911 rcExit = SignToolPkcs7_InitFromFile(&Dst, pszDst, cVerbosity);
1912 if (rcExit == RTEXITCODE_SUCCESS)
1913 {
1914 /* Do the signature manipulation. */
1915 if (pszSrc)
1916 rcExit = SignToolPkcs7_AddNestedSignature(&Dst, &Src, cVerbosity, fPrepend);
1917 if (rcExit == RTEXITCODE_SUCCESS)
1918 rcExit = SignToolPkcs7_Encode(&Dst, cVerbosity);
1919
1920 /* Update the destination executable file. */
1921 if (rcExit == RTEXITCODE_SUCCESS)
1922 rcExit = SignToolPkcs7_WriteSignatureToFile(&Dst, pszDst, cVerbosity);
1923
1924 SignToolPkcs7_Delete(&Dst);
1925 }
1926 if (pszSrc)
1927 SignToolPkcs7_Delete(&Src);
1928 }
1929
1930 return rcExit;
1931}
1932
1933
1934/*********************************************************************************************************************************
1935* Option handlers shared by 'sign-exe', 'sign-cat', 'add-timestamp-exe-signature' and others. *
1936*********************************************************************************************************************************/
1937#ifndef IPRT_IN_BUILD_TOOL
1938
1939static RTEXITCODE HandleOptCertFile(SIGNTOOLKEYPAIR *pKeyPair, const char *pszFile)
1940{
1941 if (pKeyPair->pCertificate == &pKeyPair->Cert)
1942 RTCrX509Certificate_Delete(&pKeyPair->Cert);
1943 pKeyPair->pCertificate = NULL;
1944
1945 RTERRINFOSTATIC ErrInfo;
1946 int rc = RTCrX509Certificate_ReadFromFile(&pKeyPair->Cert, pszFile, 0, &g_RTAsn1DefaultAllocator,
1947 RTErrInfoInitStatic(&ErrInfo));
1948 if (RT_FAILURE(rc))
1949 return RTMsgErrorExitFailure("Error reading certificate from '%s': %Rrc%#RTeim", pszFile, rc, &ErrInfo.Core);
1950 pKeyPair->pCertificate = &pKeyPair->Cert;
1951 return RTEXITCODE_SUCCESS;
1952}
1953
1954static RTEXITCODE HandleOptKeyFile(SIGNTOOLKEYPAIR *pKeyPair, const char *pszFile)
1955{
1956 RTCrKeyRelease(pKeyPair->hPrivateKey);
1957
1958 RTERRINFOSTATIC ErrInfo;
1959 int rc = RTCrKeyCreateFromFile(&pKeyPair->hPrivateKey, 0 /*fFlags*/, pszFile,
1960 NULL /*pszPassword*/, RTErrInfoInitStatic(&ErrInfo));
1961 if (RT_SUCCESS(rc))
1962 return RTEXITCODE_SUCCESS;
1963
1964 pKeyPair->hPrivateKey = NIL_RTCRKEY;
1965 return RTMsgErrorExitFailure("Error reading private key from '%s': %Rrc%#RTeim", pszFile, rc, &ErrInfo.Core);
1966}
1967
1968static RTEXITCODE HandleOptAddCert(PRTCRSTORE phStore, const char *pszFile)
1969{
1970 if (*phStore == NIL_RTCRSTORE)
1971 {
1972 int rc = RTCrStoreCreateInMem(phStore, 2);
1973 if (RT_FAILURE(rc))
1974 return RTMsgErrorExitFailure("RTCrStoreCreateInMem(,2) failed: %Rrc", rc);
1975 }
1976 RTERRINFOSTATIC ErrInfo;
1977 int rc = RTCrStoreCertAddFromFile(*phStore, RTCRCERTCTX_F_ADD_IF_NOT_FOUND, pszFile, RTErrInfoInitStatic(&ErrInfo));
1978 if (RT_FAILURE(rc))
1979 return RTMsgErrorExitFailure("Error reading certificate from '%s': %Rrc%#RTeim", pszFile, rc, &ErrInfo.Core);
1980 return RTEXITCODE_SUCCESS;
1981}
1982
1983static RTEXITCODE HandleOptSignatureType(RTDIGESTTYPE *penmSigType, const char *pszType)
1984{
1985 if ( RTStrICmpAscii(pszType, "sha1") == 0
1986 || RTStrICmpAscii(pszType, "sha-1") == 0)
1987 *penmSigType = RTDIGESTTYPE_SHA1;
1988 else if ( RTStrICmpAscii(pszType, "sha256") == 0
1989 || RTStrICmpAscii(pszType, "sha-256") == 0)
1990 *penmSigType = RTDIGESTTYPE_SHA256;
1991 else
1992 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown signature type: %s (expected sha1 or sha256)", pszType);
1993 return RTEXITCODE_SUCCESS;
1994}
1995
1996
1997static RTEXITCODE HandleOptTimestampType(bool *pfOldType, const char *pszType)
1998{
1999 if (strcmp(pszType, "old") == 0)
2000 *pfOldType = true;
2001 else if (strcmp(pszType, "new") == 0)
2002 *pfOldType = false;
2003 else
2004 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown timestamp type: %s", pszType);
2005 return RTEXITCODE_SUCCESS;
2006}
2007
2008static RTEXITCODE HandleOptTimestampOverride(PRTTIMESPEC pSigningTime, const char *pszPartialTs)
2009{
2010 /*
2011 * First try use it as-is.
2012 */
2013 if (RTTimeSpecFromString(pSigningTime, pszPartialTs) != NULL)
2014 return RTEXITCODE_SUCCESS;
2015
2016 /* Check the input against a pattern, making sure we've got something that
2017 makes sense before trying to merge. */
2018 size_t const cchPartialTs = strlen(pszPartialTs);
2019 static char s_szPattern[] = "0000-00-00T00:00:";
2020 if (cchPartialTs > sizeof(s_szPattern) - 1) /* It is not a partial timestamp if we've got the seconds component. */
2021 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid timestamp: %s", pszPartialTs);
2022
2023 for (size_t off = 0; off < cchPartialTs; off++)
2024 switch (s_szPattern[off])
2025 {
2026 case '0':
2027 if (!RT_C_IS_DIGIT(pszPartialTs[off]))
2028 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid timestamp, expected digit at position %u: %s",
2029 off + 1, pszPartialTs);
2030 break;
2031 case '-':
2032 case ':':
2033 if (pszPartialTs[off] != s_szPattern[off])
2034 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid timestamp, expected '%c' at position %u: %s",
2035 s_szPattern[off], off + 1, pszPartialTs);
2036 break;
2037 case 'T':
2038 if ( pszPartialTs[off] != 'T'
2039 && pszPartialTs[off] != 't'
2040 && pszPartialTs[off] != ' ')
2041 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid timestamp, expected 'T' or space at position %u: %s",
2042 off + 1, pszPartialTs);
2043 break;
2044 default:
2045 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Internal error");
2046 }
2047
2048 if (RT_C_IS_DIGIT(s_szPattern[cchPartialTs]) && RT_C_IS_DIGIT(s_szPattern[cchPartialTs - 1]))
2049 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Incomplete timstamp component: %s", pszPartialTs);
2050
2051 /*
2052 * Take the current time and merge in the components from pszPartialTs.
2053 */
2054 char szSigningTime[RTTIME_STR_LEN];
2055 RTTIMESPEC Now;
2056 RTTimeSpecToString(RTTimeNow(&Now), szSigningTime, sizeof(szSigningTime));
2057 memcpy(szSigningTime, pszPartialTs, cchPartialTs);
2058 szSigningTime[4+1+2+1+2] = 'T';
2059
2060 /* Fix 29th for non-leap override: */
2061 if (memcmp(&szSigningTime[5], RT_STR_TUPLE("02-29")) == 0)
2062 {
2063 if (!RTTimeIsLeapYear(RTStrToUInt32(szSigningTime)))
2064 szSigningTime[9] = '8';
2065 }
2066 if (RTTimeSpecFromString(pSigningTime, szSigningTime) == NULL)
2067 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid timestamp: %s (%s)", pszPartialTs, szSigningTime);
2068
2069 return RTEXITCODE_SUCCESS;
2070}
2071
2072#endif /* !IPRT_IN_BUILD_TOOL */
2073
2074
2075/*********************************************************************************************************************************
2076* The 'add-timestamp-exe-signature' command. *
2077*********************************************************************************************************************************/
2078#ifndef IPRT_IN_BUILD_TOOL
2079
2080static RTEXITCODE HelpAddTimestampExeSignature(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
2081{
2082 RT_NOREF_PV(enmLevel);
2083
2084 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
2085 "add-timestamp-exe-signature [-v|--verbose] [--signature-index|-i <num>] "
2086 "[--timestamp-cert-file <file>] "
2087 "[--timestamp-key-file <file>] "
2088 "[--timestamp-type old|new] "
2089 "[--timestamp-date <fake-isots>] "
2090 "[--timestamp-year <fake-year>] "
2091 "[--replace-existing|-r] "
2092 "<exe>\n");
2093 if (enmLevel == RTSIGNTOOLHELP_FULL)
2094 RTStrmWrappedPrintf(pStrm, 0,
2095 "This is mainly to test timestamp code.\n"
2096 "\n"
2097 "The --timestamp-override option can take a partial or full ISO timestamp. It is merged "
2098 "with the current time if partial.\n"
2099 "\n");
2100 return RTEXITCODE_SUCCESS;
2101}
2102
2103static RTEXITCODE HandleAddTimestampExeSignature(int cArgs, char **papszArgs)
2104{
2105 /*
2106 * Parse arguments.
2107 */
2108 static const RTGETOPTDEF s_aOptions[] =
2109 {
2110 { "--signature-index", 'i', RTGETOPT_REQ_UINT32 },
2111 { "--timestamp-cert-file", OPT_TIMESTAMP_CERT_FILE, RTGETOPT_REQ_STRING },
2112 { "--timestamp-key-file", OPT_TIMESTAMP_KEY_FILE, RTGETOPT_REQ_STRING },
2113 { "--timestamp-type", OPT_TIMESTAMP_TYPE, RTGETOPT_REQ_STRING },
2114 { "--timestamp-override", OPT_TIMESTAMP_OVERRIDE, RTGETOPT_REQ_STRING },
2115 { "--replace-existing", 'r', RTGETOPT_REQ_NOTHING },
2116 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
2117 };
2118
2119 unsigned cVerbosity = 0;
2120 unsigned iSignature = 0;
2121 bool fReplaceExisting = false;
2122 bool fTimestampTypeOld = true;
2123 SIGNTOOLKEYPAIR TimestampCertKey;
2124 RTTIMESPEC SigningTime;
2125 RTTimeNow(&SigningTime);
2126
2127 RTGETOPTSTATE GetState;
2128 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
2129 AssertRCReturn(rc, RTEXITCODE_FAILURE);
2130
2131 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
2132 RTGETOPTUNION ValueUnion;
2133 int ch;
2134 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
2135 {
2136 RTEXITCODE rcExit2 = RTEXITCODE_SUCCESS;
2137 switch (ch)
2138 {
2139 case 'i': iSignature = ValueUnion.u32; break;
2140 case OPT_TIMESTAMP_CERT_FILE: rcExit2 = HandleOptCertFile(&TimestampCertKey, ValueUnion.psz); break;
2141 case OPT_TIMESTAMP_KEY_FILE: rcExit2 = HandleOptKeyFile(&TimestampCertKey, ValueUnion.psz); break;
2142 case OPT_TIMESTAMP_TYPE: rcExit2 = HandleOptTimestampType(&fTimestampTypeOld, ValueUnion.psz); break;
2143 case OPT_TIMESTAMP_OVERRIDE: rcExit2 = HandleOptTimestampOverride(&SigningTime, ValueUnion.psz); break;
2144 case 'r': fReplaceExisting = true; break;
2145 case 'v': cVerbosity++; break;
2146 case 'V': return HandleVersion(cArgs, papszArgs);
2147 case 'h': return HelpAddTimestampExeSignature(g_pStdOut, RTSIGNTOOLHELP_FULL);
2148
2149 case VINF_GETOPT_NOT_OPTION:
2150 /* check that we've got all the info we need: */
2151 if (TimestampCertKey.isComplete())
2152 {
2153 /* Do the work: */
2154 SIGNTOOLPKCS7EXE Exe;
2155 rcExit2 = SignToolPkcs7Exe_InitFromFile(&Exe, ValueUnion.psz, cVerbosity);
2156 if (rcExit2 == RTEXITCODE_SUCCESS)
2157 {
2158 rcExit2 = SignToolPkcs7_AddTimestampSignature(&Exe, cVerbosity, iSignature, fReplaceExisting,
2159 fTimestampTypeOld, SigningTime, &TimestampCertKey);
2160 if (rcExit2 == RTEXITCODE_SUCCESS)
2161 rcExit2 = SignToolPkcs7_Encode(&Exe, cVerbosity);
2162 if (rcExit2 == RTEXITCODE_SUCCESS)
2163 rcExit2 = SignToolPkcs7Exe_WriteSignatureToFile(&Exe, cVerbosity);
2164 SignToolPkcs7Exe_Delete(&Exe);
2165 }
2166 if (rcExit2 != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
2167 rcExit = rcExit2;
2168 rcExit2 = RTEXITCODE_SUCCESS;
2169 }
2170 else
2171 {
2172 if (!TimestampCertKey.pCertificate)
2173 RTMsgError("No timestamp certificate was specified");
2174 if (TimestampCertKey.hPrivateKey == NIL_RTCRKEY)
2175 RTMsgError("No timestamp private key was specified");
2176 rcExit2 = RTEXITCODE_SYNTAX;
2177 }
2178 break;
2179
2180 default:
2181 return RTGetOptPrintError(ch, &ValueUnion);
2182 }
2183
2184 if (rcExit2 != RTEXITCODE_SUCCESS)
2185 {
2186 rcExit = rcExit2;
2187 break;
2188 }
2189 }
2190 return rcExit;
2191}
2192
2193#endif /*!IPRT_IN_BUILD_TOOL */
2194
2195
2196/*********************************************************************************************************************************
2197* The 'sign-exe' command. *
2198*********************************************************************************************************************************/
2199#ifndef IPRT_IN_BUILD_TOOL
2200
2201static RTEXITCODE HelpSignExe(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
2202{
2203 RT_NOREF_PV(enmLevel);
2204
2205 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
2206 "sign-exe [-v|--verbose] "
2207 "[--type sha1|sha256] "
2208 "[--hash-pages] "
2209 "[--no-hash-pages] "
2210 "[--append] "
2211 "[--cert-file <file>] "
2212 "[--cert-key <file>] "
2213 "[--add-cert <file>] "
2214 "[--timestamp-cert-file <file>] "
2215 "[--timestamp-key-file <file>] "
2216 "[--timestamp-type old|new] "
2217 "[--timestamp-date <fake-isots>] "
2218 "[--timestamp-year <fake-year>] "
2219 "[--replace-existing|-r] "
2220 "<exe>\n");
2221 if (enmLevel == RTSIGNTOOLHELP_FULL)
2222 RTStrmWrappedPrintf(pStrm, 0,
2223 "Create a new code signature for an executable.\n"
2224 "\n"
2225 "The --timestamp-override option can take a partial or full ISO timestamp. It is merged "
2226 "with the current time if partial.\n"
2227 "\n");
2228 return RTEXITCODE_SUCCESS;
2229}
2230
2231
2232static RTEXITCODE HandleSignExe(int cArgs, char **papszArgs)
2233{
2234 /*
2235 * Parse arguments.
2236 */
2237 static const RTGETOPTDEF s_aOptions[] =
2238 {
2239 { "--append", 'a', RTGETOPT_REQ_NOTHING },
2240 { "/as", 'a', RTGETOPT_REQ_NOTHING },
2241 { "--type", 't', RTGETOPT_REQ_STRING },
2242 { "/fd", 't', RTGETOPT_REQ_STRING },
2243 { "--hash-pages", OPT_HASH_PAGES, RTGETOPT_REQ_NOTHING },
2244 { "/ph", OPT_HASH_PAGES, RTGETOPT_REQ_NOTHING },
2245 { "--no-hash-pages", OPT_NO_HASH_PAGES, RTGETOPT_REQ_NOTHING },
2246 { "/nph", OPT_NO_HASH_PAGES, RTGETOPT_REQ_NOTHING },
2247 { "--add-cert", OPT_ADD_CERT, RTGETOPT_REQ_STRING },
2248 { "/ac", OPT_ADD_CERT, RTGETOPT_REQ_STRING },
2249 { "--cert-file", OPT_CERT_FILE, RTGETOPT_REQ_STRING },
2250 { "--key-file", OPT_KEY_FILE, RTGETOPT_REQ_STRING },
2251 { "--timestamp-cert-file", OPT_TIMESTAMP_CERT_FILE, RTGETOPT_REQ_STRING },
2252 { "--timestamp-key-file", OPT_TIMESTAMP_KEY_FILE, RTGETOPT_REQ_STRING },
2253 { "--timestamp-type", OPT_TIMESTAMP_TYPE, RTGETOPT_REQ_STRING },
2254 { "--timestamp-override", OPT_TIMESTAMP_OVERRIDE, RTGETOPT_REQ_STRING },
2255 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
2256 { "/v", 'v', RTGETOPT_REQ_NOTHING },
2257 { "/debug", 'v', RTGETOPT_REQ_NOTHING },
2258 };
2259
2260 unsigned cVerbosity = 0;
2261 RTDIGESTTYPE enmSigType = RTDIGESTTYPE_SHA1;
2262 bool fReplaceExisting = true;
2263 bool fHashPages = false;
2264 SIGNTOOLKEYPAIR SigningCertKey;
2265 RTCRSTORE hAddCerts = NIL_RTCRSTORE; /* leaked if returning directly (--help, --version) */
2266 bool fTimestampTypeOld = true;
2267 SIGNTOOLKEYPAIR TimestampCertKey;
2268 RTTIMESPEC SigningTime;
2269 RTTimeNow(&SigningTime);
2270
2271 RTGETOPTSTATE GetState;
2272 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
2273 AssertRCReturn(rc, RTEXITCODE_FAILURE);
2274
2275 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
2276 RTGETOPTUNION ValueUnion;
2277 int ch;
2278 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
2279 {
2280 RTEXITCODE rcExit2 = RTEXITCODE_SUCCESS;
2281 switch (ch)
2282 {
2283 case 't': rcExit2 = HandleOptSignatureType(&enmSigType, ValueUnion.psz); break;
2284 case 'a': fReplaceExisting = false; break;
2285 case OPT_HASH_PAGES: fHashPages = true; break;
2286 case OPT_NO_HASH_PAGES: fHashPages = false; break;
2287 case OPT_CERT_FILE: rcExit2 = HandleOptCertFile(&SigningCertKey, ValueUnion.psz); break;
2288 case OPT_KEY_FILE: rcExit2 = HandleOptKeyFile(&SigningCertKey, ValueUnion.psz); break;
2289 case OPT_ADD_CERT: rcExit2 = HandleOptAddCert(&hAddCerts, ValueUnion.psz); break;
2290 case OPT_TIMESTAMP_CERT_FILE: rcExit2 = HandleOptCertFile(&TimestampCertKey, ValueUnion.psz); break;
2291 case OPT_TIMESTAMP_KEY_FILE: rcExit2 = HandleOptKeyFile(&TimestampCertKey, ValueUnion.psz); break;
2292 case OPT_TIMESTAMP_TYPE: rcExit2 = HandleOptTimestampType(&fTimestampTypeOld, ValueUnion.psz); break;
2293 case OPT_TIMESTAMP_OVERRIDE: rcExit2 = HandleOptTimestampOverride(&SigningTime, ValueUnion.psz); break;
2294 case 'v': cVerbosity++; break;
2295 case 'V': return HandleVersion(cArgs, papszArgs);
2296 case 'h': return HelpSignExe(g_pStdOut, RTSIGNTOOLHELP_FULL);
2297
2298 case VINF_GETOPT_NOT_OPTION:
2299 /* check that we've got all the info we need: */
2300 if ( SigningCertKey.isComplete()
2301 && (TimestampCertKey.isNull() || TimestampCertKey.isComplete()))
2302 {
2303 /* Do the work: */
2304 SIGNTOOLPKCS7EXE Exe;
2305 /** @todo will fail if not already signed. */
2306 rcExit2 = SignToolPkcs7Exe_InitFromFile(&Exe, ValueUnion.psz, cVerbosity);
2307 if (rcExit2 == RTEXITCODE_SUCCESS)
2308 {
2309 rcExit2 = SignToolPkcs7_AddOrReplaceSignature(&Exe, cVerbosity, enmSigType, fReplaceExisting, fHashPages,
2310 &SigningCertKey, hAddCerts,
2311 fTimestampTypeOld, SigningTime, &TimestampCertKey);
2312 if (rcExit2 == RTEXITCODE_SUCCESS)
2313 rcExit2 = SignToolPkcs7_Encode(&Exe, cVerbosity);
2314 if (rcExit2 == RTEXITCODE_SUCCESS)
2315 rcExit2 = SignToolPkcs7Exe_WriteSignatureToFile(&Exe, cVerbosity);
2316 SignToolPkcs7Exe_Delete(&Exe);
2317 }
2318 if (rcExit2 != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
2319 rcExit = rcExit2;
2320 rcExit2 = RTEXITCODE_SUCCESS;
2321 }
2322 else
2323 {
2324 if (!TimestampCertKey.pCertificate)
2325 RTMsgError("No signing certificate was specified");
2326 if (TimestampCertKey.hPrivateKey == NIL_RTCRKEY)
2327 RTMsgError("No signing private key was specified");
2328
2329 if (!TimestampCertKey.pCertificate && !TimestampCertKey.isNull())
2330 RTMsgError("No timestamp certificate was specified");
2331 if (TimestampCertKey.hPrivateKey == NIL_RTCRKEY && !TimestampCertKey.isNull())
2332 RTMsgError("No timestamp private key was specified");
2333 rcExit2 = RTEXITCODE_SYNTAX;
2334 }
2335 break;
2336
2337 default:
2338 return RTGetOptPrintError(ch, &ValueUnion);
2339 }
2340 if (rcExit2 != RTEXITCODE_SUCCESS)
2341 {
2342 rcExit = rcExit2;
2343 break;
2344 }
2345 }
2346
2347 if (hAddCerts != NIL_RTCRSTORE)
2348 RTCrStoreRelease(hAddCerts);
2349 return rcExit;
2350}
2351
2352#endif /*!IPRT_IN_BUILD_TOOL */
2353
2354
2355/*********************************************************************************************************************************
2356* The 'verify-exe' command. *
2357*********************************************************************************************************************************/
2358#ifndef IPRT_IN_BUILD_TOOL
2359
2360static RTEXITCODE HelpVerifyExe(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
2361{
2362 RT_NOREF_PV(enmLevel);
2363 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
2364 "verify-exe [--verbose|--quiet] [--kernel] [--root <root-cert.der>] [--additional <supp-cert.der>] "
2365 "[--type <win|osx>] <exe1> [exe2 [..]]\n");
2366 return RTEXITCODE_SUCCESS;
2367}
2368
2369typedef struct VERIFYEXESTATE
2370{
2371 RTCRSTORE hRootStore;
2372 RTCRSTORE hKernelRootStore;
2373 RTCRSTORE hAdditionalStore;
2374 bool fKernel;
2375 int cVerbose;
2376 enum { kSignType_Windows, kSignType_OSX } enmSignType;
2377 RTLDRARCH enmLdrArch;
2378 uint32_t cBad;
2379 uint32_t cOkay;
2380 const char *pszFilename;
2381} VERIFYEXESTATE;
2382
2383# ifdef VBOX
2384/** Certificate store load set.
2385 * Declared outside HandleVerifyExe because of braindead gcc visibility crap. */
2386struct STSTORESET
2387{
2388 RTCRSTORE hStore;
2389 PCSUPTAENTRY paTAs;
2390 unsigned cTAs;
2391};
2392# endif
2393
2394/**
2395 * @callback_method_impl{FNRTCRPKCS7VERIFYCERTCALLBACK,
2396 * Standard code signing. Use this for Microsoft SPC.}
2397 */
2398static DECLCALLBACK(int) VerifyExecCertVerifyCallback(PCRTCRX509CERTIFICATE pCert, RTCRX509CERTPATHS hCertPaths, uint32_t fFlags,
2399 void *pvUser, PRTERRINFO pErrInfo)
2400{
2401 VERIFYEXESTATE *pState = (VERIFYEXESTATE *)pvUser;
2402 uint32_t cPaths = RTCrX509CertPathsGetPathCount(hCertPaths);
2403
2404 /*
2405 * Dump all the paths.
2406 */
2407 if (pState->cVerbose > 0)
2408 {
2409 for (uint32_t iPath = 0; iPath < cPaths; iPath++)
2410 {
2411 RTPrintf("---\n");
2412 RTCrX509CertPathsDumpOne(hCertPaths, iPath, pState->cVerbose, RTStrmDumpPrintfV, g_pStdOut);
2413 *pErrInfo->pszMsg = '\0';
2414 }
2415 RTPrintf("---\n");
2416 }
2417
2418 /*
2419 * Test signing certificates normally doesn't have all the necessary
2420 * features required below. So, treat them as special cases.
2421 */
2422 if ( hCertPaths == NIL_RTCRX509CERTPATHS
2423 && RTCrX509Name_Compare(&pCert->TbsCertificate.Issuer, &pCert->TbsCertificate.Subject) == 0)
2424 {
2425 RTMsgInfo("Test signed.\n");
2426 return VINF_SUCCESS;
2427 }
2428
2429 if (hCertPaths == NIL_RTCRX509CERTPATHS)
2430 RTMsgInfo("Signed by trusted certificate.\n");
2431
2432 /*
2433 * Standard code signing capabilites required.
2434 */
2435 int rc = RTCrPkcs7VerifyCertCallbackCodeSigning(pCert, hCertPaths, fFlags, NULL, pErrInfo);
2436 if ( RT_SUCCESS(rc)
2437 && (fFlags & RTCRPKCS7VCC_F_SIGNED_DATA))
2438 {
2439 /*
2440 * If windows kernel signing, a valid certificate path must be anchored
2441 * by the microsoft kernel signing root certificate. The only
2442 * alternative is test signing.
2443 */
2444 if ( pState->fKernel
2445 && hCertPaths != NIL_RTCRX509CERTPATHS
2446 && pState->enmSignType == VERIFYEXESTATE::kSignType_Windows)
2447 {
2448 uint32_t cFound = 0;
2449 uint32_t cValid = 0;
2450 for (uint32_t iPath = 0; iPath < cPaths; iPath++)
2451 {
2452 bool fTrusted;
2453 PCRTCRX509NAME pSubject;
2454 PCRTCRX509SUBJECTPUBLICKEYINFO pPublicKeyInfo;
2455 int rcVerify;
2456 rc = RTCrX509CertPathsQueryPathInfo(hCertPaths, iPath, &fTrusted, NULL /*pcNodes*/, &pSubject, &pPublicKeyInfo,
2457 NULL, NULL /*pCertCtx*/, &rcVerify);
2458 AssertRCBreak(rc);
2459
2460 if (RT_SUCCESS(rcVerify))
2461 {
2462 Assert(fTrusted);
2463 cValid++;
2464
2465 /* Search the kernel signing root store for a matching anchor. */
2466 RTCRSTORECERTSEARCH Search;
2467 rc = RTCrStoreCertFindBySubjectOrAltSubjectByRfc5280(pState->hKernelRootStore, pSubject, &Search);
2468 AssertRCBreak(rc);
2469 PCRTCRCERTCTX pCertCtx;
2470 while ((pCertCtx = RTCrStoreCertSearchNext(pState->hKernelRootStore, &Search)) != NULL)
2471 {
2472 PCRTCRX509SUBJECTPUBLICKEYINFO pPubKeyInfo;
2473 if (pCertCtx->pCert)
2474 pPubKeyInfo = &pCertCtx->pCert->TbsCertificate.SubjectPublicKeyInfo;
2475 else if (pCertCtx->pTaInfo)
2476 pPubKeyInfo = &pCertCtx->pTaInfo->PubKey;
2477 else
2478 pPubKeyInfo = NULL;
2479 if (RTCrX509SubjectPublicKeyInfo_Compare(pPubKeyInfo, pPublicKeyInfo) == 0)
2480 cFound++;
2481 RTCrCertCtxRelease(pCertCtx);
2482 }
2483
2484 int rc2 = RTCrStoreCertSearchDestroy(pState->hKernelRootStore, &Search); AssertRC(rc2);
2485 }
2486 }
2487 if (RT_SUCCESS(rc) && cFound == 0)
2488 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE, "Not valid kernel code signature.");
2489 if (RT_SUCCESS(rc) && cValid != 2)
2490 RTMsgWarning("%u valid paths, expected 2", cValid);
2491 }
2492 /*
2493 * For Mac OS X signing, check for special developer ID attributes.
2494 */
2495 else if (pState->enmSignType == VERIFYEXESTATE::kSignType_OSX)
2496 {
2497 uint32_t cDevIdApp = 0;
2498 uint32_t cDevIdKext = 0;
2499 uint32_t cDevIdMacDev = 0;
2500 for (uint32_t i = 0; i < pCert->TbsCertificate.T3.Extensions.cItems; i++)
2501 {
2502 PCRTCRX509EXTENSION pExt = pCert->TbsCertificate.T3.Extensions.papItems[i];
2503 if (RTAsn1ObjId_CompareWithString(&pExt->ExtnId, RTCR_APPLE_CS_DEVID_APPLICATION_OID) == 0)
2504 {
2505 cDevIdApp++;
2506 if (!pExt->Critical.fValue)
2507 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
2508 "Dev ID Application certificate extension is not flagged critical");
2509 }
2510 else if (RTAsn1ObjId_CompareWithString(&pExt->ExtnId, RTCR_APPLE_CS_DEVID_KEXT_OID) == 0)
2511 {
2512 cDevIdKext++;
2513 if (!pExt->Critical.fValue)
2514 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
2515 "Dev ID kext certificate extension is not flagged critical");
2516 }
2517 else if (RTAsn1ObjId_CompareWithString(&pExt->ExtnId, RTCR_APPLE_CS_DEVID_MAC_SW_DEV_OID) == 0)
2518 {
2519 cDevIdMacDev++;
2520 if (!pExt->Critical.fValue)
2521 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
2522 "Dev ID Mac SW dev certificate extension is not flagged critical");
2523 }
2524 }
2525 if (cDevIdApp == 0)
2526 {
2527 if (cDevIdMacDev == 0)
2528 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
2529 "Certificate is missing the 'Dev ID Application' extension");
2530 else
2531 RTMsgWarning("Mac SW dev certificate used to sign code.");
2532 }
2533 if (cDevIdKext == 0 && pState->fKernel)
2534 {
2535 if (cDevIdMacDev == 0)
2536 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
2537 "Certificate is missing the 'Dev ID kext' extension");
2538 else
2539 RTMsgWarning("Mac SW dev certificate used to sign kernel code.");
2540 }
2541 }
2542 }
2543
2544 return rc;
2545}
2546
2547/** @callback_method_impl{FNRTLDRVALIDATESIGNEDDATA} */
2548static DECLCALLBACK(int) VerifyExeCallback(RTLDRMOD hLdrMod, PCRTLDRSIGNATUREINFO pInfo, PRTERRINFO pErrInfo, void *pvUser)
2549{
2550 VERIFYEXESTATE *pState = (VERIFYEXESTATE *)pvUser;
2551 RT_NOREF_PV(hLdrMod);
2552
2553 switch (pInfo->enmType)
2554 {
2555 case RTLDRSIGNATURETYPE_PKCS7_SIGNED_DATA:
2556 {
2557 PCRTCRPKCS7CONTENTINFO pContentInfo = (PCRTCRPKCS7CONTENTINFO)pInfo->pvSignature;
2558
2559 /*
2560 * Dump the signed data if so requested and it's the first one, assuming that
2561 * additional signatures in contained wihtin the same ContentInfo structure.
2562 */
2563 if (pState->cVerbose && pInfo->iSignature == 0)
2564 RTAsn1Dump(&pContentInfo->SeqCore.Asn1Core, 0, 0, RTStrmDumpPrintfV, g_pStdOut);
2565
2566 /*
2567 * We'll try different alternative timestamps here.
2568 */
2569 struct { RTTIMESPEC TimeSpec; const char *pszDesc; } aTimes[2];
2570 unsigned cTimes = 0;
2571
2572 /* Linking timestamp: */
2573 uint64_t uLinkingTime = 0;
2574 int rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &uLinkingTime, sizeof(uLinkingTime));
2575 if (RT_SUCCESS(rc))
2576 {
2577 RTTimeSpecSetSeconds(&aTimes[0].TimeSpec, uLinkingTime);
2578 aTimes[0].pszDesc = "at link time";
2579 cTimes++;
2580 }
2581 else if (rc != VERR_NOT_FOUND)
2582 RTMsgError("RTLdrQueryProp/RTLDRPROP_TIMESTAMP_SECONDS failed on '%s': %Rrc\n", pState->pszFilename, rc);
2583
2584 /* Now: */
2585 RTTimeNow(&aTimes[cTimes].TimeSpec);
2586 aTimes[cTimes].pszDesc = "now";
2587 cTimes++;
2588
2589 /*
2590 * Do the actual verification.
2591 */
2592 for (unsigned iTime = 0; iTime < cTimes; iTime++)
2593 {
2594 if (pInfo->pvExternalData)
2595 rc = RTCrPkcs7VerifySignedDataWithExternalData(pContentInfo,
2596 RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE_SIGNING_TIME_ONLY
2597 | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_SIGNING_TIME_IF_PRESENT
2598 | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_MS_TIMESTAMP_IF_PRESENT
2599 | RTCRPKCS7VERIFY_SD_F_CHECK_TRUST_ANCHORS,
2600 pState->hAdditionalStore, pState->hRootStore,
2601 &aTimes[iTime].TimeSpec,
2602 VerifyExecCertVerifyCallback, pState,
2603 pInfo->pvExternalData, pInfo->cbExternalData, pErrInfo);
2604 else
2605 rc = RTCrPkcs7VerifySignedData(pContentInfo,
2606 RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE_SIGNING_TIME_ONLY
2607 | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_SIGNING_TIME_IF_PRESENT
2608 | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_MS_TIMESTAMP_IF_PRESENT
2609 | RTCRPKCS7VERIFY_SD_F_CHECK_TRUST_ANCHORS,
2610 pState->hAdditionalStore, pState->hRootStore,
2611 &aTimes[iTime].TimeSpec,
2612 VerifyExecCertVerifyCallback, pState, pErrInfo);
2613 if (RT_SUCCESS(rc))
2614 {
2615 Assert(rc == VINF_SUCCESS || rc == VINF_CR_DIGEST_DEPRECATED);
2616 const char *pszNote = rc == VINF_CR_DIGEST_DEPRECATED ? " (deprecated digest)" : "";
2617 if (pInfo->cSignatures == 1)
2618 RTMsgInfo("'%s' is valid %s%s.\n", pState->pszFilename, aTimes[iTime].pszDesc, pszNote);
2619 else
2620 RTMsgInfo("'%s' signature #%u is valid %s%s.\n",
2621 pState->pszFilename, pInfo->iSignature + 1, aTimes[iTime].pszDesc, pszNote);
2622 pState->cOkay++;
2623 return VINF_SUCCESS;
2624 }
2625 if (rc != VERR_CR_X509_CPV_NOT_VALID_AT_TIME)
2626 {
2627 if (pInfo->cSignatures == 1)
2628 RTMsgError("%s: Failed to verify signature: %Rrc%#RTeim\n", pState->pszFilename, rc, pErrInfo);
2629 else
2630 RTMsgError("%s: Failed to verify signature #%u: %Rrc%#RTeim\n",
2631 pState->pszFilename, pInfo->iSignature + 1, rc, pErrInfo);
2632 pState->cBad++;
2633 return VINF_SUCCESS;
2634 }
2635 }
2636
2637 if (pInfo->cSignatures == 1)
2638 RTMsgError("%s: Signature is not valid at present or link time.\n", pState->pszFilename);
2639 else
2640 RTMsgError("%s: Signature #%u is not valid at present or link time.\n",
2641 pState->pszFilename, pInfo->iSignature + 1);
2642 pState->cBad++;
2643 return VINF_SUCCESS;
2644 }
2645
2646 default:
2647 return RTErrInfoSetF(pErrInfo, VERR_NOT_SUPPORTED, "Unsupported signature type: %d", pInfo->enmType);
2648 }
2649}
2650
2651/**
2652 * Worker for HandleVerifyExe.
2653 */
2654static RTEXITCODE HandleVerifyExeWorker(VERIFYEXESTATE *pState, const char *pszFilename, PRTERRINFOSTATIC pStaticErrInfo)
2655{
2656 /*
2657 * Open the executable image and verify it.
2658 */
2659 RTLDRMOD hLdrMod;
2660 int rc = RTLdrOpen(pszFilename, RTLDR_O_FOR_VALIDATION, pState->enmLdrArch, &hLdrMod);
2661 if (RT_FAILURE(rc))
2662 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error opening executable image '%s': %Rrc", pszFilename, rc);
2663
2664 /* Reset the state. */
2665 pState->cBad = 0;
2666 pState->cOkay = 0;
2667 pState->pszFilename = pszFilename;
2668
2669 rc = RTLdrVerifySignature(hLdrMod, VerifyExeCallback, pState, RTErrInfoInitStatic(pStaticErrInfo));
2670 if (RT_FAILURE(rc))
2671 RTMsgError("RTLdrVerifySignature failed on '%s': %Rrc - %s\n", pszFilename, rc, pStaticErrInfo->szMsg);
2672
2673 int rc2 = RTLdrClose(hLdrMod);
2674 if (RT_FAILURE(rc2))
2675 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTLdrClose failed: %Rrc\n", rc2);
2676 if (RT_FAILURE(rc))
2677 return rc != VERR_LDRVI_NOT_SIGNED ? RTEXITCODE_FAILURE : RTEXITCODE_SKIPPED;
2678
2679 return pState->cOkay > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2680}
2681
2682
2683static RTEXITCODE HandleVerifyExe(int cArgs, char **papszArgs)
2684{
2685 RTERRINFOSTATIC StaticErrInfo;
2686
2687 /* Note! This code does not try to clean up the crypto stores on failure.
2688 This is intentional as the code is only expected to be used in a
2689 one-command-per-process environment where we do exit() upon
2690 returning from this function. */
2691
2692 /*
2693 * Parse arguments.
2694 */
2695 static const RTGETOPTDEF s_aOptions[] =
2696 {
2697 { "--kernel", 'k', RTGETOPT_REQ_NOTHING },
2698 { "--root", 'r', RTGETOPT_REQ_STRING },
2699 { "--additional", 'a', RTGETOPT_REQ_STRING },
2700 { "--add", 'a', RTGETOPT_REQ_STRING },
2701 { "--type", 't', RTGETOPT_REQ_STRING },
2702 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
2703 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
2704 };
2705
2706 VERIFYEXESTATE State =
2707 {
2708 NIL_RTCRSTORE, NIL_RTCRSTORE, NIL_RTCRSTORE, false, 0,
2709 VERIFYEXESTATE::kSignType_Windows, RTLDRARCH_WHATEVER,
2710 0, 0, NULL
2711 };
2712 int rc = RTCrStoreCreateInMem(&State.hRootStore, 0);
2713 if (RT_SUCCESS(rc))
2714 rc = RTCrStoreCreateInMem(&State.hKernelRootStore, 0);
2715 if (RT_SUCCESS(rc))
2716 rc = RTCrStoreCreateInMem(&State.hAdditionalStore, 0);
2717 if (RT_FAILURE(rc))
2718 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error creating in-memory certificate store: %Rrc", rc);
2719
2720 RTGETOPTSTATE GetState;
2721 rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
2722 AssertRCReturn(rc, RTEXITCODE_FAILURE);
2723 RTGETOPTUNION ValueUnion;
2724 int ch;
2725 while ((ch = RTGetOpt(&GetState, &ValueUnion)) && ch != VINF_GETOPT_NOT_OPTION)
2726 {
2727 switch (ch)
2728 {
2729 case 'r': case 'a':
2730 rc = RTCrStoreCertAddFromFile(ch == 'r' ? State.hRootStore : State.hAdditionalStore,
2731 RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR,
2732 ValueUnion.psz, RTErrInfoInitStatic(&StaticErrInfo));
2733 if (RT_FAILURE(rc))
2734 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error loading certificate '%s': %Rrc - %s",
2735 ValueUnion.psz, rc, StaticErrInfo.szMsg);
2736 if (RTErrInfoIsSet(&StaticErrInfo.Core))
2737 RTMsgWarning("Warnings loading certificate '%s': %s", ValueUnion.psz, StaticErrInfo.szMsg);
2738 break;
2739
2740 case 't':
2741 if (!strcmp(ValueUnion.psz, "win") || !strcmp(ValueUnion.psz, "windows"))
2742 State.enmSignType = VERIFYEXESTATE::kSignType_Windows;
2743 else if (!strcmp(ValueUnion.psz, "osx") || !strcmp(ValueUnion.psz, "apple"))
2744 State.enmSignType = VERIFYEXESTATE::kSignType_OSX;
2745 else
2746 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown signing type: '%s'", ValueUnion.psz);
2747 break;
2748
2749 case 'k': State.fKernel = true; break;
2750 case 'v': State.cVerbose++; break;
2751 case 'q': State.cVerbose = 0; break;
2752 case 'V': return HandleVersion(cArgs, papszArgs);
2753 case 'h': return HelpVerifyExe(g_pStdOut, RTSIGNTOOLHELP_FULL);
2754 default: return RTGetOptPrintError(ch, &ValueUnion);
2755 }
2756 }
2757 if (ch != VINF_GETOPT_NOT_OPTION)
2758 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
2759
2760 /*
2761 * Populate the certificate stores according to the signing type.
2762 */
2763# ifdef VBOX
2764 unsigned cSets = 0;
2765 struct STSTORESET aSets[6];
2766 switch (State.enmSignType)
2767 {
2768 case VERIFYEXESTATE::kSignType_Windows:
2769 aSets[cSets].hStore = State.hRootStore;
2770 aSets[cSets].paTAs = g_aSUPTimestampTAs;
2771 aSets[cSets].cTAs = g_cSUPTimestampTAs;
2772 cSets++;
2773 aSets[cSets].hStore = State.hRootStore;
2774 aSets[cSets].paTAs = g_aSUPSpcRootTAs;
2775 aSets[cSets].cTAs = g_cSUPSpcRootTAs;
2776 cSets++;
2777 aSets[cSets].hStore = State.hRootStore;
2778 aSets[cSets].paTAs = g_aSUPNtKernelRootTAs;
2779 aSets[cSets].cTAs = g_cSUPNtKernelRootTAs;
2780 cSets++;
2781 aSets[cSets].hStore = State.hKernelRootStore;
2782 aSets[cSets].paTAs = g_aSUPNtKernelRootTAs;
2783 aSets[cSets].cTAs = g_cSUPNtKernelRootTAs;
2784 cSets++;
2785 break;
2786
2787 case VERIFYEXESTATE::kSignType_OSX:
2788 aSets[cSets].hStore = State.hRootStore;
2789 aSets[cSets].paTAs = g_aSUPAppleRootTAs;
2790 aSets[cSets].cTAs = g_cSUPAppleRootTAs;
2791 cSets++;
2792 break;
2793 }
2794 for (unsigned i = 0; i < cSets; i++)
2795 for (unsigned j = 0; j < aSets[i].cTAs; j++)
2796 {
2797 rc = RTCrStoreCertAddEncoded(aSets[i].hStore, RTCRCERTCTX_F_ENC_TAF_DER, aSets[i].paTAs[j].pch,
2798 aSets[i].paTAs[j].cb, RTErrInfoInitStatic(&StaticErrInfo));
2799 if (RT_FAILURE(rc))
2800 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTCrStoreCertAddEncoded failed (%u/%u): %s",
2801 i, j, StaticErrInfo.szMsg);
2802 }
2803# endif /* VBOX */
2804
2805 /*
2806 * Do it.
2807 */
2808 RTEXITCODE rcExit;
2809 for (;;)
2810 {
2811 rcExit = HandleVerifyExeWorker(&State, ValueUnion.psz, &StaticErrInfo);
2812 if (rcExit != RTEXITCODE_SUCCESS)
2813 break;
2814
2815 /*
2816 * Next file
2817 */
2818 ch = RTGetOpt(&GetState, &ValueUnion);
2819 if (ch == 0)
2820 break;
2821 if (ch != VINF_GETOPT_NOT_OPTION)
2822 {
2823 rcExit = RTGetOptPrintError(ch, &ValueUnion);
2824 break;
2825 }
2826 }
2827
2828 /*
2829 * Clean up.
2830 */
2831 uint32_t cRefs;
2832 cRefs = RTCrStoreRelease(State.hRootStore); Assert(cRefs == 0);
2833 cRefs = RTCrStoreRelease(State.hKernelRootStore); Assert(cRefs == 0);
2834 cRefs = RTCrStoreRelease(State.hAdditionalStore); Assert(cRefs == 0);
2835
2836 return rcExit;
2837}
2838
2839#endif /* !IPRT_IN_BUILD_TOOL */
2840
2841/*
2842 * common code for show-exe and show-cat:
2843 */
2844
2845/**
2846 * Display an object ID.
2847 *
2848 * @returns IPRT status code.
2849 * @param pThis The show exe instance data.
2850 * @param pObjId The object ID to display.
2851 * @param pszLabel The field label (prefixed by szPrefix).
2852 * @param pszPost What to print after the ID (typically newline).
2853 */
2854static void HandleShowExeWorkerDisplayObjId(PSHOWEXEPKCS7 pThis, PCRTASN1OBJID pObjId, const char *pszLabel, const char *pszPost)
2855{
2856 int rc = RTAsn1QueryObjIdName(pObjId, pThis->szTmp, sizeof(pThis->szTmp));
2857 if (RT_SUCCESS(rc))
2858 {
2859 if (pThis->cVerbosity > 1)
2860 RTPrintf("%s%s%s (%s)%s", pThis->szPrefix, pszLabel, pThis->szTmp, pObjId->szObjId, pszPost);
2861 else
2862 RTPrintf("%s%s%s%s", pThis->szPrefix, pszLabel, pThis->szTmp, pszPost);
2863 }
2864 else
2865 RTPrintf("%s%s%s%s", pThis->szPrefix, pszLabel, pObjId->szObjId, pszPost);
2866}
2867
2868
2869/**
2870 * Display an object ID, without prefix and label
2871 *
2872 * @returns IPRT status code.
2873 * @param pThis The show exe instance data.
2874 * @param pObjId The object ID to display.
2875 * @param pszPost What to print after the ID (typically newline).
2876 */
2877static void HandleShowExeWorkerDisplayObjIdSimple(PSHOWEXEPKCS7 pThis, PCRTASN1OBJID pObjId, const char *pszPost)
2878{
2879 int rc = RTAsn1QueryObjIdName(pObjId, pThis->szTmp, sizeof(pThis->szTmp));
2880 if (RT_SUCCESS(rc))
2881 {
2882 if (pThis->cVerbosity > 1)
2883 RTPrintf("%s (%s)%s", pThis->szTmp, pObjId->szObjId, pszPost);
2884 else
2885 RTPrintf("%s%s", pThis->szTmp, pszPost);
2886 }
2887 else
2888 RTPrintf("%s%s", pObjId->szObjId, pszPost);
2889}
2890
2891
2892/**
2893 * Display a signer info attribute.
2894 *
2895 * @returns IPRT status code.
2896 * @param pThis The show exe instance data.
2897 * @param offPrefix The current prefix offset.
2898 * @param pAttr The attribute to display.
2899 */
2900static int HandleShowExeWorkerPkcs7DisplayAttrib(PSHOWEXEPKCS7 pThis, size_t offPrefix, PCRTCRPKCS7ATTRIBUTE pAttr)
2901{
2902 HandleShowExeWorkerDisplayObjId(pThis, &pAttr->Type, "", ":\n");
2903 if (pThis->cVerbosity > 4 && pAttr->SeqCore.Asn1Core.uData.pu8)
2904 RTPrintf("%s uData.pu8=%p cb=%#x\n", pThis->szPrefix, pAttr->SeqCore.Asn1Core.uData.pu8, pAttr->SeqCore.Asn1Core.cb);
2905
2906 int rc = VINF_SUCCESS;
2907 switch (pAttr->enmType)
2908 {
2909 case RTCRPKCS7ATTRIBUTETYPE_UNKNOWN:
2910 if (pAttr->uValues.pCores->cItems <= 1)
2911 RTPrintf("%s %u bytes\n", pThis->szPrefix,pAttr->uValues.pCores->SetCore.Asn1Core.cb);
2912 else
2913 RTPrintf("%s %u bytes divided by %u items\n", pThis->szPrefix, pAttr->uValues.pCores->SetCore.Asn1Core.cb, pAttr->uValues.pCores->cItems);
2914 break;
2915
2916 /* Object IDs, use pObjIds. */
2917 case RTCRPKCS7ATTRIBUTETYPE_OBJ_IDS:
2918 if (pAttr->uValues.pObjIds->cItems != 1)
2919 RTPrintf("%s%u object IDs:", pThis->szPrefix, pAttr->uValues.pObjIds->cItems);
2920 for (unsigned i = 0; i < pAttr->uValues.pObjIds->cItems; i++)
2921 {
2922 if (pAttr->uValues.pObjIds->cItems == 1)
2923 RTPrintf("%s ", pThis->szPrefix);
2924 else
2925 RTPrintf("%s ObjId[%u]: ", pThis->szPrefix, i);
2926 HandleShowExeWorkerDisplayObjIdSimple(pThis, pAttr->uValues.pObjIds->papItems[i], "\n");
2927 }
2928 break;
2929
2930 /* Sequence of object IDs, use pObjIdSeqs. */
2931 case RTCRPKCS7ATTRIBUTETYPE_MS_STATEMENT_TYPE:
2932 if (pAttr->uValues.pObjIdSeqs->cItems != 1)
2933 RTPrintf("%s%u object IDs:", pThis->szPrefix, pAttr->uValues.pObjIdSeqs->cItems);
2934 for (unsigned i = 0; i < pAttr->uValues.pObjIdSeqs->cItems; i++)
2935 {
2936 uint32_t const cObjIds = pAttr->uValues.pObjIdSeqs->papItems[i]->cItems;
2937 for (unsigned j = 0; j < cObjIds; j++)
2938 {
2939 if (pAttr->uValues.pObjIdSeqs->cItems == 1)
2940 RTPrintf("%s ", pThis->szPrefix);
2941 else
2942 RTPrintf("%s ObjIdSeq[%u]: ", pThis->szPrefix, i);
2943 if (cObjIds != 1)
2944 RTPrintf(" ObjId[%u]: ", j);
2945 HandleShowExeWorkerDisplayObjIdSimple(pThis, pAttr->uValues.pObjIdSeqs->papItems[i]->papItems[i], "\n");
2946 }
2947 }
2948 break;
2949
2950 /* Octet strings, use pOctetStrings. */
2951 case RTCRPKCS7ATTRIBUTETYPE_OCTET_STRINGS:
2952 if (pAttr->uValues.pOctetStrings->cItems != 1)
2953 RTPrintf("%s%u octet strings:", pThis->szPrefix, pAttr->uValues.pOctetStrings->cItems);
2954 for (unsigned i = 0; i < pAttr->uValues.pOctetStrings->cItems; i++)
2955 {
2956 PCRTASN1OCTETSTRING pOctetString = pAttr->uValues.pOctetStrings->papItems[i];
2957 uint32_t cbContent = pOctetString->Asn1Core.cb;
2958 if (cbContent > 0 && (cbContent <= 128 || pThis->cVerbosity >= 2))
2959 {
2960 uint8_t const *pbContent = pOctetString->Asn1Core.uData.pu8;
2961 uint32_t off = 0;
2962 while (off < cbContent)
2963 {
2964 uint32_t cbNow = RT_MIN(cbContent - off, 16);
2965 if (pAttr->uValues.pOctetStrings->cItems == 1)
2966 RTPrintf("%s %#06x: %.*Rhxs\n", pThis->szPrefix, off, cbNow, &pbContent[off]);
2967 else
2968 RTPrintf("%s OctetString[%u]: %#06x: %.*Rhxs\n", pThis->szPrefix, i, off, cbNow, &pbContent[off]);
2969 off += cbNow;
2970 }
2971 }
2972 else
2973 RTPrintf("%s: OctetString[%u]: %u bytes\n", pThis->szPrefix, i, pOctetString->Asn1Core.cb);
2974 }
2975 break;
2976
2977 /* Counter signatures (PKCS \#9), use pCounterSignatures. */
2978 case RTCRPKCS7ATTRIBUTETYPE_COUNTER_SIGNATURES:
2979 RTPrintf("%s%u counter signatures, %u bytes in total\n", pThis->szPrefix,
2980 pAttr->uValues.pCounterSignatures->cItems, pAttr->uValues.pCounterSignatures->SetCore.Asn1Core.cb);
2981 for (uint32_t i = 0; i < pAttr->uValues.pCounterSignatures->cItems; i++)
2982 {
2983 size_t offPrefix2 = offPrefix;
2984 if (pAttr->uValues.pContentInfos->cItems > 1)
2985 offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, "CounterSig[%u]: ", i);
2986 else
2987 offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, " ");
2988
2989 int rc2 = HandleShowExeWorkerPkcs7DisplaySignerInfo(pThis, offPrefix2,
2990 pAttr->uValues.pCounterSignatures->papItems[i]);
2991 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
2992 rc = rc2;
2993 }
2994 break;
2995
2996 /* Signing time (PKCS \#9), use pSigningTime. */
2997 case RTCRPKCS7ATTRIBUTETYPE_SIGNING_TIME:
2998 for (uint32_t i = 0; i < pAttr->uValues.pSigningTime->cItems; i++)
2999 {
3000 PCRTASN1TIME pTime = pAttr->uValues.pSigningTime->papItems[i];
3001 char szTS[RTTIME_STR_LEN];
3002 RTTimeToString(&pTime->Time, szTS, sizeof(szTS));
3003 if (pAttr->uValues.pSigningTime->cItems == 1)
3004 RTPrintf("%s %s (%.*s)\n", pThis->szPrefix, szTS, pTime->Asn1Core.cb, pTime->Asn1Core.uData.pch);
3005 else
3006 RTPrintf("%s #%u: %s (%.*s)\n", pThis->szPrefix, i, szTS, pTime->Asn1Core.cb, pTime->Asn1Core.uData.pch);
3007 }
3008 break;
3009
3010 /* Microsoft timestamp info (RFC-3161) signed data, use pContentInfo. */
3011 case RTCRPKCS7ATTRIBUTETYPE_MS_TIMESTAMP:
3012 case RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE:
3013 if (pAttr->uValues.pContentInfos->cItems > 1)
3014 RTPrintf("%s%u nested signatures, %u bytes in total\n", pThis->szPrefix,
3015 pAttr->uValues.pContentInfos->cItems, pAttr->uValues.pContentInfos->SetCore.Asn1Core.cb);
3016 for (unsigned i = 0; i < pAttr->uValues.pContentInfos->cItems; i++)
3017 {
3018 size_t offPrefix2 = offPrefix;
3019 if (pAttr->uValues.pContentInfos->cItems > 1)
3020 offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, "NestedSig[%u]: ", i);
3021 else
3022 offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, " ");
3023 // offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, "NestedSig: ", i);
3024 PCRTCRPKCS7CONTENTINFO pContentInfo = pAttr->uValues.pContentInfos->papItems[i];
3025 int rc2;
3026 if (RTCrPkcs7ContentInfo_IsSignedData(pContentInfo))
3027 rc2 = HandleShowExeWorkerPkcs7Display(pThis, pContentInfo->u.pSignedData, offPrefix2, pContentInfo);
3028 else
3029 rc2 = RTMsgErrorRc(VERR_ASN1_UNEXPECTED_OBJ_ID, "%sPKCS#7 content in nested signature is not 'signedData': %s",
3030 pThis->szPrefix, pContentInfo->ContentType.szObjId);
3031 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
3032 rc = rc2;
3033 }
3034 break;
3035
3036 case RTCRPKCS7ATTRIBUTETYPE_APPLE_MULTI_CD_PLIST:
3037 if (pAttr->uValues.pContentInfos->cItems != 1)
3038 RTPrintf("%s%u plists, expected only 1.\n", pThis->szPrefix, pAttr->uValues.pOctetStrings->cItems);
3039 for (unsigned i = 0; i < pAttr->uValues.pOctetStrings->cItems; i++)
3040 {
3041 PCRTASN1OCTETSTRING pOctetString = pAttr->uValues.pOctetStrings->papItems[i];
3042 size_t cbContent = pOctetString->Asn1Core.cb;
3043 char const *pchContent = pOctetString->Asn1Core.uData.pch;
3044 rc = RTStrValidateEncodingEx(pchContent, cbContent, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
3045 if (RT_SUCCESS(rc))
3046 {
3047 while (cbContent > 0)
3048 {
3049 const char *pchNewLine = (const char *)memchr(pchContent, '\n', cbContent);
3050 size_t cchToWrite = pchNewLine ? pchNewLine - pchContent : cbContent;
3051 if (pAttr->uValues.pOctetStrings->cItems == 1)
3052 RTPrintf("%s %.*s\n", pThis->szPrefix, cchToWrite, pchContent);
3053 else
3054 RTPrintf("%s plist[%u]: %.*s\n", pThis->szPrefix, i, cchToWrite, pchContent);
3055 if (!pchNewLine)
3056 break;
3057 pchContent = pchNewLine + 1;
3058 cbContent -= cchToWrite + 1;
3059 }
3060 }
3061 else
3062 {
3063 if (pAttr->uValues.pContentInfos->cItems != 1)
3064 RTPrintf("%s: plist[%u]: Invalid UTF-8: %Rrc\n", pThis->szPrefix, i, rc);
3065 else
3066 RTPrintf("%s: Invalid UTF-8: %Rrc\n", pThis->szPrefix, rc);
3067 for (uint32_t off = 0; off < cbContent; off += 16)
3068 {
3069 size_t cbNow = RT_MIN(cbContent - off, 16);
3070 if (pAttr->uValues.pOctetStrings->cItems == 1)
3071 RTPrintf("%s %#06x: %.*Rhxs\n", pThis->szPrefix, off, cbNow, &pchContent[off]);
3072 else
3073 RTPrintf("%s plist[%u]: %#06x: %.*Rhxs\n", pThis->szPrefix, i, off, cbNow, &pchContent[off]);
3074 }
3075 }
3076 }
3077 break;
3078
3079 case RTCRPKCS7ATTRIBUTETYPE_INVALID:
3080 RTPrintf("%sINVALID!\n", pThis->szPrefix);
3081 break;
3082 case RTCRPKCS7ATTRIBUTETYPE_NOT_PRESENT:
3083 RTPrintf("%sNOT PRESENT!\n", pThis->szPrefix);
3084 break;
3085 default:
3086 RTPrintf("%senmType=%d!\n", pThis->szPrefix, pAttr->enmType);
3087 break;
3088 }
3089 return rc;
3090}
3091
3092
3093/**
3094 * Displays a SignerInfo structure.
3095 *
3096 * @returns IPRT status code.
3097 * @param pThis The show exe instance data.
3098 * @param offPrefix The current prefix offset.
3099 * @param pSignerInfo The structure to display.
3100 */
3101static int HandleShowExeWorkerPkcs7DisplaySignerInfo(PSHOWEXEPKCS7 pThis, size_t offPrefix, PCRTCRPKCS7SIGNERINFO pSignerInfo)
3102{
3103 int rc = RTAsn1Integer_ToString(&pSignerInfo->IssuerAndSerialNumber.SerialNumber,
3104 pThis->szTmp, sizeof(pThis->szTmp), 0 /*fFlags*/, NULL);
3105 if (RT_FAILURE(rc))
3106 RTStrPrintf(pThis->szTmp, sizeof(pThis->szTmp), "%Rrc", rc);
3107 RTPrintf("%s Serial No: %s\n", pThis->szPrefix, pThis->szTmp);
3108
3109 rc = RTCrX509Name_FormatAsString(&pSignerInfo->IssuerAndSerialNumber.Name, pThis->szTmp, sizeof(pThis->szTmp), NULL);
3110 if (RT_FAILURE(rc))
3111 RTStrPrintf(pThis->szTmp, sizeof(pThis->szTmp), "%Rrc", rc);
3112 RTPrintf("%s Issuer: %s\n", pThis->szPrefix, pThis->szTmp);
3113
3114 const char *pszType = RTCrDigestTypeToName(RTCrX509AlgorithmIdentifier_QueryDigestType(&pSignerInfo->DigestAlgorithm));
3115 if (!pszType)
3116 pszType = pSignerInfo->DigestAlgorithm.Algorithm.szObjId;
3117 RTPrintf("%s Digest Algorithm: %s", pThis->szPrefix, pszType);
3118 if (pThis->cVerbosity > 1)
3119 RTPrintf(" (%s)\n", pSignerInfo->DigestAlgorithm.Algorithm.szObjId);
3120 else
3121 RTPrintf("\n");
3122
3123 HandleShowExeWorkerDisplayObjId(pThis, &pSignerInfo->DigestEncryptionAlgorithm.Algorithm,
3124 "Digest Encryption Algorithm: ", "\n");
3125
3126 if (pSignerInfo->AuthenticatedAttributes.cItems == 0)
3127 RTPrintf("%s Authenticated Attributes: none\n", pThis->szPrefix);
3128 else
3129 {
3130 RTPrintf("%s Authenticated Attributes: %u item%s\n", pThis->szPrefix,
3131 pSignerInfo->AuthenticatedAttributes.cItems, pSignerInfo->AuthenticatedAttributes.cItems > 1 ? "s" : "");
3132 for (unsigned j = 0; j < pSignerInfo->AuthenticatedAttributes.cItems; j++)
3133 {
3134 PRTCRPKCS7ATTRIBUTE pAttr = pSignerInfo->AuthenticatedAttributes.papItems[j];
3135 size_t offPrefix3 = offPrefix+ RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix,
3136 " AuthAttrib[%u]: ", j);
3137 HandleShowExeWorkerPkcs7DisplayAttrib(pThis, offPrefix3, pAttr);
3138 }
3139 pThis->szPrefix[offPrefix] = '\0';
3140 }
3141
3142 if (pSignerInfo->UnauthenticatedAttributes.cItems == 0)
3143 RTPrintf("%s Unauthenticated Attributes: none\n", pThis->szPrefix);
3144 else
3145 {
3146 RTPrintf("%s Unauthenticated Attributes: %u item%s\n", pThis->szPrefix,
3147 pSignerInfo->UnauthenticatedAttributes.cItems, pSignerInfo->UnauthenticatedAttributes.cItems > 1 ? "s" : "");
3148 for (unsigned j = 0; j < pSignerInfo->UnauthenticatedAttributes.cItems; j++)
3149 {
3150 PRTCRPKCS7ATTRIBUTE pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[j];
3151 size_t offPrefix3 = offPrefix + RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix,
3152 " UnauthAttrib[%u]: ", j);
3153 HandleShowExeWorkerPkcs7DisplayAttrib(pThis, offPrefix3, pAttr);
3154 }
3155 pThis->szPrefix[offPrefix] = '\0';
3156 }
3157
3158 /** @todo show the encrypted stuff (EncryptedDigest)? */
3159 return rc;
3160}
3161
3162
3163/**
3164 * Displays a Microsoft SPC indirect data structure.
3165 *
3166 * @returns IPRT status code.
3167 * @param pThis The show exe instance data.
3168 * @param offPrefix The current prefix offset.
3169 * @param pIndData The indirect data to display.
3170 */
3171static int HandleShowExeWorkerPkcs7DisplaySpcIdirectDataContent(PSHOWEXEPKCS7 pThis, size_t offPrefix,
3172 PCRTCRSPCINDIRECTDATACONTENT pIndData)
3173{
3174 /*
3175 * The image hash.
3176 */
3177 RTDIGESTTYPE const enmDigestType = RTCrX509AlgorithmIdentifier_QueryDigestType(&pIndData->DigestInfo.DigestAlgorithm);
3178 const char *pszDigestType = RTCrDigestTypeToName(enmDigestType);
3179 RTPrintf("%s Digest Type: %s", pThis->szPrefix, pszDigestType);
3180 if (pThis->cVerbosity > 1)
3181 RTPrintf(" (%s)\n", pIndData->DigestInfo.DigestAlgorithm.Algorithm.szObjId);
3182 else
3183 RTPrintf("\n");
3184 RTPrintf("%s Digest: %.*Rhxs\n",
3185 pThis->szPrefix, pIndData->DigestInfo.Digest.Asn1Core.cb, pIndData->DigestInfo.Digest.Asn1Core.uData.pu8);
3186
3187 /*
3188 * The data/file/url.
3189 */
3190 switch (pIndData->Data.enmType)
3191 {
3192 case RTCRSPCAAOVTYPE_PE_IMAGE_DATA:
3193 {
3194 RTPrintf("%s Data Type: PE Image Data\n", pThis->szPrefix);
3195 PRTCRSPCPEIMAGEDATA pPeImage = pIndData->Data.uValue.pPeImage;
3196 /** @todo display "Flags". */
3197
3198 switch (pPeImage->T0.File.enmChoice)
3199 {
3200 case RTCRSPCLINKCHOICE_MONIKER:
3201 {
3202 PRTCRSPCSERIALIZEDOBJECT pMoniker = pPeImage->T0.File.u.pMoniker;
3203 if (RTCrSpcSerializedObject_IsPresent(pMoniker))
3204 {
3205 if (RTUuidCompareStr(pMoniker->Uuid.Asn1Core.uData.pUuid, RTCRSPCSERIALIZEDOBJECT_UUID_STR) == 0)
3206 {
3207 RTPrintf("%s Moniker: SpcSerializedObject (%RTuuid)\n",
3208 pThis->szPrefix, pMoniker->Uuid.Asn1Core.uData.pUuid);
3209
3210 PCRTCRSPCSERIALIZEDOBJECTATTRIBUTES pData = pMoniker->u.pData;
3211 if (pData)
3212 for (uint32_t i = 0; i < pData->cItems; i++)
3213 {
3214 RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix,
3215 "MonikerAttrib[%u]: ", i);
3216
3217 switch (pData->papItems[i]->enmType)
3218 {
3219 case RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V2:
3220 case RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V1:
3221 {
3222 PCRTCRSPCSERIALIZEDPAGEHASHES pPgHashes = pData->papItems[i]->u.pPageHashes;
3223 uint32_t const cbHash = pData->papItems[i]->enmType
3224 == RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V1
3225 ? 160/8 /*SHA-1*/ : 256/8 /*SHA-256*/;
3226 uint32_t const cPages = pPgHashes->RawData.Asn1Core.cb / (cbHash + sizeof(uint32_t));
3227
3228 RTPrintf("%sPage Hashes version %u - %u pages (%u bytes total)\n", pThis->szPrefix,
3229 pData->papItems[i]->enmType
3230 == RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V1 ? 1 : 2,
3231 cPages, pPgHashes->RawData.Asn1Core.cb);
3232 if (pThis->cVerbosity > 0)
3233 {
3234 PCRTCRSPCPEIMAGEPAGEHASHES pPg = pPgHashes->pData;
3235 for (unsigned iPg = 0; iPg < cPages; iPg++)
3236 {
3237 uint32_t offHash = 0;
3238 do
3239 {
3240 if (offHash == 0)
3241 RTPrintf("%.*s Page#%04u/%#08x: ",
3242 offPrefix, pThis->szPrefix, iPg, pPg->Generic.offFile);
3243 else
3244 RTPrintf("%.*s ", offPrefix, pThis->szPrefix);
3245 uint32_t cbLeft = cbHash - offHash;
3246 if (cbLeft > 24)
3247 cbLeft = 16;
3248 RTPrintf("%.*Rhxs\n", cbLeft, &pPg->Generic.abHash[offHash]);
3249 offHash += cbLeft;
3250 } while (offHash < cbHash);
3251 pPg = (PCRTCRSPCPEIMAGEPAGEHASHES)&pPg->Generic.abHash[cbHash];
3252 }
3253
3254 if (pThis->cVerbosity > 3)
3255 RTPrintf("%.*Rhxd\n",
3256 pPgHashes->RawData.Asn1Core.cb,
3257 pPgHashes->RawData.Asn1Core.uData.pu8);
3258 }
3259 break;
3260 }
3261
3262 case RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_UNKNOWN:
3263 HandleShowExeWorkerDisplayObjIdSimple(pThis, &pData->papItems[i]->Type, "\n");
3264 break;
3265 case RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_NOT_PRESENT:
3266 RTPrintf("%sNot present!\n", pThis->szPrefix);
3267 break;
3268 default:
3269 RTPrintf("%senmType=%d!\n", pThis->szPrefix, pData->papItems[i]->enmType);
3270 break;
3271 }
3272 pThis->szPrefix[offPrefix] = '\0';
3273 }
3274 else
3275 RTPrintf("%s pData is NULL!\n", pThis->szPrefix);
3276 }
3277 else
3278 RTPrintf("%s Moniker: Unknown UUID: %RTuuid\n",
3279 pThis->szPrefix, pMoniker->Uuid.Asn1Core.uData.pUuid);
3280 }
3281 else
3282 RTPrintf("%s Moniker: not present\n", pThis->szPrefix);
3283 break;
3284 }
3285
3286 case RTCRSPCLINKCHOICE_URL:
3287 {
3288 const char *pszUrl = NULL;
3289 int rc = pPeImage->T0.File.u.pUrl
3290 ? RTAsn1String_QueryUtf8(pPeImage->T0.File.u.pUrl, &pszUrl, NULL)
3291 : VERR_NOT_FOUND;
3292 if (RT_SUCCESS(rc))
3293 RTPrintf("%s URL: '%s'\n", pThis->szPrefix, pszUrl);
3294 else
3295 RTPrintf("%s URL: rc=%Rrc\n", pThis->szPrefix, rc);
3296 break;
3297 }
3298
3299 case RTCRSPCLINKCHOICE_FILE:
3300 {
3301 const char *pszFile = NULL;
3302 int rc = pPeImage->T0.File.u.pT2 && pPeImage->T0.File.u.pT2->File.u.pAscii
3303 ? RTAsn1String_QueryUtf8(pPeImage->T0.File.u.pT2->File.u.pAscii, &pszFile, NULL)
3304 : VERR_NOT_FOUND;
3305 if (RT_SUCCESS(rc))
3306 RTPrintf("%s File: '%s'\n", pThis->szPrefix, pszFile);
3307 else
3308 RTPrintf("%s File: rc=%Rrc\n", pThis->szPrefix, rc);
3309 break;
3310 }
3311
3312 case RTCRSPCLINKCHOICE_NOT_PRESENT:
3313 RTPrintf("%s File not present!\n", pThis->szPrefix);
3314 break;
3315 default:
3316 RTPrintf("%s enmChoice=%d!\n", pThis->szPrefix, pPeImage->T0.File.enmChoice);
3317 break;
3318 }
3319 break;
3320 }
3321
3322 case RTCRSPCAAOVTYPE_UNKNOWN:
3323 HandleShowExeWorkerDisplayObjId(pThis, &pIndData->Data.Type, " Data Type: ", "\n");
3324 break;
3325 case RTCRSPCAAOVTYPE_NOT_PRESENT:
3326 RTPrintf("%s Data Type: Not present!\n", pThis->szPrefix);
3327 break;
3328 default:
3329 RTPrintf("%s Data Type: enmType=%d!\n", pThis->szPrefix, pIndData->Data.enmType);
3330 break;
3331 }
3332
3333 return VINF_SUCCESS;
3334}
3335
3336
3337/**
3338 * Display an PKCS#7 signed data instance.
3339 *
3340 * @returns IPRT status code.
3341 * @param pThis The show exe instance data.
3342 * @param pSignedData The signed data to display.
3343 * @param offPrefix The current prefix offset.
3344 * @param pContentInfo The content info structure (for the size).
3345 */
3346static int HandleShowExeWorkerPkcs7Display(PSHOWEXEPKCS7 pThis, PRTCRPKCS7SIGNEDDATA pSignedData, size_t offPrefix,
3347 PCRTCRPKCS7CONTENTINFO pContentInfo)
3348{
3349 pThis->szPrefix[offPrefix] = '\0';
3350 RTPrintf("%sPKCS#7 signature: %u (%#x) bytes\n", pThis->szPrefix,
3351 RTASN1CORE_GET_RAW_ASN1_SIZE(&pContentInfo->SeqCore.Asn1Core),
3352 RTASN1CORE_GET_RAW_ASN1_SIZE(&pContentInfo->SeqCore.Asn1Core));
3353
3354 /*
3355 * Display list of signing algorithms.
3356 */
3357 RTPrintf("%sDigestAlgorithms: ", pThis->szPrefix);
3358 if (pSignedData->DigestAlgorithms.cItems == 0)
3359 RTPrintf("none");
3360 for (unsigned i = 0; i < pSignedData->DigestAlgorithms.cItems; i++)
3361 {
3362 PCRTCRX509ALGORITHMIDENTIFIER pAlgoId = pSignedData->DigestAlgorithms.papItems[i];
3363 const char *pszDigestType = RTCrDigestTypeToName(RTCrX509AlgorithmIdentifier_QueryDigestType(pAlgoId));
3364 if (!pszDigestType)
3365 pszDigestType = pAlgoId->Algorithm.szObjId;
3366 RTPrintf(i == 0 ? "%s" : ", %s", pszDigestType);
3367 if (pThis->cVerbosity > 1)
3368 RTPrintf(" (%s)", pAlgoId->Algorithm.szObjId);
3369 }
3370 RTPrintf("\n");
3371
3372 /*
3373 * Display the signed data content.
3374 */
3375 if (RTAsn1ObjId_CompareWithString(&pSignedData->ContentInfo.ContentType, RTCRSPCINDIRECTDATACONTENT_OID) == 0)
3376 {
3377 RTPrintf("%s ContentType: SpcIndirectDataContent (" RTCRSPCINDIRECTDATACONTENT_OID ")\n", pThis->szPrefix);
3378 size_t offPrefix2 = RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, " SPC Ind Data: ");
3379 HandleShowExeWorkerPkcs7DisplaySpcIdirectDataContent(pThis, offPrefix2 + offPrefix,
3380 pSignedData->ContentInfo.u.pIndirectDataContent);
3381 pThis->szPrefix[offPrefix] = '\0';
3382 }
3383 else
3384 HandleShowExeWorkerDisplayObjId(pThis, &pSignedData->ContentInfo.ContentType, " ContentType: ", " - not implemented.\n");
3385
3386 /*
3387 * Display certificates (Certificates).
3388 */
3389 if (pSignedData->Certificates.cItems > 0)
3390 {
3391 RTPrintf("%s Certificates: %u\n", pThis->szPrefix, pSignedData->Certificates.cItems);
3392 for (uint32_t i = 0; i < pSignedData->Certificates.cItems; i++)
3393 {
3394 PCRTCRPKCS7CERT pCert = pSignedData->Certificates.papItems[i];
3395 if (i != 0 && pThis->cVerbosity >= 2)
3396 RTPrintf("\n");
3397 switch (pCert->enmChoice)
3398 {
3399 case RTCRPKCS7CERTCHOICE_X509:
3400 {
3401 PCRTCRX509CERTIFICATE pX509Cert = pCert->u.pX509Cert;
3402 int rc2 = RTAsn1QueryObjIdName(&pX509Cert->SignatureAlgorithm.Algorithm, pThis->szTmp, sizeof(pThis->szTmp));
3403 RTPrintf("%s Certificate #%u: %s\n", pThis->szPrefix, i,
3404 RT_SUCCESS(rc2) ? pThis->szTmp : pX509Cert->SignatureAlgorithm.Algorithm.szObjId);
3405
3406 rc2 = RTCrX509Name_FormatAsString(&pX509Cert->TbsCertificate.Subject,
3407 pThis->szTmp, sizeof(pThis->szTmp), NULL);
3408 if (RT_FAILURE(rc2))
3409 RTStrPrintf(pThis->szTmp, sizeof(pThis->szTmp), "%Rrc", rc2);
3410 RTPrintf("%s Subject: %s\n", pThis->szPrefix, pThis->szTmp);
3411
3412 rc2 = RTCrX509Name_FormatAsString(&pX509Cert->TbsCertificate.Issuer,
3413 pThis->szTmp, sizeof(pThis->szTmp), NULL);
3414 if (RT_FAILURE(rc2))
3415 RTStrPrintf(pThis->szTmp, sizeof(pThis->szTmp), "%Rrc", rc2);
3416 RTPrintf("%s Issuer: %s\n", pThis->szPrefix, pThis->szTmp);
3417
3418
3419 char szNotAfter[RTTIME_STR_LEN];
3420 RTPrintf("%s Valid: %s thru %s\n", pThis->szPrefix,
3421 RTTimeToString(&pX509Cert->TbsCertificate.Validity.NotBefore.Time,
3422 pThis->szTmp, sizeof(pThis->szTmp)),
3423 RTTimeToString(&pX509Cert->TbsCertificate.Validity.NotAfter.Time,
3424 szNotAfter, sizeof(szNotAfter)));
3425 break;
3426 }
3427
3428 default:
3429 RTPrintf("%s Certificate #%u: Unsupported type\n", pThis->szPrefix, i);
3430 break;
3431 }
3432
3433
3434 if (pThis->cVerbosity >= 2)
3435 RTAsn1Dump(RTCrPkcs7Cert_GetAsn1Core(pSignedData->Certificates.papItems[i]), 0,
3436 ((uint32_t)offPrefix + 9) / 2, RTStrmDumpPrintfV, g_pStdOut);
3437 }
3438
3439 /** @todo display certificates properly. */
3440 }
3441
3442 if (pSignedData->Crls.cb > 0)
3443 RTPrintf("%s CRLs: %u bytes\n", pThis->szPrefix, pSignedData->Crls.cb);
3444
3445 /*
3446 * Show signatures (SignerInfos).
3447 */
3448 unsigned const cSigInfos = pSignedData->SignerInfos.cItems;
3449 if (cSigInfos != 1)
3450 RTPrintf("%s SignerInfos: %u signers\n", pThis->szPrefix, cSigInfos);
3451 else
3452 RTPrintf("%s SignerInfos:\n", pThis->szPrefix);
3453 int rc = VINF_SUCCESS;
3454 for (unsigned i = 0; i < cSigInfos; i++)
3455 {
3456 size_t offPrefix2 = offPrefix;
3457 if (cSigInfos != 1)
3458 offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, "SignerInfo[%u]: ", i);
3459
3460 int rc2 = HandleShowExeWorkerPkcs7DisplaySignerInfo(pThis, offPrefix2, pSignedData->SignerInfos.papItems[i]);
3461 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
3462 rc = rc2;
3463 }
3464 pThis->szPrefix[offPrefix] = '\0';
3465
3466 return rc;
3467}
3468
3469
3470/*
3471 * The 'show-exe' command.
3472 */
3473static RTEXITCODE HelpShowExe(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
3474{
3475 RT_NOREF_PV(enmLevel);
3476 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT, "show-exe [--verbose|-v] [--quiet|-q] <exe1> [exe2 [..]]\n");
3477 return RTEXITCODE_SUCCESS;
3478}
3479
3480
3481static RTEXITCODE HandleShowExe(int cArgs, char **papszArgs)
3482{
3483 /*
3484 * Parse arguments.
3485 */
3486 static const RTGETOPTDEF s_aOptions[] =
3487 {
3488 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
3489 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
3490 };
3491
3492 unsigned cVerbosity = 0;
3493 RTLDRARCH enmLdrArch = RTLDRARCH_WHATEVER;
3494
3495 RTGETOPTSTATE GetState;
3496 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
3497 AssertRCReturn(rc, RTEXITCODE_FAILURE);
3498 RTGETOPTUNION ValueUnion;
3499 int ch;
3500 while ((ch = RTGetOpt(&GetState, &ValueUnion)) && ch != VINF_GETOPT_NOT_OPTION)
3501 {
3502 switch (ch)
3503 {
3504 case 'v': cVerbosity++; break;
3505 case 'q': cVerbosity = 0; break;
3506 case 'V': return HandleVersion(cArgs, papszArgs);
3507 case 'h': return HelpShowExe(g_pStdOut, RTSIGNTOOLHELP_FULL);
3508 default: return RTGetOptPrintError(ch, &ValueUnion);
3509 }
3510 }
3511 if (ch != VINF_GETOPT_NOT_OPTION)
3512 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
3513
3514 /*
3515 * Do it.
3516 */
3517 unsigned iFile = 0;
3518 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
3519 do
3520 {
3521 RTPrintf(iFile == 0 ? "%s:\n" : "\n%s:\n", ValueUnion.psz);
3522
3523 SHOWEXEPKCS7 This;
3524 RT_ZERO(This);
3525 This.cVerbosity = cVerbosity;
3526
3527 RTEXITCODE rcExitThis = SignToolPkcs7Exe_InitFromFile(&This, ValueUnion.psz, cVerbosity, enmLdrArch);
3528 if (rcExitThis == RTEXITCODE_SUCCESS)
3529 {
3530 rc = HandleShowExeWorkerPkcs7Display(&This, This.pSignedData, 0, &This.ContentInfo);
3531 if (RT_FAILURE(rc))
3532 rcExit = RTEXITCODE_FAILURE;
3533 SignToolPkcs7Exe_Delete(&This);
3534 }
3535 if (rcExitThis != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
3536 rcExit = rcExitThis;
3537
3538 iFile++;
3539 } while ((ch = RTGetOpt(&GetState, &ValueUnion)) == VINF_GETOPT_NOT_OPTION);
3540 if (ch != 0)
3541 return RTGetOptPrintError(ch, &ValueUnion);
3542
3543 return rcExit;
3544}
3545
3546
3547/*
3548 * The 'show-cat' command.
3549 */
3550static RTEXITCODE HelpShowCat(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
3551{
3552 RT_NOREF_PV(enmLevel);
3553 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT, "show-cat [--verbose|-v] [--quiet|-q] <cat1> [cat2 [..]]\n");
3554 return RTEXITCODE_SUCCESS;
3555}
3556
3557
3558static RTEXITCODE HandleShowCat(int cArgs, char **papszArgs)
3559{
3560 /*
3561 * Parse arguments.
3562 */
3563 static const RTGETOPTDEF s_aOptions[] =
3564 {
3565 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
3566 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
3567 };
3568
3569 unsigned cVerbosity = 0;
3570
3571 RTGETOPTSTATE GetState;
3572 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
3573 AssertRCReturn(rc, RTEXITCODE_FAILURE);
3574 RTGETOPTUNION ValueUnion;
3575 int ch;
3576 while ((ch = RTGetOpt(&GetState, &ValueUnion)) && ch != VINF_GETOPT_NOT_OPTION)
3577 {
3578 switch (ch)
3579 {
3580 case 'v': cVerbosity++; break;
3581 case 'q': cVerbosity = 0; break;
3582 case 'V': return HandleVersion(cArgs, papszArgs);
3583 case 'h': return HelpShowCat(g_pStdOut, RTSIGNTOOLHELP_FULL);
3584 default: return RTGetOptPrintError(ch, &ValueUnion);
3585 }
3586 }
3587 if (ch != VINF_GETOPT_NOT_OPTION)
3588 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
3589
3590 /*
3591 * Do it.
3592 */
3593 unsigned iFile = 0;
3594 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
3595 do
3596 {
3597 RTPrintf(iFile == 0 ? "%s:\n" : "\n%s:\n", ValueUnion.psz);
3598
3599 SHOWEXEPKCS7 This;
3600 RT_ZERO(This);
3601 This.cVerbosity = cVerbosity;
3602
3603 RTEXITCODE rcExitThis = SignToolPkcs7_InitFromFile(&This, ValueUnion.psz, cVerbosity);
3604 if (rcExitThis == RTEXITCODE_SUCCESS)
3605 {
3606 This.hLdrMod = NIL_RTLDRMOD;
3607
3608 rc = HandleShowExeWorkerPkcs7Display(&This, This.pSignedData, 0, &This.ContentInfo);
3609 if (RT_FAILURE(rc))
3610 rcExit = RTEXITCODE_FAILURE;
3611 SignToolPkcs7Exe_Delete(&This);
3612 }
3613 if (rcExitThis != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
3614 rcExit = rcExitThis;
3615
3616 iFile++;
3617 } while ((ch = RTGetOpt(&GetState, &ValueUnion)) == VINF_GETOPT_NOT_OPTION);
3618 if (ch != 0)
3619 return RTGetOptPrintError(ch, &ValueUnion);
3620
3621 return rcExit;
3622}
3623
3624
3625/*
3626 * The 'make-tainfo' command.
3627 */
3628static RTEXITCODE HelpMakeTaInfo(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
3629{
3630 RT_NOREF_PV(enmLevel);
3631 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
3632 "make-tainfo [--verbose|--quiet] [--cert <cert.der>] [-o|--output] <tainfo.der>\n");
3633 return RTEXITCODE_SUCCESS;
3634}
3635
3636
3637typedef struct MAKETAINFOSTATE
3638{
3639 int cVerbose;
3640 const char *pszCert;
3641 const char *pszOutput;
3642} MAKETAINFOSTATE;
3643
3644
3645/** @callback_method_impl{FNRTASN1ENCODEWRITER} */
3646static DECLCALLBACK(int) handleMakeTaInfoWriter(const void *pvBuf, size_t cbToWrite, void *pvUser, PRTERRINFO pErrInfo)
3647{
3648 RT_NOREF_PV(pErrInfo);
3649 return RTStrmWrite((PRTSTREAM)pvUser, pvBuf, cbToWrite);
3650}
3651
3652
3653static RTEXITCODE HandleMakeTaInfo(int cArgs, char **papszArgs)
3654{
3655 /*
3656 * Parse arguments.
3657 */
3658 static const RTGETOPTDEF s_aOptions[] =
3659 {
3660 { "--cert", 'c', RTGETOPT_REQ_STRING },
3661 { "--output", 'o', RTGETOPT_REQ_STRING },
3662 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
3663 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
3664 };
3665
3666 MAKETAINFOSTATE State = { 0, NULL, NULL };
3667
3668 RTGETOPTSTATE GetState;
3669 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
3670 AssertRCReturn(rc, RTEXITCODE_FAILURE);
3671 RTGETOPTUNION ValueUnion;
3672 int ch;
3673 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
3674 {
3675 switch (ch)
3676 {
3677 case 'c':
3678 if (State.pszCert)
3679 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The --cert option can only be used once.");
3680 State.pszCert = ValueUnion.psz;
3681 break;
3682
3683 case 'o':
3684 case VINF_GETOPT_NOT_OPTION:
3685 if (State.pszOutput)
3686 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Multiple output files specified.");
3687 State.pszOutput = ValueUnion.psz;
3688 break;
3689
3690 case 'v': State.cVerbose++; break;
3691 case 'q': State.cVerbose = 0; break;
3692 case 'V': return HandleVersion(cArgs, papszArgs);
3693 case 'h': return HelpMakeTaInfo(g_pStdOut, RTSIGNTOOLHELP_FULL);
3694 default: return RTGetOptPrintError(ch, &ValueUnion);
3695 }
3696 }
3697 if (!State.pszCert)
3698 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No input certificate was specified.");
3699 if (!State.pszOutput)
3700 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No output file was specified.");
3701
3702 /*
3703 * Read the certificate.
3704 */
3705 RTERRINFOSTATIC StaticErrInfo;
3706 RTCRX509CERTIFICATE Certificate;
3707 rc = RTCrX509Certificate_ReadFromFile(&Certificate, State.pszCert, 0, &g_RTAsn1DefaultAllocator,
3708 RTErrInfoInitStatic(&StaticErrInfo));
3709 if (RT_FAILURE(rc))
3710 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error reading certificate from %s: %Rrc - %s",
3711 State.pszCert, rc, StaticErrInfo.szMsg);
3712 /*
3713 * Construct the trust anchor information.
3714 */
3715 RTCRTAFTRUSTANCHORINFO TrustAnchor;
3716 rc = RTCrTafTrustAnchorInfo_Init(&TrustAnchor, &g_RTAsn1DefaultAllocator);
3717 if (RT_SUCCESS(rc))
3718 {
3719 /* Public key. */
3720 Assert(RTCrX509SubjectPublicKeyInfo_IsPresent(&TrustAnchor.PubKey));
3721 RTCrX509SubjectPublicKeyInfo_Delete(&TrustAnchor.PubKey);
3722 rc = RTCrX509SubjectPublicKeyInfo_Clone(&TrustAnchor.PubKey, &Certificate.TbsCertificate.SubjectPublicKeyInfo,
3723 &g_RTAsn1DefaultAllocator);
3724 if (RT_FAILURE(rc))
3725 RTMsgError("RTCrX509SubjectPublicKeyInfo_Clone failed: %Rrc", rc);
3726 RTAsn1Core_ResetImplict(RTCrX509SubjectPublicKeyInfo_GetAsn1Core(&TrustAnchor.PubKey)); /* temporary hack. */
3727
3728 /* Key Identifier. */
3729 PCRTASN1OCTETSTRING pKeyIdentifier = NULL;
3730 if (Certificate.TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_SUBJECT_KEY_IDENTIFIER)
3731 pKeyIdentifier = Certificate.TbsCertificate.T3.pSubjectKeyIdentifier;
3732 else if ( (Certificate.TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_AUTHORITY_KEY_IDENTIFIER)
3733 && RTCrX509Certificate_IsSelfSigned(&Certificate)
3734 && RTAsn1OctetString_IsPresent(&Certificate.TbsCertificate.T3.pAuthorityKeyIdentifier->KeyIdentifier) )
3735 pKeyIdentifier = &Certificate.TbsCertificate.T3.pAuthorityKeyIdentifier->KeyIdentifier;
3736 else if ( (Certificate.TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_OLD_AUTHORITY_KEY_IDENTIFIER)
3737 && RTCrX509Certificate_IsSelfSigned(&Certificate)
3738 && RTAsn1OctetString_IsPresent(&Certificate.TbsCertificate.T3.pOldAuthorityKeyIdentifier->KeyIdentifier) )
3739 pKeyIdentifier = &Certificate.TbsCertificate.T3.pOldAuthorityKeyIdentifier->KeyIdentifier;
3740 if (pKeyIdentifier && pKeyIdentifier->Asn1Core.cb > 0)
3741 {
3742 Assert(RTAsn1OctetString_IsPresent(&TrustAnchor.KeyIdentifier));
3743 RTAsn1OctetString_Delete(&TrustAnchor.KeyIdentifier);
3744 rc = RTAsn1OctetString_Clone(&TrustAnchor.KeyIdentifier, pKeyIdentifier, &g_RTAsn1DefaultAllocator);
3745 if (RT_FAILURE(rc))
3746 RTMsgError("RTAsn1OctetString_Clone failed: %Rrc", rc);
3747 RTAsn1Core_ResetImplict(RTAsn1OctetString_GetAsn1Core(&TrustAnchor.KeyIdentifier)); /* temporary hack. */
3748 }
3749 else
3750 RTMsgWarning("No key identifier found or has zero length.");
3751
3752 /* Subject */
3753 if (RT_SUCCESS(rc))
3754 {
3755 Assert(!RTCrTafCertPathControls_IsPresent(&TrustAnchor.CertPath));
3756 rc = RTCrTafCertPathControls_Init(&TrustAnchor.CertPath, &g_RTAsn1DefaultAllocator);
3757 if (RT_SUCCESS(rc))
3758 {
3759 Assert(RTCrX509Name_IsPresent(&TrustAnchor.CertPath.TaName));
3760 RTCrX509Name_Delete(&TrustAnchor.CertPath.TaName);
3761 rc = RTCrX509Name_Clone(&TrustAnchor.CertPath.TaName, &Certificate.TbsCertificate.Subject,
3762 &g_RTAsn1DefaultAllocator);
3763 if (RT_SUCCESS(rc))
3764 {
3765 RTAsn1Core_ResetImplict(RTCrX509Name_GetAsn1Core(&TrustAnchor.CertPath.TaName)); /* temporary hack. */
3766 rc = RTCrX509Name_RecodeAsUtf8(&TrustAnchor.CertPath.TaName, &g_RTAsn1DefaultAllocator);
3767 if (RT_FAILURE(rc))
3768 RTMsgError("RTCrX509Name_RecodeAsUtf8 failed: %Rrc", rc);
3769 }
3770 else
3771 RTMsgError("RTCrX509Name_Clone failed: %Rrc", rc);
3772 }
3773 else
3774 RTMsgError("RTCrTafCertPathControls_Init failed: %Rrc", rc);
3775 }
3776
3777 /* Check that what we've constructed makes some sense. */
3778 if (RT_SUCCESS(rc))
3779 {
3780 rc = RTCrTafTrustAnchorInfo_CheckSanity(&TrustAnchor, 0, RTErrInfoInitStatic(&StaticErrInfo), "TAI");
3781 if (RT_FAILURE(rc))
3782 RTMsgError("RTCrTafTrustAnchorInfo_CheckSanity failed: %Rrc - %s", rc, StaticErrInfo.szMsg);
3783 }
3784
3785 if (RT_SUCCESS(rc))
3786 {
3787 /*
3788 * Encode it and write it to the output file.
3789 */
3790 uint32_t cbEncoded;
3791 rc = RTAsn1EncodePrepare(RTCrTafTrustAnchorInfo_GetAsn1Core(&TrustAnchor), RTASN1ENCODE_F_DER, &cbEncoded,
3792 RTErrInfoInitStatic(&StaticErrInfo));
3793 if (RT_SUCCESS(rc))
3794 {
3795 if (State.cVerbose >= 1)
3796 RTAsn1Dump(RTCrTafTrustAnchorInfo_GetAsn1Core(&TrustAnchor), 0, 0, RTStrmDumpPrintfV, g_pStdOut);
3797
3798 PRTSTREAM pStrm;
3799 rc = RTStrmOpen(State.pszOutput, "wb", &pStrm);
3800 if (RT_SUCCESS(rc))
3801 {
3802 rc = RTAsn1EncodeWrite(RTCrTafTrustAnchorInfo_GetAsn1Core(&TrustAnchor), RTASN1ENCODE_F_DER,
3803 handleMakeTaInfoWriter, pStrm, RTErrInfoInitStatic(&StaticErrInfo));
3804 if (RT_SUCCESS(rc))
3805 {
3806 rc = RTStrmClose(pStrm);
3807 if (RT_SUCCESS(rc))
3808 RTMsgInfo("Successfully wrote TrustedAnchorInfo to '%s'.", State.pszOutput);
3809 else
3810 RTMsgError("RTStrmClose failed: %Rrc", rc);
3811 }
3812 else
3813 {
3814 RTMsgError("RTAsn1EncodeWrite failed: %Rrc - %s", rc, StaticErrInfo.szMsg);
3815 RTStrmClose(pStrm);
3816 }
3817 }
3818 else
3819 RTMsgError("Error opening '%s' for writing: %Rrcs", State.pszOutput, rc);
3820 }
3821 else
3822 RTMsgError("RTAsn1EncodePrepare failed: %Rrc - %s", rc, StaticErrInfo.szMsg);
3823 }
3824
3825 RTCrTafTrustAnchorInfo_Delete(&TrustAnchor);
3826 }
3827 else
3828 RTMsgError("RTCrTafTrustAnchorInfo_Init failed: %Rrc", rc);
3829
3830 RTCrX509Certificate_Delete(&Certificate);
3831 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
3832}
3833
3834
3835
3836/*
3837 * The 'version' command.
3838 */
3839static RTEXITCODE HelpVersion(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
3840{
3841 RT_NOREF_PV(enmLevel);
3842 RTStrmPrintf(pStrm, "version\n");
3843 return RTEXITCODE_SUCCESS;
3844}
3845
3846static RTEXITCODE HandleVersion(int cArgs, char **papszArgs)
3847{
3848 RT_NOREF_PV(cArgs); RT_NOREF_PV(papszArgs);
3849#ifndef IN_BLD_PROG /* RTBldCfgVersion or RTBldCfgRevision in build time IPRT lib. */
3850 RTPrintf("%s\n", RTBldCfgVersion());
3851 return RTEXITCODE_SUCCESS;
3852#else
3853 return RTEXITCODE_FAILURE;
3854#endif
3855}
3856
3857
3858
3859/**
3860 * Command mapping.
3861 */
3862static struct
3863{
3864 /** The command. */
3865 const char *pszCmd;
3866 /**
3867 * Handle the command.
3868 * @returns Program exit code.
3869 * @param cArgs Number of arguments.
3870 * @param papszArgs The argument vector, starting with the command name.
3871 */
3872 RTEXITCODE (*pfnHandler)(int cArgs, char **papszArgs);
3873 /**
3874 * Produce help.
3875 * @returns RTEXITCODE_SUCCESS to simplify handling '--help' in the handler.
3876 * @param pStrm Where to send help text.
3877 * @param enmLevel The level of the help information.
3878 */
3879 RTEXITCODE (*pfnHelp)(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel);
3880}
3881/** Mapping commands to handler and helper functions. */
3882const g_aCommands[] =
3883{
3884 { "extract-exe-signer-cert", HandleExtractExeSignerCert, HelpExtractExeSignerCert },
3885 { "add-nested-exe-signature", HandleAddNestedExeSignature, HelpAddNestedExeSignature },
3886 { "add-nested-cat-signature", HandleAddNestedCatSignature, HelpAddNestedCatSignature },
3887#ifndef IPRT_IN_BUILD_TOOL
3888 { "add-timestamp-exe-signature", HandleAddTimestampExeSignature, HelpAddTimestampExeSignature },
3889 { "sign-exe", HandleSignExe, HelpSignExe },
3890#endif
3891#ifndef IPRT_IN_BUILD_TOOL
3892 { "verify-exe", HandleVerifyExe, HelpVerifyExe },
3893#endif
3894 { "show-exe", HandleShowExe, HelpShowExe },
3895 { "show-cat", HandleShowCat, HelpShowCat },
3896 { "make-tainfo", HandleMakeTaInfo, HelpMakeTaInfo },
3897 { "help", HandleHelp, HelpHelp },
3898 { "--help", HandleHelp, NULL },
3899 { "-h", HandleHelp, NULL },
3900 { "version", HandleVersion, HelpVersion },
3901 { "--version", HandleVersion, NULL },
3902 { "-V", HandleVersion, NULL },
3903};
3904
3905
3906/*
3907 * The 'help' command.
3908 */
3909static RTEXITCODE HelpHelp(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
3910{
3911 RT_NOREF_PV(enmLevel);
3912 RTStrmPrintf(pStrm, "help [cmd-patterns]\n");
3913 return RTEXITCODE_SUCCESS;
3914}
3915
3916static RTEXITCODE HandleHelp(int cArgs, char **papszArgs)
3917{
3918 RTSIGNTOOLHELP enmLevel = cArgs <= 1 ? RTSIGNTOOLHELP_USAGE : RTSIGNTOOLHELP_FULL;
3919 uint32_t cShowed = 0;
3920 uint32_t cchWidth;
3921 if (RT_FAILURE(RTStrmQueryTerminalWidth(g_pStdOut, &cchWidth)))
3922 cchWidth = 80;
3923 for (uint32_t iCmd = 0; iCmd < RT_ELEMENTS(g_aCommands); iCmd++)
3924 {
3925 if (g_aCommands[iCmd].pfnHelp)
3926 {
3927 bool fShow = false;
3928 if (cArgs <= 1)
3929 fShow = true;
3930 else
3931 for (int iArg = 1; iArg < cArgs; iArg++)
3932 if (RTStrSimplePatternMultiMatch(papszArgs[iArg], RTSTR_MAX, g_aCommands[iCmd].pszCmd, RTSTR_MAX, NULL))
3933 {
3934 fShow = true;
3935 break;
3936 }
3937 if (fShow)
3938 {
3939 if (cShowed && enmLevel == RTSIGNTOOLHELP_FULL)
3940 RTPrintf("%.*s\n", RT_MIN(cchWidth, 100),
3941 "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
3942 g_aCommands[iCmd].pfnHelp(g_pStdOut, enmLevel);
3943 cShowed++;
3944 }
3945 }
3946 }
3947 return cShowed ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
3948}
3949
3950
3951
3952int main(int argc, char **argv)
3953{
3954 int rc = RTR3InitExe(argc, &argv, 0);
3955 if (RT_FAILURE(rc))
3956 return RTMsgInitFailure(rc);
3957
3958 /*
3959 * Parse global arguments.
3960 */
3961 int iArg = 1;
3962 /* none presently. */
3963
3964 /*
3965 * Command dispatcher.
3966 */
3967 if (iArg < argc)
3968 {
3969 const char *pszCmd = argv[iArg];
3970 uint32_t i = RT_ELEMENTS(g_aCommands);
3971 while (i-- > 0)
3972 if (!strcmp(g_aCommands[i].pszCmd, pszCmd))
3973 return g_aCommands[i].pfnHandler(argc - iArg, &argv[iArg]);
3974 RTMsgError("Unknown command '%s'.", pszCmd);
3975 }
3976 else
3977 RTMsgError("No command given. (try --help)");
3978
3979 return RTEXITCODE_SYNTAX;
3980}
3981
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