VirtualBox

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

Last change on this file since 64858 was 64858, checked in by vboxsync, 8 years ago

build fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 53.8 KB
Line 
1/* $Id: RTSignTool.cpp 64858 2016-12-13 17:54:43Z vboxsync $ */
2/** @file
3 * IPRT - Signing Tool.
4 */
5
6/*
7 * Copyright (C) 2006-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#include <iprt/assert.h>
32#include <iprt/buildconfig.h>
33#include <iprt/err.h>
34#include <iprt/getopt.h>
35#include <iprt/file.h>
36#include <iprt/initterm.h>
37#include <iprt/ldr.h>
38#include <iprt/message.h>
39#include <iprt/mem.h>
40#include <iprt/path.h>
41#include <iprt/stream.h>
42#include <iprt/string.h>
43#include <iprt/crypto/digest.h>
44#include <iprt/crypto/x509.h>
45#include <iprt/crypto/pkcs7.h>
46#include <iprt/crypto/store.h>
47#include <iprt/crypto/spc.h>
48#ifdef VBOX
49# include <VBox/sup.h> /* Certificates */
50#endif
51
52
53/*******************************************************************************
54* Structures and Typedefs *
55*******************************************************************************/
56/** Help detail levels. */
57typedef enum RTSIGNTOOLHELP
58{
59 RTSIGNTOOLHELP_USAGE,
60 RTSIGNTOOLHELP_FULL
61} RTSIGNTOOLHELP;
62
63
64typedef struct SHOWEXEPKCS7
65{
66 uint8_t const *pbBuf;
67 size_t cbBuf;
68 const char *pszFilename;
69 unsigned cVerbosity;
70 RTLDRMOD hLdrMod;
71 /** The outer content info wrapper. */
72 RTCRPKCS7CONTENTINFO ContentInfo;
73 /** Pointer to the decoded SignedData inside the ContentInfo member. */
74 PRTCRPKCS7SIGNEDDATA pSignedData;
75 /** Pointer to the indirect data content. */
76 PRTCRSPCINDIRECTDATACONTENT pIndData;
77
78 char szPrefix[256];
79 char szTmp[4096];
80} SHOWEXEPKCS7;
81typedef SHOWEXEPKCS7 *PSHOWEXEPKCS7;
82
83
84/*******************************************************************************
85* Internal Functions *
86*******************************************************************************/
87static RTEXITCODE HandleHelp(int cArgs, char **papszArgs);
88static RTEXITCODE HelpHelp(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel);
89static RTEXITCODE HandleVersion(int cArgs, char **papszArgs);
90#ifndef IPRT_IN_BUILD_TOOL
91static int HandleShowExeWorkerPkcs7Display(PSHOWEXEPKCS7 pThis, PRTCRPKCS7SIGNEDDATA pSignedData, size_t offPrefix);
92#endif
93
94
95
96
97/*
98 * The 'extract-exe-signer-cert' command.
99 */
100static RTEXITCODE HelpExtractExeSignerCert(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
101{
102 RT_NOREF_PV(enmLevel);
103 RTStrmPrintf(pStrm, "extract-exe-signer-cert [--ber|--cer|--der] [--exe|-e] <exe> [--output|-o] <outfile.cer>\n");
104 return RTEXITCODE_SUCCESS;
105}
106
107static RTEXITCODE HandleExtractExeSignerCert(int cArgs, char **papszArgs)
108{
109 /*
110 * Parse arguments.
111 */
112 static const RTGETOPTDEF s_aOptions[] =
113 {
114 { "--ber", 'b', RTGETOPT_REQ_NOTHING },
115 { "--cer", 'c', RTGETOPT_REQ_NOTHING },
116 { "--der", 'd', RTGETOPT_REQ_NOTHING },
117 { "--exe", 'e', RTGETOPT_REQ_STRING },
118 { "--output", 'o', RTGETOPT_REQ_STRING },
119 };
120
121 const char *pszExe = NULL;
122 const char *pszOut = NULL;
123 RTLDRARCH enmLdrArch = RTLDRARCH_WHATEVER;
124 uint32_t fCursorFlags = RTASN1CURSOR_FLAGS_DER;
125
126 RTGETOPTSTATE GetState;
127 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
128 AssertRCReturn(rc, RTEXITCODE_FAILURE);
129 RTGETOPTUNION ValueUnion;
130 int ch;
131 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
132 {
133 switch (ch)
134 {
135 case 'e': pszExe = ValueUnion.psz; break;
136 case 'o': pszOut = ValueUnion.psz; break;
137 case 'b': fCursorFlags = 0; break;
138 case 'c': fCursorFlags = RTASN1CURSOR_FLAGS_CER; break;
139 case 'd': fCursorFlags = RTASN1CURSOR_FLAGS_DER; break;
140 case 'V': return HandleVersion(cArgs, papszArgs);
141 case 'h': return HelpExtractExeSignerCert(g_pStdOut, RTSIGNTOOLHELP_FULL);
142
143 case VINF_GETOPT_NOT_OPTION:
144 if (!pszExe)
145 pszExe = ValueUnion.psz;
146 else if (!pszOut)
147 pszOut = ValueUnion.psz;
148 else
149 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many file arguments: %s", ValueUnion.psz);
150 break;
151
152 default:
153 return RTGetOptPrintError(ch, &ValueUnion);
154 }
155 }
156 if (!pszExe)
157 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
158 if (!pszOut)
159 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No output file given.");
160 if (RTPathExists(pszOut))
161 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The output file '%s' exists.", pszOut);
162
163 /*
164 * Do it.
165 */
166
167 /* Open the executable image and query the PKCS7 info. */
168 RTLDRMOD hLdrMod;
169 rc = RTLdrOpen(pszExe, RTLDR_O_FOR_VALIDATION, enmLdrArch, &hLdrMod);
170 if (RT_FAILURE(rc))
171 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error opening executable image '%s': %Rrc", pszExe, rc);
172
173 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
174#ifdef DEBUG
175 size_t cbBuf = 64;
176#else
177 size_t cbBuf = _512K;
178#endif
179 void *pvBuf = RTMemAlloc(cbBuf);
180 size_t cbRet = 0;
181 rc = RTLdrQueryPropEx(hLdrMod, RTLDRPROP_PKCS7_SIGNED_DATA, NULL /*pvBits*/, pvBuf, cbBuf, &cbRet);
182 if (rc == VERR_BUFFER_OVERFLOW && cbRet < _4M && cbRet > 0)
183 {
184 RTMemFree(pvBuf);
185 cbBuf = cbRet;
186 pvBuf = RTMemAlloc(cbBuf);
187 rc = RTLdrQueryPropEx(hLdrMod, RTLDRPROP_PKCS7_SIGNED_DATA, NULL /*pvBits*/, pvBuf, cbBuf, &cbRet);
188 }
189 if (RT_SUCCESS(rc))
190 {
191 static RTERRINFOSTATIC s_StaticErrInfo;
192 RTErrInfoInitStatic(&s_StaticErrInfo);
193
194 /*
195 * Decode the output.
196 */
197 RTASN1CURSORPRIMARY PrimaryCursor;
198 RTAsn1CursorInitPrimary(&PrimaryCursor, pvBuf, (uint32_t)cbRet, &s_StaticErrInfo.Core,
199 &g_RTAsn1DefaultAllocator, fCursorFlags, "exe");
200 RTCRPKCS7CONTENTINFO Pkcs7Ci;
201 rc = RTCrPkcs7ContentInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, &Pkcs7Ci, "pkcs7");
202 if (RT_SUCCESS(rc))
203 {
204 if (RTCrPkcs7ContentInfo_IsSignedData(&Pkcs7Ci))
205 {
206 PCRTCRPKCS7SIGNEDDATA pSd = Pkcs7Ci.u.pSignedData;
207 if (pSd->SignerInfos.cItems == 1)
208 {
209 PCRTCRPKCS7ISSUERANDSERIALNUMBER pISN = &pSd->SignerInfos.paItems[0].IssuerAndSerialNumber;
210 PCRTCRX509CERTIFICATE pCert;
211 pCert = RTCrPkcs7SetOfCerts_FindX509ByIssuerAndSerialNumber(&pSd->Certificates,
212 &pISN->Name, &pISN->SerialNumber);
213 if (pCert)
214 {
215 /*
216 * Write it out.
217 */
218 RTFILE hFile;
219 rc = RTFileOpen(&hFile, pszOut, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE);
220 if (RT_SUCCESS(rc))
221 {
222 uint32_t cbCert = pCert->SeqCore.Asn1Core.cbHdr + pCert->SeqCore.Asn1Core.cb;
223 rc = RTFileWrite(hFile, pCert->SeqCore.Asn1Core.uData.pu8 - pCert->SeqCore.Asn1Core.cbHdr,
224 cbCert, NULL);
225 if (RT_SUCCESS(rc))
226 {
227 rc = RTFileClose(hFile);
228 if (RT_SUCCESS(rc))
229 {
230 hFile = NIL_RTFILE;
231 rcExit = RTEXITCODE_SUCCESS;
232 RTMsgInfo("Successfully wrote %u bytes to '%s'", cbCert, pszOut);
233 }
234 else
235 RTMsgError("RTFileClose failed: %Rrc", rc);
236 }
237 else
238 RTMsgError("RTFileWrite failed: %Rrc", rc);
239 RTFileClose(hFile);
240 }
241 else
242 RTMsgError("Error opening '%s': %Rrc", pszOut, rc);
243 }
244 else
245 RTMsgError("Certificate not found.");
246 }
247 else
248 RTMsgError("SignerInfo count: %u", pSd->SignerInfos.cItems);
249 }
250 else
251 RTMsgError("No PKCS7 content: ContentType=%s", Pkcs7Ci.ContentType.szObjId);
252 RTAsn1VtDelete(&Pkcs7Ci.SeqCore.Asn1Core);
253 }
254 else
255 RTMsgError("RTPkcs7ContentInfoDecodeAsn1 failed: %Rrc - %s", rc, s_StaticErrInfo.szMsg);
256 }
257 else
258 RTMsgError("RTLDRPROP_PKCS7_SIGNED_DATA failed on '%s': %Rrc", pszExe, rc);
259 RTMemFree(pvBuf);
260 rc = RTLdrClose(hLdrMod);
261 if (RT_FAILURE(rc))
262 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTLdrClose failed: %Rrc\n", rc);
263 return rcExit;
264}
265
266#ifndef IPRT_IN_BUILD_TOOL
267
268/*
269 * The 'verify-exe' command.
270 */
271static RTEXITCODE HelpVerifyExe(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
272{
273 RT_NOREF_PV(enmLevel);
274 RTStrmPrintf(pStrm,
275 "verify-exe [--verbose|--quiet] [--kernel] [--root <root-cert.der>] [--additional <supp-cert.der>]\n"
276 " [--type <win|osx>] <exe1> [exe2 [..]]\n");
277 return RTEXITCODE_SUCCESS;
278}
279
280typedef struct VERIFYEXESTATE
281{
282 RTCRSTORE hRootStore;
283 RTCRSTORE hKernelRootStore;
284 RTCRSTORE hAdditionalStore;
285 bool fKernel;
286 int cVerbose;
287 enum { kSignType_Windows, kSignType_OSX } enmSignType;
288 uint64_t uTimestamp;
289 RTLDRARCH enmLdrArch;
290} VERIFYEXESTATE;
291
292# ifdef VBOX
293/** Certificate store load set.
294 * Declared outside HandleVerifyExe because of braindead gcc visibility crap. */
295struct STSTORESET
296{
297 RTCRSTORE hStore;
298 PCSUPTAENTRY paTAs;
299 unsigned cTAs;
300};
301# endif
302
303/**
304 * @callback_method_impl{FNRTCRPKCS7VERIFYCERTCALLBACK,
305 * Standard code signing. Use this for Microsoft SPC.}
306 */
307static DECLCALLBACK(int) VerifyExecCertVerifyCallback(PCRTCRX509CERTIFICATE pCert, RTCRX509CERTPATHS hCertPaths, uint32_t fFlags,
308 void *pvUser, PRTERRINFO pErrInfo)
309{
310 VERIFYEXESTATE *pState = (VERIFYEXESTATE *)pvUser;
311 uint32_t cPaths = hCertPaths != NIL_RTCRX509CERTPATHS ? RTCrX509CertPathsGetPathCount(hCertPaths) : 0;
312
313 /*
314 * Dump all the paths.
315 */
316 if (pState->cVerbose > 0)
317 {
318 for (uint32_t iPath = 0; iPath < cPaths; iPath++)
319 {
320 RTPrintf("---\n");
321 RTCrX509CertPathsDumpOne(hCertPaths, iPath, pState->cVerbose, RTStrmDumpPrintfV, g_pStdOut);
322 *pErrInfo->pszMsg = '\0';
323 }
324 RTPrintf("---\n");
325 }
326
327 /*
328 * Test signing certificates normally doesn't have all the necessary
329 * features required below. So, treat them as special cases.
330 */
331 if ( hCertPaths == NIL_RTCRX509CERTPATHS
332 && RTCrX509Name_Compare(&pCert->TbsCertificate.Issuer, &pCert->TbsCertificate.Subject) == 0)
333 {
334 RTMsgInfo("Test signed.\n");
335 return VINF_SUCCESS;
336 }
337
338 if (hCertPaths == NIL_RTCRX509CERTPATHS)
339 RTMsgInfo("Signed by trusted certificate.\n");
340
341 /*
342 * Standard code signing capabilites required.
343 */
344 int rc = RTCrPkcs7VerifyCertCallbackCodeSigning(pCert, hCertPaths, fFlags, NULL, pErrInfo);
345 if ( RT_SUCCESS(rc)
346 && (fFlags & RTCRPKCS7VCC_F_SIGNED_DATA))
347 {
348 /*
349 * If kernel signing, a valid certificate path must be anchored by the
350 * microsoft kernel signing root certificate. The only alternative is
351 * test signing.
352 */
353 if (pState->fKernel && hCertPaths != NIL_RTCRX509CERTPATHS)
354 {
355 uint32_t cFound = 0;
356 uint32_t cValid = 0;
357 for (uint32_t iPath = 0; iPath < cPaths; iPath++)
358 {
359 bool fTrusted;
360 PCRTCRX509NAME pSubject;
361 PCRTCRX509SUBJECTPUBLICKEYINFO pPublicKeyInfo;
362 int rcVerify;
363 rc = RTCrX509CertPathsQueryPathInfo(hCertPaths, iPath, &fTrusted, NULL /*pcNodes*/, &pSubject, &pPublicKeyInfo,
364 NULL, NULL /*pCertCtx*/, &rcVerify);
365 AssertRCBreak(rc);
366
367 if (RT_SUCCESS(rcVerify))
368 {
369 Assert(fTrusted);
370 cValid++;
371
372 /* Search the kernel signing root store for a matching anchor. */
373 RTCRSTORECERTSEARCH Search;
374 rc = RTCrStoreCertFindBySubjectOrAltSubjectByRfc5280(pState->hKernelRootStore, pSubject, &Search);
375 AssertRCBreak(rc);
376 PCRTCRCERTCTX pCertCtx;
377 while ((pCertCtx = RTCrStoreCertSearchNext(pState->hKernelRootStore, &Search)) != NULL)
378 {
379 PCRTCRX509SUBJECTPUBLICKEYINFO pPubKeyInfo;
380 if (pCertCtx->pCert)
381 pPubKeyInfo = &pCertCtx->pCert->TbsCertificate.SubjectPublicKeyInfo;
382 else if (pCertCtx->pTaInfo)
383 pPubKeyInfo = &pCertCtx->pTaInfo->PubKey;
384 else
385 pPubKeyInfo = NULL;
386 if (RTCrX509SubjectPublicKeyInfo_Compare(pPubKeyInfo, pPublicKeyInfo) == 0)
387 cFound++;
388 RTCrCertCtxRelease(pCertCtx);
389 }
390
391 int rc2 = RTCrStoreCertSearchDestroy(pState->hKernelRootStore, &Search); AssertRC(rc2);
392 }
393 }
394 if (RT_SUCCESS(rc) && cFound == 0)
395 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE, "Not valid kernel code signature.");
396 if (RT_SUCCESS(rc) && cValid != 2)
397 RTMsgWarning("%u valid paths, expected 2", cValid);
398 }
399 }
400
401 return rc;
402}
403
404
405/** @callback_method_impl{FNRTLDRVALIDATESIGNEDDATA} */
406static DECLCALLBACK(int) VerifyExeCallback(RTLDRMOD hLdrMod, RTLDRSIGNATURETYPE enmSignature,
407 void const *pvSignature, size_t cbSignature,
408 PRTERRINFO pErrInfo, void *pvUser)
409{
410 VERIFYEXESTATE *pState = (VERIFYEXESTATE *)pvUser;
411 RT_NOREF_PV(hLdrMod); RT_NOREF_PV(cbSignature);
412
413 switch (enmSignature)
414 {
415 case RTLDRSIGNATURETYPE_PKCS7_SIGNED_DATA:
416 {
417 PCRTCRPKCS7CONTENTINFO pContentInfo = (PCRTCRPKCS7CONTENTINFO)pvSignature;
418
419 RTTIMESPEC ValidationTime;
420 RTTimeSpecSetSeconds(&ValidationTime, pState->uTimestamp);
421
422 /*
423 * Dump the signed data if so requested.
424 */
425 if (pState->cVerbose)
426 RTAsn1Dump(&pContentInfo->SeqCore.Asn1Core, 0, 0, RTStrmDumpPrintfV, g_pStdOut);
427
428
429 /*
430 * Do the actual verification. Will have to modify this so it takes
431 * the authenticode policies into account.
432 */
433 return RTCrPkcs7VerifySignedData(pContentInfo,
434 RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE_SIGNING_TIME_ONLY
435 | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_SIGNING_TIME_IF_PRESENT
436 | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_MS_TIMESTAMP_IF_PRESENT,
437 pState->hAdditionalStore, pState->hRootStore, &ValidationTime,
438 VerifyExecCertVerifyCallback, pState, pErrInfo);
439 }
440
441 default:
442 return RTErrInfoSetF(pErrInfo, VERR_NOT_SUPPORTED, "Unsupported signature type: %d", enmSignature);
443 }
444}
445
446/** Worker for HandleVerifyExe. */
447static RTEXITCODE HandleVerifyExeWorker(VERIFYEXESTATE *pState, const char *pszFilename, PRTERRINFOSTATIC pStaticErrInfo)
448{
449 /*
450 * Open the executable image and verify it.
451 */
452 RTLDRMOD hLdrMod;
453 int rc = RTLdrOpen(pszFilename, RTLDR_O_FOR_VALIDATION, pState->enmLdrArch, &hLdrMod);
454 if (RT_FAILURE(rc))
455 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error opening executable image '%s': %Rrc", pszFilename, rc);
456
457
458 rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &pState->uTimestamp, sizeof(pState->uTimestamp));
459 if (RT_SUCCESS(rc))
460 {
461 rc = RTLdrVerifySignature(hLdrMod, VerifyExeCallback, pState, RTErrInfoInitStatic(pStaticErrInfo));
462 if (RT_SUCCESS(rc))
463 RTMsgInfo("'%s' is valid.\n", pszFilename);
464 else if (rc == VERR_CR_X509_CPV_NOT_VALID_AT_TIME)
465 {
466 RTTIMESPEC Now;
467 pState->uTimestamp = RTTimeSpecGetSeconds(RTTimeNow(&Now));
468 rc = RTLdrVerifySignature(hLdrMod, VerifyExeCallback, pState, RTErrInfoInitStatic(pStaticErrInfo));
469 if (RT_SUCCESS(rc))
470 RTMsgInfo("'%s' is valid now, but not at link time.\n", pszFilename);
471 }
472 if (RT_FAILURE(rc))
473 RTMsgError("RTLdrVerifySignature failed on '%s': %Rrc - %s\n", pszFilename, rc, pStaticErrInfo->szMsg);
474 }
475 else
476 RTMsgError("RTLdrQueryProp/RTLDRPROP_TIMESTAMP_SECONDS failed on '%s': %Rrc\n", pszFilename, rc);
477
478 int rc2 = RTLdrClose(hLdrMod);
479 if (RT_FAILURE(rc2))
480 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTLdrClose failed: %Rrc\n", rc2);
481 if (RT_FAILURE(rc))
482 return rc != VERR_LDRVI_NOT_SIGNED ? RTEXITCODE_FAILURE : RTEXITCODE_SKIPPED;
483
484 return RTEXITCODE_SUCCESS;
485}
486
487
488static RTEXITCODE HandleVerifyExe(int cArgs, char **papszArgs)
489{
490 RTERRINFOSTATIC StaticErrInfo;
491
492 /* Note! This code does not try to clean up the crypto stores on failure.
493 This is intentional as the code is only expected to be used in a
494 one-command-per-process environment where we do exit() upon
495 returning from this function. */
496
497 /*
498 * Parse arguments.
499 */
500 static const RTGETOPTDEF s_aOptions[] =
501 {
502 { "--kernel", 'k', RTGETOPT_REQ_NOTHING },
503 { "--root", 'r', RTGETOPT_REQ_STRING },
504 { "--additional", 'a', RTGETOPT_REQ_STRING },
505 { "--add", 'a', RTGETOPT_REQ_STRING },
506 { "--type", 't', RTGETOPT_REQ_STRING },
507 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
508 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
509 };
510
511 VERIFYEXESTATE State =
512 {
513 NIL_RTCRSTORE, NIL_RTCRSTORE, NIL_RTCRSTORE, false, false,
514 VERIFYEXESTATE::kSignType_Windows, 0, RTLDRARCH_WHATEVER
515 };
516 int rc = RTCrStoreCreateInMem(&State.hRootStore, 0);
517 if (RT_SUCCESS(rc))
518 rc = RTCrStoreCreateInMem(&State.hKernelRootStore, 0);
519 if (RT_SUCCESS(rc))
520 rc = RTCrStoreCreateInMem(&State.hAdditionalStore, 0);
521 if (RT_FAILURE(rc))
522 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error creating in-memory certificate store: %Rrc", rc);
523
524 RTGETOPTSTATE GetState;
525 rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
526 AssertRCReturn(rc, RTEXITCODE_FAILURE);
527 RTGETOPTUNION ValueUnion;
528 int ch;
529 while ((ch = RTGetOpt(&GetState, &ValueUnion)) && ch != VINF_GETOPT_NOT_OPTION)
530 {
531 switch (ch)
532 {
533 case 'r': case 'a':
534 rc = RTCrStoreCertAddFromFile(ch == 'r' ? State.hRootStore : State.hAdditionalStore,
535 RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR,
536 ValueUnion.psz, RTErrInfoInitStatic(&StaticErrInfo));
537 if (RT_FAILURE(rc))
538 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error loading certificate '%s': %Rrc - %s",
539 ValueUnion.psz, rc, StaticErrInfo.szMsg);
540 if (RTErrInfoIsSet(&StaticErrInfo.Core))
541 RTMsgWarning("Warnings loading certificate '%s': %s", ValueUnion.psz, StaticErrInfo.szMsg);
542 break;
543
544 case 't':
545 if (!strcmp(ValueUnion.psz, "win") || !strcmp(ValueUnion.psz, "windows"))
546 State.enmSignType = VERIFYEXESTATE::kSignType_Windows;
547 else if (!strcmp(ValueUnion.psz, "osx") || !strcmp(ValueUnion.psz, "apple"))
548 State.enmSignType = VERIFYEXESTATE::kSignType_OSX;
549 else
550 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown signing type: '%s'", ValueUnion.psz);
551 break;
552
553 case 'k': State.fKernel = true; break;
554 case 'v': State.cVerbose++; break;
555 case 'q': State.cVerbose = 0; break;
556 case 'V': return HandleVersion(cArgs, papszArgs);
557 case 'h': return HelpVerifyExe(g_pStdOut, RTSIGNTOOLHELP_FULL);
558 default: return RTGetOptPrintError(ch, &ValueUnion);
559 }
560 }
561 if (ch != VINF_GETOPT_NOT_OPTION)
562 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
563
564 /*
565 * Populate the certificate stores according to the signing type.
566 */
567#ifdef VBOX
568 unsigned cSets = 0;
569 struct STSTORESET aSets[6];
570#endif
571
572 switch (State.enmSignType)
573 {
574 case VERIFYEXESTATE::kSignType_Windows:
575#ifdef VBOX
576 aSets[cSets].hStore = State.hRootStore;
577 aSets[cSets].paTAs = g_aSUPTimestampTAs;
578 aSets[cSets].cTAs = g_cSUPTimestampTAs;
579 cSets++;
580 aSets[cSets].hStore = State.hRootStore;
581 aSets[cSets].paTAs = g_aSUPSpcRootTAs;
582 aSets[cSets].cTAs = g_cSUPSpcRootTAs;
583 cSets++;
584 aSets[cSets].hStore = State.hRootStore;
585 aSets[cSets].paTAs = g_aSUPNtKernelRootTAs;
586 aSets[cSets].cTAs = g_cSUPNtKernelRootTAs;
587 cSets++;
588 aSets[cSets].hStore = State.hKernelRootStore;
589 aSets[cSets].paTAs = g_aSUPNtKernelRootTAs;
590 aSets[cSets].cTAs = g_cSUPNtKernelRootTAs;
591 cSets++;
592#endif
593 break;
594
595 case VERIFYEXESTATE::kSignType_OSX:
596 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Mac OS X executable signing is not implemented.");
597 }
598
599#ifdef VBOX
600 for (unsigned i = 0; i < cSets; i++)
601 for (unsigned j = 0; j < aSets[i].cTAs; j++)
602 {
603 rc = RTCrStoreCertAddEncoded(aSets[i].hStore, RTCRCERTCTX_F_ENC_TAF_DER, aSets[i].paTAs[j].pch,
604 aSets[i].paTAs[j].cb, RTErrInfoInitStatic(&StaticErrInfo));
605 if (RT_FAILURE(rc))
606 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTCrStoreCertAddEncoded failed (%u/%u): %s",
607 i, j, StaticErrInfo.szMsg);
608 }
609#endif
610
611 /*
612 * Do it.
613 */
614 RTEXITCODE rcExit;
615 for (;;)
616 {
617 rcExit = HandleVerifyExeWorker(&State, ValueUnion.psz, &StaticErrInfo);
618 if (rcExit != RTEXITCODE_SUCCESS)
619 break;
620
621 /*
622 * Next file
623 */
624 ch = RTGetOpt(&GetState, &ValueUnion);
625 if (ch == 0)
626 break;
627 if (ch != VINF_GETOPT_NOT_OPTION)
628 {
629 rcExit = RTGetOptPrintError(ch, &ValueUnion);
630 break;
631 }
632 }
633
634 /*
635 * Clean up.
636 */
637 uint32_t cRefs;
638 cRefs = RTCrStoreRelease(State.hRootStore); Assert(cRefs == 0);
639 cRefs = RTCrStoreRelease(State.hKernelRootStore); Assert(cRefs == 0);
640 cRefs = RTCrStoreRelease(State.hAdditionalStore); Assert(cRefs == 0);
641
642 return rcExit;
643}
644
645#endif /* !IPRT_IN_BUILD_TOOL */
646////
647#ifndef IPRT_IN_BUILD_TOOL
648
649/*
650 * The 'show-exe' command.
651 */
652static RTEXITCODE HelpShowExe(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
653{
654 RT_NOREF_PV(enmLevel);
655 RTStrmPrintf(pStrm,
656 "show-exe [--verbose|-v] [--quiet|-q] <exe1> [exe2 [..]]\n");
657 return RTEXITCODE_SUCCESS;
658}
659
660
661static int HandleShowExeWorkerPkcs7Decode(PSHOWEXEPKCS7 pThis)
662{
663 RTERRINFOSTATIC ErrInfo;
664 RTASN1CURSORPRIMARY PrimaryCursor;
665 RTAsn1CursorInitPrimary(&PrimaryCursor, pThis->pbBuf, (uint32_t)pThis->cbBuf, RTErrInfoInitStatic(&ErrInfo),
666 &g_RTAsn1DefaultAllocator, 0, "WinCert");
667
668 int rc = RTCrPkcs7ContentInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, &pThis->ContentInfo, "CI");
669 if (RT_SUCCESS(rc))
670 {
671 if (RTCrPkcs7ContentInfo_IsSignedData(&pThis->ContentInfo))
672 {
673 pThis->pSignedData = pThis->ContentInfo.u.pSignedData;
674
675 /*
676 * Decode the authenticode bits.
677 */
678 if (!strcmp(pThis->pSignedData->ContentInfo.ContentType.szObjId, RTCRSPCINDIRECTDATACONTENT_OID))
679 {
680 pThis->pIndData = pThis->pSignedData->ContentInfo.u.pIndirectDataContent;
681 Assert(pThis->pIndData);
682
683 /*
684 * Check that things add up.
685 */
686 rc = RTCrPkcs7SignedData_CheckSanity(pThis->pSignedData,
687 RTCRPKCS7SIGNEDDATA_SANITY_F_AUTHENTICODE
688 | RTCRPKCS7SIGNEDDATA_SANITY_F_ONLY_KNOWN_HASH
689 | RTCRPKCS7SIGNEDDATA_SANITY_F_SIGNING_CERT_PRESENT,
690 RTErrInfoInitStatic(&ErrInfo), "SD");
691 if (RT_FAILURE(rc))
692 RTMsgError("PKCS#7 sanity check failed for '%s': %Rrc - %s\n", pThis->pszFilename, rc, ErrInfo.szMsg);
693 if (RT_SUCCESS(rc))
694 {
695 rc = RTCrSpcIndirectDataContent_CheckSanityEx(pThis->pIndData,
696 pThis->pSignedData,
697 RTCRSPCINDIRECTDATACONTENT_SANITY_F_ONLY_KNOWN_HASH,
698 RTErrInfoInitStatic(&ErrInfo));
699 if (RT_FAILURE(rc))
700 RTMsgError("SPC indirect data content sanity check failed for '%s': %Rrc - %s\n",
701 pThis->pszFilename, rc, ErrInfo.szMsg);
702 }
703 }
704 else
705 RTMsgError("Unexpected the signed content in '%s': %s (expected %s)", pThis->pszFilename,
706 pThis->pSignedData->ContentInfo.ContentType.szObjId, RTCRSPCINDIRECTDATACONTENT_OID);
707 }
708 else
709 RTMsgError("PKCS#7 content is inside '%s' is not 'signedData': %s\n",
710 pThis->pszFilename, pThis->ContentInfo.ContentType.szObjId);
711 }
712 else
713 RTMsgError("RTCrPkcs7ContentInfo_DecodeAsn1 failed on '%s': %Rrc - %s\n", pThis->pszFilename, rc, ErrInfo.szMsg);
714 return rc;
715}
716
717
718static int HandleShowExeWorkerPkcs7DisplayAttrib(PSHOWEXEPKCS7 pThis, size_t offPrefix, PCRTCRPKCS7ATTRIBUTE pAttr)
719{
720 int rc = RTAsn1QueryObjIdName(&pAttr->Type, pThis->szTmp, sizeof(pThis->szTmp));
721 if (RT_SUCCESS(rc))
722 RTPrintf("%s%s (%s)\n", pThis->szPrefix, pThis->szTmp, pAttr->Type.szObjId);
723 else
724 RTPrintf("%s%s\n", pThis->szPrefix, pAttr->Type.szObjId);
725
726 rc = VINF_SUCCESS;
727 switch (pAttr->enmType)
728 {
729 case RTCRPKCS7ATTRIBUTETYPE_UNKNOWN:
730 if (pAttr->uValues.pCores->cItems <= 1)
731 RTPrintf("%s %u bytes\n", pThis->szPrefix,pAttr->uValues.pCores->SetCore.Asn1Core.cb);
732 else
733 RTPrintf("%s %u bytes divided by %u items\n", pThis->szPrefix, pAttr->uValues.pCores->SetCore.Asn1Core.cb, pAttr->uValues.pCores->cItems);
734 for (unsigned i = 0; i < pAttr->uValues.pCores->cItems; i++)
735 {
736
737 }
738 break;
739
740 /** Object IDs, use pObjIds. */
741 case RTCRPKCS7ATTRIBUTETYPE_OBJ_IDS:
742 /** Octet strings, use pOctetStrings. */
743 case RTCRPKCS7ATTRIBUTETYPE_OCTET_STRINGS:
744 /** Counter signatures (PKCS \#9), use pCounterSignatures. */
745 case RTCRPKCS7ATTRIBUTETYPE_COUNTER_SIGNATURES:
746 /** Signing time (PKCS \#9), use pSigningTime. */
747 case RTCRPKCS7ATTRIBUTETYPE_SIGNING_TIME:
748 /** Microsoft timestamp info (RFC-3161) signed data, use pContentInfo. */
749 case RTCRPKCS7ATTRIBUTETYPE_MS_TIMESTAMP:
750 case RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE:
751 if (pAttr->uValues.pContentInfos->cItems > 1)
752 RTPrintf("%s%u nested signatures, %u bytes in total\n", pThis->szPrefix,
753 pAttr->uValues.pContentInfos->cItems, pAttr->uValues.pContentInfos->SetCore.Asn1Core.cb);
754 for (unsigned i = 0; i < pAttr->uValues.pContentInfos->cItems; i++)
755 {
756 size_t offPrefix2 = offPrefix;
757 if (pAttr->uValues.pContentInfos->cItems > 1)
758 offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, "NestedSig[%u]: ", i);
759 else
760 offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, " ");
761 // offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, "NestedSig: ", i);
762 PCRTCRPKCS7CONTENTINFO pContentInfo = &pAttr->uValues.pContentInfos->paItems[i];
763 int rc2;
764 if (RTCrPkcs7ContentInfo_IsSignedData(&pThis->ContentInfo))
765 rc2 = HandleShowExeWorkerPkcs7Display(pThis, pContentInfo->u.pSignedData, offPrefix2);
766 else
767 rc2 = RTMsgErrorRc(VERR_ASN1_UNEXPECTED_OBJ_ID, "%sPKCS#7 content in nested signature is not 'signedData': %s",
768 pThis->szPrefix, pContentInfo->ContentType.szObjId);
769 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
770 rc = rc2;
771 }
772 break;
773
774 case RTCRPKCS7ATTRIBUTETYPE_INVALID:
775 RTPrintf("%sINVALID!\n", pThis->szPrefix);
776 break;
777 case RTCRPKCS7ATTRIBUTETYPE_NOT_PRESENT:
778 RTPrintf("%sNOT PRESENT!\n", pThis->szPrefix);
779 break;
780 default:
781 RTPrintf("%senmType=%d!\n", pThis->szPrefix, pAttr->enmType);
782 break;
783 }
784 //if (RTAsn1ObjId_CompareWithString(&pAttr->Type, ))
785 //{
786 //}
787 return rc;
788}
789
790
791static int HandleShowExeWorkerPkcs7Display(PSHOWEXEPKCS7 pThis, PRTCRPKCS7SIGNEDDATA pSignedData, size_t offPrefix)
792{
793 pThis->szPrefix[offPrefix] = '\0';
794
795 /* Display list of signing algorithms. */
796 RTPrintf("%sDigestAlgorithms: ", pThis->szPrefix);
797 for (unsigned i = 0; i < pSignedData->DigestAlgorithms.cItems; i++)
798 {
799 PCRTCRX509ALGORITHMIDENTIFIER pAlgoId = &pSignedData->DigestAlgorithms.paItems[i];
800 const char *pszDigestType = RTCrDigestTypeToName(RTCrX509AlgorithmIdentifier_QueryDigestType(pAlgoId));
801 if (!pszDigestType)
802 pszDigestType = pAlgoId->Algorithm.szObjId;
803 RTPrintf(i == 0 ? "%s" : ", %s", pszDigestType);
804 }
805 RTPrintf("\n");
806
807 /* Display certificates (Certificates). */
808
809 /* Show signatures (SignerInfos). */
810 unsigned const cSigInfos = pSignedData->SignerInfos.cItems;
811 for (unsigned i = 0; i < cSigInfos; i++)
812 {
813 PRTCRPKCS7SIGNERINFO pSigInfo = &pSignedData->SignerInfos.paItems[i];
814 size_t offPrefix2 = offPrefix;
815 if (cSigInfos != 1)
816 offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, "SignerInfo[%u]: ", i);
817
818 int rc = RTAsn1Integer_ToString(&pSigInfo->IssuerAndSerialNumber.SerialNumber,
819 pThis->szTmp, sizeof(pThis->szTmp), 0 /*fFlags*/, NULL);
820 if (RT_FAILURE(rc))
821 RTStrPrintf(pThis->szTmp, sizeof(pThis->szTmp), "%Rrc", rc);
822 RTPrintf("%s Serial No: %s\n", pThis->szPrefix, pThis->szTmp);
823
824 rc = RTCrX509Name_FormatAsString(&pSigInfo->IssuerAndSerialNumber.Name, pThis->szTmp, sizeof(pThis->szTmp), NULL);
825 if (RT_FAILURE(rc))
826 RTStrPrintf(pThis->szTmp, sizeof(pThis->szTmp), "%Rrc", rc);
827 RTPrintf("%s Issuer: %s\n", pThis->szPrefix, pThis->szTmp);
828
829 const char *pszType = RTCrDigestTypeToName(RTCrX509AlgorithmIdentifier_QueryDigestType(&pSigInfo->DigestAlgorithm));
830 if (!pszType)
831 pszType = pSigInfo->DigestAlgorithm.Algorithm.szObjId;
832 RTPrintf("%s Digest Algorithm: %s\n", pThis->szPrefix, pszType);
833
834 rc = RTAsn1QueryObjIdName(&pSigInfo->DigestEncryptionAlgorithm.Algorithm, pThis->szTmp, sizeof(pThis->szTmp));
835 if (RT_SUCCESS(rc))
836 pszType = pThis->szTmp;
837 else
838 pszType = pSigInfo->DigestAlgorithm.Algorithm.szObjId;
839 RTPrintf("%sDigest Encryption Algorithm: %s\n", pThis->szPrefix, pszType);
840
841 if (pSigInfo->AuthenticatedAttributes.cItems == 0)
842 RTPrintf("%s Authenticated Attributes: none\n", pThis->szPrefix);
843 else
844 {
845 RTPrintf("%s Authenticated Attributes: %u item%s\n", pThis->szPrefix,
846 pSigInfo->AuthenticatedAttributes.cItems, pSigInfo->AuthenticatedAttributes.cItems > 1 ? "s" : "");
847 for (unsigned j = 0; j < pSigInfo->AuthenticatedAttributes.cItems; j++)
848 {
849 PRTCRPKCS7ATTRIBUTE pAttr = &pSigInfo->AuthenticatedAttributes.paItems[j];
850 size_t offPrefix3 = offPrefix2 + RTStrPrintf(&pThis->szPrefix[offPrefix2], sizeof(pThis->szPrefix) - offPrefix2,
851 " AuthAttrib[%u]: ", j);
852 HandleShowExeWorkerPkcs7DisplayAttrib(pThis, offPrefix3, pAttr);
853 }
854 pThis->szPrefix[offPrefix2] = '\0';
855 }
856
857 if (pSigInfo->UnauthenticatedAttributes.cItems == 0)
858 RTPrintf("%s Unauthenticated Attributes: none\n", pThis->szPrefix);
859 else
860 {
861 RTPrintf("%s Unauthenticated Attributes: %u item%s\n", pThis->szPrefix,
862 pSigInfo->UnauthenticatedAttributes.cItems, pSigInfo->UnauthenticatedAttributes.cItems > 1 ? "s" : "");
863 for (unsigned j = 0; j < pSigInfo->UnauthenticatedAttributes.cItems; j++)
864 {
865 PRTCRPKCS7ATTRIBUTE pAttr = &pSigInfo->UnauthenticatedAttributes.paItems[j];
866 size_t offPrefix3 = offPrefix2 + RTStrPrintf(&pThis->szPrefix[offPrefix2], sizeof(pThis->szPrefix) - offPrefix2,
867 " UnauthAttrib[%u]: ", j);
868 HandleShowExeWorkerPkcs7DisplayAttrib(pThis, offPrefix3, pAttr);
869 }
870 pThis->szPrefix[offPrefix2] = '\0';
871 }
872
873#if 0
874 /** Authenticated attributes, optional [0].
875 * @todo Check how other producers formats this. The microsoft one does not
876 * have explicit tags, but combines it with the SET OF. */
877 RTCRPKCS7ATTRIBUTES AuthenticatedAttributes;
878
879 /** The encrypted digest. */
880 RTASN1OCTETSTRING EncryptedDigest;
881 /** Unauthenticated attributes, optional [1].
882 * @todo Check how other producers formats this. The microsoft one does not
883 * have explicit tags, but combines it with the SET OF. */
884 RTCRPKCS7ATTRIBUTES UnauthenticatedAttributes;
885#endif
886 }
887 pThis->szPrefix[offPrefix] = '\0';
888
889 return VINF_SUCCESS;
890}
891
892
893/**
894 * Shows the signing info for one executable.
895 *
896 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE on failure.
897 * @param pszFilename The path to the executable.
898 * @param cVerbosity The verbosity level.
899 * @param enmLdrArch Sub image selector.
900 */
901static RTEXITCODE HandleShowExeWorker(const char *pszFilename, unsigned cVerbosity, RTLDRARCH enmLdrArch)
902{
903 /*
904 * Open the image and check if it's signed.
905 */
906 RTLDRMOD hLdrMod;
907 int rc = RTLdrOpen(pszFilename, RTLDR_O_FOR_VALIDATION, enmLdrArch, &hLdrMod);
908 if (RT_FAILURE(rc))
909 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error opening executable image '%s': %Rrc", pszFilename, rc);
910
911 bool fIsSigned = false;
912 rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_IS_SIGNED, &fIsSigned, sizeof(fIsSigned));
913 if (RT_SUCCESS(rc) && fIsSigned)
914 {
915 /*
916 * Query the PKCS#7 data (assuming M$ style signing) and hand it to a worker.
917 */
918 size_t cbActual = 0;
919 size_t cbBuf = _64K;
920 void *pvBuf = RTMemAllocZ(cbBuf);
921 if (pvBuf)
922 {
923 rc = RTLdrQueryPropEx(hLdrMod, RTLDRPROP_PKCS7_SIGNED_DATA, NULL /*pvBits*/, pvBuf, cbBuf, &cbActual);
924 if (rc == VERR_BUFFER_OVERFLOW)
925 {
926 RTMemFree(pvBuf);
927 cbBuf = cbActual;
928 pvBuf = RTMemAllocZ(cbActual);
929 if (pvBuf)
930 rc = RTLdrQueryPropEx(hLdrMod, RTLDRPROP_PKCS7_SIGNED_DATA, NULL /*pvBits*/, pvBuf, cbBuf, &cbActual);
931 else
932 rc = VERR_NO_MEMORY;
933 }
934 }
935 else
936 rc = VERR_NO_MEMORY;
937 if (RT_SUCCESS(rc))
938 {
939 SHOWEXEPKCS7 This;
940 RT_ZERO(This);
941 This.pbBuf = (uint8_t const *)pvBuf;
942 This.cbBuf = cbActual;
943 This.cVerbosity = cVerbosity;
944 This.pszFilename = pszFilename;
945 This.hLdrMod = hLdrMod;
946 rc = HandleShowExeWorkerPkcs7Decode(&This);
947 if (RT_SUCCESS(rc))
948 rc = HandleShowExeWorkerPkcs7Display(&This, This.pSignedData, 0);
949 RTCrPkcs7ContentInfo_Delete(&This.ContentInfo);
950 }
951 else
952 RTMsgError("RTLdrQueryPropEx/RTLDRPROP_PKCS7_SIGNED_DATA failed on '%s': %Rrc\n", pszFilename, rc);
953 RTMemFree(pvBuf);
954 }
955 else if (RT_SUCCESS(rc))
956 RTMsgInfo("'%s': not signed\n", pszFilename);
957 else
958 RTMsgError("RTLdrQueryProp/RTLDRPROP_IS_SIGNED failed on '%s': %Rrc\n", pszFilename, rc);
959
960 int rc2 = RTLdrClose(hLdrMod);
961 if (RT_FAILURE(rc2))
962 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTLdrClose failed: %Rrc\n", rc2);
963 if (RT_FAILURE(rc))
964 return rc != VERR_LDRVI_NOT_SIGNED ? RTEXITCODE_FAILURE : RTEXITCODE_SKIPPED;
965
966 return RTEXITCODE_SUCCESS;
967}
968
969static RTEXITCODE HandleShowExe(int cArgs, char **papszArgs)
970{
971 /* Note! This code does not try to clean up the crypto stores on failure.
972 This is intentional as the code is only expected to be used in a
973 one-command-per-process environment where we do exit() upon
974 returning from this function. */
975
976 /*
977 * Parse arguments.
978 */
979 static const RTGETOPTDEF s_aOptions[] =
980 {
981 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
982 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
983 };
984
985 unsigned cVerbose = 0;
986 RTLDRARCH enmLdrArch = RTLDRARCH_WHATEVER;
987
988 RTGETOPTSTATE GetState;
989 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
990 AssertRCReturn(rc, RTEXITCODE_FAILURE);
991 RTGETOPTUNION ValueUnion;
992 int ch;
993 while ((ch = RTGetOpt(&GetState, &ValueUnion)) && ch != VINF_GETOPT_NOT_OPTION)
994 {
995 switch (ch)
996 {
997 case 'v': cVerbose++; break;
998 case 'q': cVerbose = 0; break;
999 case 'V': return HandleVersion(cArgs, papszArgs);
1000 case 'h': return HelpShowExe(g_pStdOut, RTSIGNTOOLHELP_FULL);
1001 default: return RTGetOptPrintError(ch, &ValueUnion);
1002 }
1003 }
1004 if (ch != VINF_GETOPT_NOT_OPTION)
1005 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
1006
1007 /*
1008 * Do it.
1009 */
1010 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1011 do
1012 {
1013 RTEXITCODE rcExitThis = HandleShowExeWorker(ValueUnion.psz, cVerbose, enmLdrArch);
1014 if (rcExitThis != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
1015 rcExit = rcExitThis;
1016 } while ((ch = RTGetOpt(&GetState, &ValueUnion)) == VINF_GETOPT_NOT_OPTION);
1017 if (ch != 0)
1018 return RTGetOptPrintError(ch, &ValueUnion);
1019
1020 return rcExit;
1021}
1022
1023#endif /* !IPRT_IN_BUILD_TOOL */
1024////
1025
1026/*
1027 * The 'make-tainfo' command.
1028 */
1029static RTEXITCODE HelpMakeTaInfo(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
1030{
1031 RT_NOREF_PV(enmLevel);
1032 RTStrmPrintf(pStrm,
1033 "make-tainfo [--verbose|--quiet] [--cert <cert.der>] [-o|--output] <tainfo.der>\n");
1034 return RTEXITCODE_SUCCESS;
1035}
1036
1037
1038typedef struct MAKETAINFOSTATE
1039{
1040 int cVerbose;
1041 const char *pszCert;
1042 const char *pszOutput;
1043} MAKETAINFOSTATE;
1044
1045
1046/** @callback_method_impl{FNRTASN1ENCODEWRITER} */
1047static DECLCALLBACK(int) handleMakeTaInfoWriter(const void *pvBuf, size_t cbToWrite, void *pvUser, PRTERRINFO pErrInfo)
1048{
1049 RT_NOREF_PV(pErrInfo);
1050 return RTStrmWrite((PRTSTREAM)pvUser, pvBuf, cbToWrite);
1051}
1052
1053
1054static RTEXITCODE HandleMakeTaInfo(int cArgs, char **papszArgs)
1055{
1056 /*
1057 * Parse arguments.
1058 */
1059 static const RTGETOPTDEF s_aOptions[] =
1060 {
1061 { "--cert", 'c', RTGETOPT_REQ_STRING },
1062 { "--output", 'o', RTGETOPT_REQ_STRING },
1063 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
1064 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
1065 };
1066
1067 MAKETAINFOSTATE State = { 0, NULL, NULL };
1068
1069 RTGETOPTSTATE GetState;
1070 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1071 AssertRCReturn(rc, RTEXITCODE_FAILURE);
1072 RTGETOPTUNION ValueUnion;
1073 int ch;
1074 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
1075 {
1076 switch (ch)
1077 {
1078 case 'c':
1079 if (State.pszCert)
1080 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The --cert option can only be used once.");
1081 State.pszCert = ValueUnion.psz;
1082 break;
1083
1084 case 'o':
1085 case VINF_GETOPT_NOT_OPTION:
1086 if (State.pszOutput)
1087 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Multiple output files specified.");
1088 State.pszOutput = ValueUnion.psz;
1089 break;
1090
1091 case 'v': State.cVerbose++; break;
1092 case 'q': State.cVerbose = 0; break;
1093 case 'V': return HandleVersion(cArgs, papszArgs);
1094 case 'h': return HelpMakeTaInfo(g_pStdOut, RTSIGNTOOLHELP_FULL);
1095 default: return RTGetOptPrintError(ch, &ValueUnion);
1096 }
1097 }
1098 if (!State.pszCert)
1099 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No input certificate was specified.");
1100 if (!State.pszOutput)
1101 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No output file was specified.");
1102
1103 /*
1104 * Read the certificate.
1105 */
1106 RTERRINFOSTATIC StaticErrInfo;
1107 RTCRX509CERTIFICATE Certificate;
1108 rc = RTCrX509Certificate_ReadFromFile(&Certificate, State.pszCert, 0, &g_RTAsn1DefaultAllocator,
1109 RTErrInfoInitStatic(&StaticErrInfo));
1110 if (RT_FAILURE(rc))
1111 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error reading certificate from %s: %Rrc - %s",
1112 State.pszCert, rc, StaticErrInfo.szMsg);
1113 /*
1114 * Construct the trust anchor information.
1115 */
1116 RTCRTAFTRUSTANCHORINFO TrustAnchor;
1117 rc = RTCrTafTrustAnchorInfo_Init(&TrustAnchor, &g_RTAsn1DefaultAllocator);
1118 if (RT_SUCCESS(rc))
1119 {
1120 /* Public key. */
1121 Assert(RTCrX509SubjectPublicKeyInfo_IsPresent(&TrustAnchor.PubKey));
1122 RTCrX509SubjectPublicKeyInfo_Delete(&TrustAnchor.PubKey);
1123 rc = RTCrX509SubjectPublicKeyInfo_Clone(&TrustAnchor.PubKey, &Certificate.TbsCertificate.SubjectPublicKeyInfo,
1124 &g_RTAsn1DefaultAllocator);
1125 if (RT_FAILURE(rc))
1126 RTMsgError("RTCrX509SubjectPublicKeyInfo_Clone failed: %Rrc", rc);
1127 RTAsn1Core_ResetImplict(RTCrX509SubjectPublicKeyInfo_GetAsn1Core(&TrustAnchor.PubKey)); /* temporary hack. */
1128
1129 /* Key Identifier. */
1130 PCRTASN1OCTETSTRING pKeyIdentifier = NULL;
1131 if (Certificate.TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_SUBJECT_KEY_IDENTIFIER)
1132 pKeyIdentifier = Certificate.TbsCertificate.T3.pSubjectKeyIdentifier;
1133 else if ( (Certificate.TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_AUTHORITY_KEY_IDENTIFIER)
1134 && RTCrX509Certificate_IsSelfSigned(&Certificate)
1135 && RTAsn1OctetString_IsPresent(&Certificate.TbsCertificate.T3.pAuthorityKeyIdentifier->KeyIdentifier) )
1136 pKeyIdentifier = &Certificate.TbsCertificate.T3.pAuthorityKeyIdentifier->KeyIdentifier;
1137 else if ( (Certificate.TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_OLD_AUTHORITY_KEY_IDENTIFIER)
1138 && RTCrX509Certificate_IsSelfSigned(&Certificate)
1139 && RTAsn1OctetString_IsPresent(&Certificate.TbsCertificate.T3.pOldAuthorityKeyIdentifier->KeyIdentifier) )
1140 pKeyIdentifier = &Certificate.TbsCertificate.T3.pOldAuthorityKeyIdentifier->KeyIdentifier;
1141 if (pKeyIdentifier && pKeyIdentifier->Asn1Core.cb > 0)
1142 {
1143 Assert(RTAsn1OctetString_IsPresent(&TrustAnchor.KeyIdentifier));
1144 RTAsn1OctetString_Delete(&TrustAnchor.KeyIdentifier);
1145 rc = RTAsn1OctetString_Clone(&TrustAnchor.KeyIdentifier, pKeyIdentifier, &g_RTAsn1DefaultAllocator);
1146 if (RT_FAILURE(rc))
1147 RTMsgError("RTAsn1OctetString_Clone failed: %Rrc", rc);
1148 RTAsn1Core_ResetImplict(RTAsn1OctetString_GetAsn1Core(&TrustAnchor.KeyIdentifier)); /* temporary hack. */
1149 }
1150 else
1151 RTMsgWarning("No key identifier found or has zero length.");
1152
1153 /* Subject */
1154 if (RT_SUCCESS(rc))
1155 {
1156 Assert(!RTCrTafCertPathControls_IsPresent(&TrustAnchor.CertPath));
1157 rc = RTCrTafCertPathControls_Init(&TrustAnchor.CertPath, &g_RTAsn1DefaultAllocator);
1158 if (RT_SUCCESS(rc))
1159 {
1160 Assert(RTCrX509Name_IsPresent(&TrustAnchor.CertPath.TaName));
1161 RTCrX509Name_Delete(&TrustAnchor.CertPath.TaName);
1162 rc = RTCrX509Name_Clone(&TrustAnchor.CertPath.TaName, &Certificate.TbsCertificate.Subject,
1163 &g_RTAsn1DefaultAllocator);
1164 if (RT_SUCCESS(rc))
1165 {
1166 RTAsn1Core_ResetImplict(RTCrX509Name_GetAsn1Core(&TrustAnchor.CertPath.TaName)); /* temporary hack. */
1167 rc = RTCrX509Name_RecodeAsUtf8(&TrustAnchor.CertPath.TaName, &g_RTAsn1DefaultAllocator);
1168 if (RT_FAILURE(rc))
1169 RTMsgError("RTCrX509Name_RecodeAsUtf8 failed: %Rrc", rc);
1170 }
1171 else
1172 RTMsgError("RTCrX509Name_Clone failed: %Rrc", rc);
1173 }
1174 else
1175 RTMsgError("RTCrTafCertPathControls_Init failed: %Rrc", rc);
1176 }
1177
1178 /* Check that what we've constructed makes some sense. */
1179 if (RT_SUCCESS(rc))
1180 {
1181 rc = RTCrTafTrustAnchorInfo_CheckSanity(&TrustAnchor, 0, RTErrInfoInitStatic(&StaticErrInfo), "TAI");
1182 if (RT_FAILURE(rc))
1183 RTMsgError("RTCrTafTrustAnchorInfo_CheckSanity failed: %Rrc - %s", rc, StaticErrInfo.szMsg);
1184 }
1185
1186 if (RT_SUCCESS(rc))
1187 {
1188 /*
1189 * Encode it and write it to the output file.
1190 */
1191 uint32_t cbEncoded;
1192 rc = RTAsn1EncodePrepare(RTCrTafTrustAnchorInfo_GetAsn1Core(&TrustAnchor), RTASN1ENCODE_F_DER, &cbEncoded,
1193 RTErrInfoInitStatic(&StaticErrInfo));
1194 if (RT_SUCCESS(rc))
1195 {
1196 if (State.cVerbose >= 1)
1197 RTAsn1Dump(RTCrTafTrustAnchorInfo_GetAsn1Core(&TrustAnchor), 0, 0, RTStrmDumpPrintfV, g_pStdOut);
1198
1199 PRTSTREAM pStrm;
1200 rc = RTStrmOpen(State.pszOutput, "wb", &pStrm);
1201 if (RT_SUCCESS(rc))
1202 {
1203 rc = RTAsn1EncodeWrite(RTCrTafTrustAnchorInfo_GetAsn1Core(&TrustAnchor), RTASN1ENCODE_F_DER,
1204 handleMakeTaInfoWriter, pStrm, RTErrInfoInitStatic(&StaticErrInfo));
1205 if (RT_SUCCESS(rc))
1206 {
1207 rc = RTStrmClose(pStrm);
1208 if (RT_SUCCESS(rc))
1209 RTMsgInfo("Successfully wrote TrustedAnchorInfo to '%s'.", State.pszOutput);
1210 else
1211 RTMsgError("RTStrmClose failed: %Rrc", rc);
1212 }
1213 else
1214 {
1215 RTMsgError("RTAsn1EncodeWrite failed: %Rrc - %s", rc, StaticErrInfo.szMsg);
1216 RTStrmClose(pStrm);
1217 }
1218 }
1219 else
1220 RTMsgError("Error opening '%s' for writing: %Rrcs", State.pszOutput, rc);
1221 }
1222 else
1223 RTMsgError("RTAsn1EncodePrepare failed: %Rrc - %s", rc, StaticErrInfo.szMsg);
1224 }
1225
1226 RTCrTafTrustAnchorInfo_Delete(&TrustAnchor);
1227 }
1228 else
1229 RTMsgError("RTCrTafTrustAnchorInfo_Init failed: %Rrc", rc);
1230
1231 RTCrX509Certificate_Delete(&Certificate);
1232 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1233}
1234
1235
1236
1237/*
1238 * The 'version' command.
1239 */
1240static RTEXITCODE HelpVersion(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
1241{
1242 RT_NOREF_PV(enmLevel);
1243 RTStrmPrintf(pStrm, "version\n");
1244 return RTEXITCODE_SUCCESS;
1245}
1246
1247static RTEXITCODE HandleVersion(int cArgs, char **papszArgs)
1248{
1249 RT_NOREF_PV(cArgs); RT_NOREF_PV(papszArgs);
1250#ifndef IN_BLD_PROG /* RTBldCfgVersion or RTBldCfgRevision in build time IPRT lib. */
1251 RTPrintf("%s\n", RTBldCfgVersion());
1252 return RTEXITCODE_SUCCESS;
1253#else
1254 return RTEXITCODE_FAILURE;
1255#endif
1256}
1257
1258
1259
1260/**
1261 * Command mapping.
1262 */
1263static struct
1264{
1265 /** The command. */
1266 const char *pszCmd;
1267 /**
1268 * Handle the command.
1269 * @returns Program exit code.
1270 * @param cArgs Number of arguments.
1271 * @param papszArgs The argument vector, starting with the command name.
1272 */
1273 RTEXITCODE (*pfnHandler)(int cArgs, char **papszArgs);
1274 /**
1275 * Produce help.
1276 * @returns RTEXITCODE_SUCCESS to simplify handling '--help' in the handler.
1277 * @param pStrm Where to send help text.
1278 * @param enmLevel The level of the help information.
1279 */
1280 RTEXITCODE (*pfnHelp)(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel);
1281}
1282/** Mapping commands to handler and helper functions. */
1283const g_aCommands[] =
1284{
1285 { "extract-exe-signer-cert", HandleExtractExeSignerCert, HelpExtractExeSignerCert },
1286#ifndef IPRT_IN_BUILD_TOOL
1287 { "verify-exe", HandleVerifyExe, HelpVerifyExe },
1288 { "show-exe", HandleShowExe, HelpShowExe },
1289#endif
1290 { "make-tainfo", HandleMakeTaInfo, HelpMakeTaInfo },
1291 { "help", HandleHelp, HelpHelp },
1292 { "--help", HandleHelp, NULL },
1293 { "-h", HandleHelp, NULL },
1294 { "version", HandleVersion, HelpVersion },
1295 { "--version", HandleVersion, NULL },
1296 { "-V", HandleVersion, NULL },
1297};
1298
1299
1300/*
1301 * The 'help' command.
1302 */
1303static RTEXITCODE HelpHelp(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
1304{
1305 RT_NOREF_PV(enmLevel);
1306 RTStrmPrintf(pStrm, "help [cmd-patterns]\n");
1307 return RTEXITCODE_SUCCESS;
1308}
1309
1310static RTEXITCODE HandleHelp(int cArgs, char **papszArgs)
1311{
1312 RTSIGNTOOLHELP enmLevel = cArgs <= 1 ? RTSIGNTOOLHELP_USAGE : RTSIGNTOOLHELP_FULL;
1313 uint32_t cShowed = 0;
1314 for (uint32_t iCmd = 0; iCmd < RT_ELEMENTS(g_aCommands); iCmd++)
1315 {
1316 if (g_aCommands[iCmd].pfnHelp)
1317 {
1318 bool fShow = false;
1319 if (cArgs <= 1)
1320 fShow = true;
1321 else
1322 {
1323 for (int iArg = 1; iArg < cArgs; iArg++)
1324 if (RTStrSimplePatternMultiMatch(papszArgs[iArg], RTSTR_MAX, g_aCommands[iCmd].pszCmd, RTSTR_MAX, NULL))
1325 {
1326 fShow = true;
1327 break;
1328 }
1329 }
1330 if (fShow)
1331 {
1332 g_aCommands[iCmd].pfnHelp(g_pStdOut, enmLevel);
1333 cShowed++;
1334 }
1335 }
1336 }
1337 return cShowed ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1338}
1339
1340
1341
1342int main(int argc, char **argv)
1343{
1344 int rc = RTR3InitExe(argc, &argv, 0);
1345 if (RT_FAILURE(rc))
1346 return RTMsgInitFailure(rc);
1347
1348 /*
1349 * Parse global arguments.
1350 */
1351 int iArg = 1;
1352 /* none presently. */
1353
1354 /*
1355 * Command dispatcher.
1356 */
1357 if (iArg < argc)
1358 {
1359 const char *pszCmd = argv[iArg];
1360 uint32_t i = RT_ELEMENTS(g_aCommands);
1361 while (i-- > 0)
1362 if (!strcmp(g_aCommands[i].pszCmd, pszCmd))
1363 return g_aCommands[i].pfnHandler(argc - iArg, &argv[iArg]);
1364 RTMsgError("Unknown command '%s'.", pszCmd);
1365 }
1366 else
1367 RTMsgError("No command given. (try --help)");
1368
1369 return RTEXITCODE_SYNTAX;
1370}
1371
1372
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