VirtualBox

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

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

RTSignTool: Don't add the dummy timestamp certificate we feed to OpenSSL when using a certificate with a private key in a windows crypto store. Cleaned up that real vs dummy cert thing a little better. bugref:8691

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 254.9 KB
Line 
1/* $Id: RTSignTool.cpp 95690 2022-07-18 00:59:34Z 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#ifdef RT_OS_WINDOWS
45# include <iprt/utf16.h>
46#endif
47#include <iprt/uuid.h>
48#include <iprt/zero.h>
49#include <iprt/formats/asn1.h>
50#include <iprt/formats/mach-o.h>
51#ifndef RT_OS_WINDOWS
52# include <iprt/formats/pecoff.h>
53#endif
54#include <iprt/crypto/applecodesign.h>
55#include <iprt/crypto/digest.h>
56#include <iprt/crypto/key.h>
57#include <iprt/crypto/x509.h>
58#include <iprt/crypto/pkcs7.h>
59#include <iprt/crypto/store.h>
60#include <iprt/crypto/spc.h>
61#include <iprt/crypto/tsp.h>
62#include <iprt/cpp/ministring.h>
63#ifdef VBOX
64# include <VBox/sup.h> /* Certificates */
65#endif
66#ifdef RT_OS_WINDOWS
67# include <iprt/win/windows.h>
68# include <iprt/win/imagehlp.h>
69# include <wincrypt.h>
70# include <ncrypt.h>
71#endif
72#include "internal/ldr.h" /* for IMAGE_XX_SIGNATURE defines */
73
74
75/*********************************************************************************************************************************
76* Defined Constants And Macros *
77*********************************************************************************************************************************/
78#define OPT_OFF_CERT_FILE 0 /**< signtool /f file */
79#define OPT_OFF_CERT_SHA1 1 /**< signtool /sha1 thumbprint */
80#define OPT_OFF_CERT_SUBJECT 2 /**< signtool /n name */
81#define OPT_OFF_CERT_STORE 3 /**< signtool /s store */
82#define OPT_OFF_CERT_STORE_MACHINE 4 /**< signtool /sm */
83#define OPT_OFF_KEY_FILE 5 /**< no signtool equivalent, other than maybe /f. */
84#define OPT_OFF_KEY_PASSWORD 6 /**< signtool /p pass */
85#define OPT_OFF_KEY_PASSWORD_FILE 7 /**< no signtool equivalent. */
86#define OPT_OFF_KEY_NAME 8 /**< signtool /kc name */
87#define OPT_OFF_KEY_PROVIDER 9 /**< signtool /csp name (CSP = cryptographic service provider) */
88
89#define OPT_CERT_KEY_SWITCH_CASES(a_Instance, a_uBase, a_chOpt, a_ValueUnion, a_rcExit) \
90 case (a_uBase) + OPT_OFF_CERT_FILE: \
91 case (a_uBase) + OPT_OFF_CERT_SHA1: \
92 case (a_uBase) + OPT_OFF_CERT_SUBJECT: \
93 case (a_uBase) + OPT_OFF_CERT_STORE: \
94 case (a_uBase) + OPT_OFF_CERT_STORE_MACHINE: \
95 case (a_uBase) + OPT_OFF_KEY_FILE: \
96 case (a_uBase) + OPT_OFF_KEY_PASSWORD: \
97 case (a_uBase) + OPT_OFF_KEY_PASSWORD_FILE: \
98 case (a_uBase) + OPT_OFF_KEY_NAME: \
99 case (a_uBase) + OPT_OFF_KEY_PROVIDER: \
100 a_rcExit = a_Instance.handleOption((a_chOpt) - (a_uBase), &(a_ValueUnion)); \
101 break
102
103#define OPT_CERT_KEY_GETOPTDEF_ENTRIES(a_szPrefix, a_uBase) \
104 { a_szPrefix "cert-file", (a_uBase) + OPT_OFF_CERT_FILE, RTGETOPT_REQ_STRING }, \
105 { a_szPrefix "cert-sha1", (a_uBase) + OPT_OFF_CERT_SHA1, RTGETOPT_REQ_STRING }, \
106 { a_szPrefix "cert-subject", (a_uBase) + OPT_OFF_CERT_SUBJECT, RTGETOPT_REQ_STRING }, \
107 { a_szPrefix "cert-store", (a_uBase) + OPT_OFF_CERT_STORE, RTGETOPT_REQ_STRING }, \
108 { a_szPrefix "cert-machine-store", (a_uBase) + OPT_OFF_CERT_STORE_MACHINE, RTGETOPT_REQ_NOTHING }, \
109 { a_szPrefix "key-file", (a_uBase) + OPT_OFF_KEY_FILE, RTGETOPT_REQ_STRING }, \
110 { a_szPrefix "key-password", (a_uBase) + OPT_OFF_KEY_PASSWORD, RTGETOPT_REQ_STRING }, \
111 { a_szPrefix "key-password-file", (a_uBase) + OPT_OFF_KEY_PASSWORD_FILE, RTGETOPT_REQ_STRING }, \
112 { a_szPrefix "key-name", (a_uBase) + OPT_OFF_KEY_NAME, RTGETOPT_REQ_STRING }, \
113 { a_szPrefix "key-provider", (a_uBase) + OPT_OFF_KEY_PROVIDER, RTGETOPT_REQ_STRING }
114
115#define OPT_CERT_KEY_GETOPTDEF_COMPAT_ENTRIES(a_uBase) \
116 { "/f", (a_uBase) + OPT_OFF_CERT_FILE, RTGETOPT_REQ_STRING }, \
117 { "/sha1", (a_uBase) + OPT_OFF_CERT_SHA1, RTGETOPT_REQ_STRING }, \
118 { "/n", (a_uBase) + OPT_OFF_CERT_SUBJECT, RTGETOPT_REQ_STRING }, \
119 { "/s", (a_uBase) + OPT_OFF_CERT_STORE, RTGETOPT_REQ_STRING }, \
120 { "/sm", (a_uBase) + OPT_OFF_CERT_STORE_MACHINE, RTGETOPT_REQ_NOTHING }, \
121 { "/p", (a_uBase) + OPT_OFF_KEY_PASSWORD, RTGETOPT_REQ_STRING }, \
122 { "/kc", (a_uBase) + OPT_OFF_KEY_NAME, RTGETOPT_REQ_STRING }, \
123 { "/csp", (a_uBase) + OPT_OFF_KEY_PROVIDER, RTGETOPT_REQ_STRING }
124
125#define OPT_CERT_KEY_SYNOPSIS(a_szPrefix) \
126 "[" a_szPrefix "cert-file <file.pem|file.crt>] " \
127 "[" a_szPrefix "cert-sha1 <fingerprint>] " \
128 "[" a_szPrefix "cert-subject <part-name>] " \
129 "[" a_szPrefix "cert-store <store>] " \
130 "[" a_szPrefix "cert-machine-store] " \
131 "[" a_szPrefix "key-file <file.pem|file.p12>] " \
132 "[" a_szPrefix "key-password <password>] " \
133 "[" a_szPrefix "key-password-file <file>|stdin] " \
134 "[" a_szPrefix "key-name <name>] " \
135 "[" a_szPrefix "key-provider <csp>] "
136
137#define OPT_HASH_PAGES 1040
138#define OPT_NO_HASH_PAGES 1041
139#define OPT_ADD_CERT 1042
140#define OPT_TIMESTAMP_TYPE 1043
141#define OPT_TIMESTAMP_OVERRIDE 1044
142#define OPT_NO_SIGNING_TIME 1045
143#define OPT_FILE_TYPE 1046
144#define OPT_IGNORED 1047
145
146
147/*********************************************************************************************************************************
148* Structures and Typedefs *
149*********************************************************************************************************************************/
150/** Help detail levels. */
151typedef enum RTSIGNTOOLHELP
152{
153 RTSIGNTOOLHELP_USAGE,
154 RTSIGNTOOLHELP_FULL
155} RTSIGNTOOLHELP;
156
157
158/** Filetypes. */
159typedef enum RTSIGNTOOLFILETYPE
160{
161 RTSIGNTOOLFILETYPE_INVALID = 0,
162 RTSIGNTOOLFILETYPE_DETECT,
163 RTSIGNTOOLFILETYPE_EXE,
164 RTSIGNTOOLFILETYPE_CAT,
165 RTSIGNTOOLFILETYPE_UNKNOWN,
166 RTSIGNTOOLFILETYPE_END
167} RTSIGNTOOLFILETYPE;
168
169
170/**
171 * PKCS\#7 signature data.
172 */
173typedef struct SIGNTOOLPKCS7
174{
175 /** The file type. */
176 RTSIGNTOOLFILETYPE enmType;
177 /** The raw signature. */
178 uint8_t *pbBuf;
179 /** Size of the raw signature. */
180 size_t cbBuf;
181 /** The filename. */
182 const char *pszFilename;
183 /** The outer content info wrapper. */
184 RTCRPKCS7CONTENTINFO ContentInfo;
185 /** Pointer to the decoded SignedData inside the ContentInfo member. */
186 PRTCRPKCS7SIGNEDDATA pSignedData;
187
188 /** Newly encoded raw signature.
189 * @sa SignToolPkcs7_Encode() */
190 uint8_t *pbNewBuf;
191 /** Size of newly encoded raw signature. */
192 size_t cbNewBuf;
193
194} SIGNTOOLPKCS7;
195typedef SIGNTOOLPKCS7 *PSIGNTOOLPKCS7;
196
197
198/**
199 * PKCS\#7 signature data for executable.
200 */
201typedef struct SIGNTOOLPKCS7EXE : public SIGNTOOLPKCS7
202{
203 /** The module handle. */
204 RTLDRMOD hLdrMod;
205} SIGNTOOLPKCS7EXE;
206typedef SIGNTOOLPKCS7EXE *PSIGNTOOLPKCS7EXE;
207
208
209/**
210 * Data for the show exe (signature) command.
211 */
212typedef struct SHOWEXEPKCS7 : public SIGNTOOLPKCS7EXE
213{
214 /** The verbosity. */
215 unsigned cVerbosity;
216 /** The prefix buffer. */
217 char szPrefix[256];
218 /** Temporary buffer. */
219 char szTmp[4096];
220} SHOWEXEPKCS7;
221typedef SHOWEXEPKCS7 *PSHOWEXEPKCS7;
222
223
224/*********************************************************************************************************************************
225* Internal Functions *
226*********************************************************************************************************************************/
227static RTEXITCODE HandleHelp(int cArgs, char **papszArgs);
228static RTEXITCODE HelpHelp(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel);
229static RTEXITCODE HandleVersion(int cArgs, char **papszArgs);
230static int HandleShowExeWorkerPkcs7DisplaySignerInfo(PSHOWEXEPKCS7 pThis, size_t offPrefix, PCRTCRPKCS7SIGNERINFO pSignerInfo);
231static int HandleShowExeWorkerPkcs7Display(PSHOWEXEPKCS7 pThis, PRTCRPKCS7SIGNEDDATA pSignedData, size_t offPrefix,
232 PCRTCRPKCS7CONTENTINFO pContentInfo);
233
234
235/*********************************************************************************************************************************
236* Certificate and Private Key Handling (options, ++). *
237*********************************************************************************************************************************/
238#ifdef RT_OS_WINDOWS
239
240/** @todo create a better fake certificate. */
241const unsigned char g_abFakeCertificate[] =
242{
243 0x30, 0x82, 0x03, 0xb2, 0x30, 0x82, 0x02, 0x9a, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x31, /* 0x00000000: 0...0..........1 */
244 0xba, 0xd6, 0xbc, 0x5d, 0x9a, 0xe0, 0xb0, 0x4e, 0xd4, 0xfa, 0xcc, 0xfb, 0x47, 0x00, 0x5c, 0x30, /* 0x00000010: ...]...N....G.\0 */
245 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x71, /* 0x00000020: ...*.H........0q */
246 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x13, 0x54, 0x69, 0x6d, 0x65, 0x73, /* 0x00000030: 1.0...U....Times */
247 0x74, 0x61, 0x6d, 0x70, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x32, 0x31, 0x0c, /* 0x00000040: tamp Signing 21. */
248 0x30, 0x0a, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x03, 0x44, 0x65, 0x76, 0x31, 0x15, 0x30, 0x13, /* 0x00000050: 0...U....Dev1.0. */
249 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0c, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x6f, 0x6d, 0x70, /* 0x00000060: ..U....Test Comp */
250 0x61, 0x6e, 0x79, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x09, 0x53, 0x74, /* 0x00000070: any1.0...U....St */
251 0x75, 0x74, 0x74, 0x67, 0x61, 0x72, 0x74, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, /* 0x00000080: uttgart1.0...U.. */
252 0x0c, 0x02, 0x42, 0x42, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x44, /* 0x00000090: ..BB1.0...U....D */
253 0x45, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x31, 0x30, /* 0x000000a0: E0...00010100010 */
254 0x31, 0x5a, 0x17, 0x0d, 0x33, 0x36, 0x31, 0x32, 0x33, 0x31, 0x32, 0x32, 0x35, 0x39, 0x35, 0x39, /* 0x000000b0: 1Z..361231225959 */
255 0x5a, 0x30, 0x71, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x13, 0x54, 0x69, /* 0x000000c0: Z0q1.0...U....Ti */
256 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, /* 0x000000d0: mestamp Signing */
257 0x32, 0x31, 0x0c, 0x30, 0x0a, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x03, 0x44, 0x65, 0x76, 0x31, /* 0x000000e0: 21.0...U....Dev1 */
258 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0c, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, /* 0x000000f0: .0...U....Test C */
259 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, /* 0x00000100: ompany1.0...U... */
260 0x09, 0x53, 0x74, 0x75, 0x74, 0x74, 0x67, 0x61, 0x72, 0x74, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, /* 0x00000110: .Stuttgart1.0... */
261 0x55, 0x04, 0x08, 0x0c, 0x02, 0x42, 0x42, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, /* 0x00000120: U....BB1.0...U.. */
262 0x13, 0x02, 0x44, 0x45, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, /* 0x00000130: ..DE0.."0...*.H. */
263 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, /* 0x00000140: ............0... */
264 0x02, 0x82, 0x01, 0x01, 0x00, 0xdb, 0x18, 0x63, 0x33, 0xf2, 0x08, 0x90, 0x5a, 0xab, 0xda, 0x88, /* 0x00000150: .......c3...Z... */
265 0x73, 0x86, 0x49, 0xea, 0x8b, 0xaf, 0xcf, 0x67, 0x15, 0xa5, 0x39, 0xe6, 0xa2, 0x94, 0x0c, 0x3f, /* 0x00000160: s.I....g..9....? */
266 0xa1, 0x2e, 0x6c, 0xd2, 0xdf, 0x01, 0x65, 0x6d, 0xed, 0x6c, 0x4c, 0xac, 0xe7, 0x77, 0x7a, 0x45, /* 0x00000170: ..l...em.lL..wzE */
267 0x05, 0x6b, 0x24, 0xf3, 0xaf, 0x45, 0x35, 0x6e, 0x64, 0x0a, 0xac, 0x1d, 0x37, 0xe1, 0x33, 0xa4, /* 0x00000180: .k$..E5nd...7.3. */
268 0x92, 0xec, 0x45, 0xe8, 0x99, 0xc1, 0xde, 0x6f, 0xab, 0x7c, 0xf0, 0xdc, 0xe2, 0xc5, 0x42, 0xa3, /* 0x00000190: ..E....o.|....B. */
269 0xea, 0xf5, 0x8a, 0xf9, 0x0e, 0xe7, 0xb3, 0x35, 0xa2, 0x75, 0x5e, 0x87, 0xd2, 0x2a, 0xd1, 0x27, /* 0x000001a0: .......5.u^..*.' */
270 0xa6, 0x79, 0x9e, 0xfe, 0x90, 0xbf, 0x97, 0xa4, 0xa1, 0xd8, 0xf7, 0xd7, 0x05, 0x59, 0x44, 0x27, /* 0x000001b0: .y...........YD' */
271 0x39, 0x6e, 0x33, 0x01, 0x2e, 0x46, 0x92, 0x47, 0xbe, 0x50, 0x91, 0x26, 0x27, 0xe5, 0x4b, 0x3a, /* 0x000001c0: 9n3..F.G.P.&'.K: */
272 0x76, 0x26, 0x64, 0x92, 0x0c, 0xa0, 0x54, 0x43, 0x6f, 0x56, 0xcc, 0x7b, 0xd0, 0xe3, 0xd8, 0x39, /* 0x000001d0: v&d...TCoV.{...9 */
273 0x5f, 0xb9, 0x41, 0xda, 0x1c, 0x62, 0x88, 0x0c, 0x45, 0x03, 0x63, 0xf8, 0xff, 0xe5, 0x3e, 0x87, /* 0x000001e0: _.A..b..E.c...>. */
274 0x0c, 0x75, 0xc9, 0xdd, 0xa2, 0xc0, 0x1b, 0x63, 0x19, 0xeb, 0x09, 0x9d, 0xa1, 0xbb, 0x0f, 0x63, /* 0x000001f0: .u.....c.......c */
275 0x67, 0x1c, 0xa3, 0xfd, 0x2f, 0xd1, 0x2a, 0xda, 0xd8, 0x93, 0x66, 0x45, 0x54, 0xef, 0x8b, 0x6d, /* 0x00000200: g.....*...fET..m */
276 0x12, 0x15, 0x0f, 0xd4, 0xb5, 0x04, 0x17, 0x30, 0x5b, 0xfa, 0x12, 0x96, 0x48, 0x5b, 0x38, 0x65, /* 0x00000210: .......0[...H[8e */
277 0xfd, 0x8f, 0x0c, 0xa3, 0x11, 0x46, 0x49, 0xe0, 0x62, 0xc3, 0xcc, 0x34, 0xe6, 0xfb, 0xab, 0x51, /* 0x00000220: .....FI.b..4...Q */
278 0xc3, 0xd4, 0x0b, 0xdc, 0x39, 0x93, 0x87, 0x90, 0x10, 0x9f, 0xce, 0x43, 0x27, 0x31, 0xd5, 0x4e, /* 0x00000230: ....9......C'1.N */
279 0x52, 0x60, 0xf1, 0x93, 0xd5, 0x06, 0xc4, 0x4e, 0x65, 0xb6, 0x35, 0x4a, 0x64, 0x15, 0xf8, 0xaf, /* 0x00000240: R`.....Ne.5Jd... */
280 0x71, 0xb2, 0x42, 0x50, 0x89, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x46, 0x30, 0x44, 0x30, 0x0e, /* 0x00000250: q.BP.......F0D0. */
281 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, 0x30, 0x13, /* 0x00000260: ..U...........0. */
282 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x0c, 0x30, 0x0a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, /* 0x00000270: ..U.%..0...+.... */
283 0x07, 0x03, 0x08, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x52, 0x9d, /* 0x00000280: ...0...U......R. */
284 0x4d, 0xcd, 0x41, 0xe1, 0xd2, 0x68, 0x22, 0xd3, 0x10, 0x33, 0x01, 0xca, 0xff, 0x00, 0x1d, 0x27, /* 0x00000290: M.A..h"..3.....' */
285 0xa4, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, /* 0x000002a0: ..0...*.H....... */
286 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xc5, 0x5a, 0x51, 0x83, 0x68, 0x3f, 0x06, 0x39, 0x79, 0x13, /* 0x000002b0: .......ZQ.h?.9y. */
287 0xa6, 0xf0, 0x1a, 0xf9, 0x29, 0x16, 0x2d, 0xa2, 0x07, 0xaa, 0x9b, 0xc3, 0x13, 0x88, 0x39, 0x69, /* 0x000002c0: ....).-.......9i */
288 0xba, 0xf7, 0x0d, 0xfb, 0xc0, 0x6e, 0x3a, 0x0b, 0x49, 0x10, 0xd1, 0xbe, 0x36, 0x91, 0x3f, 0x9d, /* 0x000002d0: .....n:.I...6.?. */
289 0xa1, 0xe8, 0xc4, 0x91, 0xf9, 0x02, 0xe1, 0xf1, 0x01, 0x15, 0x09, 0xb7, 0xa1, 0xf1, 0xec, 0x43, /* 0x000002e0: ...............C */
290 0x0d, 0x73, 0xd1, 0x31, 0x02, 0x4a, 0xce, 0x21, 0xf2, 0xa7, 0x99, 0x7c, 0xee, 0x85, 0x54, 0xc0, /* 0x000002f0: .s.1.J.!...|..T. */
291 0x55, 0x9b, 0x19, 0x37, 0xe8, 0xcf, 0x94, 0x41, 0x10, 0x6e, 0x67, 0xdd, 0x86, 0xaf, 0xb7, 0xfe, /* 0x00000300: U..7...A.ng..... */
292 0x50, 0x05, 0xf6, 0xfb, 0x0a, 0xdf, 0x88, 0xb5, 0x59, 0x69, 0x98, 0x27, 0xf8, 0x81, 0x6a, 0x4a, /* 0x00000310: P.......Yi.'..jJ */
293 0x7c, 0xf3, 0x63, 0xa9, 0x41, 0x78, 0x76, 0x12, 0xdb, 0x0e, 0x94, 0x0a, 0xdb, 0x1d, 0x3c, 0x87, /* 0x00000320: |.c.Axv.......<. */
294 0x35, 0xca, 0x28, 0xeb, 0xb0, 0x62, 0x27, 0x69, 0xe2, 0xf3, 0x84, 0x48, 0xa2, 0x2d, 0xd7, 0x0e, /* 0x00000330: 5.(..b'i...H.-.. */
295 0x4b, 0x6d, 0x39, 0xa7, 0x3e, 0x04, 0x94, 0x8e, 0xb6, 0x4b, 0x91, 0x01, 0x68, 0xf9, 0xd2, 0x75, /* 0x00000340: Km9.>....K..h..u */
296 0x1b, 0xac, 0x42, 0x3b, 0x85, 0xfc, 0x5b, 0x48, 0x3a, 0x13, 0xe7, 0x1c, 0x17, 0xcd, 0x84, 0x89, /* 0x00000350: ..B;..[H:....... */
297 0x9e, 0x5f, 0xe3, 0x77, 0xc0, 0xae, 0x34, 0xc3, 0x87, 0x76, 0x4a, 0x23, 0x30, 0xa0, 0xe1, 0x45, /* 0x00000360: ._.w..4..vJ#0..E */
298 0x94, 0x2a, 0x5b, 0x6b, 0x5a, 0xf0, 0x1a, 0x7e, 0xa6, 0xc4, 0xed, 0xe4, 0xac, 0x5d, 0xdf, 0x87, /* 0x00000370: .*[kZ..~.....].. */
299 0x8f, 0xc5, 0xb4, 0x8c, 0xbc, 0x70, 0xc1, 0xf7, 0xb2, 0x72, 0xbd, 0x73, 0xc9, 0x4e, 0xed, 0x8d, /* 0x00000380: .....p...r.s.N.. */
300 0x29, 0x33, 0xe9, 0x14, 0xc1, 0x5e, 0xff, 0x39, 0xa8, 0xe7, 0x9a, 0x3b, 0x7a, 0x3c, 0xce, 0x5d, /* 0x00000390: )3...^.9...;z<.] */
301 0x0f, 0x3c, 0x82, 0x90, 0xff, 0x81, 0x82, 0x00, 0x82, 0x5f, 0xba, 0x08, 0x79, 0xb1, 0x97, 0xc3, /* 0x000003a0: .<......._..y... */
302 0x09, 0x75, 0xc0, 0x04, 0x9b, 0x67, /* 0x000003b0: .u...g */
303};
304
305const unsigned char g_abFakeRsaKey[] =
306{
307 0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xdb, 0x18, 0x63, 0x33, /* 0x00000000: 0.............c3 */
308 0xf2, 0x08, 0x90, 0x5a, 0xab, 0xda, 0x88, 0x73, 0x86, 0x49, 0xea, 0x8b, 0xaf, 0xcf, 0x67, 0x15, /* 0x00000010: ...Z...s.I....g. */
309 0xa5, 0x39, 0xe6, 0xa2, 0x94, 0x0c, 0x3f, 0xa1, 0x2e, 0x6c, 0xd2, 0xdf, 0x01, 0x65, 0x6d, 0xed, /* 0x00000020: .9....?..l...em. */
310 0x6c, 0x4c, 0xac, 0xe7, 0x77, 0x7a, 0x45, 0x05, 0x6b, 0x24, 0xf3, 0xaf, 0x45, 0x35, 0x6e, 0x64, /* 0x00000030: lL..wzE.k$..E5nd */
311 0x0a, 0xac, 0x1d, 0x37, 0xe1, 0x33, 0xa4, 0x92, 0xec, 0x45, 0xe8, 0x99, 0xc1, 0xde, 0x6f, 0xab, /* 0x00000040: ...7.3...E....o. */
312 0x7c, 0xf0, 0xdc, 0xe2, 0xc5, 0x42, 0xa3, 0xea, 0xf5, 0x8a, 0xf9, 0x0e, 0xe7, 0xb3, 0x35, 0xa2, /* 0x00000050: |....B........5. */
313 0x75, 0x5e, 0x87, 0xd2, 0x2a, 0xd1, 0x27, 0xa6, 0x79, 0x9e, 0xfe, 0x90, 0xbf, 0x97, 0xa4, 0xa1, /* 0x00000060: u^..*.'.y....... */
314 0xd8, 0xf7, 0xd7, 0x05, 0x59, 0x44, 0x27, 0x39, 0x6e, 0x33, 0x01, 0x2e, 0x46, 0x92, 0x47, 0xbe, /* 0x00000070: ....YD'9n3..F.G. */
315 0x50, 0x91, 0x26, 0x27, 0xe5, 0x4b, 0x3a, 0x76, 0x26, 0x64, 0x92, 0x0c, 0xa0, 0x54, 0x43, 0x6f, /* 0x00000080: P.&'.K:v&d...TCo */
316 0x56, 0xcc, 0x7b, 0xd0, 0xe3, 0xd8, 0x39, 0x5f, 0xb9, 0x41, 0xda, 0x1c, 0x62, 0x88, 0x0c, 0x45, /* 0x00000090: V.{...9_.A..b..E */
317 0x03, 0x63, 0xf8, 0xff, 0xe5, 0x3e, 0x87, 0x0c, 0x75, 0xc9, 0xdd, 0xa2, 0xc0, 0x1b, 0x63, 0x19, /* 0x000000a0: .c...>..u.....c. */
318 0xeb, 0x09, 0x9d, 0xa1, 0xbb, 0x0f, 0x63, 0x67, 0x1c, 0xa3, 0xfd, 0x2f, 0xd1, 0x2a, 0xda, 0xd8, /* 0x000000b0: ......cg.....*.. */
319 0x93, 0x66, 0x45, 0x54, 0xef, 0x8b, 0x6d, 0x12, 0x15, 0x0f, 0xd4, 0xb5, 0x04, 0x17, 0x30, 0x5b, /* 0x000000c0: .fET..m.......0[ */
320 0xfa, 0x12, 0x96, 0x48, 0x5b, 0x38, 0x65, 0xfd, 0x8f, 0x0c, 0xa3, 0x11, 0x46, 0x49, 0xe0, 0x62, /* 0x000000d0: ...H[8e.....FI.b */
321 0xc3, 0xcc, 0x34, 0xe6, 0xfb, 0xab, 0x51, 0xc3, 0xd4, 0x0b, 0xdc, 0x39, 0x93, 0x87, 0x90, 0x10, /* 0x000000e0: ..4...Q....9.... */
322 0x9f, 0xce, 0x43, 0x27, 0x31, 0xd5, 0x4e, 0x52, 0x60, 0xf1, 0x93, 0xd5, 0x06, 0xc4, 0x4e, 0x65, /* 0x000000f0: ..C'1.NR`.....Ne */
323 0xb6, 0x35, 0x4a, 0x64, 0x15, 0xf8, 0xaf, 0x71, 0xb2, 0x42, 0x50, 0x89, 0x02, 0x03, 0x01, 0x00, /* 0x00000100: .5Jd...q.BP..... */
324 0x01, 0x02, 0x82, 0x01, 0x01, 0x00, 0xd0, 0x5e, 0x09, 0x3a, 0xc5, 0xdc, 0xcf, 0x2c, 0xec, 0x74, /* 0x00000110: .......^.:...,.t */
325 0x11, 0x81, 0x8d, 0x1d, 0x8f, 0x2a, 0xfa, 0x31, 0x4d, 0xe0, 0x90, 0x1a, 0xd8, 0xf5, 0x95, 0xc7, /* 0x00000120: .....*.1M....... */
326 0x70, 0x5c, 0x62, 0x42, 0xac, 0xe9, 0xd9, 0xf2, 0x14, 0xf1, 0xd0, 0x25, 0xbb, 0xeb, 0x06, 0xfe, /* 0x00000130: p\bB.......%.... */
327 0x09, 0xd6, 0x75, 0x67, 0xd7, 0x39, 0xc1, 0xa0, 0x67, 0x34, 0x4d, 0xd2, 0x12, 0x97, 0xaa, 0x5d, /* 0x00000140: ..ug.9..g4M....] */
328 0xeb, 0x0e, 0xb0, 0x16, 0x6c, 0x78, 0x8e, 0xa0, 0x75, 0xa3, 0xaa, 0x57, 0x88, 0x3b, 0x43, 0x4f, /* 0x00000150: ....lx..u..W.;CO */
329 0x75, 0x85, 0x67, 0xb0, 0x9b, 0xdd, 0x49, 0x0e, 0x6e, 0xdb, 0xea, 0xb3, 0xd4, 0x88, 0x54, 0xa0, /* 0x00000160: u.g...I.n.....T. */
330 0x46, 0x0d, 0x55, 0x6d, 0x98, 0xbd, 0x20, 0xf9, 0x9f, 0x61, 0x2d, 0x6f, 0xc7, 0xd7, 0x16, 0x66, /* 0x00000170: F.Um.. ..a-o...f */
331 0x72, 0xc7, 0x73, 0xbe, 0x9e, 0x48, 0xdc, 0x65, 0x12, 0x46, 0x35, 0x69, 0x55, 0xd8, 0x6b, 0x81, /* 0x00000180: r.s..H.e.F5iU.k. */
332 0x78, 0x40, 0x15, 0x93, 0x60, 0x31, 0x4e, 0x87, 0x15, 0x2a, 0x74, 0x74, 0x7b, 0xa0, 0x1f, 0x59, /* 0x00000190: x@..`1N..*tt{..Y */
333 0x8d, 0xc8, 0x3f, 0xdd, 0xf0, 0x13, 0x88, 0x2a, 0x4a, 0xf2, 0xf5, 0xf1, 0x9e, 0xf3, 0x2d, 0x9c, /* 0x000001a0: ..?....*J.....-. */
334 0x8e, 0xbc, 0xb1, 0x21, 0x45, 0xc7, 0x44, 0x0c, 0x6a, 0xfe, 0x4c, 0x20, 0xdc, 0x73, 0xda, 0x62, /* 0x000001b0: ...!E.D.j.L .s.b */
335 0x21, 0xcb, 0xdf, 0x06, 0xfc, 0x90, 0xc2, 0xbd, 0xd6, 0xde, 0xfb, 0xf6, 0x08, 0x69, 0x5d, 0xea, /* 0x000001c0: !............i]. */
336 0xb3, 0x7f, 0x93, 0x61, 0xf2, 0xc1, 0xd0, 0x61, 0x4f, 0xd5, 0x5b, 0x63, 0xba, 0xb0, 0x3b, 0x07, /* 0x000001d0: ...a...aO.[c..;. */
337 0x7a, 0x55, 0xcd, 0xa1, 0xae, 0x8a, 0x92, 0x21, 0xcc, 0x2f, 0x5b, 0xf8, 0x40, 0x6a, 0xcd, 0xd5, /* 0x000001e0: zU.....!..[.@j.. */
338 0x5f, 0x15, 0xf4, 0xb6, 0xbd, 0xe5, 0x91, 0xb9, 0xa8, 0xcc, 0x2a, 0xa8, 0xa6, 0x67, 0x57, 0x2b, /* 0x000001f0: _.........*..gW+ */
339 0x4b, 0xe9, 0x88, 0xe0, 0xbb, 0x58, 0xac, 0x69, 0x5f, 0x3c, 0x76, 0x28, 0xa6, 0x9d, 0xbc, 0x71, /* 0x00000200: K....X.i_<v(...q */
340 0x7f, 0xcb, 0x0c, 0xc0, 0xbd, 0x61, 0x02, 0x81, 0x81, 0x00, 0xfc, 0x62, 0x79, 0x5b, 0xac, 0xf6, /* 0x00000210: .....a.....by[.. */
341 0x9b, 0x8c, 0xaa, 0x76, 0x2a, 0x30, 0x0e, 0xcf, 0x6b, 0x88, 0x72, 0x54, 0x8c, 0xdf, 0xf3, 0x9d, /* 0x00000220: ...v*0..k.rT.... */
342 0x84, 0xbb, 0xe7, 0x9d, 0xd4, 0x04, 0x29, 0x3c, 0xb5, 0x9d, 0x60, 0x9a, 0xcc, 0x12, 0xf3, 0xfa, /* 0x00000230: ......)<..`..... */
343 0x64, 0x30, 0x23, 0x47, 0xc6, 0xa4, 0x8b, 0x6c, 0x73, 0x6c, 0x6b, 0x78, 0x82, 0xec, 0x05, 0x19, /* 0x00000240: d0#G...lslkx.... */
344 0xde, 0xdd, 0xde, 0x52, 0xc5, 0x20, 0xd1, 0x11, 0x58, 0x19, 0x07, 0x5a, 0x90, 0xdd, 0x22, 0x91, /* 0x00000250: ...R. ..X..Z..". */
345 0x89, 0x22, 0x3f, 0x12, 0x54, 0x1a, 0xb8, 0x79, 0xd8, 0x6c, 0xbc, 0xf5, 0x0d, 0xc7, 0x73, 0x5c, /* 0x00000260: ."?.T..y.l....s\ */
346 0xed, 0xba, 0x40, 0x2b, 0x72, 0x34, 0x34, 0x97, 0xfa, 0x49, 0xf6, 0x43, 0x7c, 0xbc, 0x61, 0x30, /* 0x00000270: ..@+r44..I.C|.a0 */
347 0x54, 0x22, 0x21, 0x5f, 0x77, 0x68, 0x6b, 0x83, 0x95, 0xc6, 0x8d, 0xb8, 0x25, 0x3a, 0xd3, 0xb2, /* 0x00000280: T"!_whk.....%:.. */
348 0xbe, 0x29, 0x94, 0x01, 0x15, 0xf0, 0x36, 0x9d, 0x3e, 0xff, 0x02, 0x81, 0x81, 0x00, 0xde, 0x3b, /* 0x00000290: .)....6.>......; */
349 0xd6, 0x4b, 0x38, 0x69, 0x9b, 0x71, 0x29, 0x89, 0xd4, 0x6d, 0x8c, 0x41, 0xee, 0xe2, 0x4d, 0xfc, /* 0x000002a0: .K8i.q)..m.A..M. */
350 0xf0, 0x9a, 0x73, 0xf1, 0x15, 0x94, 0xac, 0x1b, 0x68, 0x5f, 0x79, 0x15, 0x3a, 0x41, 0x55, 0x09, /* 0x000002b0: ..s.....h_y.:AU. */
351 0xc7, 0x1e, 0xec, 0x27, 0x67, 0xe2, 0xdc, 0x54, 0xa8, 0x09, 0xe6, 0x46, 0x92, 0x92, 0x03, 0x8d, /* 0x000002c0: ...'g..T...F.... */
352 0xe5, 0x96, 0xfb, 0x1a, 0xdd, 0x59, 0x6f, 0x92, 0xf1, 0xf6, 0x8f, 0x76, 0xb0, 0xc5, 0xe6, 0xd7, /* 0x000002d0: .....Yo....v.... */
353 0x1b, 0x25, 0xaf, 0x04, 0x9f, 0xd8, 0x71, 0x27, 0x97, 0x99, 0x23, 0x09, 0x7d, 0xef, 0x06, 0x13, /* 0x000002e0: .%....q'..#.}... */
354 0xab, 0xdc, 0xa2, 0xd8, 0x5f, 0xc5, 0xec, 0xf3, 0x62, 0x20, 0x72, 0x7b, 0xa8, 0xc7, 0x09, 0x24, /* 0x000002f0: ...._...b r{...$ */
355 0xaf, 0x72, 0xc9, 0xea, 0xb8, 0x2d, 0xda, 0x00, 0xc8, 0xfe, 0xb4, 0x9f, 0x9f, 0xc7, 0xa9, 0xf7, /* 0x00000300: .r...-.......... */
356 0x1d, 0xce, 0xb1, 0xdb, 0xc5, 0x8a, 0x4e, 0xe8, 0x88, 0x77, 0x68, 0xdd, 0xf8, 0x77, 0x02, 0x81, /* 0x00000310: ......N..wh..w.. */
357 0x80, 0x5b, 0xa5, 0x8e, 0x98, 0x01, 0xa8, 0xd3, 0x37, 0x33, 0x37, 0x11, 0x7e, 0xbe, 0x02, 0x07, /* 0x00000320: .[......737.~... */
358 0xf4, 0x56, 0x3f, 0xe9, 0x9f, 0xf1, 0x20, 0xc3, 0xf0, 0x4f, 0xdc, 0xf9, 0xfe, 0x40, 0xd3, 0x30, /* 0x00000330: .V?... [email protected] */
359 0xc7, 0xe3, 0x2a, 0x92, 0xec, 0x56, 0xf8, 0x17, 0xa5, 0x7b, 0x4a, 0x37, 0x11, 0xcd, 0x27, 0x26, /* 0x00000340: ..*..V...{J7..'& */
360 0x8a, 0xba, 0x43, 0xda, 0x96, 0xc6, 0x0b, 0x6c, 0xe8, 0x78, 0x30, 0xea, 0x30, 0x4e, 0x7a, 0xd3, /* 0x00000350: ..C....l.x0.0Nz. */
361 0xd8, 0xd2, 0xd8, 0xca, 0x3d, 0xe2, 0xad, 0xa2, 0x74, 0x73, 0x1e, 0xbe, 0xb7, 0xad, 0x41, 0x61, /* 0x00000360: ....=...ts....Aa */
362 0x9b, 0xaa, 0xc9, 0xf9, 0xa4, 0xf1, 0x79, 0x4f, 0x42, 0x10, 0xc7, 0x36, 0x03, 0x4b, 0x0d, 0xdc, /* 0x00000370: ......yOB..6.K.. */
363 0xef, 0x3a, 0xa3, 0xab, 0x09, 0xe4, 0xe8, 0xdd, 0xc4, 0x3f, 0x06, 0x21, 0xa0, 0x23, 0x5a, 0x76, /* 0x00000380: .:.......?.!.#Zv */
364 0xea, 0xd0, 0xcf, 0x8b, 0x85, 0x5f, 0x16, 0x4b, 0x03, 0x62, 0x21, 0x3a, 0xcc, 0x2d, 0xa8, 0xd0, /* 0x00000390: ....._.K.b!:.-.. */
365 0x15, 0x02, 0x81, 0x80, 0x51, 0xf6, 0x89, 0xbb, 0xa6, 0x6b, 0xb4, 0xcb, 0xd0, 0xc1, 0x27, 0xda, /* 0x000003a0: ....Q....k....'. */
366 0xdb, 0x6e, 0xf9, 0xd6, 0xf7, 0x62, 0x81, 0xae, 0xc5, 0x72, 0x36, 0x3e, 0x66, 0x17, 0x99, 0xb0, /* 0x000003b0: .n...b...r6>f... */
367 0x14, 0xad, 0x52, 0x96, 0x03, 0xf2, 0x1e, 0x41, 0x76, 0x61, 0xb6, 0x3c, 0x02, 0x7d, 0x2a, 0x98, /* 0x000003c0: ..R....Ava.<.}*. */
368 0xb4, 0x18, 0x75, 0x38, 0x6b, 0x1d, 0x2b, 0x7f, 0x3a, 0xcf, 0x96, 0xb1, 0xc4, 0xa7, 0xd2, 0x9b, /* 0x000003d0: ..u8k.+.:....... */
369 0xd8, 0x1f, 0xb3, 0x64, 0xda, 0x15, 0x9d, 0xca, 0x91, 0x39, 0x48, 0x67, 0x00, 0x9c, 0xd4, 0x99, /* 0x000003e0: ...d.....9Hg.... */
370 0xc3, 0x45, 0x5d, 0xf0, 0x09, 0x32, 0xba, 0x21, 0x1e, 0xe2, 0x64, 0xb8, 0x50, 0x03, 0x17, 0xbe, /* 0x000003f0: .E]..2.!..d.P... */
371 0xd5, 0xda, 0x6b, 0xce, 0x34, 0xbe, 0x16, 0x03, 0x65, 0x1b, 0x2f, 0xa0, 0xa1, 0x95, 0xc6, 0x8b, /* 0x00000400: ..k.4...e....... */
372 0xc2, 0x3c, 0x59, 0x26, 0xbf, 0xb6, 0x07, 0x85, 0x53, 0x2d, 0xb6, 0x36, 0xa3, 0x91, 0xb9, 0xbb, /* 0x00000410: .<Y&....S-.6.... */
373 0x28, 0xaf, 0x2d, 0x53, 0x02, 0x81, 0x81, 0x00, 0xd7, 0xbc, 0x70, 0xd8, 0x18, 0x4f, 0x65, 0x8c, /* 0x00000420: (.-S......p..Oe. */
374 0x68, 0xca, 0x35, 0x77, 0x43, 0x50, 0x9b, 0xa1, 0xa3, 0x9a, 0x0e, 0x2d, 0x7b, 0x38, 0xf8, 0xba, /* 0x00000430: h.5wCP.....-{8.. */
375 0x14, 0x91, 0x3b, 0xc3, 0x3b, 0x1b, 0xa0, 0x6d, 0x45, 0xe4, 0xa8, 0x28, 0x97, 0xf6, 0x89, 0x13, /* 0x00000440: ..;.;..mE..(.... */
376 0xb6, 0x16, 0x6d, 0x65, 0x47, 0x8c, 0xa6, 0x21, 0xf8, 0x6a, 0xce, 0x4e, 0x44, 0x5e, 0x81, 0x47, /* 0x00000450: ..meG..!.j.ND^.G */
377 0xd9, 0xad, 0x8a, 0xb9, 0xd9, 0xe9, 0x3e, 0x33, 0x1e, 0x5f, 0xe9, 0xe9, 0xa7, 0xea, 0x60, 0x75, /* 0x00000460: ......>3._....`u */
378 0x02, 0x57, 0x71, 0xb5, 0xed, 0x47, 0x77, 0xda, 0x1a, 0x40, 0x38, 0xab, 0x82, 0xd2, 0x0d, 0xf5, /* 0x00000470: .Wq..Gw..@8..... */
379 0x0e, 0x8e, 0xa9, 0x24, 0xdc, 0x30, 0xc9, 0x98, 0xa2, 0x05, 0xcd, 0xca, 0x01, 0xcf, 0xae, 0x1d, /* 0x00000480: ...$.0.......... */
380 0xe9, 0x02, 0x47, 0x0e, 0x46, 0x1d, 0x52, 0x02, 0x9a, 0x99, 0x22, 0x23, 0x7f, 0xf8, 0x9e, 0xc2, /* 0x00000490: ..G.F.R..."#.... */
381 0x16, 0x86, 0xca, 0xa0, 0xa7, 0x34, 0xfb, 0xbc, /* 0x000004a0: .....4.. */
382};
383
384#endif /* RT_OS_WINDOWS */
385
386
387/**
388 * Certificate w/ public key + private key pair for signing.
389 */
390class SignToolKeyPair
391{
392protected:
393 /* Context: */
394 const char *m_pszWhat;
395 bool m_fMandatory;
396
397 /* Parameters kept till finalizing parsing: */
398 const char *m_pszCertFile;
399 const char *m_pszCertSha1;
400 uint8_t m_abCertSha1[RTSHA1_HASH_SIZE];
401 const char *m_pszCertSubject;
402 const char *m_pszCertStore;
403 bool m_fMachineStore; /**< false = personal store */
404
405 const char *m_pszKeyFile;
406 const char *m_pszKeyPassword;
407 const char *m_pszKeyName;
408 const char *m_pszKeyProvider;
409
410 /** String buffer for m_pszKeyPassword when read from file. */
411 RTCString m_strPassword;
412 /** Storage for pCertificate when it's loaded from a file. */
413 RTCRX509CERTIFICATE m_DecodedCert;
414#ifdef RT_OS_WINDOWS
415 /** For the fake certificate */
416 RTCRX509CERTIFICATE m_DecodedFakeCert;
417 /** The certificate store. */
418 HCERTSTORE m_hStore;
419 /** The windows certificate context. */
420 PCCERT_CONTEXT m_pCertCtx;
421 /** Whether hNCryptPrivateKey/hLegacyPrivateKey needs freeing or not. */
422 BOOL m_fFreePrivateHandle;
423#endif
424
425 /** Set if already finalized. */
426 bool m_fFinalized;
427
428 /** Store containing the intermediate certificates available to the host.
429 * */
430 static RTCRSTORE s_hStoreIntermediate;
431 /** Instance counter for helping cleaning up m_hStoreIntermediate. */
432 static uint32_t s_cInstances;
433
434public: /* used to be a struct, thus not prefix either. */
435 /* Result: */
436 PCRTCRX509CERTIFICATE pCertificate;
437 RTCRKEY hPrivateKey;
438#ifdef RT_OS_WINDOWS
439 PCRTCRX509CERTIFICATE pCertificateReal;
440 NCRYPT_KEY_HANDLE hNCryptPrivateKey;
441 HCRYPTPROV hLegacyPrivateKey;
442#endif
443
444public:
445 SignToolKeyPair(const char *a_pszWhat, bool a_fMandatory = false)
446 : m_pszWhat(a_pszWhat)
447 , m_fMandatory(a_fMandatory)
448 , m_pszCertFile(NULL)
449 , m_pszCertSha1(NULL)
450 , m_pszCertSubject(NULL)
451 , m_pszCertStore("MY")
452 , m_fMachineStore(false)
453 , m_pszKeyFile(NULL)
454 , m_pszKeyPassword(NULL)
455 , m_pszKeyName(NULL)
456 , m_pszKeyProvider(NULL)
457#ifdef RT_OS_WINDOWS
458 , m_hStore(NULL)
459 , m_pCertCtx(NULL)
460 , m_fFreePrivateHandle(FALSE)
461#endif
462 , m_fFinalized(false)
463 , pCertificate(NULL)
464 , hPrivateKey(NIL_RTCRKEY)
465#ifdef RT_OS_WINDOWS
466 , pCertificateReal(NULL)
467 , hNCryptPrivateKey(0)
468 , hLegacyPrivateKey(0)
469#endif
470 {
471 RT_ZERO(m_DecodedCert);
472#ifdef RT_OS_WINDOWS
473 RT_ZERO(m_DecodedFakeCert);
474#endif
475 s_cInstances++;
476 }
477
478 ~SignToolKeyPair()
479 {
480 if (hPrivateKey != NIL_RTCRKEY)
481 {
482 RTCrKeyRelease(hPrivateKey);
483 hPrivateKey = NIL_RTCRKEY;
484 }
485 if (pCertificate == &m_DecodedCert)
486 {
487 RTCrX509Certificate_Delete(&m_DecodedCert);
488 pCertificate = NULL;
489 }
490#ifdef RT_OS_WINDOWS
491 if (pCertificate == &m_DecodedFakeCert)
492 {
493 RTCrX509Certificate_Delete(&m_DecodedFakeCert);
494 RTCrX509Certificate_Delete(&m_DecodedCert);
495 pCertificate = NULL;
496 pCertificateReal = NULL;
497 }
498#endif
499#ifdef RT_OS_WINDOWS
500 if (m_pCertCtx != NULL)
501 {
502 CertFreeCertificateContext(m_pCertCtx);
503 m_pCertCtx = NULL;
504 }
505 if (m_hStore != NULL)
506 {
507 CertCloseStore(m_hStore, 0);
508 m_hStore = NULL;
509 }
510#endif
511 s_cInstances--;
512 if (s_cInstances == 0)
513 {
514 RTCrStoreRelease(s_hStoreIntermediate);
515 s_hStoreIntermediate = NIL_RTCRSTORE;
516 }
517 }
518
519 bool isComplete(void) const
520 {
521 return pCertificate && hPrivateKey != NIL_RTCRKEY;
522 }
523
524 bool isNull(void) const
525 {
526 return pCertificate == NULL && hPrivateKey == NIL_RTCRKEY;
527 }
528
529 RTEXITCODE handleOption(unsigned offOpt, PRTGETOPTUNION pValueUnion)
530 {
531 AssertReturn(!m_fFinalized, RTMsgErrorExitFailure("Cannot handle options after finalizeOptions was called!"));
532 switch (offOpt)
533 {
534 case OPT_OFF_CERT_FILE:
535 m_pszCertFile = pValueUnion->psz;
536 m_pszCertSha1 = NULL;
537 m_pszCertSubject = NULL;
538 break;
539 case OPT_OFF_CERT_SHA1:
540 {
541 /* Crude normalization of input separators to colons, since it's likely
542 to use spaces and our conversion function only does colons or nothing. */
543 char szDigest[RTSHA1_DIGEST_LEN * 3 + 1];
544 int rc = RTStrCopy(szDigest, sizeof(szDigest), pValueUnion->psz);
545 if (RT_SUCCESS(rc))
546 {
547 char *pszDigest = RTStrStrip(szDigest);
548 size_t offDst = 0;
549 size_t offSrc = 0;
550 char ch;
551 while ((ch = pszDigest[offSrc++]) != '\0')
552 {
553 if (ch == ' ' || ch == '\t' || ch == ':')
554 {
555 while ((ch = pszDigest[offSrc]) == ' ' || ch == '\t' || ch == ':')
556 offSrc++;
557 ch = ch ? ':' : '\0';
558 }
559 pszDigest[offDst++] = ch;
560 }
561 pszDigest[offDst] = '\0';
562
563 /** @todo add a more relaxed input mode to RTStrConvertHexBytes that can deal
564 * with spaces as well as multi-byte cluster of inputs. */
565 rc = RTStrConvertHexBytes(pszDigest, m_abCertSha1, RTSHA1_HASH_SIZE, RTSTRCONVERTHEXBYTES_F_SEP_COLON);
566 if (RT_SUCCESS(rc))
567 {
568 m_pszCertFile = NULL;
569 m_pszCertSha1 = pValueUnion->psz;
570 m_pszCertSubject = NULL;
571 break;
572 }
573 }
574 return RTMsgErrorExitFailure("malformed SHA-1 certificate fingerprint (%Rrc): %s", rc, pValueUnion->psz);
575 }
576 case OPT_OFF_CERT_SUBJECT:
577 m_pszCertFile = NULL;
578 m_pszCertSha1 = NULL;
579 m_pszCertSubject = pValueUnion->psz;
580 break;
581 case OPT_OFF_CERT_STORE:
582 m_pszCertStore = pValueUnion->psz;
583 break;
584 case OPT_OFF_CERT_STORE_MACHINE:
585 m_fMachineStore = true;
586 break;
587
588 case OPT_OFF_KEY_FILE:
589 m_pszKeyFile = pValueUnion->psz;
590 m_pszKeyName = NULL;
591 break;
592 case OPT_OFF_KEY_NAME:
593 m_pszKeyFile = NULL;
594 m_pszKeyName = pValueUnion->psz;
595 break;
596 case OPT_OFF_KEY_PROVIDER:
597 m_pszKeyProvider = pValueUnion->psz;
598 break;
599 case OPT_OFF_KEY_PASSWORD:
600 m_pszKeyPassword = pValueUnion->psz;
601 break;
602 case OPT_OFF_KEY_PASSWORD_FILE:
603 {
604 m_pszKeyPassword = NULL;
605
606 size_t const cchMax = 512;
607 int rc = m_strPassword.reserveNoThrow(cchMax + 1);
608 if (RT_FAILURE(rc))
609 return RTMsgErrorExitFailure("out of memory");
610
611 PRTSTREAM pStrm = g_pStdIn;
612 bool const fClose = strcmp(pValueUnion->psz, "stdin") != 0;
613 if (fClose)
614 {
615 rc = RTStrmOpen(pValueUnion->psz, "r", &pStrm);
616 if (RT_FAILURE(rc))
617 return RTMsgErrorExitFailure("Failed to open password file '%s' for reading: %Rrc", pValueUnion->psz, rc);
618 }
619 rc = RTStrmGetLine(pStrm, m_strPassword.mutableRaw(), cchMax);
620 if (fClose)
621 RTStrmClose(pStrm);
622 if (rc == VERR_BUFFER_OVERFLOW || rc == VINF_BUFFER_OVERFLOW)
623 return RTMsgErrorExitFailure("Password from '%s' is too long (max %zu)", pValueUnion->psz, cchMax);
624 if (RT_FAILURE(rc))
625 return RTMsgErrorExitFailure("Error reading password from '%s': %Rrc", pValueUnion->psz, rc);
626
627 m_strPassword.jolt();
628 m_strPassword.stripRight();
629 m_pszKeyPassword = m_strPassword.c_str();
630 break;
631 }
632 default:
633 AssertFailedReturn(RTMsgErrorExitFailure("Invalid offOpt=%u!\n", offOpt));
634 }
635 return RTEXITCODE_SUCCESS;
636 }
637
638 RTEXITCODE finalizeOptions(unsigned cVerbosity)
639 {
640 RT_NOREF(cVerbosity);
641
642 /* Only do this once. */
643 if (m_fFinalized)
644 return RTEXITCODE_SUCCESS;
645 m_fFinalized = true;
646
647 /*
648 * Got a cert? Is it required?
649 */
650 bool const fHasKey = ( m_pszKeyFile != NULL
651 || m_pszKeyName != NULL);
652 bool const fHasCert = ( m_pszCertFile != NULL
653 || m_pszCertSha1 != NULL
654 || m_pszCertSubject != NULL);
655 if (!fHasCert)
656 {
657 if (m_fMandatory)
658 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Specifying a %s certificiate is required.", m_pszWhat);
659 return RTEXITCODE_SUCCESS;
660 }
661
662 /*
663 * Get the certificate.
664 */
665 RTERRINFOSTATIC ErrInfo;
666 /* From file: */
667 if (m_pszCertFile)
668 {
669 int rc = RTCrX509Certificate_ReadFromFile(&m_DecodedCert, m_pszCertFile, 0, &g_RTAsn1DefaultAllocator,
670 RTErrInfoInitStatic(&ErrInfo));
671 if (RT_FAILURE(rc))
672 return RTMsgErrorExitFailure("Error reading %s certificate from '%s': %Rrc%#RTeim",
673 m_pszWhat, m_pszCertFile, rc, &ErrInfo.Core);
674 pCertificate = &m_DecodedCert;
675 }
676 /* From certificate store by name (substring) or fingerprint: */
677 else
678 {
679#ifdef RT_OS_WINDOWS
680 m_hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, X509_ASN_ENCODING, NULL,
681 CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG | CERT_STORE_READONLY_FLAG
682 | CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_ENUM_ARCHIVED_FLAG
683 | (m_fMachineStore ? CERT_SYSTEM_STORE_LOCAL_MACHINE : CERT_SYSTEM_STORE_CURRENT_USER),
684 m_pszCertStore);
685 if (m_hStore == NULL)
686 return RTMsgErrorExitFailure("Failed to open %s store '%s': %Rwc (%u)", m_fMachineStore ? "machine" : "user",
687 m_pszCertStore, GetLastError(), GetLastError());
688
689 CRYPT_HASH_BLOB Thumbprint = { RTSHA1_HASH_SIZE, m_abCertSha1 };
690 PRTUTF16 pwszSubject = NULL;
691 void const *pvFindParam = &Thumbprint;
692 DWORD fFind = CERT_FIND_SHA1_HASH;
693 if (!m_pszCertSha1)
694 {
695 int rc = RTStrToUtf16(m_pszCertSubject, &pwszSubject);
696 if (RT_FAILURE(rc))
697 return RTMsgErrorExitFailure("RTStrToUtf16 failed: %Rrc, input %.*Rhxs",
698 rc, strlen(m_pszCertSubject), m_pszCertSubject);
699 pvFindParam = pwszSubject;
700 fFind = CERT_FIND_SUBJECT_STR;
701 }
702
703 while ((m_pCertCtx = CertFindCertificateInStore(m_hStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0 /*fFlags*/,
704 fFind, pvFindParam, m_pCertCtx)) != NULL)
705 {
706 if (m_pCertCtx->dwCertEncodingType & X509_ASN_ENCODING)
707 {
708 RTASN1CURSORPRIMARY PrimaryCursor;
709 RTAsn1CursorInitPrimary(&PrimaryCursor, m_pCertCtx->pbCertEncoded, m_pCertCtx->cbCertEncoded,
710 RTErrInfoInitStatic(&ErrInfo),
711 &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, "CurCtx");
712 int rc = RTCrX509Certificate_DecodeAsn1(&PrimaryCursor.Cursor, 0, &m_DecodedCert, "Cert");
713 if (RT_SUCCESS(rc))
714 {
715 pCertificate = &m_DecodedCert;
716 break;
717 }
718 RTMsgError("failed to decode certificate %p: %Rrc%#RTeim", m_pCertCtx, rc, &ErrInfo.Core);
719 }
720 }
721
722 RTUtf16Free(pwszSubject);
723 if (!m_pCertCtx)
724 return RTMsgErrorExitFailure("No certificate found matching %s '%s' (%Rwc / %u)",
725 m_pszCertSha1 ? "thumbprint" : "subject substring",
726 m_pszCertSha1 ? m_pszCertSha1 : m_pszCertSubject, GetLastError(), GetLastError());
727
728 /* Use this for private key too? */
729 if (!fHasKey)
730 {
731 HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hTmpPrivateKey = 0;
732 DWORD dwKeySpec = 0;
733 if (CryptAcquireCertificatePrivateKey(m_pCertCtx,
734 CRYPT_ACQUIRE_SILENT_FLAG | CRYPT_ACQUIRE_COMPARE_KEY_FLAG
735 | CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG
736 | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG,
737 NULL, &hTmpPrivateKey, &dwKeySpec, &m_fFreePrivateHandle))
738 {
739 if (cVerbosity > 1)
740 RTMsgInfo("hTmpPrivateKey=%p m_fFreePrivateHandle=%d dwKeySpec=%#x",
741 hTmpPrivateKey, m_fFreePrivateHandle, dwKeySpec);
742 Assert(dwKeySpec == CERT_NCRYPT_KEY_SPEC);
743 if (dwKeySpec == CERT_NCRYPT_KEY_SPEC)
744 hNCryptPrivateKey = hTmpPrivateKey;
745 else
746 hLegacyPrivateKey = hTmpPrivateKey; /** @todo remove or drop CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG */
747 return loadFakePrivateKeyAndCert();
748 }
749 return RTMsgErrorExitFailure("CryptAcquireCertificatePrivateKey failed: %Rwc (%d)", GetLastError(), GetLastError());
750 }
751#else
752 return RTMsgErrorExitFailure("Certificate store support is missing on this host");
753#endif
754 }
755
756 /*
757 * Get hold of the private key (if someone above already did, they'd returned already).
758 */
759 Assert(hPrivateKey == NIL_RTCRKEY);
760 /* Use cert file if nothing else specified. */
761 if (!fHasKey && m_pszCertFile)
762 m_pszKeyFile = m_pszCertFile;
763
764 /* Load from file:*/
765 if (m_pszKeyFile)
766 {
767 int rc = RTCrKeyCreateFromFile(&hPrivateKey, 0 /*fFlags*/, m_pszKeyFile, m_pszKeyPassword,
768 RTErrInfoInitStatic(&ErrInfo));
769 if (RT_FAILURE(rc))
770 return RTMsgErrorExitFailure("Error reading the %s private key from '%s': %Rrc%#RTeim",
771 m_pszWhat, m_pszKeyFile, rc, &ErrInfo.Core);
772 }
773 /* From key store: */
774 else
775 {
776 return RTMsgErrorExitFailure("Key store support is missing on this host");
777 }
778
779 return RTEXITCODE_SUCCESS;
780 }
781
782 /** Returns the real certificate. */
783 PCRTCRX509CERTIFICATE getRealCertificate() const
784 {
785#ifdef RT_OS_WINDOWS
786 if (pCertificateReal)
787 return pCertificateReal;
788#endif
789 return pCertificate;
790 }
791
792#ifdef RT_OS_WINDOWS
793 RTEXITCODE loadFakePrivateKeyAndCert()
794 {
795 int rc = RTCrX509Certificate_ReadFromBuffer(&m_DecodedFakeCert, g_abFakeCertificate, sizeof(g_abFakeCertificate),
796 0 /*fFlags*/, &g_RTAsn1DefaultAllocator, NULL, NULL);
797 if (RT_FAILURE(rc))
798 return RTMsgErrorExitFailure("RTCrX509Certificate_ReadFromBuffer/g_abFakeCertificate failed: %Rrc", rc);
799 pCertificateReal = pCertificate;
800 pCertificate = &m_DecodedFakeCert;
801
802 rc = RTCrKeyCreateFromBuffer(&hPrivateKey, 0 /*fFlags*/, g_abFakeRsaKey, sizeof(g_abFakeRsaKey), NULL, NULL, NULL);
803 if (RT_FAILURE(rc))
804 return RTMsgErrorExitFailure("RTCrKeyCreateFromBuffer/g_abFakeRsaKey failed: %Rrc", rc);
805 return RTEXITCODE_SUCCESS;
806 }
807
808#endif
809
810 /**
811 * Search for intermediate CA.
812 *
813 * Currently this only do a single certificate path, so this may go south if
814 * there are multiple paths available. It may work fine for a cross signing
815 * path, as long as the cross over is at the level immediately below the root.
816 */
817 PCRTCRCERTCTX findNextIntermediateCert(PCRTCRCERTCTX pPrev)
818 {
819 /*
820 * Make sure the store is loaded before we start.
821 */
822 if (s_hStoreIntermediate == NIL_RTCRSTORE)
823 {
824 Assert(!pPrev);
825 RTERRINFOSTATIC ErrInfo;
826 int rc = RTCrStoreCreateSnapshotById(&s_hStoreIntermediate,
827 !m_fMachineStore
828 ? RTCRSTOREID_USER_INTERMEDIATE_CAS : RTCRSTOREID_SYSTEM_INTERMEDIATE_CAS,
829 RTErrInfoInitStatic(&ErrInfo));
830 if (RT_FAILURE(rc))
831 {
832 RTMsgError("RTCrStoreCreateSnapshotById/%s-intermediate-CAs failed: %Rrc%#RTeim",
833 m_fMachineStore ? "user" : "machine", rc, &ErrInfo.Core);
834 return NULL;
835 }
836 }
837
838 /*
839 * Open the search handle for the parent of the previous/end certificate.
840 *
841 * We don't need to consider RTCRCERTCTX::pTaInfo here as we're not
842 * after trust anchors, only intermediate certificates.
843 */
844#ifdef RT_OS_WINDOWS
845 PCRTCRX509CERTIFICATE pChildCert = pPrev ? pPrev->pCert : pCertificateReal ? pCertificateReal : pCertificate;
846#else
847 PCRTCRX509CERTIFICATE pChildCert = pPrev ? pPrev->pCert : pCertificate;
848#endif
849 AssertReturnStmt(pChildCert, RTCrCertCtxRelease(pPrev), NULL);
850
851 RTCRSTORECERTSEARCH Search;
852 int rc = RTCrStoreCertFindBySubjectOrAltSubjectByRfc5280(s_hStoreIntermediate, &pChildCert->TbsCertificate.Issuer,
853 &Search);
854 if (RT_FAILURE(rc))
855 {
856 RTMsgError("RTCrStoreCertFindBySubjectOrAltSubjectByRfc5280 failed: %Rrc", rc);
857 return NULL;
858 }
859
860 /*
861 * We only gave the subject so, we have to check the serial number our selves.
862 */
863 PCRTCRCERTCTX pCertCtx;
864 while ((pCertCtx = RTCrStoreCertSearchNext(s_hStoreIntermediate, &Search)) != NULL)
865 {
866 if ( pCertCtx->pCert
867 && RTAsn1BitString_Compare(&pCertCtx->pCert->TbsCertificate.T1.IssuerUniqueId,
868 &pChildCert->TbsCertificate.T1.IssuerUniqueId) == 0 /* compares presentness too */
869 && !RTCrX509Certificate_IsSelfSigned(pCertCtx->pCert))
870 {
871 break; /** @todo compare valid periode too and keep a best match when outside the desired period? */
872 }
873 RTCrCertCtxRelease(pCertCtx);
874 }
875
876 RTCrStoreCertSearchDestroy(s_hStoreIntermediate, & Search);
877 RTCrCertCtxRelease(pPrev);
878 return pCertCtx;
879 }
880
881 /**
882 * Merges the user specified certificates with the signing certificate and any
883 * intermediate CAs we can find in the system store.
884 *
885 * @returns Merged store, NIL_RTCRSTORE on failure (messaged).
886 * @param hUserSpecifiedCertificates The user certificate store.
887 */
888 RTCRSTORE assembleAllAdditionalCertificates(RTCRSTORE hUserSpecifiedCertificates)
889 {
890 RTCRSTORE hRetStore;
891 int rc = RTCrStoreCreateInMemEx(&hRetStore, 0, hUserSpecifiedCertificates);
892 if (RT_SUCCESS(rc))
893 {
894 /* Add the signing certificate: */
895 RTERRINFOSTATIC ErrInfo;
896 rc = RTCrStoreCertAddX509(hRetStore, RTCRCERTCTX_F_ENC_X509_DER | RTCRCERTCTX_F_ADD_IF_NOT_FOUND,
897#ifdef RT_OS_WINDOWS
898 (PRTCRX509CERTIFICATE)(pCertificateReal ? pCertificateReal : pCertificate),
899#else
900 (PRTCRX509CERTIFICATE)pCertificate,
901#endif
902 RTErrInfoInitStatic(&ErrInfo));
903 if (RT_SUCCESS(rc))
904 {
905 /* Add all intermediate CAs certificates we can find. */
906 PCRTCRCERTCTX pInterCaCert = NULL;
907 while ((pInterCaCert = findNextIntermediateCert(pInterCaCert)) != NULL)
908 {
909 rc = RTCrStoreCertAddEncoded(hRetStore, RTCRCERTCTX_F_ENC_X509_DER | RTCRCERTCTX_F_ADD_IF_NOT_FOUND,
910 pInterCaCert->pabEncoded, pInterCaCert->cbEncoded,
911 RTErrInfoInitStatic(&ErrInfo));
912 if (RT_FAILURE(rc))
913 {
914 RTMsgError("RTCrStoreCertAddEncoded/InterCA failed: %Rrc%#RTeim", rc, &ErrInfo.Core);
915 RTCrCertCtxRelease(pInterCaCert);
916 break;
917 }
918 }
919 if (RT_SUCCESS(rc))
920 return hRetStore;
921 }
922 else
923 RTMsgError("RTCrStoreCertAddX509/signer failed: %Rrc%#RTeim", rc, &ErrInfo.Core);
924 RTCrStoreRelease(hRetStore);
925 }
926 else
927 RTMsgError("RTCrStoreCreateInMemEx failed: %Rrc", rc);
928 return NIL_RTCRSTORE;
929 }
930
931};
932
933/*static*/ RTCRSTORE SignToolKeyPair::s_hStoreIntermediate = NIL_RTCRSTORE;
934/*static*/ uint32_t SignToolKeyPair::s_cInstances = 0;
935
936
937
938/*********************************************************************************************************************************
939* Workers. *
940*********************************************************************************************************************************/
941
942
943/**
944 * Deletes the structure.
945 *
946 * @param pThis The structure to initialize.
947 */
948static void SignToolPkcs7_Delete(PSIGNTOOLPKCS7 pThis)
949{
950 RTCrPkcs7ContentInfo_Delete(&pThis->ContentInfo);
951 pThis->pSignedData = NULL;
952 RTMemFree(pThis->pbBuf);
953 pThis->pbBuf = NULL;
954 pThis->cbBuf = 0;
955 RTMemFree(pThis->pbNewBuf);
956 pThis->pbNewBuf = NULL;
957 pThis->cbNewBuf = 0;
958}
959
960
961/**
962 * Deletes the structure.
963 *
964 * @param pThis The structure to initialize.
965 */
966static void SignToolPkcs7Exe_Delete(PSIGNTOOLPKCS7EXE pThis)
967{
968 if (pThis->hLdrMod != NIL_RTLDRMOD)
969 {
970 int rc2 = RTLdrClose(pThis->hLdrMod);
971 if (RT_FAILURE(rc2))
972 RTMsgError("RTLdrClose failed: %Rrc\n", rc2);
973 pThis->hLdrMod = NIL_RTLDRMOD;
974 }
975 SignToolPkcs7_Delete(pThis);
976}
977
978
979/**
980 * Decodes the PKCS #7 blob pointed to by pThis->pbBuf.
981 *
982 * @returns IPRT status code (error message already shown on failure).
983 * @param pThis The PKCS\#7 signature to decode.
984 * @param fCatalog Set if catalog file, clear if executable.
985 */
986static int SignToolPkcs7_Decode(PSIGNTOOLPKCS7 pThis, bool fCatalog)
987{
988 RTERRINFOSTATIC ErrInfo;
989 RTASN1CURSORPRIMARY PrimaryCursor;
990 RTAsn1CursorInitPrimary(&PrimaryCursor, pThis->pbBuf, (uint32_t)pThis->cbBuf, RTErrInfoInitStatic(&ErrInfo),
991 &g_RTAsn1DefaultAllocator, 0, "WinCert");
992
993 int rc = RTCrPkcs7ContentInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, &pThis->ContentInfo, "CI");
994 if (RT_SUCCESS(rc))
995 {
996 if (RTCrPkcs7ContentInfo_IsSignedData(&pThis->ContentInfo))
997 {
998 pThis->pSignedData = pThis->ContentInfo.u.pSignedData;
999
1000 /*
1001 * Decode the authenticode bits.
1002 */
1003 if (!strcmp(pThis->pSignedData->ContentInfo.ContentType.szObjId, RTCRSPCINDIRECTDATACONTENT_OID))
1004 {
1005 PRTCRSPCINDIRECTDATACONTENT pIndData = pThis->pSignedData->ContentInfo.u.pIndirectDataContent;
1006 Assert(pIndData);
1007
1008 /*
1009 * Check that things add up.
1010 */
1011 rc = RTCrPkcs7SignedData_CheckSanity(pThis->pSignedData,
1012 RTCRPKCS7SIGNEDDATA_SANITY_F_AUTHENTICODE
1013 | RTCRPKCS7SIGNEDDATA_SANITY_F_ONLY_KNOWN_HASH
1014 | RTCRPKCS7SIGNEDDATA_SANITY_F_SIGNING_CERT_PRESENT,
1015 RTErrInfoInitStatic(&ErrInfo), "SD");
1016 if (RT_SUCCESS(rc))
1017 {
1018 rc = RTCrSpcIndirectDataContent_CheckSanityEx(pIndData,
1019 pThis->pSignedData,
1020 RTCRSPCINDIRECTDATACONTENT_SANITY_F_ONLY_KNOWN_HASH,
1021 RTErrInfoInitStatic(&ErrInfo));
1022 if (RT_FAILURE(rc))
1023 RTMsgError("SPC indirect data content sanity check failed for '%s': %Rrc - %s\n",
1024 pThis->pszFilename, rc, ErrInfo.szMsg);
1025 }
1026 else
1027 RTMsgError("PKCS#7 sanity check failed for '%s': %Rrc - %s\n", pThis->pszFilename, rc, ErrInfo.szMsg);
1028 }
1029 else if (!strcmp(pThis->pSignedData->ContentInfo.ContentType.szObjId, RTCR_PKCS7_DATA_OID))
1030 { /* apple code signing */ }
1031 else if (!fCatalog)
1032 RTMsgError("Unexpected the signed content in '%s': %s (expected %s)", pThis->pszFilename,
1033 pThis->pSignedData->ContentInfo.ContentType.szObjId, RTCRSPCINDIRECTDATACONTENT_OID);
1034 }
1035 else
1036 rc = RTMsgErrorRc(VERR_CR_PKCS7_NOT_SIGNED_DATA,
1037 "PKCS#7 content is inside '%s' is not 'signedData': %s\n",
1038 pThis->pszFilename, pThis->ContentInfo.ContentType.szObjId);
1039 }
1040 else
1041 RTMsgError("RTCrPkcs7ContentInfo_DecodeAsn1 failed on '%s': %Rrc - %s\n", pThis->pszFilename, rc, ErrInfo.szMsg);
1042 return rc;
1043}
1044
1045
1046/**
1047 * Reads and decodes PKCS\#7 signature from the given cat file.
1048 *
1049 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error message
1050 * on failure.
1051 * @param pThis The structure to initialize.
1052 * @param pszFilename The catalog (or any other DER PKCS\#7) filename.
1053 * @param cVerbosity The verbosity.
1054 */
1055static RTEXITCODE SignToolPkcs7_InitFromFile(PSIGNTOOLPKCS7 pThis, const char *pszFilename, unsigned cVerbosity)
1056{
1057 /*
1058 * Init the return structure.
1059 */
1060 RT_ZERO(*pThis);
1061 pThis->pszFilename = pszFilename;
1062 pThis->enmType = RTSIGNTOOLFILETYPE_CAT;
1063
1064 /*
1065 * Lazy bird uses RTFileReadAll and duplicates the allocation.
1066 */
1067 void *pvFile;
1068 int rc = RTFileReadAll(pszFilename, &pvFile, &pThis->cbBuf);
1069 if (RT_SUCCESS(rc))
1070 {
1071 pThis->pbBuf = (uint8_t *)RTMemDup(pvFile, pThis->cbBuf);
1072 RTFileReadAllFree(pvFile, pThis->cbBuf);
1073 if (pThis->pbBuf)
1074 {
1075 if (cVerbosity > 2)
1076 RTPrintf("PKCS#7 signature: %u bytes\n", pThis->cbBuf);
1077
1078 /*
1079 * Decode it.
1080 */
1081 rc = SignToolPkcs7_Decode(pThis, true /*fCatalog*/);
1082 if (RT_SUCCESS(rc))
1083 return RTEXITCODE_SUCCESS;
1084 }
1085 else
1086 RTMsgError("Out of memory!");
1087 }
1088 else
1089 RTMsgError("Error reading '%s' into memory: %Rrc", pszFilename, rc);
1090
1091 SignToolPkcs7_Delete(pThis);
1092 return RTEXITCODE_FAILURE;
1093}
1094
1095
1096/**
1097 * Encodes the signature into the SIGNTOOLPKCS7::pbNewBuf and
1098 * SIGNTOOLPKCS7::cbNewBuf members.
1099 *
1100 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error message
1101 * on failure.
1102 * @param pThis The signature to encode.
1103 * @param cVerbosity The verbosity.
1104 */
1105static RTEXITCODE SignToolPkcs7_Encode(PSIGNTOOLPKCS7 pThis, unsigned cVerbosity)
1106{
1107 RTERRINFOSTATIC StaticErrInfo;
1108 PRTASN1CORE pRoot = RTCrPkcs7ContentInfo_GetAsn1Core(&pThis->ContentInfo);
1109 uint32_t cbEncoded;
1110 int rc = RTAsn1EncodePrepare(pRoot, RTASN1ENCODE_F_DER, &cbEncoded, RTErrInfoInitStatic(&StaticErrInfo));
1111 if (RT_SUCCESS(rc))
1112 {
1113 if (cVerbosity >= 4)
1114 RTAsn1Dump(pRoot, 0, 0, RTStrmDumpPrintfV, g_pStdOut);
1115
1116 RTMemFree(pThis->pbNewBuf);
1117 pThis->cbNewBuf = cbEncoded;
1118 pThis->pbNewBuf = (uint8_t *)RTMemAllocZ(cbEncoded);
1119 if (pThis->pbNewBuf)
1120 {
1121 rc = RTAsn1EncodeToBuffer(pRoot, RTASN1ENCODE_F_DER, pThis->pbNewBuf, pThis->cbNewBuf,
1122 RTErrInfoInitStatic(&StaticErrInfo));
1123 if (RT_SUCCESS(rc))
1124 {
1125 if (cVerbosity > 1)
1126 RTMsgInfo("Encoded signature to %u bytes", cbEncoded);
1127 return RTEXITCODE_SUCCESS;
1128 }
1129 RTMsgError("RTAsn1EncodeToBuffer failed: %Rrc", rc);
1130
1131 RTMemFree(pThis->pbNewBuf);
1132 pThis->pbNewBuf = NULL;
1133 }
1134 else
1135 RTMsgError("Failed to allocate %u bytes!", cbEncoded);
1136 }
1137 else
1138 RTMsgError("RTAsn1EncodePrepare failed: %Rrc - %s", rc, StaticErrInfo.szMsg);
1139 return RTEXITCODE_FAILURE;
1140}
1141
1142
1143/**
1144 * Helper that makes sure the UnauthenticatedAttributes are present in the given
1145 * SignerInfo structure.
1146 *
1147 * Call this before trying to modify the array.
1148 *
1149 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error already
1150 * displayed on failure.
1151 * @param pSignerInfo The SignerInfo structure in question.
1152 */
1153static RTEXITCODE SignToolPkcs7_EnsureUnauthenticatedAttributesPresent(PRTCRPKCS7SIGNERINFO pSignerInfo)
1154{
1155 if (pSignerInfo->UnauthenticatedAttributes.cItems == 0)
1156 {
1157 /* HACK ALERT! Invent ASN.1 setters/whatever for members to replace this mess. */
1158
1159 if (pSignerInfo->AuthenticatedAttributes.cItems == 0)
1160 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No authenticated or unauthenticated attributes! Sorry, no can do.");
1161
1162 Assert(pSignerInfo->UnauthenticatedAttributes.SetCore.Asn1Core.uTag == 0);
1163 int rc = RTAsn1SetCore_Init(&pSignerInfo->UnauthenticatedAttributes.SetCore,
1164 pSignerInfo->AuthenticatedAttributes.SetCore.Asn1Core.pOps);
1165 if (RT_FAILURE(rc))
1166 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTAsn1SetCore_Init failed: %Rrc", rc);
1167 pSignerInfo->UnauthenticatedAttributes.SetCore.Asn1Core.uTag = 1;
1168 pSignerInfo->UnauthenticatedAttributes.SetCore.Asn1Core.fClass = ASN1_TAGCLASS_CONTEXT | ASN1_TAGFLAG_CONSTRUCTED;
1169 RTAsn1MemInitArrayAllocation(&pSignerInfo->UnauthenticatedAttributes.Allocation,
1170 pSignerInfo->AuthenticatedAttributes.Allocation.pAllocator,
1171 sizeof(**pSignerInfo->UnauthenticatedAttributes.papItems));
1172 }
1173 return RTEXITCODE_SUCCESS;
1174}
1175
1176
1177/**
1178 * Adds the @a pSrc signature as a nested signature.
1179 *
1180 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error message
1181 * on failure.
1182 * @param pThis The signature to modify.
1183 * @param pSrc The signature to add as nested.
1184 * @param cVerbosity The verbosity.
1185 * @param fPrepend Whether to prepend (true) or append (false) the
1186 * source signature to the nested attribute.
1187 */
1188static RTEXITCODE SignToolPkcs7_AddNestedSignature(PSIGNTOOLPKCS7 pThis, PSIGNTOOLPKCS7 pSrc,
1189 unsigned cVerbosity, bool fPrepend)
1190{
1191 PRTCRPKCS7SIGNERINFO pSignerInfo = pThis->pSignedData->SignerInfos.papItems[0];
1192
1193 /*
1194 * Deal with UnauthenticatedAttributes being absent before trying to append to the array.
1195 */
1196 RTEXITCODE rcExit = SignToolPkcs7_EnsureUnauthenticatedAttributesPresent(pSignerInfo);
1197 if (rcExit != RTEXITCODE_SUCCESS)
1198 return rcExit;
1199
1200 /*
1201 * Find or add an unauthenticated attribute for nested signatures.
1202 */
1203 int rc = VERR_NOT_FOUND;
1204 PRTCRPKCS7ATTRIBUTE pAttr = NULL;
1205 int32_t iPos = pSignerInfo->UnauthenticatedAttributes.cItems;
1206 while (iPos-- > 0)
1207 if (pSignerInfo->UnauthenticatedAttributes.papItems[iPos]->enmType == RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE)
1208 {
1209 pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[iPos];
1210 rc = VINF_SUCCESS;
1211 break;
1212 }
1213 if (iPos < 0)
1214 {
1215 iPos = RTCrPkcs7Attributes_Append(&pSignerInfo->UnauthenticatedAttributes);
1216 if (iPos >= 0)
1217 {
1218 if (cVerbosity >= 3)
1219 RTMsgInfo("Adding UnauthenticatedAttribute #%u...", iPos);
1220 Assert((uint32_t)iPos < pSignerInfo->UnauthenticatedAttributes.cItems);
1221
1222 pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[iPos];
1223 rc = RTAsn1ObjId_InitFromString(&pAttr->Type, RTCR_PKCS9_ID_MS_NESTED_SIGNATURE, pAttr->Allocation.pAllocator);
1224 if (RT_SUCCESS(rc))
1225 {
1226 /** @todo Generalize the Type + enmType DYN stuff and generate setters. */
1227 Assert(pAttr->enmType == RTCRPKCS7ATTRIBUTETYPE_NOT_PRESENT);
1228 Assert(pAttr->uValues.pContentInfos == NULL);
1229 pAttr->enmType = RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE;
1230 rc = RTAsn1MemAllocZ(&pAttr->Allocation, (void **)&pAttr->uValues.pContentInfos,
1231 sizeof(*pAttr->uValues.pContentInfos));
1232 if (RT_SUCCESS(rc))
1233 {
1234 rc = RTCrPkcs7SetOfContentInfos_Init(pAttr->uValues.pContentInfos, pAttr->Allocation.pAllocator);
1235 if (!RT_SUCCESS(rc))
1236 RTMsgError("RTCrPkcs7ContentInfos_Init failed: %Rrc", rc);
1237 }
1238 else
1239 RTMsgError("RTAsn1MemAllocZ failed: %Rrc", rc);
1240 }
1241 else
1242 RTMsgError("RTAsn1ObjId_InitFromString failed: %Rrc", rc);
1243 }
1244 else
1245 RTMsgError("RTCrPkcs7Attributes_Append failed: %Rrc", iPos);
1246 }
1247 else if (cVerbosity >= 2)
1248 RTMsgInfo("Found UnauthenticatedAttribute #%u...", iPos);
1249 if (RT_SUCCESS(rc))
1250 {
1251 /*
1252 * Append/prepend the signature.
1253 */
1254 uint32_t iActualPos = UINT32_MAX;
1255 iPos = fPrepend ? 0 : pAttr->uValues.pContentInfos->cItems;
1256 rc = RTCrPkcs7SetOfContentInfos_InsertEx(pAttr->uValues.pContentInfos, iPos, &pSrc->ContentInfo,
1257 pAttr->Allocation.pAllocator, &iActualPos);
1258 if (RT_SUCCESS(rc))
1259 {
1260 if (cVerbosity > 0)
1261 RTMsgInfo("Added nested signature (#%u)", iActualPos);
1262 if (cVerbosity >= 3)
1263 {
1264 RTMsgInfo("SingerInfo dump after change:");
1265 RTAsn1Dump(RTCrPkcs7SignerInfo_GetAsn1Core(pSignerInfo), 0, 2, RTStrmDumpPrintfV, g_pStdOut);
1266 }
1267 return RTEXITCODE_SUCCESS;
1268 }
1269
1270 RTMsgError("RTCrPkcs7ContentInfos_InsertEx failed: %Rrc", rc);
1271 }
1272 return RTEXITCODE_FAILURE;
1273}
1274
1275
1276/**
1277 * Writes the signature to the file.
1278 *
1279 * Caller must have called SignToolPkcs7_Encode() prior to this function.
1280 *
1281 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error
1282 * message on failure.
1283 * @param pThis The file which to write.
1284 * @param cVerbosity The verbosity.
1285 */
1286static RTEXITCODE SignToolPkcs7_WriteSignatureToFile(PSIGNTOOLPKCS7 pThis, const char *pszFilename, unsigned cVerbosity)
1287{
1288 AssertReturn(pThis->cbNewBuf && pThis->pbNewBuf, RTEXITCODE_FAILURE);
1289
1290 /*
1291 * Open+truncate file, write new signature, close. Simple.
1292 */
1293 RTFILE hFile;
1294 int rc = RTFileOpen(&hFile, pszFilename, RTFILE_O_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_TRUNCATE | RTFILE_O_DENY_WRITE);
1295 if (RT_SUCCESS(rc))
1296 {
1297 rc = RTFileWrite(hFile, pThis->pbNewBuf, pThis->cbNewBuf, NULL);
1298 if (RT_SUCCESS(rc))
1299 {
1300 rc = RTFileClose(hFile);
1301 if (RT_SUCCESS(rc))
1302 {
1303 if (cVerbosity > 0)
1304 RTMsgInfo("Wrote %u bytes to %s", pThis->cbNewBuf, pszFilename);
1305 return RTEXITCODE_SUCCESS;
1306 }
1307
1308 RTMsgError("RTFileClose failed on %s: %Rrc", pszFilename, rc);
1309 }
1310 else
1311 RTMsgError("Write error on %s: %Rrc", pszFilename, rc);
1312 }
1313 else
1314 RTMsgError("Failed to open %s for writing: %Rrc", pszFilename, rc);
1315 return RTEXITCODE_FAILURE;
1316}
1317
1318
1319
1320/**
1321 * Worker for recursively searching for MS nested signatures and signer infos.
1322 *
1323 * @returns Pointer to the signer info corresponding to @a iReqSignature. NULL
1324 * if not found.
1325 * @param pSignedData The signature to search.
1326 * @param piNextSignature Pointer to the variable keeping track of the next
1327 * signature number.
1328 * @param iReqSignature The request signature number.
1329 * @param ppSignedData Where to return the signature data structure.
1330 * Optional.
1331 */
1332static PRTCRPKCS7SIGNERINFO SignToolPkcs7_FindNestedSignatureByIndexWorker(PRTCRPKCS7SIGNEDDATA pSignedData,
1333 uint32_t *piNextSignature,
1334 uint32_t iReqSignature,
1335 PRTCRPKCS7SIGNEDDATA *ppSignedData)
1336{
1337 for (uint32_t iSignerInfo = 0; iSignerInfo < pSignedData->SignerInfos.cItems; iSignerInfo++)
1338 {
1339 /* Match?*/
1340 PRTCRPKCS7SIGNERINFO pSignerInfo = pSignedData->SignerInfos.papItems[iSignerInfo];
1341 if (*piNextSignature == iReqSignature)
1342 {
1343 if (ppSignedData)
1344 *ppSignedData = pSignedData;
1345 return pSignerInfo;
1346 }
1347 *piNextSignature += 1;
1348
1349 /* Look for nested signatures. */
1350 for (uint32_t iAttrib = 0; iAttrib < pSignerInfo->UnauthenticatedAttributes.cItems; iAttrib++)
1351 if (pSignerInfo->UnauthenticatedAttributes.papItems[iAttrib]->enmType == RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE)
1352 {
1353 PRTCRPKCS7SETOFCONTENTINFOS pCntInfos;
1354 pCntInfos = pSignerInfo->UnauthenticatedAttributes.papItems[iAttrib]->uValues.pContentInfos;
1355 for (uint32_t iCntInfo = 0; iCntInfo < pCntInfos->cItems; iCntInfo++)
1356 {
1357 PRTCRPKCS7CONTENTINFO pCntInfo = pCntInfos->papItems[iCntInfo];
1358 if (RTCrPkcs7ContentInfo_IsSignedData(pCntInfo))
1359 {
1360 PRTCRPKCS7SIGNERINFO pRet;
1361 pRet = SignToolPkcs7_FindNestedSignatureByIndexWorker(pCntInfo->u.pSignedData, piNextSignature,
1362 iReqSignature, ppSignedData);
1363 if (pRet)
1364 return pRet;
1365 }
1366 }
1367 }
1368 }
1369 return NULL;
1370}
1371
1372
1373/**
1374 * Locates the given nested signature.
1375 *
1376 * @returns Pointer to the signer info corresponding to @a iReqSignature. NULL
1377 * if not found.
1378 * @param pThis The PKCS\#7 structure to search.
1379 * @param iReqSignature The requested signature number.
1380 * @param ppSignedData Where to return the pointer to the signed data that
1381 * the returned signer info belongs to.
1382 *
1383 * @todo Move into SPC or PKCS\#7.
1384 */
1385static PRTCRPKCS7SIGNERINFO SignToolPkcs7_FindNestedSignatureByIndex(PSIGNTOOLPKCS7 pThis, uint32_t iReqSignature,
1386 PRTCRPKCS7SIGNEDDATA *ppSignedData)
1387{
1388 uint32_t iNextSignature = 0;
1389 return SignToolPkcs7_FindNestedSignatureByIndexWorker(pThis->pSignedData, &iNextSignature, iReqSignature, ppSignedData);
1390}
1391
1392
1393
1394/**
1395 * Reads and decodes PKCS\#7 signature from the given executable, if it has one.
1396 *
1397 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error message
1398 * on failure.
1399 * @param pThis The structure to initialize.
1400 * @param pszFilename The executable filename.
1401 * @param cVerbosity The verbosity.
1402 * @param enmLdrArch For FAT binaries.
1403 * @param fAllowUnsigned Whether to allow unsigned binaries.
1404 */
1405static RTEXITCODE SignToolPkcs7Exe_InitFromFile(PSIGNTOOLPKCS7EXE pThis, const char *pszFilename, unsigned cVerbosity,
1406 RTLDRARCH enmLdrArch = RTLDRARCH_WHATEVER, bool fAllowUnsigned = false)
1407{
1408 /*
1409 * Init the return structure.
1410 */
1411 RT_ZERO(*pThis);
1412 pThis->hLdrMod = NIL_RTLDRMOD;
1413 pThis->pszFilename = pszFilename;
1414 pThis->enmType = RTSIGNTOOLFILETYPE_EXE;
1415
1416 /*
1417 * Open the image and check if it's signed.
1418 */
1419 int rc = RTLdrOpen(pszFilename, RTLDR_O_FOR_VALIDATION, enmLdrArch, &pThis->hLdrMod);
1420 if (RT_SUCCESS(rc))
1421 {
1422 bool fIsSigned = false;
1423 rc = RTLdrQueryProp(pThis->hLdrMod, RTLDRPROP_IS_SIGNED, &fIsSigned, sizeof(fIsSigned));
1424 if (RT_SUCCESS(rc) && fIsSigned)
1425 {
1426 /*
1427 * Query the PKCS#7 data (assuming M$ style signing) and hand it to a worker.
1428 */
1429 size_t cbActual = 0;
1430#ifdef DEBUG
1431 size_t cbBuf = 64;
1432#else
1433 size_t cbBuf = _512K;
1434#endif
1435 void *pvBuf = RTMemAllocZ(cbBuf);
1436 if (pvBuf)
1437 {
1438 rc = RTLdrQueryPropEx(pThis->hLdrMod, RTLDRPROP_PKCS7_SIGNED_DATA, NULL /*pvBits*/, pvBuf, cbBuf, &cbActual);
1439 if (rc == VERR_BUFFER_OVERFLOW)
1440 {
1441 RTMemFree(pvBuf);
1442 cbBuf = cbActual;
1443 pvBuf = RTMemAllocZ(cbActual);
1444 if (pvBuf)
1445 rc = RTLdrQueryPropEx(pThis->hLdrMod, RTLDRPROP_PKCS7_SIGNED_DATA, NULL /*pvBits*/,
1446 pvBuf, cbBuf, &cbActual);
1447 else
1448 rc = VERR_NO_MEMORY;
1449 }
1450 }
1451 else
1452 rc = VERR_NO_MEMORY;
1453
1454 pThis->pbBuf = (uint8_t *)pvBuf;
1455 pThis->cbBuf = cbActual;
1456 if (RT_SUCCESS(rc))
1457 {
1458 if (cVerbosity > 2)
1459 RTPrintf("PKCS#7 signature: %u bytes\n", cbActual);
1460 if (cVerbosity > 3)
1461 RTPrintf("%.*Rhxd\n", cbActual, pvBuf);
1462
1463 /*
1464 * Decode it.
1465 */
1466 rc = SignToolPkcs7_Decode(pThis, false /*fCatalog*/);
1467 if (RT_SUCCESS(rc))
1468 return RTEXITCODE_SUCCESS;
1469 }
1470 else
1471 RTMsgError("RTLdrQueryPropEx/RTLDRPROP_PKCS7_SIGNED_DATA failed on '%s': %Rrc\n", pszFilename, rc);
1472 }
1473 else if (RT_SUCCESS(rc))
1474 {
1475 if (!fAllowUnsigned || cVerbosity >= 2)
1476 RTMsgInfo("'%s': not signed\n", pszFilename);
1477 if (fAllowUnsigned)
1478 return RTEXITCODE_SUCCESS;
1479 }
1480 else
1481 RTMsgError("RTLdrQueryProp/RTLDRPROP_IS_SIGNED failed on '%s': %Rrc\n", pszFilename, rc);
1482 }
1483 else
1484 RTMsgError("Error opening executable image '%s': %Rrc", pszFilename, rc);
1485
1486 SignToolPkcs7Exe_Delete(pThis);
1487 return RTEXITCODE_FAILURE;
1488}
1489
1490
1491/**
1492 * Calculates the checksum of an executable.
1493 *
1494 * @returns Success indicator (errors are reported)
1495 * @param pThis The exe file to checksum.
1496 * @param hFile The file handle.
1497 * @param puCheckSum Where to return the checksum.
1498 */
1499static bool SignToolPkcs7Exe_CalcPeCheckSum(PSIGNTOOLPKCS7EXE pThis, RTFILE hFile, uint32_t *puCheckSum)
1500{
1501#ifdef RT_OS_WINDOWS
1502 /*
1503 * Try use IMAGEHLP!MapFileAndCheckSumW first.
1504 */
1505 PRTUTF16 pwszPath;
1506 int rc = RTStrToUtf16(pThis->pszFilename, &pwszPath);
1507 if (RT_SUCCESS(rc))
1508 {
1509 decltype(MapFileAndCheckSumW) *pfnMapFileAndCheckSumW;
1510 pfnMapFileAndCheckSumW = (decltype(MapFileAndCheckSumW) *)RTLdrGetSystemSymbol("IMAGEHLP.DLL", "MapFileAndCheckSumW");
1511 if (pfnMapFileAndCheckSumW)
1512 {
1513 DWORD uOldSum = UINT32_MAX;
1514 DWORD uCheckSum = UINT32_MAX;
1515 DWORD dwRc = pfnMapFileAndCheckSumW(pwszPath, &uOldSum, &uCheckSum);
1516 if (dwRc == CHECKSUM_SUCCESS)
1517 {
1518 *puCheckSum = uCheckSum;
1519 return true;
1520 }
1521 }
1522 }
1523#endif
1524
1525 RT_NOREF(pThis, hFile, puCheckSum);
1526 RTMsgError("Implement check sum calcuation fallback!");
1527 return false;
1528}
1529
1530
1531/**
1532 * Writes the signature to the file.
1533 *
1534 * This has the side-effect of closing the hLdrMod member. So, it can only be
1535 * called once!
1536 *
1537 * Caller must have called SignToolPkcs7_Encode() prior to this function.
1538 *
1539 * @returns RTEXITCODE_SUCCESS on success, RTEXITCODE_FAILURE with error
1540 * message on failure.
1541 * @param pThis The file which to write.
1542 * @param cVerbosity The verbosity.
1543 */
1544static RTEXITCODE SignToolPkcs7Exe_WriteSignatureToFile(PSIGNTOOLPKCS7EXE pThis, unsigned cVerbosity)
1545{
1546 AssertReturn(pThis->cbNewBuf && pThis->pbNewBuf, RTEXITCODE_FAILURE);
1547
1548 /*
1549 * Get the file header offset and arch before closing the destination handle.
1550 */
1551 uint32_t offNtHdrs;
1552 int rc = RTLdrQueryProp(pThis->hLdrMod, RTLDRPROP_FILE_OFF_HEADER, &offNtHdrs, sizeof(offNtHdrs));
1553 if (RT_SUCCESS(rc))
1554 {
1555 RTLDRARCH enmLdrArch = RTLdrGetArch(pThis->hLdrMod);
1556 if (enmLdrArch != RTLDRARCH_INVALID)
1557 {
1558 RTLdrClose(pThis->hLdrMod);
1559 pThis->hLdrMod = NIL_RTLDRMOD;
1560 unsigned cbNtHdrs = 0;
1561 switch (enmLdrArch)
1562 {
1563 case RTLDRARCH_AMD64:
1564 cbNtHdrs = sizeof(IMAGE_NT_HEADERS64);
1565 break;
1566 case RTLDRARCH_X86_32:
1567 cbNtHdrs = sizeof(IMAGE_NT_HEADERS32);
1568 break;
1569 default:
1570 RTMsgError("Unknown image arch: %d", enmLdrArch);
1571 }
1572 if (cbNtHdrs > 0)
1573 {
1574 if (cVerbosity > 0)
1575 RTMsgInfo("offNtHdrs=%#x cbNtHdrs=%u\n", offNtHdrs, cbNtHdrs);
1576
1577 /*
1578 * Open the executable file for writing.
1579 */
1580 RTFILE hFile;
1581 rc = RTFileOpen(&hFile, pThis->pszFilename, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1582 if (RT_SUCCESS(rc))
1583 {
1584 /* Read the file header and locate the security directory entry. */
1585 union
1586 {
1587 IMAGE_NT_HEADERS32 NtHdrs32;
1588 IMAGE_NT_HEADERS64 NtHdrs64;
1589 } uBuf;
1590 PIMAGE_DATA_DIRECTORY pSecDir = cbNtHdrs == sizeof(IMAGE_NT_HEADERS64)
1591 ? &uBuf.NtHdrs64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]
1592 : &uBuf.NtHdrs32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
1593
1594 rc = RTFileReadAt(hFile, offNtHdrs, &uBuf, cbNtHdrs, NULL);
1595 if ( RT_SUCCESS(rc)
1596 && uBuf.NtHdrs32.Signature == IMAGE_NT_SIGNATURE)
1597 {
1598 /*
1599 * Drop any old signature by truncating the file.
1600 */
1601 if ( pSecDir->Size > 8
1602 && pSecDir->VirtualAddress > offNtHdrs + sizeof(IMAGE_NT_HEADERS32))
1603 {
1604 rc = RTFileSetSize(hFile, pSecDir->VirtualAddress);
1605 if (RT_FAILURE(rc))
1606 RTMsgError("Error truncating file to %#x bytes: %Rrc", pSecDir->VirtualAddress, rc);
1607 }
1608 else if (pSecDir->Size != 0 && pSecDir->VirtualAddress == 0)
1609 rc = RTMsgErrorRc(VERR_BAD_EXE_FORMAT, "Bad security directory entry: VA=%#x Size=%#x",
1610 pSecDir->VirtualAddress, pSecDir->Size);
1611 if (RT_SUCCESS(rc))
1612 {
1613 /*
1614 * Sector align the signature portion.
1615 */
1616 uint32_t const cbWinCert = RT_UOFFSETOF(WIN_CERTIFICATE, bCertificate);
1617 uint64_t offCur = 0;
1618 rc = RTFileQuerySize(hFile, &offCur);
1619 if ( RT_SUCCESS(rc)
1620 && offCur < _2G)
1621 {
1622 if (offCur & 0x1ff)
1623 {
1624 uint32_t cbNeeded = 0x200 - ((uint32_t)offCur & 0x1ff);
1625 rc = RTFileWriteAt(hFile, offCur, g_abRTZero4K, cbNeeded, NULL);
1626 if (RT_SUCCESS(rc))
1627 offCur += cbNeeded;
1628 }
1629 if (RT_SUCCESS(rc))
1630 {
1631 /*
1632 * Write the header followed by the signature data.
1633 */
1634 uint32_t const cbZeroPad = (uint32_t)(RT_ALIGN_Z(pThis->cbNewBuf, 8) - pThis->cbNewBuf);
1635 pSecDir->VirtualAddress = (uint32_t)offCur;
1636 pSecDir->Size = cbWinCert + (uint32_t)pThis->cbNewBuf + cbZeroPad;
1637 if (cVerbosity >= 2)
1638 RTMsgInfo("Writing %u (%#x) bytes of signature at %#x (%u).\n",
1639 pSecDir->Size, pSecDir->Size, pSecDir->VirtualAddress, pSecDir->VirtualAddress);
1640
1641 WIN_CERTIFICATE WinCert;
1642 WinCert.dwLength = pSecDir->Size;
1643 WinCert.wRevision = WIN_CERT_REVISION_2_0;
1644 WinCert.wCertificateType = WIN_CERT_TYPE_PKCS_SIGNED_DATA;
1645
1646 rc = RTFileWriteAt(hFile, offCur, &WinCert, cbWinCert, NULL);
1647 if (RT_SUCCESS(rc))
1648 {
1649 offCur += cbWinCert;
1650 rc = RTFileWriteAt(hFile, offCur, pThis->pbNewBuf, pThis->cbNewBuf, NULL);
1651 }
1652 if (RT_SUCCESS(rc) && cbZeroPad)
1653 {
1654 offCur += pThis->cbNewBuf;
1655 rc = RTFileWriteAt(hFile, offCur, g_abRTZero4K, cbZeroPad, NULL);
1656 }
1657 if (RT_SUCCESS(rc))
1658 {
1659 /*
1660 * Reset the checksum (sec dir updated already) and rewrite the header.
1661 */
1662 uBuf.NtHdrs32.OptionalHeader.CheckSum = 0;
1663 offCur = offNtHdrs;
1664 rc = RTFileWriteAt(hFile, offNtHdrs, &uBuf, cbNtHdrs, NULL);
1665 if (RT_SUCCESS(rc))
1666 rc = RTFileFlush(hFile);
1667 if (RT_SUCCESS(rc))
1668 {
1669 /*
1670 * Calc checksum and write out the header again.
1671 */
1672 uint32_t uCheckSum = UINT32_MAX;
1673 if (SignToolPkcs7Exe_CalcPeCheckSum(pThis, hFile, &uCheckSum))
1674 {
1675 uBuf.NtHdrs32.OptionalHeader.CheckSum = uCheckSum;
1676 rc = RTFileWriteAt(hFile, offNtHdrs, &uBuf, cbNtHdrs, NULL);
1677 if (RT_SUCCESS(rc))
1678 rc = RTFileFlush(hFile);
1679 if (RT_SUCCESS(rc))
1680 {
1681 rc = RTFileClose(hFile);
1682 if (RT_SUCCESS(rc))
1683 return RTEXITCODE_SUCCESS;
1684 RTMsgError("RTFileClose failed: %Rrc\n", rc);
1685 return RTEXITCODE_FAILURE;
1686 }
1687 }
1688 }
1689 }
1690 }
1691 if (RT_FAILURE(rc))
1692 RTMsgError("Write error at %#RX64: %Rrc", offCur, rc);
1693 }
1694 else if (RT_SUCCESS(rc))
1695 RTMsgError("File to big: %'RU64 bytes", offCur);
1696 else
1697 RTMsgError("RTFileQuerySize failed: %Rrc", rc);
1698 }
1699 }
1700 else if (RT_SUCCESS(rc))
1701 RTMsgError("Not NT executable header!");
1702 else
1703 RTMsgError("Error reading NT headers (%#x bytes) at %#x: %Rrc", cbNtHdrs, offNtHdrs, rc);
1704 RTFileClose(hFile);
1705 }
1706 else
1707 RTMsgError("Failed to open '%s' for writing: %Rrc", pThis->pszFilename, rc);
1708 }
1709 }
1710 else
1711 RTMsgError("RTLdrGetArch failed!");
1712 }
1713 else
1714 RTMsgError("RTLdrQueryProp/RTLDRPROP_FILE_OFF_HEADER failed: %Rrc", rc);
1715 return RTEXITCODE_FAILURE;
1716}
1717
1718#ifndef IPRT_SIGNTOOL_NO_SIGNING
1719
1720static PRTCRPKCS7ATTRIBUTE SignToolPkcs7_AuthAttribAppend(PRTCRPKCS7ATTRIBUTES pAuthAttribs)
1721{
1722 int32_t iPos = RTCrPkcs7Attributes_Append(pAuthAttribs);
1723 if (iPos >= 0)
1724 return pAuthAttribs->papItems[iPos];
1725 RTMsgError("RTCrPkcs7Attributes_Append failed: %Rrc", iPos);
1726 return NULL;
1727}
1728
1729
1730static RTEXITCODE SignToolPkcs7_AuthAttribsAddSigningTime(PRTCRPKCS7ATTRIBUTES pAuthAttribs, RTTIMESPEC SigningTime)
1731{
1732 /*
1733 * Signing time. For the old-style timestamps, Symantec used ASN.1 UTC TIME.
1734 * start -vv vv=ASN1_TAG_UTC_TIME
1735 * 00000187d6a65fd0/23b0: 0d 01 09 05 31 0f 17 0d-31 36 31 30 30 35 30 37 ....1...16100507
1736 * 00000187d6a65fe0/23c0: 35 30 33 30 5a 30 23 06-09 2a 86 48 86 f7 0d 01 5030Z0#..*.H....
1737 * ^^- end 2016-10-05T07:50:30.000000000Z (161005075030Z)
1738 */
1739 PRTCRPKCS7ATTRIBUTE pAttr = SignToolPkcs7_AuthAttribAppend(pAuthAttribs);
1740 if (!pAttr)
1741 return RTEXITCODE_FAILURE;
1742
1743 int rc = RTCrPkcs7Attribute_SetSigningTime(pAttr, NULL, pAuthAttribs->Allocation.pAllocator);
1744 if (RT_FAILURE(rc))
1745 return RTMsgErrorExitFailure("RTCrPkcs7Attribute_SetSigningTime failed: %Rrc", rc);
1746
1747 /* Create the timestamp. */
1748 int32_t iPos = RTAsn1SetOfTimes_Append(pAttr->uValues.pSigningTime);
1749 if (iPos < 0)
1750 return RTMsgErrorExitFailure("RTAsn1SetOfTimes_Append failed: %Rrc", iPos);
1751
1752 PRTASN1TIME pTime = pAttr->uValues.pSigningTime->papItems[iPos];
1753 rc = RTAsn1Time_SetTimeSpec(pTime, pAttr->Allocation.pAllocator, &SigningTime);
1754 if (RT_FAILURE(rc))
1755 return RTMsgErrorExitFailure("RTAsn1Time_SetTimeSpec failed: %Rrc", rc);
1756
1757 return RTEXITCODE_SUCCESS;
1758}
1759
1760
1761static RTEXITCODE SignToolPkcs7_AuthAttribsAddSpcOpusInfo(PRTCRPKCS7ATTRIBUTES pAuthAttribs, void *pvInfo)
1762{
1763 /** @todo The OpusInfo is a structure with an optional SpcString and an
1764 * optional SpcLink (url). The two attributes can be set using the /d and /du
1765 * options of MS signtool.exe, I think. We shouldn't be using them atm. */
1766
1767 PRTCRPKCS7ATTRIBUTE pAttr = SignToolPkcs7_AuthAttribAppend(pAuthAttribs);
1768 if (!pAttr)
1769 return RTEXITCODE_FAILURE;
1770
1771 int rc = RTCrPkcs7Attribute_SetMsStatementType(pAttr, NULL, pAuthAttribs->Allocation.pAllocator);
1772 if (RT_FAILURE(rc))
1773 return RTMsgErrorExitFailure("RTCrPkcs7Attribute_SetMsStatementType failed: %Rrc", rc);
1774
1775 /* Override the ID. */
1776 rc = RTAsn1ObjId_SetFromString(&pAttr->Type, RTCR_PKCS9_ID_MS_SP_OPUS_INFO, pAuthAttribs->Allocation.pAllocator);
1777 if (RT_FAILURE(rc))
1778 return RTMsgErrorExitFailure("RTAsn1ObjId_SetFromString failed: %Rrc", rc);
1779
1780 /* Add attribute value entry. */
1781 int32_t iPos = RTAsn1SetOfObjIdSeqs_Append(pAttr->uValues.pObjIdSeqs);
1782 if (iPos < 0)
1783 return RTMsgErrorExitFailure("RTAsn1SetOfObjIdSeqs_Append failed: %Rrc", iPos);
1784
1785 RT_NOREF(pvInfo); Assert(!pvInfo);
1786 return RTEXITCODE_SUCCESS;
1787}
1788
1789
1790static RTEXITCODE SignToolPkcs7_AuthAttribsAddMsStatementType(PRTCRPKCS7ATTRIBUTES pAuthAttribs, const char *pszTypeId)
1791{
1792 PRTCRPKCS7ATTRIBUTE pAttr = SignToolPkcs7_AuthAttribAppend(pAuthAttribs);
1793 if (!pAttr)
1794 return RTEXITCODE_FAILURE;
1795
1796 int rc = RTCrPkcs7Attribute_SetMsStatementType(pAttr, NULL, pAuthAttribs->Allocation.pAllocator);
1797 if (RT_FAILURE(rc))
1798 return RTMsgErrorExitFailure("RTCrPkcs7Attribute_SetMsStatementType failed: %Rrc", rc);
1799
1800 /* Add attribute value entry. */
1801 int32_t iPos = RTAsn1SetOfObjIdSeqs_Append(pAttr->uValues.pObjIdSeqs);
1802 if (iPos < 0)
1803 return RTMsgErrorExitFailure("RTAsn1SetOfObjIdSeqs_Append failed: %Rrc", iPos);
1804 PRTASN1SEQOFOBJIDS pSeqObjIds = pAttr->uValues.pObjIdSeqs->papItems[iPos];
1805
1806 /* Add a object id to the value. */
1807 RTASN1OBJID ObjIdValue;
1808 rc = RTAsn1ObjId_InitFromString(&ObjIdValue, pszTypeId, &g_RTAsn1DefaultAllocator);
1809 if (RT_FAILURE(rc))
1810 return RTMsgErrorExitFailure("RTAsn1ObjId_InitFromString/%s failed: %Rrc", pszTypeId, rc);
1811
1812 rc = RTAsn1SeqOfObjIds_InsertEx(pSeqObjIds, 0 /*iPos*/, &ObjIdValue, &g_RTAsn1DefaultAllocator, NULL);
1813 RTAsn1ObjId_Delete(&ObjIdValue);
1814 if (RT_FAILURE(rc))
1815 return RTMsgErrorExitFailure("RTAsn1SeqOfObjIds_InsertEx failed: %Rrc", rc);
1816
1817 return RTEXITCODE_SUCCESS;
1818}
1819
1820
1821static RTEXITCODE SignToolPkcs7_AuthAttribsAddContentType(PRTCRPKCS7ATTRIBUTES pAuthAttribs, const char *pszContentTypeId)
1822{
1823 PRTCRPKCS7ATTRIBUTE pAttr = SignToolPkcs7_AuthAttribAppend(pAuthAttribs);
1824 if (!pAttr)
1825 return RTEXITCODE_FAILURE;
1826
1827 int rc = RTCrPkcs7Attribute_SetContentType(pAttr, NULL, pAuthAttribs->Allocation.pAllocator);
1828 if (RT_FAILURE(rc))
1829 return RTMsgErrorExitFailure("RTCrPkcs7Attribute_SetContentType failed: %Rrc", rc);
1830
1831 /* Add a object id to the value. */
1832 RTASN1OBJID ObjIdValue;
1833 rc = RTAsn1ObjId_InitFromString(&ObjIdValue, pszContentTypeId, pAuthAttribs->Allocation.pAllocator);
1834 if (RT_FAILURE(rc))
1835 return RTMsgErrorExitFailure("RTAsn1ObjId_InitFromString/%s failed: %Rrc", pszContentTypeId, rc);
1836
1837 rc = RTAsn1SetOfObjIds_InsertEx(pAttr->uValues.pObjIds, 0 /*iPos*/, &ObjIdValue, pAuthAttribs->Allocation.pAllocator, NULL);
1838 RTAsn1ObjId_Delete(&ObjIdValue);
1839 if (RT_FAILURE(rc))
1840 return RTMsgErrorExitFailure("RTAsn1SetOfObjIds_InsertEx failed: %Rrc", rc);
1841
1842 return RTEXITCODE_SUCCESS;
1843}
1844
1845
1846static RTEXITCODE SignToolPkcs7_AddAuthAttribsForTimestamp(PRTCRPKCS7ATTRIBUTES pAuthAttribs, bool fTimestampTypeOld,
1847 RTTIMESPEC SigningTime, PCRTCRX509CERTIFICATE pTimestampCert)
1848{
1849 /*
1850 * Add content type.
1851 */
1852 RTEXITCODE rcExit = SignToolPkcs7_AuthAttribsAddContentType(pAuthAttribs,
1853 fTimestampTypeOld ? RTCR_PKCS7_DATA_OID : RTCRTSPTSTINFO_OID);
1854 if (rcExit != RTEXITCODE_SUCCESS)
1855 return rcExit;
1856
1857 /*
1858 * Add signing time.
1859 */
1860 rcExit = SignToolPkcs7_AuthAttribsAddSigningTime(pAuthAttribs, SigningTime);
1861 if (rcExit != RTEXITCODE_SUCCESS)
1862 return rcExit;
1863
1864 /*
1865 * More later if we want to support fTimestampTypeOld = false perhaps?
1866 */
1867 Assert(fTimestampTypeOld);
1868 RT_NOREF(fTimestampTypeOld, pTimestampCert);
1869
1870 return RTEXITCODE_SUCCESS;
1871}
1872
1873
1874static RTEXITCODE SignToolPkcs7_AddAuthAttribsForImageOrCatSignature(PRTCRPKCS7ATTRIBUTES pAuthAttribs, RTTIMESPEC SigningTime,
1875 bool fNoSigningTime, const char *pszContentTypeId)
1876{
1877 /*
1878 * Add SpcOpusInfo. No attribute values.
1879 * SEQ start -vv vv- Type ObjId
1880 * 1c60: 0e 03 02 1a 05 00 a0 70-30 10 06 0a 2b 06 01 04 .......p0...+...
1881 * 1c70: 01 82 37 02 01 0c 31 02-30 00 30 19 06 09 2a 86 ..7...1.0.0...*.
1882 * Set Of -^^ ^^- Empty Sequence.
1883 */
1884 RTEXITCODE rcExit = SignToolPkcs7_AuthAttribsAddSpcOpusInfo(pAuthAttribs, NULL /*pvInfo - none*/);
1885 if (rcExit != RTEXITCODE_SUCCESS)
1886 return rcExit;
1887
1888 /*
1889 * Add ContentType = Ms-SpcIndirectDataContext?
1890 * SEQ start -vv vv- Type ObjId
1891 * 1c70: 01 82 37 02 01 0c 31 02-30 00 30 19 06 09 2a 86 ..7...1.0.0...*.
1892 * 1c80: 48 86 f7 0d 01 09 03 31-0c 06 0a 2b 06 01 04 01 H......1...+....
1893 * 1c90: 82 37 02 01 04 ^^- ^^- ObjId
1894 * ^- Set Of
1895 */
1896 rcExit = SignToolPkcs7_AuthAttribsAddContentType(pAuthAttribs, pszContentTypeId);
1897 if (rcExit != RTEXITCODE_SUCCESS)
1898 return rcExit;
1899
1900 /*
1901 * Add Ms-SpcStatementType = Ms-SpcIndividualCodeSigning.
1902 * SEQ start -vv vv- Type ObjId
1903 * 1c90: 82 37 02 01 04 30 1c 06-0a 2b 06 01 04 01 82 37 .7...0...+.....7
1904 * 1ca0: 02 01 0b 31 0e 30 0c 06-0a 2b 06 01 04 01 82 37 ...1.0...+.....7
1905 * 1cb0: 02 01 15 ^^ ^^ ^^- ObjId
1906 * Set Of -^^ ^^- Sequence Of
1907 */
1908 rcExit = SignToolPkcs7_AuthAttribsAddMsStatementType(pAuthAttribs, RTCRSPC_STMT_TYPE_INDIVIDUAL_CODE_SIGNING);
1909 if (rcExit != RTEXITCODE_SUCCESS)
1910 return rcExit;
1911
1912 /*
1913 * Add signing time. We add this, even if signtool.exe, since OpenSSL will always do it otherwise.
1914 */
1915 if (!fNoSigningTime) /** @todo requires disabling the code in do_pkcs7_signed_attrib that adds it when absent */
1916 {
1917 rcExit = SignToolPkcs7_AuthAttribsAddSigningTime(pAuthAttribs, SigningTime);
1918 if (rcExit != RTEXITCODE_SUCCESS)
1919 return rcExit;
1920 }
1921
1922 /** @todo more? Some certificate stuff? */
1923
1924 return RTEXITCODE_SUCCESS;
1925}
1926
1927
1928static RTEXITCODE SignToolPkcs7_PrependCounterSignature(PRTCRPKCS7SIGNERINFO pSignerInfo,
1929 PCRTCRPKCS7SIGNERINFO pCounterSignerInfo, unsigned cVerbosity)
1930{
1931 /* Make sure the UnauthenticatedAttributes member is there. */
1932 RTEXITCODE rcExit = SignToolPkcs7_EnsureUnauthenticatedAttributesPresent(pSignerInfo);
1933 if (rcExit != RTEXITCODE_SUCCESS)
1934 return rcExit;
1935
1936 /* Append an entry to UnauthenticatedAttributes. */
1937 uint32_t iPos;
1938 int rc = RTCrPkcs7Attributes_InsertEx(&pSignerInfo->UnauthenticatedAttributes, 0 /*iPosition*/, NULL /*pToClone*/,
1939 &g_RTAsn1DefaultAllocator, &iPos);
1940 if (RT_FAILURE(rc))
1941 return RTMsgErrorExitFailure("RTCrPkcs7Attributes_Append failed: %Rrc", rc);
1942 Assert(iPos < pSignerInfo->UnauthenticatedAttributes.cItems); Assert(iPos == 0);
1943 PRTCRPKCS7ATTRIBUTE pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[iPos];
1944
1945 if (cVerbosity >= 2)
1946 RTMsgInfo("Adding UnauthenticatedAttribute #%u...", iPos);
1947
1948 /* Create the attrib and its sub-set of counter signatures. */
1949 rc = RTCrPkcs7Attribute_SetCounterSignatures(pAttr, NULL, pAttr->Allocation.pAllocator);
1950 if (RT_FAILURE(rc))
1951 return RTMsgErrorExitFailure("RTCrPkcs7Attribute_SetCounterSignatures failed: %Rrc", rc);
1952
1953 /* Insert the counter signature. */
1954 rc = RTCrPkcs7SignerInfos_InsertEx(pAttr->uValues.pCounterSignatures, 0 /*iPosition*/, pCounterSignerInfo,
1955 pAttr->Allocation.pAllocator, NULL);
1956 if (RT_FAILURE(rc))
1957 return RTMsgErrorExitFailure("RTCrPkcs7SignerInfos_InsertEx failed: %Rrc", rc);
1958
1959 return RTEXITCODE_SUCCESS;
1960}
1961
1962
1963static RTEXITCODE SignToolPkcs7_AppendCertificate(PRTCRPKCS7SIGNEDDATA pSignedData, PCRTCRX509CERTIFICATE pCertToAppend)
1964{
1965 if (pSignedData->Certificates.cItems == 0 && !RTCrPkcs7SetOfCerts_IsPresent(&pSignedData->Certificates))
1966 return RTMsgErrorExitFailure("PKCS#7 signature includes no certificates! Didn't expect that");
1967
1968 /* Already there? */
1969 PCRTCRX509CERTIFICATE pExisting
1970 = RTCrPkcs7SetOfCerts_FindX509ByIssuerAndSerialNumber(&pSignedData->Certificates, &pCertToAppend->TbsCertificate.Issuer,
1971 &pCertToAppend->TbsCertificate.SerialNumber);
1972 if (!pExisting || RTCrX509Certificate_Compare(pExisting, pCertToAppend) != 0)
1973 {
1974 /* Prepend a RTCRPKCS7CERT entry. */
1975 uint32_t iPos;
1976 int rc = RTCrPkcs7SetOfCerts_InsertEx(&pSignedData->Certificates, 0 /*iPosition*/, NULL /*pToClone*/,
1977 &g_RTAsn1DefaultAllocator, &iPos);
1978 if (RT_FAILURE(rc))
1979 return RTMsgErrorExitFailure("RTCrPkcs7SetOfCerts_Append failed: %Rrc", rc);
1980 PRTCRPKCS7CERT pCertEntry = pSignedData->Certificates.papItems[iPos];
1981
1982 /* Set (clone) the certificate. */
1983 rc = RTCrPkcs7Cert_SetX509Cert(pCertEntry, pCertToAppend, pCertEntry->Allocation.pAllocator);
1984 if (RT_FAILURE(rc))
1985 return RTMsgErrorExitFailure("RTCrPkcs7Cert_X509Cert failed: %Rrc", rc);
1986 }
1987 return RTEXITCODE_SUCCESS;
1988}
1989
1990#ifdef RT_OS_WINDOWS
1991
1992static PCRTUTF16 GetBCryptNameFromCrDigest(RTCRDIGEST hDigest)
1993{
1994 switch (RTCrDigestGetType(hDigest))
1995 {
1996 case RTDIGESTTYPE_MD2: return BCRYPT_MD2_ALGORITHM;
1997 case RTDIGESTTYPE_MD4: return BCRYPT_MD4_ALGORITHM;
1998 case RTDIGESTTYPE_SHA1: return BCRYPT_SHA1_ALGORITHM;
1999 case RTDIGESTTYPE_SHA256: return BCRYPT_SHA256_ALGORITHM;
2000 case RTDIGESTTYPE_SHA384: return BCRYPT_SHA384_ALGORITHM;
2001 case RTDIGESTTYPE_SHA512: return BCRYPT_SHA512_ALGORITHM;
2002 default:
2003 RTMsgError("No BCrypt translation for %s/%d!", RTCrDigestGetAlgorithmOid(hDigest), RTCrDigestGetType(hDigest));
2004 return L"No BCrypt translation";
2005 }
2006}
2007
2008static RTEXITCODE
2009SignToolPkcs7_Pkcs7SignStuffAgainWithReal(const char *pszWhat, SignToolKeyPair *pCertKeyPair, unsigned cVerbosity,
2010 PRTCRPKCS7CONTENTINFO pContentInfo, void **ppvSigned, size_t *pcbSigned)
2011
2012{
2013 RT_NOREF(cVerbosity);
2014
2015 /*
2016 * First remove the fake certificate from the PKCS7 structure and insert the real one.
2017 */
2018 PRTCRPKCS7SIGNEDDATA pSignedData = pContentInfo->u.pSignedData;
2019 unsigned iCert = pSignedData->Certificates.cItems;
2020 unsigned cErased = 0;
2021 while (iCert-- > 0)
2022 {
2023 PCRTCRPKCS7CERT pCert = pSignedData->Certificates.papItems[iCert];
2024 if ( pCert->enmChoice == RTCRPKCS7CERTCHOICE_X509
2025 && RTCrX509Certificate_MatchIssuerAndSerialNumber(pCert->u.pX509Cert,
2026 &pCertKeyPair->pCertificate->TbsCertificate.Issuer,
2027 &pCertKeyPair->pCertificate->TbsCertificate.SerialNumber))
2028 {
2029 RTCrPkcs7SetOfCerts_Erase(&pSignedData->Certificates, iCert);
2030 cErased++;
2031 }
2032 }
2033 if (cErased == 0)
2034 return RTMsgErrorExitFailure("(%s) Failed to find temporary signing certificate in PKCS#7 from OpenSSL: %u certs",
2035 pszWhat, pSignedData->Certificates.cItems);
2036
2037 /* Then insert the real signing certificate. */
2038 PCRTCRX509CERTIFICATE const pRealCertificate = pCertKeyPair->getRealCertificate();
2039 RTEXITCODE rcExit = SignToolPkcs7_AppendCertificate(pSignedData, pRealCertificate);
2040 if (rcExit != RTEXITCODE_SUCCESS)
2041 return rcExit;
2042
2043 /*
2044 * Modify the signer info to reflect the real certificate.
2045 */
2046 PRTCRPKCS7SIGNERINFO pSignerInfo = pSignedData->SignerInfos.papItems[0];
2047 RTCrX509Name_Delete(&pSignerInfo->IssuerAndSerialNumber.Name);
2048 int rc = RTCrX509Name_Clone(&pSignerInfo->IssuerAndSerialNumber.Name,
2049 &pRealCertificate->TbsCertificate.Issuer, &g_RTAsn1DefaultAllocator);
2050 if (RT_FAILURE(rc))
2051 return RTMsgErrorExitFailure("(%s) RTCrX509Name_Clone failed: %Rrc", pszWhat, rc);
2052
2053 RTAsn1Integer_Delete(&pSignerInfo->IssuerAndSerialNumber.SerialNumber);
2054 rc = RTAsn1Integer_Clone(&pSignerInfo->IssuerAndSerialNumber.SerialNumber,
2055 &pRealCertificate->TbsCertificate.SerialNumber, &g_RTAsn1DefaultAllocator);
2056 if (RT_FAILURE(rc))
2057 return RTMsgErrorExitFailure("(%s) RTAsn1Integer_Clone failed: %Rrc", pszWhat, rc);
2058
2059 /* There shouldn't be anything in the authenticated attributes that
2060 we need to modify... */
2061
2062 /*
2063 * Now a create a new signature using the real key. Since we haven't modified
2064 * the authenticated attributes, we can just hash them as-is.
2065 */
2066 /* Create the hash to sign. */
2067 RTCRDIGEST hDigest;
2068 rc = RTCrDigestCreateByObjId(&hDigest, &pSignerInfo->DigestAlgorithm.Algorithm);
2069 if (RT_FAILURE(rc))
2070 return RTMsgErrorExitFailure("(%s) RTCrDigestCreateByObjId failed on '%s': %Rrc",
2071 pszWhat, pSignerInfo->DigestAlgorithm.Algorithm.szObjId, rc);
2072
2073 rcExit = RTEXITCODE_FAILURE;
2074 RTERRINFOSTATIC ErrInfo;
2075 rc = RTCrPkcs7Attributes_HashAttributes(&pSignerInfo->AuthenticatedAttributes, hDigest, RTErrInfoInitStatic(&ErrInfo));
2076 if (RT_SUCCESS(rc))
2077 {
2078 BCRYPT_PKCS1_PADDING_INFO PaddingInfo = { GetBCryptNameFromCrDigest(hDigest) };
2079 DWORD cbSignature = 0;
2080 SECURITY_STATUS rcNCrypt = NCryptSignHash(pCertKeyPair->hNCryptPrivateKey, &PaddingInfo,
2081 (PBYTE)RTCrDigestGetHash(hDigest), RTCrDigestGetHashSize(hDigest),
2082 NULL, 0, &cbSignature, NCRYPT_SILENT_FLAG | BCRYPT_PAD_PKCS1);
2083 if (rcNCrypt == ERROR_SUCCESS)
2084 {
2085 if (cVerbosity)
2086 RTMsgInfo("PaddingInfo: '%ls' cb=%#x, was %#zx\n",
2087 PaddingInfo.pszAlgId, cbSignature, pSignerInfo->EncryptedDigest.Asn1Core.cb);
2088
2089 rc = RTAsn1OctetString_AllocContent(&pSignerInfo->EncryptedDigest, NULL /*pvSrc*/, cbSignature,
2090 &g_RTAsn1DefaultAllocator);
2091 if (RT_SUCCESS(rc))
2092 {
2093 Assert(pSignerInfo->EncryptedDigest.Asn1Core.uData.pv);
2094 rcNCrypt = NCryptSignHash(pCertKeyPair->hNCryptPrivateKey, &PaddingInfo,
2095 (PBYTE)RTCrDigestGetHash(hDigest), RTCrDigestGetHashSize(hDigest),
2096 (PBYTE)pSignerInfo->EncryptedDigest.Asn1Core.uData.pv, cbSignature, &cbSignature,
2097 /*NCRYPT_SILENT_FLAG |*/ BCRYPT_PAD_PKCS1);
2098 if (rcNCrypt == ERROR_SUCCESS)
2099 {
2100 /*
2101 * Now we need to re-encode the whole thing and decode it again.
2102 */
2103 PRTASN1CORE pRoot = RTCrPkcs7ContentInfo_GetAsn1Core(pContentInfo);
2104 uint32_t cbRealSigned;
2105 rc = RTAsn1EncodePrepare(pRoot, RTASN1ENCODE_F_DER, &cbRealSigned, RTErrInfoInitStatic(&ErrInfo));
2106 if (RT_SUCCESS(rc))
2107 {
2108 void *pvRealSigned = RTMemAllocZ(cbRealSigned);
2109 if (pvRealSigned)
2110 {
2111 rc = RTAsn1EncodeToBuffer(pRoot, RTASN1ENCODE_F_DER, pvRealSigned, cbRealSigned,
2112 RTErrInfoInitStatic(&ErrInfo));
2113 if (RT_SUCCESS(rc))
2114 {
2115 /* Decode it */
2116 RTCrPkcs7ContentInfo_Delete(pContentInfo);
2117
2118 RTASN1CURSORPRIMARY PrimaryCursor;
2119 RTAsn1CursorInitPrimary(&PrimaryCursor, pvRealSigned, cbRealSigned, RTErrInfoInitStatic(&ErrInfo),
2120 &g_RTAsn1DefaultAllocator, 0, pszWhat);
2121 rc = RTCrPkcs7ContentInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, pContentInfo, "CI");
2122 if (RT_SUCCESS(rc))
2123 {
2124 Assert(RTCrPkcs7ContentInfo_IsSignedData(pContentInfo));
2125
2126 /* Almost done! Just replace output buffer. */
2127 RTMemFree(*ppvSigned);
2128 *ppvSigned = pvRealSigned;
2129 *pcbSigned = cbRealSigned;
2130 pvRealSigned = NULL;
2131 rcExit = RTEXITCODE_SUCCESS;
2132 }
2133 else
2134 RTMsgError("(%s) RTCrPkcs7ContentInfo_DecodeAsn1 failed: %Rrc%#RTeim",
2135 pszWhat, rc, &ErrInfo.Core);
2136 }
2137 else
2138 RTMsgError("(%s) RTAsn1EncodeToBuffer failed: %Rrc%#RTeim", pszWhat, rc, &ErrInfo.Core);
2139
2140 RTMemFree(pvRealSigned);
2141 }
2142 else
2143 RTMsgError("(%s) Failed to allocate %u bytes!", pszWhat, cbRealSigned);
2144 }
2145 else
2146 RTMsgError("(%s) RTAsn1EncodePrepare failed: %Rrc%#RTeim", pszWhat, rc, &ErrInfo.Core);
2147 }
2148 else
2149 RTMsgError("(%s) NCryptSignHash/2 failed: %Rwc %#x (%u)", pszWhat, rcNCrypt, rcNCrypt, rcNCrypt);
2150 }
2151 else
2152 RTMsgError("(%s) RTAsn1OctetString_AllocContent(,,%#x) failed: %Rrc", pszWhat, cbSignature, rc);
2153 }
2154 else
2155 RTMsgError("(%s) NCryptSignHash/1 failed: %Rwc %#x (%u)", pszWhat, rcNCrypt, rcNCrypt, rcNCrypt);
2156 }
2157 else
2158 RTMsgError("(%s) RTCrPkcs7Attributes_HashAttributes failed: %Rrc%#RTeim", pszWhat, rc, &ErrInfo.Core);
2159 RTCrDigestRelease(hDigest);
2160 return rcExit;
2161}
2162
2163#endif /* RT_OS_WINDOWS */
2164
2165static RTEXITCODE SignToolPkcs7_Pkcs7SignStuffInner(const char *pszWhat, const void *pvToDataToSign, size_t cbToDataToSign,
2166 PCRTCRPKCS7ATTRIBUTES pAuthAttribs, RTCRSTORE hAdditionalCerts,
2167 uint32_t fExtraFlags, RTDIGESTTYPE enmDigestType,
2168 SignToolKeyPair *pCertKeyPair, unsigned cVerbosity,
2169 void **ppvSigned, size_t *pcbSigned, PRTCRPKCS7CONTENTINFO pContentInfo,
2170 PRTCRPKCS7SIGNEDDATA *ppSignedData)
2171{
2172 *ppvSigned = NULL;
2173 if (pcbSigned)
2174 *pcbSigned = 0;
2175 if (ppSignedData)
2176 *ppSignedData = NULL;
2177
2178 /* Figure out how large the signature will be. */
2179 uint32_t const fSignFlags = RTCRPKCS7SIGN_SD_F_USE_V1 | RTCRPKCS7SIGN_SD_F_NO_SMIME_CAP | fExtraFlags;
2180 size_t cbSigned = 1024;
2181 RTERRINFOSTATIC ErrInfo;
2182 int rc = RTCrPkcs7SimpleSignSignedData(fSignFlags, pCertKeyPair->pCertificate, pCertKeyPair->hPrivateKey,
2183 pvToDataToSign, cbToDataToSign,enmDigestType, hAdditionalCerts, pAuthAttribs,
2184 NULL, &cbSigned, RTErrInfoInitStatic(&ErrInfo));
2185 if (rc != VERR_BUFFER_OVERFLOW)
2186 return RTMsgErrorExitFailure("(%s) RTCrPkcs7SimpleSignSignedData failed: %Rrc%#RTeim", pszWhat, rc, &ErrInfo.Core);
2187
2188 /* Allocate memory for it and do the actual signing. */
2189 void *pvSigned = RTMemAllocZ(cbSigned);
2190 if (!pvSigned)
2191 return RTMsgErrorExitFailure("(%s) Failed to allocate %#zx bytes for %s signature", pszWhat, cbSigned, pszWhat);
2192 rc = RTCrPkcs7SimpleSignSignedData(fSignFlags, pCertKeyPair->pCertificate, pCertKeyPair->hPrivateKey,
2193 pvToDataToSign, cbToDataToSign, enmDigestType, hAdditionalCerts, pAuthAttribs,
2194 pvSigned, &cbSigned, RTErrInfoInitStatic(&ErrInfo));
2195 if (RT_SUCCESS(rc))
2196 {
2197 if (cVerbosity > 2)
2198 RTMsgInfo("%s signature: %#zx bytes\n%.*Rhxd\n", pszWhat, cbSigned, cbSigned, pvSigned);
2199
2200 /*
2201 * Decode the signature and check that it is SignedData.
2202 */
2203 RTASN1CURSORPRIMARY PrimaryCursor;
2204 RTAsn1CursorInitPrimary(&PrimaryCursor, pvSigned, (uint32_t)cbSigned, RTErrInfoInitStatic(&ErrInfo),
2205 &g_RTAsn1DefaultAllocator, 0, pszWhat);
2206 rc = RTCrPkcs7ContentInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, pContentInfo, "CI");
2207 if (RT_SUCCESS(rc))
2208 {
2209 if (RTCrPkcs7ContentInfo_IsSignedData(pContentInfo))
2210 {
2211#ifdef RT_OS_WINDOWS
2212 /*
2213 * If we're using a fake key+cert, we now have to re-do the signing using the real
2214 * key+cert and the windows crypto API. This kludge is necessary because we can't
2215 * typically get that the encoded private key, so it isn't possible to feed it to
2216 * openssl.
2217 */
2218 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
2219 if (pCertKeyPair->pCertificateReal)
2220 rcExit = SignToolPkcs7_Pkcs7SignStuffAgainWithReal(pszWhat, pCertKeyPair, cVerbosity, pContentInfo,
2221 &pvSigned, &cbSigned);
2222 if (rcExit == RTEXITCODE_SUCCESS)
2223#endif
2224 {
2225 /*
2226 * Set returns and maybe display the result before returning.
2227 */
2228 *ppvSigned = pvSigned;
2229 if (pcbSigned)
2230 *pcbSigned = cbSigned;
2231 if (ppSignedData)
2232 *ppSignedData = pContentInfo->u.pSignedData;
2233
2234 if (cVerbosity)
2235 {
2236 SHOWEXEPKCS7 ShowExe;
2237 RT_ZERO(ShowExe);
2238 ShowExe.cVerbosity = cVerbosity;
2239 HandleShowExeWorkerPkcs7Display(&ShowExe, pContentInfo->u.pSignedData, 0, pContentInfo);
2240 }
2241 return RTEXITCODE_SUCCESS;
2242 }
2243 }
2244
2245 RTMsgError("(%s) RTCrPkcs7SimpleSignSignedData did not create SignedData: %s",
2246 pszWhat, pContentInfo->ContentType.szObjId);
2247 }
2248 else
2249 RTMsgError("(%s) RTCrPkcs7ContentInfo_DecodeAsn1 failed: %Rrc%#RTeim", pszWhat, rc, &ErrInfo.Core);
2250 RTCrPkcs7ContentInfo_Delete(pContentInfo);
2251 }
2252 RTMemFree(pvSigned);
2253 return RTEXITCODE_FAILURE;
2254}
2255
2256
2257static RTEXITCODE SignToolPkcs7_Pkcs7SignStuff(const char *pszWhat, const void *pvToDataToSign, size_t cbToDataToSign,
2258 PCRTCRPKCS7ATTRIBUTES pAuthAttribs, RTCRSTORE hAdditionalCerts,
2259 uint32_t fExtraFlags, RTDIGESTTYPE enmDigestType, SignToolKeyPair *pCertKeyPair,
2260 unsigned cVerbosity, void **ppvSigned, size_t *pcbSigned,
2261 PRTCRPKCS7CONTENTINFO pContentInfo, PRTCRPKCS7SIGNEDDATA *ppSignedData)
2262{
2263 /*
2264 * Gather all additional certificates before doing the actual work.
2265 */
2266 RTCRSTORE hAllAdditionalCerts = pCertKeyPair->assembleAllAdditionalCertificates(hAdditionalCerts);
2267 if (hAllAdditionalCerts == NIL_RTCRSTORE)
2268 return RTEXITCODE_FAILURE;
2269 RTEXITCODE rcExit = SignToolPkcs7_Pkcs7SignStuffInner(pszWhat, pvToDataToSign, cbToDataToSign, pAuthAttribs,
2270 hAllAdditionalCerts, fExtraFlags, enmDigestType, pCertKeyPair,
2271 cVerbosity, ppvSigned, pcbSigned, pContentInfo, ppSignedData);
2272 RTCrStoreRelease(hAllAdditionalCerts);
2273 return rcExit;
2274}
2275
2276
2277static RTEXITCODE SignToolPkcs7_AddTimestampSignatureEx(PRTCRPKCS7SIGNERINFO pSignerInfo, PRTCRPKCS7SIGNEDDATA pSignedData,
2278 unsigned cVerbosity, bool fReplaceExisting, bool fTimestampTypeOld,
2279 RTTIMESPEC SigningTime, SignToolKeyPair *pTimestampPair)
2280{
2281 AssertReturn(fTimestampTypeOld, RTMsgErrorExitFailure("New style signatures not supported yet"));
2282
2283 /*
2284 * Create a set of attributes we need to include in the AuthenticatedAttributes
2285 * of the timestamp signature.
2286 */
2287 RTCRPKCS7ATTRIBUTES AuthAttribs;
2288 int rc = RTCrPkcs7Attributes_Init(&AuthAttribs, &g_RTAsn1DefaultAllocator);
2289 if (RT_FAILURE(rc))
2290 return RTMsgErrorExitFailure("RTCrPkcs7SetOfAttributes_Init failed: %Rrc", rc);
2291
2292 RTEXITCODE rcExit = SignToolPkcs7_AddAuthAttribsForTimestamp(&AuthAttribs, fTimestampTypeOld, SigningTime,
2293 pTimestampPair->getRealCertificate());
2294 if (rcExit == RTEXITCODE_SUCCESS)
2295 {
2296 /*
2297 * Now create a PKCS#7 signature of the encrypted signature from the selected signer info.
2298 */
2299 void *pvSigned = NULL;
2300 PRTCRPKCS7SIGNEDDATA pTsSignedData = NULL;
2301 RTCRPKCS7CONTENTINFO TsContentInfo;
2302 rcExit = SignToolPkcs7_Pkcs7SignStuffInner("timestamp", pSignerInfo->EncryptedDigest.Asn1Core.uData.pv,
2303 pSignerInfo->EncryptedDigest.Asn1Core.cb, &AuthAttribs,
2304 NIL_RTCRSTORE /*hAdditionalCerts*/, RTCRPKCS7SIGN_SD_F_DEATCHED,
2305 RTDIGESTTYPE_SHA1, pTimestampPair, cVerbosity,
2306 &pvSigned, NULL /*pcbSigned*/, &TsContentInfo, &pTsSignedData);
2307 if (rcExit == RTEXITCODE_SUCCESS)
2308 {
2309
2310 /*
2311 * If we're replacing existing timestamp signatures, remove old ones now.
2312 */
2313 if ( fReplaceExisting
2314 && RTCrPkcs7Attributes_IsPresent(&pSignerInfo->UnauthenticatedAttributes))
2315 {
2316 uint32_t iItem = pSignerInfo->UnauthenticatedAttributes.cItems;
2317 while (iItem-- > 0)
2318 {
2319 PRTCRPKCS7ATTRIBUTE pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[iItem];
2320 if (pAttr->enmType == RTCRPKCS7ATTRIBUTETYPE_COUNTER_SIGNATURES) /* ASSUMES all counter sigs are timstamps */
2321 {
2322 if (cVerbosity > 1)
2323 RTMsgInfo("Removing counter signature in attribute #%u\n", iItem);
2324 rc = RTCrPkcs7Attributes_Erase(&pSignerInfo->UnauthenticatedAttributes, iItem);
2325 if (RT_FAILURE(rc))
2326 rcExit = RTMsgErrorExitFailure("RTCrPkcs7Attributes_Erase failed on #%u: %Rrc", iItem, rc);
2327 }
2328 }
2329 }
2330
2331 /*
2332 * Add the new one.
2333 */
2334 if (rcExit == RTEXITCODE_SUCCESS)
2335 rcExit = SignToolPkcs7_PrependCounterSignature(pSignerInfo, pTsSignedData->SignerInfos.papItems[0], cVerbosity);
2336
2337 /*
2338 * Make sure the signing certificate is included.
2339 */
2340 if (rcExit == RTEXITCODE_SUCCESS)
2341 {
2342 rcExit = SignToolPkcs7_AppendCertificate(pSignedData, pTimestampPair->getRealCertificate());
2343
2344 PCRTCRCERTCTX pInterCaCtx = NULL;
2345 while ((pInterCaCtx = pTimestampPair->findNextIntermediateCert(pInterCaCtx)) != NULL)
2346 if (rcExit == RTEXITCODE_SUCCESS)
2347 rcExit = SignToolPkcs7_AppendCertificate(pSignedData, pInterCaCtx->pCert);
2348 }
2349
2350 /*
2351 * Clean up.
2352 */
2353 RTCrPkcs7ContentInfo_Delete(&TsContentInfo);
2354 RTMemFree(pvSigned);
2355 }
2356 }
2357 RTCrPkcs7Attributes_Delete(&AuthAttribs);
2358 return rcExit;
2359}
2360
2361
2362static RTEXITCODE SignToolPkcs7_AddTimestampSignature(SIGNTOOLPKCS7EXE *pThis, unsigned cVerbosity, unsigned iSignature,
2363 bool fReplaceExisting, bool fTimestampTypeOld, RTTIMESPEC SigningTime,
2364 SignToolKeyPair *pTimestampPair)
2365{
2366 AssertReturn(fTimestampTypeOld, RTMsgErrorExitFailure("New style signatures not supported yet"));
2367
2368 /*
2369 * Locate the signature specified by iSignature and add a timestamp to it.
2370 */
2371 PRTCRPKCS7SIGNEDDATA pSignedData = NULL;
2372 PRTCRPKCS7SIGNERINFO pSignerInfo = SignToolPkcs7_FindNestedSignatureByIndex(pThis, iSignature, &pSignedData);
2373 if (!pSignerInfo)
2374 return RTMsgErrorExitFailure("No signature #%u in %s", iSignature, pThis->pszFilename);
2375
2376 return SignToolPkcs7_AddTimestampSignatureEx(pSignerInfo, pSignedData, cVerbosity, fReplaceExisting, fTimestampTypeOld,
2377 SigningTime, pTimestampPair);
2378}
2379
2380
2381typedef enum SIGNDATATWEAK
2382{
2383 kSignDataTweak_NoTweak = 1,
2384 kSignDataTweak_RootIsParent
2385} SIGNDATATWEAK;
2386
2387static RTEXITCODE SignToolPkcs7_SignData(SIGNTOOLPKCS7 *pThis, PRTASN1CORE pToSignRoot, SIGNDATATWEAK enmTweak,
2388 const char *pszContentTypeId, unsigned cVerbosity, uint32_t fExtraFlags,
2389 RTDIGESTTYPE enmSigType, bool fReplaceExisting, bool fNoSigningTime,
2390 SignToolKeyPair *pSigningCertKey, RTCRSTORE hAddCerts,
2391 bool fTimestampTypeOld, RTTIMESPEC SigningTime, SignToolKeyPair *pTimestampCertKey)
2392{
2393 /*
2394 * Encode it.
2395 */
2396 RTERRINFOSTATIC ErrInfo;
2397 uint32_t cbEncoded = 0;
2398 int rc = RTAsn1EncodePrepare(pToSignRoot, RTASN1ENCODE_F_DER, &cbEncoded, RTErrInfoInitStatic(&ErrInfo));
2399 if (RT_FAILURE(rc))
2400 return RTMsgErrorExitFailure("RTAsn1EncodePrepare failed: %Rrc%RTeim", rc, &ErrInfo.Core);
2401
2402 if (cVerbosity >= 4)
2403 RTAsn1Dump(pToSignRoot, 0, 0, RTStrmDumpPrintfV, g_pStdOut);
2404
2405 uint8_t *pbEncoded = (uint8_t *)RTMemTmpAllocZ(cbEncoded );
2406 if (!pbEncoded)
2407 return RTMsgErrorExitFailure("Failed to allocate %#z bytes for encoding data we're signing (%s)",
2408 cbEncoded, pszContentTypeId);
2409
2410 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
2411 rc = RTAsn1EncodeToBuffer(pToSignRoot, RTASN1ENCODE_F_DER, pbEncoded, cbEncoded, RTErrInfoInitStatic(&ErrInfo));
2412 if (RT_SUCCESS(rc))
2413 {
2414 size_t const cbToSign = cbEncoded - (enmTweak == kSignDataTweak_RootIsParent ? pToSignRoot->cbHdr : 0);
2415 void const *pvToSign = pbEncoded + (enmTweak == kSignDataTweak_RootIsParent ? pToSignRoot->cbHdr : 0);
2416
2417 /*
2418 * Create additional authenticated attributes.
2419 */
2420 RTCRPKCS7ATTRIBUTES AuthAttribs;
2421 rc = RTCrPkcs7Attributes_Init(&AuthAttribs, &g_RTAsn1DefaultAllocator);
2422 if (RT_SUCCESS(rc))
2423 {
2424 rcExit = SignToolPkcs7_AddAuthAttribsForImageOrCatSignature(&AuthAttribs, SigningTime, fNoSigningTime,
2425 pszContentTypeId);
2426 if (rcExit == RTEXITCODE_SUCCESS)
2427 {
2428 /*
2429 * Ditch the old signature if so desired.
2430 * (It is okay to do this in the CAT case too, as we've already
2431 * encoded the data and won't touch pToSignRoot any more.)
2432 */
2433 pToSignRoot = NULL; /* (may become invalid if replacing) */
2434 if (fReplaceExisting && pThis->pSignedData)
2435 {
2436 RTCrPkcs7ContentInfo_Delete(&pThis->ContentInfo);
2437 pThis->pSignedData = NULL;
2438 RTMemFree(pThis->pbBuf);
2439 pThis->pbBuf = NULL;
2440 pThis->cbBuf = 0;
2441 }
2442
2443 /*
2444 * Do the actual signing.
2445 */
2446 SIGNTOOLPKCS7 Src = { RTSIGNTOOLFILETYPE_DETECT, NULL, 0, NULL };
2447 PSIGNTOOLPKCS7 pSigDst = !pThis->pSignedData ? pThis : &Src;
2448 rcExit = SignToolPkcs7_Pkcs7SignStuff("image", pvToSign, cbToSign, &AuthAttribs, hAddCerts,
2449 fExtraFlags | RTCRPKCS7SIGN_SD_F_NO_DATA_ENCAP, enmSigType /** @todo ?? */,
2450 pSigningCertKey, cVerbosity,
2451 (void **)&pSigDst->pbBuf, &pSigDst->cbBuf,
2452 &pSigDst->ContentInfo, &pSigDst->pSignedData);
2453 if (rcExit == RTEXITCODE_SUCCESS)
2454 {
2455 /*
2456 * Add a timestamp signature if requested.
2457 */
2458 if (pTimestampCertKey->isComplete())
2459 rcExit = SignToolPkcs7_AddTimestampSignatureEx(pSigDst->pSignedData->SignerInfos.papItems[0],
2460 pSigDst->pSignedData,
2461 cVerbosity, false /*fReplaceExisting*/,
2462 fTimestampTypeOld, SigningTime, pTimestampCertKey);
2463
2464 /*
2465 * Append the signature to the existing one, if that's what we're doing.
2466 */
2467 if (rcExit == RTEXITCODE_SUCCESS && pSigDst == &Src)
2468 rcExit = SignToolPkcs7_AddNestedSignature(pThis, &Src, cVerbosity, true /*fPrepend*/); /** @todo prepend/append option */
2469
2470 /* cleanup */
2471 if (pSigDst == &Src)
2472 SignToolPkcs7_Delete(&Src);
2473 }
2474
2475 }
2476 RTCrPkcs7Attributes_Delete(&AuthAttribs);
2477 }
2478 else
2479 RTMsgError("RTCrPkcs7SetOfAttributes_Init failed: %Rrc", rc);
2480 }
2481 else
2482 RTMsgError("RTAsn1EncodeToBuffer failed: %Rrc", rc);
2483 RTMemTmpFree(pbEncoded);
2484 return rcExit;
2485}
2486
2487
2488static RTEXITCODE SignToolPkcs7_SpcCompleteWithoutPageHashes(RTCRSPCINDIRECTDATACONTENT *pSpcIndData)
2489{
2490 PCRTASN1ALLOCATORVTABLE const pAllocator = &g_RTAsn1DefaultAllocator;
2491 PRTCRSPCPEIMAGEDATA const pPeImage = pSpcIndData->Data.uValue.pPeImage;
2492 Assert(pPeImage);
2493
2494 /*
2495 * Set it to File with an empty name.
2496 * RTCRSPCPEIMAGEDATA::Flags -vv
2497 * RTCRSPCPEIMAGEDATA::SeqCore -vv T0 -vv vv- pT2/CtxTag2
2498 * 0040: 04 01 82 37 02 01 0f 30-09 03 01 00 a0 04 a2 02 ...7...0........
2499 * 0050: 80 00 30 21 30 09 06 05-2b 0e 03 02 1a 05 00 04 ..0!0...+.......
2500 * ^^- pUcs2 / empty string
2501 */
2502
2503 /* Create an empty BMP string. */
2504 RTASN1STRING EmptyStr;
2505 int rc = RTAsn1BmpString_Init(&EmptyStr, pAllocator);
2506 if (RT_FAILURE(rc))
2507 return RTMsgErrorExitFailure("RTAsn1BmpString_Init/Ucs2 failed: %Rrc", rc);
2508
2509 /* Create an SPC string and use the above empty string with the Ucs2 setter. */
2510 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
2511 RTCRSPCSTRING SpcString;
2512 rc = RTCrSpcString_Init(&SpcString, pAllocator);
2513 if (RT_SUCCESS(rc))
2514 {
2515 rc = RTCrSpcString_SetUcs2(&SpcString, &EmptyStr, pAllocator);
2516 if (RT_SUCCESS(rc))
2517 {
2518 /* Create a temporary SpcLink with the empty SpcString. */
2519 RTCRSPCLINK SpcLink;
2520 rc = RTCrSpcLink_Init(&SpcLink, pAllocator);
2521 if (RT_SUCCESS(rc))
2522 {
2523 /* Use the setter on the SpcLink object to copy the SpcString to it. */
2524 rc = RTCrSpcLink_SetFile(&SpcLink, &SpcString, pAllocator);
2525 if (RT_SUCCESS(rc))
2526 {
2527 /* Use the setter to copy SpcLink to the PeImage structure. */
2528 rc = RTCrSpcPeImageData_SetFile(pPeImage, &SpcLink, pAllocator);
2529 if (RT_SUCCESS(rc))
2530 rcExit = RTEXITCODE_SUCCESS;
2531 else
2532 RTMsgError("RTCrSpcPeImageData_SetFile failed: %Rrc", rc);
2533 }
2534 else
2535 RTMsgError("RTCrSpcLink_SetFile failed: %Rrc", rc);
2536 RTCrSpcLink_Delete(&SpcLink);
2537 }
2538 else
2539 RTMsgError("RTCrSpcLink_Init failed: %Rrc", rc);
2540 }
2541 else
2542 RTMsgError("RTCrSpcString_SetUcs2 failed: %Rrc", rc);
2543 RTCrSpcString_Delete(&SpcString);
2544 }
2545 else
2546 RTMsgError("RTCrSpcString_Init failed: %Rrc", rc);
2547 RTAsn1BmpString_Delete(&EmptyStr);
2548 return rcExit;
2549}
2550
2551
2552static RTEXITCODE SignToolPkcs7_SpcAddImagePageHashes(SIGNTOOLPKCS7EXE *pThis, RTCRSPCINDIRECTDATACONTENT *pSpcIndData,
2553 RTDIGESTTYPE enmSigType)
2554{
2555 PCRTASN1ALLOCATORVTABLE const pAllocator = &g_RTAsn1DefaultAllocator;
2556 PRTCRSPCPEIMAGEDATA const pPeImage = pSpcIndData->Data.uValue.pPeImage;
2557 Assert(pPeImage);
2558
2559 /*
2560 * The hashes are stored in the 'Moniker' attribute.
2561 */
2562 /* Create a temporary SpcLink with a default moniker. */
2563 RTCRSPCLINK SpcLink;
2564 int rc = RTCrSpcLink_Init(&SpcLink, pAllocator);
2565 if (RT_FAILURE(rc))
2566 return RTMsgErrorExitFailure("RTCrSpcLink_Init failed: %Rrc", rc);
2567 rc = RTCrSpcLink_SetMoniker(&SpcLink, NULL, pAllocator);
2568 if (RT_SUCCESS(rc))
2569 {
2570 /* Use the setter to copy SpcLink to the PeImage structure. */
2571 rc = RTCrSpcPeImageData_SetFile(pPeImage, &SpcLink, pAllocator);
2572 if (RT_FAILURE(rc))
2573 RTMsgError("RTCrSpcLink_SetFile failed: %Rrc", rc);
2574 }
2575 else
2576 RTMsgError("RTCrSpcLink_SetMoniker failed: %Rrc", rc);
2577 RTCrSpcLink_Delete(&SpcLink);
2578 if (RT_FAILURE(rc))
2579 return RTEXITCODE_FAILURE;
2580
2581 /*
2582 * Now go to work on the moniker. It doesn't have any autogenerated
2583 * setters, so we must do stuff manually.
2584 */
2585 PRTCRSPCSERIALIZEDOBJECT pMoniker = pPeImage->T0.File.u.pMoniker;
2586 RTUUID Uuid;
2587 rc = RTUuidFromStr(&Uuid, RTCRSPCSERIALIZEDOBJECT_UUID_STR);
2588 if (RT_FAILURE(rc))
2589 return RTMsgErrorExitFailure("RTUuidFromStr failed: %Rrc", rc);
2590
2591 rc = RTAsn1OctetString_AllocContent(&pMoniker->Uuid, &Uuid, sizeof(Uuid), pAllocator);
2592 if (RT_FAILURE(rc))
2593 return RTMsgErrorExitFailure("RTAsn1String_InitWithValue/UUID failed: %Rrc", rc);
2594
2595 /* Create a new set of attributes and associate this with the SerializedData member. */
2596 PRTCRSPCSERIALIZEDOBJECTATTRIBUTES pSpcAttribs;
2597 rc = RTAsn1MemAllocZ(&pMoniker->SerializedData.EncapsulatedAllocation,
2598 (void **)&pSpcAttribs, sizeof(*pSpcAttribs));
2599 if (RT_FAILURE(rc))
2600 return RTMsgErrorExitFailure("RTAsn1MemAllocZ/pSpcAttribs failed: %Rrc", rc);
2601 pMoniker->SerializedData.pEncapsulated = RTCrSpcSerializedObjectAttributes_GetAsn1Core(pSpcAttribs);
2602 pMoniker->enmType = RTCRSPCSERIALIZEDOBJECTTYPE_ATTRIBUTES;
2603 pMoniker->u.pData = pSpcAttribs;
2604
2605 rc = RTCrSpcSerializedObjectAttributes_Init(pSpcAttribs, pAllocator);
2606 if (RT_FAILURE(rc))
2607 return RTMsgErrorExitFailure("RTCrSpcSerializedObjectAttributes_Init failed: %Rrc", rc);
2608
2609 /*
2610 * Add a single attribute to the set that we'll use for page hashes.
2611 */
2612 int32_t iPos = RTCrSpcSerializedObjectAttributes_Append(pSpcAttribs);
2613 if (iPos < 0)
2614 return RTMsgErrorExitFailure("RTCrSpcSerializedObjectAttributes_Append failed: %Rrc", iPos);
2615 PRTCRSPCSERIALIZEDOBJECTATTRIBUTE pSpcObjAttr = pSpcAttribs->papItems[iPos];
2616
2617 if (enmSigType == RTDIGESTTYPE_SHA1)
2618 rc = RTCrSpcSerializedObjectAttribute_SetV1Hashes(pSpcObjAttr, NULL, pAllocator);
2619 else if (enmSigType == RTDIGESTTYPE_SHA256)
2620 rc = RTCrSpcSerializedObjectAttribute_SetV2Hashes(pSpcObjAttr, NULL, pAllocator);
2621 else
2622 rc = VERR_CR_DIGEST_NOT_SUPPORTED;
2623 if (RT_FAILURE(rc))
2624 return RTMsgErrorExitFailure("RTCrSpcSerializedObjectAttribute_SetV1Hashes/SetV2Hashes failed: %Rrc", rc);
2625 PRTCRSPCSERIALIZEDPAGEHASHES pSpcPageHashes = pSpcObjAttr->u.pPageHashes;
2626 Assert(pSpcPageHashes);
2627
2628 /*
2629 * Now ask the loader for the number of pages in the page hash table
2630 * and calculate its size.
2631 */
2632 uint32_t cPages = 0;
2633 rc = RTLdrQueryPropEx(pThis->hLdrMod, RTLDRPROP_HASHABLE_PAGES, NULL, &cPages, sizeof(cPages), NULL);
2634 if (RT_FAILURE(rc))
2635 return RTMsgErrorExitFailure("RTLdrQueryPropEx/RTLDRPROP_HASHABLE_PAGES failed: %Rrc", rc);
2636
2637 uint32_t const cbHash = RTCrDigestTypeToHashSize(enmSigType);
2638 AssertReturn(cbHash > 0, RTMsgErrorExitFailure("Invalid value: enmSigType=%d", enmSigType));
2639 uint32_t const cbTable = (sizeof(uint32_t) + cbHash) * cPages;
2640
2641 /*
2642 * Allocate memory in the octect string.
2643 */
2644 rc = RTAsn1ContentAllocZ(&pSpcPageHashes->RawData.Asn1Core, cbTable, pAllocator);
2645 if (RT_FAILURE(rc))
2646 return RTMsgErrorExitFailure("RTAsn1ContentAllocZ failed to allocate %#x bytes for page hashes: %Rrc", cbTable, rc);
2647 pSpcPageHashes->pData = (PCRTCRSPCPEIMAGEPAGEHASHES)pSpcPageHashes->RawData.Asn1Core.uData.pu8;
2648
2649 RTLDRPROP enmLdrProp;
2650 switch (enmSigType)
2651 {
2652 case RTDIGESTTYPE_SHA1: enmLdrProp = RTLDRPROP_SHA1_PAGE_HASHES; break;
2653 case RTDIGESTTYPE_SHA256: enmLdrProp = RTLDRPROP_SHA256_PAGE_HASHES; break;
2654 default: AssertFailedReturn(RTMsgErrorExitFailure("Invalid value: enmSigType=%d", enmSigType));
2655
2656 }
2657 rc = RTLdrQueryPropEx(pThis->hLdrMod, enmLdrProp, NULL, (void *)pSpcPageHashes->RawData.Asn1Core.uData.pv, cbTable, NULL);
2658 if (RT_FAILURE(rc))
2659 return RTMsgErrorExitFailure("RTLdrQueryPropEx/RTLDRPROP_SHA?_PAGE_HASHES/%#x failed: %Rrc", cbTable, rc);
2660
2661 return RTEXITCODE_SUCCESS;
2662}
2663
2664
2665static RTEXITCODE SignToolPkcs7_SpcAddImageHash(SIGNTOOLPKCS7EXE *pThis, RTCRSPCINDIRECTDATACONTENT *pSpcIndData,
2666 RTDIGESTTYPE enmSigType)
2667{
2668 uint32_t const cbHash = RTCrDigestTypeToHashSize(enmSigType);
2669 const char * const pszAlgId = RTCrDigestTypeToAlgorithmOid(enmSigType);
2670
2671 /*
2672 * Ask the loader for the hash.
2673 */
2674 uint8_t abHash[RTSHA512_HASH_SIZE];
2675 int rc = RTLdrHashImage(pThis->hLdrMod, enmSigType, abHash, sizeof(abHash));
2676 if (RT_FAILURE(rc))
2677 return RTMsgErrorExitFailure("RTLdrHashImage/%s failed: %Rrc", RTCrDigestTypeToName(enmSigType), rc);
2678
2679 /*
2680 * Set it.
2681 */
2682 /** @todo no setter, this should be okay, though... */
2683 rc = RTAsn1ObjId_InitFromString(&pSpcIndData->DigestInfo.DigestAlgorithm.Algorithm, pszAlgId, &g_RTAsn1DefaultAllocator);
2684 if (RT_FAILURE(rc))
2685 return RTMsgErrorExitFailure("RTAsn1ObjId_InitFromString/%s failed: %Rrc", pszAlgId, rc);
2686 RTAsn1DynType_SetToNull(&pSpcIndData->DigestInfo.DigestAlgorithm.Parameters); /* ASSUMES RSA or similar */
2687
2688 rc = RTAsn1ContentDup(&pSpcIndData->DigestInfo.Digest.Asn1Core, abHash, cbHash, &g_RTAsn1DefaultAllocator);
2689 if (RT_FAILURE(rc))
2690 return RTMsgErrorExitFailure("RTAsn1ContentDup/%#x failed: %Rrc", cbHash, rc);
2691
2692 return RTEXITCODE_SUCCESS;
2693}
2694
2695
2696static RTEXITCODE SignToolPkcs7_AddOrReplaceSignature(SIGNTOOLPKCS7EXE *pThis, unsigned cVerbosity, RTDIGESTTYPE enmSigType,
2697 bool fReplaceExisting, bool fHashPages, bool fNoSigningTime,
2698 SignToolKeyPair *pSigningCertKey,
2699 RTCRSTORE hAddCerts, bool fTimestampTypeOld,
2700 RTTIMESPEC SigningTime, SignToolKeyPair *pTimestampCertKey)
2701{
2702 AssertReturn(fTimestampTypeOld || pTimestampCertKey->isNull(),
2703 RTMsgErrorExitFailure("New style signatures not supported yet"));
2704
2705 /*
2706 * We must construct the data to be packed into the PKCS#7 signature
2707 * and signed.
2708 */
2709 PCRTASN1ALLOCATORVTABLE const pAllocator = &g_RTAsn1DefaultAllocator;
2710 RTCRSPCINDIRECTDATACONTENT SpcIndData;
2711 int rc = RTCrSpcIndirectDataContent_Init(&SpcIndData, pAllocator);
2712 if (RT_FAILURE(rc))
2713 return RTMsgErrorExitFailure("RTCrSpcIndirectDataContent_Init failed: %Rrc", rc);
2714
2715 /* Set the data to PE image. */
2716 /** @todo Generalize the Type + enmType DYN stuff and generate setters. */
2717 Assert(SpcIndData.Data.enmType == RTCRSPCAAOVTYPE_NOT_PRESENT);
2718 Assert(SpcIndData.Data.uValue.pPeImage == NULL);
2719 RTEXITCODE rcExit;
2720 rc = RTAsn1ObjId_SetFromString(&SpcIndData.Data.Type, RTCRSPCPEIMAGEDATA_OID, pAllocator);
2721 if (RT_SUCCESS(rc))
2722 {
2723 SpcIndData.Data.enmType = RTCRSPCAAOVTYPE_PE_IMAGE_DATA;
2724 rc = RTAsn1MemAllocZ(&SpcIndData.Data.Allocation, (void **)&SpcIndData.Data.uValue.pPeImage,
2725 sizeof(*SpcIndData.Data.uValue.pPeImage));
2726 if (RT_SUCCESS(rc))
2727 {
2728 rc = RTCrSpcPeImageData_Init(SpcIndData.Data.uValue.pPeImage, pAllocator);
2729 if (RT_SUCCESS(rc))
2730 {
2731 /* Old (SHA1) signatures has a Flags member, it's zero bits, though. */
2732 if (enmSigType == RTDIGESTTYPE_SHA1)
2733 {
2734 uint8_t bFlags = 0;
2735 RTASN1BITSTRING Flags;
2736 rc = RTAsn1BitString_InitWithData(&Flags, &bFlags, 0, pAllocator);
2737 if (RT_SUCCESS(rc))
2738 {
2739 rc = RTCrSpcPeImageData_SetFlags(SpcIndData.Data.uValue.pPeImage, &Flags, pAllocator);
2740 RTAsn1BitString_Delete(&Flags);
2741 if (RT_FAILURE(rc))
2742 rcExit = RTMsgErrorExitFailure("RTCrSpcPeImageData_SetFlags failed: %Rrc", rc);
2743 }
2744 else
2745 rcExit = RTMsgErrorExitFailure("RTAsn1BitString_InitWithData failed: %Rrc", rc);
2746 }
2747
2748 /*
2749 * Add the hashes.
2750 */
2751 rcExit = SignToolPkcs7_SpcAddImageHash(pThis, &SpcIndData, enmSigType);
2752 if (rcExit == RTEXITCODE_SUCCESS)
2753 {
2754 if (fHashPages)
2755 rcExit = SignToolPkcs7_SpcAddImagePageHashes(pThis, &SpcIndData, enmSigType);
2756 else
2757 rcExit = SignToolPkcs7_SpcCompleteWithoutPageHashes(&SpcIndData);
2758
2759 /*
2760 * Encode and sign the SPC data, timestamp it, and line it up for adding to the executable.
2761 */
2762 if (rcExit == RTEXITCODE_SUCCESS)
2763 rcExit = SignToolPkcs7_SignData(pThis, RTCrSpcIndirectDataContent_GetAsn1Core(&SpcIndData),
2764 kSignDataTweak_NoTweak, RTCRSPCINDIRECTDATACONTENT_OID, cVerbosity, 0,
2765 enmSigType, fReplaceExisting, fNoSigningTime, pSigningCertKey, hAddCerts,
2766 fTimestampTypeOld, SigningTime, pTimestampCertKey);
2767 }
2768 }
2769 else
2770 rcExit = RTMsgErrorExitFailure("RTCrPkcs7SignerInfos_Init failed: %Rrc", rc);
2771 }
2772 else
2773 rcExit = RTMsgErrorExitFailure("RTAsn1MemAllocZ failed for RTCRSPCPEIMAGEDATA: %Rrc", rc);
2774 }
2775 else
2776 rcExit = RTMsgErrorExitFailure("RTAsn1ObjId_SetWithString/SpcPeImageData failed: %Rrc", rc);
2777
2778 RTCrSpcIndirectDataContent_Delete(&SpcIndData);
2779 return rcExit;
2780}
2781
2782
2783static RTEXITCODE SignToolPkcs7_AddOrReplaceCatSignature(SIGNTOOLPKCS7 *pThis, unsigned cVerbosity, RTDIGESTTYPE enmSigType,
2784 bool fReplaceExisting, bool fNoSigningTime,
2785 SignToolKeyPair *pSigningCertKey,
2786 RTCRSTORE hAddCerts, bool fTimestampTypeOld,
2787 RTTIMESPEC SigningTime, SignToolKeyPair *pTimestampCertKey)
2788{
2789 AssertReturn(fTimestampTypeOld || pTimestampCertKey->isNull(),
2790 RTMsgErrorExitFailure("New style signatures not supported yet"));
2791 AssertReturn(pThis->pSignedData, RTMsgErrorExitFailure("pSignedData is NULL!"));
2792
2793 /*
2794 * Figure out what to sign first.
2795 */
2796 uint32_t fExtraFlags = 0;
2797 PRTASN1CORE pToSign = &pThis->pSignedData->ContentInfo.Content.Asn1Core;
2798 const char *pszType = pThis->pSignedData->ContentInfo.ContentType.szObjId;
2799
2800 if (!fReplaceExisting && pThis->pSignedData->SignerInfos.cItems == 0)
2801 fReplaceExisting = true;
2802 if (!fReplaceExisting)
2803 {
2804 pszType = RTCR_PKCS7_DATA_OID;
2805 fExtraFlags |= RTCRPKCS7SIGN_SD_F_DEATCHED;
2806 }
2807
2808 /*
2809 * Do the signing.
2810 */
2811 RTEXITCODE rcExit = SignToolPkcs7_SignData(pThis, pToSign, kSignDataTweak_RootIsParent,
2812 pszType, cVerbosity, fExtraFlags, enmSigType, fReplaceExisting,
2813 fNoSigningTime, pSigningCertKey, hAddCerts,
2814 fTimestampTypeOld, SigningTime, pTimestampCertKey);
2815
2816 /* probably need to clean up stuff related to nested signatures here later... */
2817 return rcExit;
2818}
2819
2820#endif /* !IPRT_SIGNTOOL_NO_SIGNING */
2821
2822
2823/*********************************************************************************************************************************
2824* The 'extract-exe-signer-cert' command. *
2825*********************************************************************************************************************************/
2826
2827static RTEXITCODE HelpExtractExeSignerCert(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
2828{
2829 RT_NOREF_PV(enmLevel);
2830 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
2831 "extract-exe-signer-cert [--ber|--cer|--der] [--signature-index|-i <num>] [--exe|-e] <exe> [--output|-o] <outfile.cer>\n");
2832 return RTEXITCODE_SUCCESS;
2833}
2834
2835static RTEXITCODE HandleExtractExeSignerCert(int cArgs, char **papszArgs)
2836{
2837 /*
2838 * Parse arguments.
2839 */
2840 static const RTGETOPTDEF s_aOptions[] =
2841 {
2842 { "--ber", 'b', RTGETOPT_REQ_NOTHING },
2843 { "--cer", 'c', RTGETOPT_REQ_NOTHING },
2844 { "--der", 'd', RTGETOPT_REQ_NOTHING },
2845 { "--exe", 'e', RTGETOPT_REQ_STRING },
2846 { "--output", 'o', RTGETOPT_REQ_STRING },
2847 { "--signature-index", 'i', RTGETOPT_REQ_UINT32 },
2848 { "--force", 'f', RTGETOPT_REQ_NOTHING },
2849 };
2850
2851 const char *pszExe = NULL;
2852 const char *pszOut = NULL;
2853 RTLDRARCH enmLdrArch = RTLDRARCH_WHATEVER;
2854 unsigned cVerbosity = 0;
2855 uint32_t fCursorFlags = RTASN1CURSOR_FLAGS_DER;
2856 uint32_t iSignature = 0;
2857 bool fForce = false;
2858
2859 RTGETOPTSTATE GetState;
2860 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
2861 AssertRCReturn(rc, RTEXITCODE_FAILURE);
2862 RTGETOPTUNION ValueUnion;
2863 int ch;
2864 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
2865 {
2866 switch (ch)
2867 {
2868 case 'e': pszExe = ValueUnion.psz; break;
2869 case 'o': pszOut = ValueUnion.psz; break;
2870 case 'b': fCursorFlags = 0; break;
2871 case 'c': fCursorFlags = RTASN1CURSOR_FLAGS_CER; break;
2872 case 'd': fCursorFlags = RTASN1CURSOR_FLAGS_DER; break;
2873 case 'f': fForce = true; break;
2874 case 'i': iSignature = ValueUnion.u32; break;
2875 case 'V': return HandleVersion(cArgs, papszArgs);
2876 case 'h': return HelpExtractExeSignerCert(g_pStdOut, RTSIGNTOOLHELP_FULL);
2877
2878 case VINF_GETOPT_NOT_OPTION:
2879 if (!pszExe)
2880 pszExe = ValueUnion.psz;
2881 else if (!pszOut)
2882 pszOut = ValueUnion.psz;
2883 else
2884 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many file arguments: %s", ValueUnion.psz);
2885 break;
2886
2887 default:
2888 return RTGetOptPrintError(ch, &ValueUnion);
2889 }
2890 }
2891 if (!pszExe)
2892 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
2893 if (!pszOut)
2894 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No output file given.");
2895 if (!fForce && RTPathExists(pszOut))
2896 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The output file '%s' exists.", pszOut);
2897
2898 /*
2899 * Do it.
2900 */
2901 /* Read & decode the PKCS#7 signature. */
2902 SIGNTOOLPKCS7EXE This;
2903 RTEXITCODE rcExit = SignToolPkcs7Exe_InitFromFile(&This, pszExe, cVerbosity, enmLdrArch);
2904 if (rcExit == RTEXITCODE_SUCCESS)
2905 {
2906 /* Find the signing certificate (ASSUMING that the certificate used is shipped in the set of certificates). */
2907 PRTCRPKCS7SIGNEDDATA pSignedData;
2908 PCRTCRPKCS7SIGNERINFO pSignerInfo = SignToolPkcs7_FindNestedSignatureByIndex(&This, iSignature, &pSignedData);
2909 rcExit = RTEXITCODE_FAILURE;
2910 if (pSignerInfo)
2911 {
2912 PCRTCRPKCS7ISSUERANDSERIALNUMBER pISN = &pSignedData->SignerInfos.papItems[0]->IssuerAndSerialNumber;
2913 PCRTCRX509CERTIFICATE pCert;
2914 pCert = RTCrPkcs7SetOfCerts_FindX509ByIssuerAndSerialNumber(&pSignedData->Certificates,
2915 &pISN->Name, &pISN->SerialNumber);
2916 if (pCert)
2917 {
2918 /*
2919 * Write it out.
2920 */
2921 RTFILE hFile;
2922 rc = RTFileOpen(&hFile, pszOut,
2923 RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | (fForce ? RTFILE_O_CREATE_REPLACE : RTFILE_O_CREATE));
2924 if (RT_SUCCESS(rc))
2925 {
2926 uint32_t cbCert = pCert->SeqCore.Asn1Core.cbHdr + pCert->SeqCore.Asn1Core.cb;
2927 rc = RTFileWrite(hFile, pCert->SeqCore.Asn1Core.uData.pu8 - pCert->SeqCore.Asn1Core.cbHdr,
2928 cbCert, NULL);
2929 if (RT_SUCCESS(rc))
2930 {
2931 rc = RTFileClose(hFile);
2932 if (RT_SUCCESS(rc))
2933 {
2934 hFile = NIL_RTFILE;
2935 rcExit = RTEXITCODE_SUCCESS;
2936 RTMsgInfo("Successfully wrote %u bytes to '%s'", cbCert, pszOut);
2937 }
2938 else
2939 RTMsgError("RTFileClose failed: %Rrc", rc);
2940 }
2941 else
2942 RTMsgError("RTFileWrite failed: %Rrc", rc);
2943 RTFileClose(hFile);
2944 }
2945 else
2946 RTMsgError("Error opening '%s' for writing: %Rrc", pszOut, rc);
2947 }
2948 else
2949 RTMsgError("Certificate not found.");
2950 }
2951 else
2952 RTMsgError("Could not locate signature #%u!", iSignature);
2953
2954 /* Delete the signature data. */
2955 SignToolPkcs7Exe_Delete(&This);
2956 }
2957 return rcExit;
2958}
2959
2960
2961/*********************************************************************************************************************************
2962* The 'extract-exe-signature' command. *
2963*********************************************************************************************************************************/
2964
2965static RTEXITCODE HelpExtractExeSignature(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
2966{
2967 RT_NOREF_PV(enmLevel);
2968 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
2969 "extract-exe-signerature [--exe|-e] <exe> [--output|-o] <outfile.pkcs7>\n");
2970 return RTEXITCODE_SUCCESS;
2971}
2972
2973static RTEXITCODE HandleExtractExeSignature(int cArgs, char **papszArgs)
2974{
2975 /*
2976 * Parse arguments.
2977 */
2978 static const RTGETOPTDEF s_aOptions[] =
2979 {
2980 { "--exe", 'e', RTGETOPT_REQ_STRING },
2981 { "--output", 'o', RTGETOPT_REQ_STRING },
2982 { "--force", 'f', RTGETOPT_REQ_NOTHING },
2983 };
2984
2985 const char *pszExe = NULL;
2986 const char *pszOut = NULL;
2987 RTLDRARCH enmLdrArch = RTLDRARCH_WHATEVER;
2988 unsigned cVerbosity = 0;
2989 bool fForce = false;
2990
2991 RTGETOPTSTATE GetState;
2992 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
2993 AssertRCReturn(rc, RTEXITCODE_FAILURE);
2994 RTGETOPTUNION ValueUnion;
2995 int ch;
2996 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
2997 {
2998 switch (ch)
2999 {
3000 case 'e': pszExe = ValueUnion.psz; break;
3001 case 'o': pszOut = ValueUnion.psz; break;
3002 case 'f': fForce = true; break;
3003 case 'V': return HandleVersion(cArgs, papszArgs);
3004 case 'h': return HelpExtractExeSignerCert(g_pStdOut, RTSIGNTOOLHELP_FULL);
3005
3006 case VINF_GETOPT_NOT_OPTION:
3007 if (!pszExe)
3008 pszExe = ValueUnion.psz;
3009 else if (!pszOut)
3010 pszOut = ValueUnion.psz;
3011 else
3012 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many file arguments: %s", ValueUnion.psz);
3013 break;
3014
3015 default:
3016 return RTGetOptPrintError(ch, &ValueUnion);
3017 }
3018 }
3019 if (!pszExe)
3020 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
3021 if (!pszOut)
3022 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No output file given.");
3023 if (!fForce && RTPathExists(pszOut))
3024 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The output file '%s' exists.", pszOut);
3025
3026 /*
3027 * Do it.
3028 */
3029 /* Read & decode the PKCS#7 signature. */
3030 SIGNTOOLPKCS7EXE This;
3031 RTEXITCODE rcExit = SignToolPkcs7Exe_InitFromFile(&This, pszExe, cVerbosity, enmLdrArch);
3032 if (rcExit == RTEXITCODE_SUCCESS)
3033 {
3034 /*
3035 * Write out the PKCS#7 signature.
3036 */
3037 RTFILE hFile;
3038 rc = RTFileOpen(&hFile, pszOut,
3039 RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | (fForce ? RTFILE_O_CREATE_REPLACE : RTFILE_O_CREATE));
3040 if (RT_SUCCESS(rc))
3041 {
3042 rc = RTFileWrite(hFile, This.pbBuf, This.cbBuf, NULL);
3043 if (RT_SUCCESS(rc))
3044 {
3045 rc = RTFileClose(hFile);
3046 if (RT_SUCCESS(rc))
3047 {
3048 hFile = NIL_RTFILE;
3049 RTMsgInfo("Successfully wrote %u bytes to '%s'", This.cbBuf, pszOut);
3050 rcExit = RTEXITCODE_SUCCESS;
3051 }
3052 else
3053 RTMsgError("RTFileClose failed: %Rrc", rc);
3054 }
3055 else
3056 RTMsgError("RTFileWrite failed: %Rrc", rc);
3057 RTFileClose(hFile);
3058 }
3059 else
3060 RTMsgError("Error opening '%s' for writing: %Rrc", pszOut, rc);
3061
3062 /* Delete the signature data. */
3063 SignToolPkcs7Exe_Delete(&This);
3064 }
3065 return rcExit;
3066}
3067
3068
3069/*********************************************************************************************************************************
3070* The 'add-nested-exe-signature' command. *
3071*********************************************************************************************************************************/
3072
3073static RTEXITCODE HelpAddNestedExeSignature(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
3074{
3075 RT_NOREF_PV(enmLevel);
3076 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
3077 "add-nested-exe-signature [-v|--verbose] [-d|--debug] [-p|--prepend] <destination-exe> <source-exe>\n");
3078 if (enmLevel == RTSIGNTOOLHELP_FULL)
3079 RTStrmWrappedPrintf(pStrm, 0,
3080 "\n"
3081 "The --debug option allows the source-exe to be omitted in order to test the "
3082 "encoding and PE file modification.\n"
3083 "\n"
3084 "The --prepend option puts the nested signature first rather than appending it "
3085 "to the end of of the nested signature set. Windows reads nested signatures in "
3086 "reverse order, so --prepend will logically putting it last.\n");
3087 return RTEXITCODE_SUCCESS;
3088}
3089
3090
3091static RTEXITCODE HandleAddNestedExeSignature(int cArgs, char **papszArgs)
3092{
3093 /*
3094 * Parse arguments.
3095 */
3096 static const RTGETOPTDEF s_aOptions[] =
3097 {
3098 { "--prepend", 'p', RTGETOPT_REQ_NOTHING },
3099 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
3100 { "--debug", 'd', RTGETOPT_REQ_NOTHING },
3101 };
3102
3103 const char *pszDst = NULL;
3104 const char *pszSrc = NULL;
3105 unsigned cVerbosity = 0;
3106 bool fDebug = false;
3107 bool fPrepend = false;
3108
3109 RTGETOPTSTATE GetState;
3110 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
3111 AssertRCReturn(rc, RTEXITCODE_FAILURE);
3112 RTGETOPTUNION ValueUnion;
3113 int ch;
3114 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
3115 {
3116 switch (ch)
3117 {
3118 case 'v': cVerbosity++; break;
3119 case 'd': fDebug = pszSrc == NULL; break;
3120 case 'p': fPrepend = true; break;
3121 case 'V': return HandleVersion(cArgs, papszArgs);
3122 case 'h': return HelpAddNestedExeSignature(g_pStdOut, RTSIGNTOOLHELP_FULL);
3123
3124 case VINF_GETOPT_NOT_OPTION:
3125 if (!pszDst)
3126 pszDst = ValueUnion.psz;
3127 else if (!pszSrc)
3128 {
3129 pszSrc = ValueUnion.psz;
3130 fDebug = false;
3131 }
3132 else
3133 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many file arguments: %s", ValueUnion.psz);
3134 break;
3135
3136 default:
3137 return RTGetOptPrintError(ch, &ValueUnion);
3138 }
3139 }
3140 if (!pszDst)
3141 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No destination executable given.");
3142 if (!pszSrc && !fDebug)
3143 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No source executable file given.");
3144
3145 /*
3146 * Do it.
3147 */
3148 /* Read & decode the source PKCS#7 signature. */
3149 SIGNTOOLPKCS7EXE Src;
3150 RTEXITCODE rcExit = pszSrc ? SignToolPkcs7Exe_InitFromFile(&Src, pszSrc, cVerbosity) : RTEXITCODE_SUCCESS;
3151 if (rcExit == RTEXITCODE_SUCCESS)
3152 {
3153 /* Ditto for the destination PKCS#7 signature. */
3154 SIGNTOOLPKCS7EXE Dst;
3155 rcExit = SignToolPkcs7Exe_InitFromFile(&Dst, pszDst, cVerbosity);
3156 if (rcExit == RTEXITCODE_SUCCESS)
3157 {
3158 /* Do the signature manipulation. */
3159 if (pszSrc)
3160 rcExit = SignToolPkcs7_AddNestedSignature(&Dst, &Src, cVerbosity, fPrepend);
3161 if (rcExit == RTEXITCODE_SUCCESS)
3162 rcExit = SignToolPkcs7_Encode(&Dst, cVerbosity);
3163
3164 /* Update the destination executable file. */
3165 if (rcExit == RTEXITCODE_SUCCESS)
3166 rcExit = SignToolPkcs7Exe_WriteSignatureToFile(&Dst, cVerbosity);
3167
3168 SignToolPkcs7Exe_Delete(&Dst);
3169 }
3170 if (pszSrc)
3171 SignToolPkcs7Exe_Delete(&Src);
3172 }
3173
3174 return rcExit;
3175}
3176
3177
3178/*********************************************************************************************************************************
3179* The 'add-nested-cat-signature' command. *
3180*********************************************************************************************************************************/
3181
3182static RTEXITCODE HelpAddNestedCatSignature(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
3183{
3184 RT_NOREF_PV(enmLevel);
3185 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
3186 "add-nested-cat-signature [-v|--verbose] [-d|--debug] [-p|--prepend] <destination-cat> <source-cat>\n");
3187 if (enmLevel == RTSIGNTOOLHELP_FULL)
3188 RTStrmWrappedPrintf(pStrm, 0,
3189 "\n"
3190 "The --debug option allows the source-cat to be omitted in order to test the "
3191 "ASN.1 re-encoding of the destination catalog file.\n"
3192 "\n"
3193 "The --prepend option puts the nested signature first rather than appending it "
3194 "to the end of of the nested signature set. Windows reads nested signatures in "
3195 "reverse order, so --prepend will logically putting it last.\n");
3196 return RTEXITCODE_SUCCESS;
3197}
3198
3199
3200static RTEXITCODE HandleAddNestedCatSignature(int cArgs, char **papszArgs)
3201{
3202 /*
3203 * Parse arguments.
3204 */
3205 static const RTGETOPTDEF s_aOptions[] =
3206 {
3207 { "--prepend", 'p', RTGETOPT_REQ_NOTHING },
3208 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
3209 { "--debug", 'd', RTGETOPT_REQ_NOTHING },
3210 };
3211
3212 const char *pszDst = NULL;
3213 const char *pszSrc = NULL;
3214 unsigned cVerbosity = 0;
3215 bool fDebug = false;
3216 bool fPrepend = false;
3217
3218 RTGETOPTSTATE GetState;
3219 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
3220 AssertRCReturn(rc, RTEXITCODE_FAILURE);
3221 RTGETOPTUNION ValueUnion;
3222 int ch;
3223 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
3224 {
3225 switch (ch)
3226 {
3227 case 'v': cVerbosity++; break;
3228 case 'd': fDebug = pszSrc == NULL; break;
3229 case 'p': fPrepend = true; break;
3230 case 'V': return HandleVersion(cArgs, papszArgs);
3231 case 'h': return HelpAddNestedCatSignature(g_pStdOut, RTSIGNTOOLHELP_FULL);
3232
3233 case VINF_GETOPT_NOT_OPTION:
3234 if (!pszDst)
3235 pszDst = ValueUnion.psz;
3236 else if (!pszSrc)
3237 {
3238 pszSrc = ValueUnion.psz;
3239 fDebug = false;
3240 }
3241 else
3242 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many file arguments: %s", ValueUnion.psz);
3243 break;
3244
3245 default:
3246 return RTGetOptPrintError(ch, &ValueUnion);
3247 }
3248 }
3249 if (!pszDst)
3250 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No destination catalog file given.");
3251 if (!pszSrc && !fDebug)
3252 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No source catalog file given.");
3253
3254 /*
3255 * Do it.
3256 */
3257 /* Read & decode the source PKCS#7 signature. */
3258 SIGNTOOLPKCS7 Src;
3259 RTEXITCODE rcExit = pszSrc ? SignToolPkcs7_InitFromFile(&Src, pszSrc, cVerbosity) : RTEXITCODE_SUCCESS;
3260 if (rcExit == RTEXITCODE_SUCCESS)
3261 {
3262 /* Ditto for the destination PKCS#7 signature. */
3263 SIGNTOOLPKCS7EXE Dst;
3264 rcExit = SignToolPkcs7_InitFromFile(&Dst, pszDst, cVerbosity);
3265 if (rcExit == RTEXITCODE_SUCCESS)
3266 {
3267 /* Do the signature manipulation. */
3268 if (pszSrc)
3269 rcExit = SignToolPkcs7_AddNestedSignature(&Dst, &Src, cVerbosity, fPrepend);
3270 if (rcExit == RTEXITCODE_SUCCESS)
3271 rcExit = SignToolPkcs7_Encode(&Dst, cVerbosity);
3272
3273 /* Update the destination executable file. */
3274 if (rcExit == RTEXITCODE_SUCCESS)
3275 rcExit = SignToolPkcs7_WriteSignatureToFile(&Dst, pszDst, cVerbosity);
3276
3277 SignToolPkcs7_Delete(&Dst);
3278 }
3279 if (pszSrc)
3280 SignToolPkcs7_Delete(&Src);
3281 }
3282
3283 return rcExit;
3284}
3285
3286
3287/*********************************************************************************************************************************
3288* Option handlers shared by 'sign-exe', 'sign-cat', 'add-timestamp-exe-signature' and others. *
3289*********************************************************************************************************************************/
3290#ifndef IPRT_SIGNTOOL_NO_SIGNING
3291
3292static RTEXITCODE HandleOptAddCert(PRTCRSTORE phStore, const char *pszFile)
3293{
3294 if (*phStore == NIL_RTCRSTORE)
3295 {
3296 int rc = RTCrStoreCreateInMem(phStore, 2);
3297 if (RT_FAILURE(rc))
3298 return RTMsgErrorExitFailure("RTCrStoreCreateInMem(,2) failed: %Rrc", rc);
3299 }
3300 RTERRINFOSTATIC ErrInfo;
3301 int rc = RTCrStoreCertAddFromFile(*phStore, RTCRCERTCTX_F_ADD_IF_NOT_FOUND, pszFile, RTErrInfoInitStatic(&ErrInfo));
3302 if (RT_FAILURE(rc))
3303 return RTMsgErrorExitFailure("Error reading certificate from '%s': %Rrc%#RTeim", pszFile, rc, &ErrInfo.Core);
3304 return RTEXITCODE_SUCCESS;
3305}
3306
3307static RTEXITCODE HandleOptSignatureType(RTDIGESTTYPE *penmSigType, const char *pszType)
3308{
3309 if ( RTStrICmpAscii(pszType, "sha1") == 0
3310 || RTStrICmpAscii(pszType, "sha-1") == 0)
3311 *penmSigType = RTDIGESTTYPE_SHA1;
3312 else if ( RTStrICmpAscii(pszType, "sha256") == 0
3313 || RTStrICmpAscii(pszType, "sha-256") == 0)
3314 *penmSigType = RTDIGESTTYPE_SHA256;
3315 else
3316 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown signature type: %s (expected sha1 or sha256)", pszType);
3317 return RTEXITCODE_SUCCESS;
3318}
3319
3320
3321static RTEXITCODE HandleOptTimestampType(bool *pfOldType, const char *pszType)
3322{
3323 if (strcmp(pszType, "old") == 0)
3324 *pfOldType = true;
3325 else if (strcmp(pszType, "new") == 0)
3326 *pfOldType = false;
3327 else
3328 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown timestamp type: %s", pszType);
3329 return RTEXITCODE_SUCCESS;
3330}
3331
3332static RTEXITCODE HandleOptTimestampOverride(PRTTIMESPEC pSigningTime, const char *pszPartialTs)
3333{
3334 /*
3335 * First try use it as-is.
3336 */
3337 if (RTTimeSpecFromString(pSigningTime, pszPartialTs) != NULL)
3338 return RTEXITCODE_SUCCESS;
3339
3340 /* Check the input against a pattern, making sure we've got something that
3341 makes sense before trying to merge. */
3342 size_t const cchPartialTs = strlen(pszPartialTs);
3343 static char s_szPattern[] = "0000-00-00T00:00:";
3344 if (cchPartialTs > sizeof(s_szPattern) - 1) /* It is not a partial timestamp if we've got the seconds component. */
3345 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid timestamp: %s", pszPartialTs);
3346
3347 for (size_t off = 0; off < cchPartialTs; off++)
3348 switch (s_szPattern[off])
3349 {
3350 case '0':
3351 if (!RT_C_IS_DIGIT(pszPartialTs[off]))
3352 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid timestamp, expected digit at position %u: %s",
3353 off + 1, pszPartialTs);
3354 break;
3355 case '-':
3356 case ':':
3357 if (pszPartialTs[off] != s_szPattern[off])
3358 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid timestamp, expected '%c' at position %u: %s",
3359 s_szPattern[off], off + 1, pszPartialTs);
3360 break;
3361 case 'T':
3362 if ( pszPartialTs[off] != 'T'
3363 && pszPartialTs[off] != 't'
3364 && pszPartialTs[off] != ' ')
3365 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid timestamp, expected 'T' or space at position %u: %s",
3366 off + 1, pszPartialTs);
3367 break;
3368 default:
3369 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Internal error");
3370 }
3371
3372 if (RT_C_IS_DIGIT(s_szPattern[cchPartialTs]) && RT_C_IS_DIGIT(s_szPattern[cchPartialTs - 1]))
3373 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Incomplete timstamp component: %s", pszPartialTs);
3374
3375 /*
3376 * Take the current time and merge in the components from pszPartialTs.
3377 */
3378 char szSigningTime[RTTIME_STR_LEN];
3379 RTTIMESPEC Now;
3380 RTTimeSpecToString(RTTimeNow(&Now), szSigningTime, sizeof(szSigningTime));
3381 memcpy(szSigningTime, pszPartialTs, cchPartialTs);
3382 szSigningTime[4+1+2+1+2] = 'T';
3383
3384 /* Fix 29th for non-leap override: */
3385 if (memcmp(&szSigningTime[5], RT_STR_TUPLE("02-29")) == 0)
3386 {
3387 if (!RTTimeIsLeapYear(RTStrToUInt32(szSigningTime)))
3388 szSigningTime[9] = '8';
3389 }
3390 if (RTTimeSpecFromString(pSigningTime, szSigningTime) == NULL)
3391 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid timestamp: %s (%s)", pszPartialTs, szSigningTime);
3392
3393 return RTEXITCODE_SUCCESS;
3394}
3395
3396static RTEXITCODE HandleOptFileType(RTSIGNTOOLFILETYPE *penmFileType, const char *pszType)
3397{
3398 if (strcmp(pszType, "detect") == 0 || strcmp(pszType, "auto") == 0)
3399 *penmFileType = RTSIGNTOOLFILETYPE_DETECT;
3400 else if (strcmp(pszType, "exe") == 0)
3401 *penmFileType = RTSIGNTOOLFILETYPE_EXE;
3402 else if (strcmp(pszType, "cat") == 0)
3403 *penmFileType = RTSIGNTOOLFILETYPE_CAT;
3404 else
3405 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown forced file type: %s", pszType);
3406 return RTEXITCODE_SUCCESS;
3407}
3408
3409/**
3410 * Detects the type of files @a pszFile is (by reading from it).
3411 *
3412 * @returns The file type, or RTSIGNTOOLFILETYPE_UNKNOWN (error displayed).
3413 * @param enmForceFileType Usually set to RTSIGNTOOLFILETYPE_DETECT, but if
3414 * not we'll return this without probing the file.
3415 * @param pszFile The name of the file to detect the type of.
3416 */
3417static RTSIGNTOOLFILETYPE DetectFileType(RTSIGNTOOLFILETYPE enmForceFileType, const char *pszFile)
3418{
3419 /*
3420 * Forced?
3421 */
3422 if (enmForceFileType != RTSIGNTOOLFILETYPE_DETECT)
3423 return enmForceFileType;
3424
3425 /*
3426 * Read the start of the file.
3427 */
3428 RTFILE hFile = NIL_RTFILE;
3429 int rc = RTFileOpen(&hFile, pszFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
3430 if (RT_FAILURE(rc))
3431 {
3432 RTMsgError("Error opening '%s' for reading: %Rrc", pszFile, rc);
3433 return RTSIGNTOOLFILETYPE_UNKNOWN;
3434 }
3435
3436 union
3437 {
3438 uint8_t ab[256];
3439 uint16_t au16[256/2];
3440 uint32_t au32[256/4];
3441 } uBuf;
3442 RT_ZERO(uBuf);
3443
3444 size_t cbRead = 0;
3445 rc = RTFileRead(hFile, &uBuf, sizeof(uBuf), &cbRead);
3446 if (RT_FAILURE(rc))
3447 RTMsgError("Error reading from '%s': %Rrc", pszFile, rc);
3448
3449 uint64_t cbFile;
3450 int rcSize = RTFileQuerySize(hFile, &cbFile);
3451 if (RT_FAILURE(rcSize))
3452 RTMsgError("Error querying size of '%s': %Rrc", pszFile, rc);
3453
3454 RTFileClose(hFile);
3455 if (RT_FAILURE(rc) || RT_FAILURE(rcSize))
3456 return RTSIGNTOOLFILETYPE_UNKNOWN;
3457
3458 /*
3459 * Try guess the kind of file.
3460 */
3461 /* All the executable magics we know: */
3462 if ( uBuf.au16[0] == RT_H2LE_U16_C(IMAGE_DOS_SIGNATURE)
3463 || uBuf.au16[0] == RT_H2LE_U16_C(IMAGE_NE_SIGNATURE)
3464 || uBuf.au16[0] == RT_H2LE_U16_C(IMAGE_LX_SIGNATURE)
3465 || uBuf.au16[0] == RT_H2LE_U16_C(IMAGE_LE_SIGNATURE)
3466 || uBuf.au32[0] == RT_H2LE_U32_C(IMAGE_NT_SIGNATURE)
3467 || uBuf.au32[0] == RT_H2LE_U32_C(IMAGE_ELF_SIGNATURE)
3468 || uBuf.au32[0] == IMAGE_FAT_SIGNATURE
3469 || uBuf.au32[0] == IMAGE_FAT_SIGNATURE_OE
3470 || uBuf.au32[0] == IMAGE_MACHO32_SIGNATURE
3471 || uBuf.au32[0] == IMAGE_MACHO32_SIGNATURE_OE
3472 || uBuf.au32[0] == IMAGE_MACHO64_SIGNATURE
3473 || uBuf.au32[0] == IMAGE_MACHO64_SIGNATURE_OE)
3474 return RTSIGNTOOLFILETYPE_EXE;
3475
3476 /*
3477 * Catalog files are PKCS#7 SignedData and starts with a ContentInfo, i.e.:
3478 * SEQUENCE {
3479 * contentType OBJECT IDENTIFIER,
3480 * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL
3481 * }
3482 *
3483 * We ASSUME that it's DER encoded and doesn't use an indefinite length form
3484 * at the start and that contentType is signedData (1.2.840.113549.1.7.2).
3485 *
3486 * Example of a 10353 (0x2871) byte long file:
3487 * vv-------- contentType -------vv
3488 * 00000000 30 82 28 6D 06 09 2A 86 48 86 F7 0D 01 07 02 A0
3489 * 00000010 82 28 5E 30 82 28 5A 02 01 01 31 0B 30 09 06 05
3490 */
3491 if ( uBuf.ab[0] == (ASN1_TAG_SEQUENCE | ASN1_TAGFLAG_CONSTRUCTED)
3492 && uBuf.ab[1] != 0x80 /* not indefinite form */
3493 && uBuf.ab[1] > 0x30)
3494 {
3495 size_t off = 1;
3496 uint32_t cbRec = uBuf.ab[1];
3497 if (cbRec & 0x80)
3498 {
3499 cbRec &= 0x7f;
3500 off += cbRec;
3501 switch (cbRec)
3502 {
3503 case 1: cbRec = uBuf.ab[2]; break;
3504 case 2: cbRec = RT_MAKE_U16( uBuf.ab[3], uBuf.ab[2]); break;
3505 case 3: cbRec = RT_MAKE_U32_FROM_U8(uBuf.ab[4], uBuf.ab[3], uBuf.ab[2], 0); break;
3506 case 4: cbRec = RT_MAKE_U32_FROM_U8(uBuf.ab[5], uBuf.ab[4], uBuf.ab[3], uBuf.ab[2]); break;
3507 default: cbRec = UINT32_MAX; break;
3508 }
3509 }
3510 if (off <= 5)
3511 {
3512 off++;
3513 if (off + cbRec == cbFile)
3514 {
3515 /* If the contentType is signedData we're going to treat it as a catalog file,
3516 we don't currently much care about the signed content of a cat file. */
3517 static const uint8_t s_abSignedDataOid[] =
3518 { ASN1_TAG_OID, 9 /*length*/, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02 };
3519 if (memcmp(&uBuf.ab[off], s_abSignedDataOid, sizeof(s_abSignedDataOid)) == 0)
3520 return RTSIGNTOOLFILETYPE_CAT;
3521 }
3522 }
3523 }
3524
3525 RTMsgError("Unable to detect type of '%s'", pszFile);
3526 return RTSIGNTOOLFILETYPE_UNKNOWN;
3527}
3528
3529#endif /* !IPRT_SIGNTOOL_NO_SIGNING */
3530
3531
3532/*********************************************************************************************************************************
3533* The 'add-timestamp-exe-signature' command. *
3534*********************************************************************************************************************************/
3535#ifndef IPRT_SIGNTOOL_NO_SIGNING
3536
3537static RTEXITCODE HelpAddTimestampExeSignature(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
3538{
3539 RT_NOREF_PV(enmLevel);
3540
3541 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
3542 "add-timestamp-exe-signature [-v|--verbose] [--signature-index|-i <num>] "
3543 OPT_CERT_KEY_SYNOPSIS("--timestamp-")
3544 "[--timestamp-type old|new] "
3545 "[--timestamp-override <partial-isots>] "
3546 "[--replace-existing|-r] "
3547 "<exe>\n");
3548 if (enmLevel == RTSIGNTOOLHELP_FULL)
3549 RTStrmWrappedPrintf(pStrm, 0,
3550 "This is mainly to test timestamp code.\n"
3551 "\n"
3552 "The --timestamp-override option can take a partial or full ISO timestamp. It is merged "
3553 "with the current time if partial.\n"
3554 "\n");
3555 return RTEXITCODE_SUCCESS;
3556}
3557
3558static RTEXITCODE HandleAddTimestampExeSignature(int cArgs, char **papszArgs)
3559{
3560 /*
3561 * Parse arguments.
3562 */
3563 static const RTGETOPTDEF s_aOptions[] =
3564 {
3565 { "--signature-index", 'i', RTGETOPT_REQ_UINT32 },
3566 OPT_CERT_KEY_GETOPTDEF_ENTRIES("--timestamp-", 1000),
3567 { "--timestamp-type", OPT_TIMESTAMP_TYPE, RTGETOPT_REQ_STRING },
3568 { "--timestamp-override", OPT_TIMESTAMP_OVERRIDE, RTGETOPT_REQ_STRING },
3569 { "--replace-existing", 'r', RTGETOPT_REQ_NOTHING },
3570 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
3571 };
3572
3573 unsigned cVerbosity = 0;
3574 unsigned iSignature = 0;
3575 bool fReplaceExisting = false;
3576 bool fTimestampTypeOld = true;
3577 SignToolKeyPair TimestampCertKey("timestamp", true);
3578 RTTIMESPEC SigningTime;
3579 RTTimeNow(&SigningTime);
3580
3581 RTGETOPTSTATE GetState;
3582 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
3583 AssertRCReturn(rc, RTEXITCODE_FAILURE);
3584
3585 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
3586 RTGETOPTUNION ValueUnion;
3587 int ch;
3588 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
3589 {
3590 RTEXITCODE rcExit2 = RTEXITCODE_SUCCESS;
3591 switch (ch)
3592 {
3593 OPT_CERT_KEY_SWITCH_CASES(TimestampCertKey, 1000, ch, ValueUnion, rcExit2);
3594 case 'i': iSignature = ValueUnion.u32; break;
3595 case OPT_TIMESTAMP_TYPE: rcExit2 = HandleOptTimestampType(&fTimestampTypeOld, ValueUnion.psz); break;
3596 case OPT_TIMESTAMP_OVERRIDE: rcExit2 = HandleOptTimestampOverride(&SigningTime, ValueUnion.psz); break;
3597 case 'r': fReplaceExisting = true; break;
3598 case 'v': cVerbosity++; break;
3599 case 'V': return HandleVersion(cArgs, papszArgs);
3600 case 'h': return HelpAddTimestampExeSignature(g_pStdOut, RTSIGNTOOLHELP_FULL);
3601
3602 case VINF_GETOPT_NOT_OPTION:
3603 /* Do final certificate and key option processing (first file only). */
3604 rcExit2 = TimestampCertKey.finalizeOptions(cVerbosity);
3605 if (rcExit2 == RTEXITCODE_SUCCESS)
3606 {
3607 /* Do the work: */
3608 SIGNTOOLPKCS7EXE Exe;
3609 rcExit2 = SignToolPkcs7Exe_InitFromFile(&Exe, ValueUnion.psz, cVerbosity);
3610 if (rcExit2 == RTEXITCODE_SUCCESS)
3611 {
3612 rcExit2 = SignToolPkcs7_AddTimestampSignature(&Exe, cVerbosity, iSignature, fReplaceExisting,
3613 fTimestampTypeOld, SigningTime, &TimestampCertKey);
3614 if (rcExit2 == RTEXITCODE_SUCCESS)
3615 rcExit2 = SignToolPkcs7_Encode(&Exe, cVerbosity);
3616 if (rcExit2 == RTEXITCODE_SUCCESS)
3617 rcExit2 = SignToolPkcs7Exe_WriteSignatureToFile(&Exe, cVerbosity);
3618 SignToolPkcs7Exe_Delete(&Exe);
3619 }
3620 if (rcExit2 != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
3621 rcExit = rcExit2;
3622 rcExit2 = RTEXITCODE_SUCCESS;
3623 }
3624 break;
3625
3626 default:
3627 return RTGetOptPrintError(ch, &ValueUnion);
3628 }
3629
3630 if (rcExit2 != RTEXITCODE_SUCCESS)
3631 {
3632 rcExit = rcExit2;
3633 break;
3634 }
3635 }
3636 return rcExit;
3637}
3638
3639#endif /*!IPRT_SIGNTOOL_NO_SIGNING */
3640
3641
3642/*********************************************************************************************************************************
3643* The 'sign-exe' command. *
3644*********************************************************************************************************************************/
3645#ifndef IPRT_SIGNTOOL_NO_SIGNING
3646
3647static RTEXITCODE HelpSign(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
3648{
3649 RT_NOREF_PV(enmLevel);
3650
3651 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
3652 "sign [-v|--verbose] "
3653 "[--file-type exe|cat] "
3654 "[--type|/fd sha1|sha256] "
3655 "[--hash-pages|/ph] "
3656 "[--no-hash-pages|/nph] "
3657 "[--append/as] "
3658 "[--no-signing-time] "
3659 "[--add-cert <file>] "
3660 "[--timestamp-type old|new] "
3661 "[--timestamp-override <partial-isots>] "
3662 "[--verbose|/debug|-v] "
3663 OPT_CERT_KEY_SYNOPSIS("--")
3664 OPT_CERT_KEY_SYNOPSIS("--timestamp-")
3665 "<exe>\n");
3666 if (enmLevel == RTSIGNTOOLHELP_FULL)
3667 RTStrmWrappedPrintf(pStrm, 0,
3668 "\n"
3669 "Create a new code signature for an executable or catalog.\n"
3670 "\n"
3671 "Options:\n"
3672 " --append, /as\n"
3673 " Append the signature if one already exists. The default is to replace any existing signature.\n"
3674 " --type sha1|sha256, /fd sha1|sha256\n"
3675 " Signature type, SHA-1 or SHA-256.\n"
3676 " --hash-pages, /ph, --no-page-hashes, /nph\n"
3677 " Enables or disables page hashing. Ignored for catalog files. Default: --no-page-hashes\n"
3678 " --add-cert <file>, /ac <file>\n"
3679 " Adds (first) certificate from the file to the signature. Both PEM and DER (binary) encodings "
3680 "are accepted. Repeat to add more certiifcates.\n"
3681 " --timestamp-override <partial-iso-timestamp>\n"
3682 " This specifies the signing time as a ISO timestamp. Partial timestamps are merged with the "
3683 "current time. This is applied to any timestamp signature as well as the signingTime attribute of "
3684 "main signature. Higher resolution than seconds is not supported. Default: Current time.\n"
3685 " --no-signing-time\n"
3686 " Don't set the signing time on the main signature, only on the timestamp one. Unfortunately, "
3687 "this doesn't work without modifying OpenSSL a little.\n"
3688 " --timestamp-type old|new\n"
3689 " Selects the timstamp type. 'old' is the old style /t <url> stuff from signtool.exe. "
3690 "'new' means a RTC-3161 timstamp - currently not implemented. Default: old\n"
3691 "\n"
3692 "Certificate and Key Options (--timestamp-cert-name etc for timestamps):\n"
3693 " --cert-subject <partial name>, /n <partial name>\n"
3694 " Locate the main signature signing certificate and key, unless anything else is given, "
3695 "by the given name substring. Overrides any previous --cert-sha1 and --cert-file options.\n"
3696 " --cert-sha1 <hex bytes>, /sha1 <hex bytes>\n"
3697 " Locate the main signature signing certificate and key, unless anything else is given, "
3698 "by the given thumbprint. The hex bytes can be space separated, colon separated, just "
3699 "bunched together, or a mix of these. This overrids any previous --cert-name and --cert-file "
3700 "options.\n"
3701 " --cert-store <name>, /s <store>\n"
3702 " Certificate store to search when using --cert-name or --cert-sha1. Default: MY\n"
3703 " --cert-machine-store, /sm\n"
3704 " Use the machine store rather the ones of the current user.\n"
3705 " --cert-file <file>, /f <file>\n"
3706 " Load the certificate and key, unless anything else is given, from given file. Both PEM and "
3707 "DER (binary) encodings are supported. Keys file can be RSA or PKCS#12 formatted.\n"
3708 " --key-file <file>\n"
3709 " Load the private key from the given file. Support RSA and PKCS#12 formatted files.\n"
3710 " --key-password <password>, /p <password>\n"
3711 " Password to use to decrypt a PKCS#12 password file.\n"
3712 " --key-password-file <file>|stdin\n"
3713 " Load password to decrypt the password file from the given file or from stdin.\n"
3714 " --key-name <name>, /kc <name>\n"
3715 " The private key container name. Not implemented.\n"
3716 " --key-provider <name>, /csp <name>\n"
3717 " The name of the crypto provider where the private key conatiner specified via --key-name "
3718 "can be found.\n"
3719 );
3720
3721 return RTEXITCODE_SUCCESS;
3722}
3723
3724
3725static RTEXITCODE HandleSign(int cArgs, char **papszArgs)
3726{
3727 /*
3728 * Parse arguments.
3729 */
3730 static const RTGETOPTDEF s_aOptions[] =
3731 {
3732 { "--append", 'A', RTGETOPT_REQ_NOTHING },
3733 { "/as", 'A', RTGETOPT_REQ_NOTHING },
3734 { "/a", OPT_IGNORED, RTGETOPT_REQ_NOTHING }, /* select best cert automatically */
3735 { "--type", 't', RTGETOPT_REQ_STRING },
3736 { "/fd", 't', RTGETOPT_REQ_STRING },
3737 { "--hash-pages", OPT_HASH_PAGES, RTGETOPT_REQ_NOTHING },
3738 { "/ph", OPT_HASH_PAGES, RTGETOPT_REQ_NOTHING },
3739 { "--no-hash-pages", OPT_NO_HASH_PAGES, RTGETOPT_REQ_NOTHING },
3740 { "/nph", OPT_NO_HASH_PAGES, RTGETOPT_REQ_NOTHING },
3741 { "--add-cert", OPT_ADD_CERT, RTGETOPT_REQ_STRING },
3742 { "/ac", OPT_ADD_CERT, RTGETOPT_REQ_STRING },
3743 { "--description", 'd', RTGETOPT_REQ_STRING },
3744 { "--desc", 'd', RTGETOPT_REQ_STRING },
3745 { "/d", 'd', RTGETOPT_REQ_STRING },
3746 { "--description-url", 'D', RTGETOPT_REQ_STRING },
3747 { "--desc-url", 'D', RTGETOPT_REQ_STRING },
3748 { "/du", 'D', RTGETOPT_REQ_STRING },
3749 { "--no-signing-time", OPT_NO_SIGNING_TIME, RTGETOPT_REQ_NOTHING },
3750 OPT_CERT_KEY_GETOPTDEF_ENTRIES("--", 1000),
3751 OPT_CERT_KEY_GETOPTDEF_COMPAT_ENTRIES( 1000),
3752 OPT_CERT_KEY_GETOPTDEF_ENTRIES("--timestamp-", 1020),
3753 { "--timestamp-type", OPT_TIMESTAMP_TYPE, RTGETOPT_REQ_STRING },
3754 { "--timestamp-override", OPT_TIMESTAMP_OVERRIDE, RTGETOPT_REQ_STRING },
3755 { "--file-type", OPT_FILE_TYPE, RTGETOPT_REQ_STRING },
3756 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
3757 { "/v", 'v', RTGETOPT_REQ_NOTHING },
3758 { "/debug", 'v', RTGETOPT_REQ_NOTHING },
3759 };
3760
3761 unsigned cVerbosity = 0;
3762 RTDIGESTTYPE enmSigType = RTDIGESTTYPE_SHA1;
3763 bool fReplaceExisting = true;
3764 bool fHashPages = false;
3765 bool fNoSigningTime = false;
3766 RTSIGNTOOLFILETYPE enmForceFileType = RTSIGNTOOLFILETYPE_DETECT;
3767 SignToolKeyPair SigningCertKey("signing", true);
3768 RTCRSTORE hAddCerts = NIL_RTCRSTORE; /* leaked if returning directly (--help, --version) */
3769 const char *pszDescription = NULL; /** @todo implement putting descriptions into the OpusInfo stuff. */
3770 const char *pszDescriptionUrl = NULL;
3771 bool fTimestampTypeOld = true;
3772 SignToolKeyPair TimestampCertKey("timestamp");
3773 RTTIMESPEC SigningTime;
3774 RTTimeNow(&SigningTime);
3775
3776 RTGETOPTSTATE GetState;
3777 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
3778 AssertRCReturn(rc, RTEXITCODE_FAILURE);
3779
3780 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
3781 RTGETOPTUNION ValueUnion;
3782 int ch;
3783 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
3784 {
3785 RTEXITCODE rcExit2 = RTEXITCODE_SUCCESS;
3786 switch (ch)
3787 {
3788 OPT_CERT_KEY_SWITCH_CASES(SigningCertKey, 1000, ch, ValueUnion, rcExit2);
3789 OPT_CERT_KEY_SWITCH_CASES(TimestampCertKey, 1020, ch, ValueUnion, rcExit2);
3790 case 't': rcExit2 = HandleOptSignatureType(&enmSigType, ValueUnion.psz); break;
3791 case 'A': fReplaceExisting = false; break;
3792 case 'd': pszDescription = ValueUnion.psz; break;
3793 case 'D': pszDescriptionUrl = ValueUnion.psz; break;
3794 case OPT_HASH_PAGES: fHashPages = true; break;
3795 case OPT_NO_HASH_PAGES: fHashPages = false; break;
3796 case OPT_NO_SIGNING_TIME: fNoSigningTime = true; break;
3797 case OPT_ADD_CERT: rcExit2 = HandleOptAddCert(&hAddCerts, ValueUnion.psz); break;
3798 case OPT_TIMESTAMP_TYPE: rcExit2 = HandleOptTimestampType(&fTimestampTypeOld, ValueUnion.psz); break;
3799 case OPT_TIMESTAMP_OVERRIDE: rcExit2 = HandleOptTimestampOverride(&SigningTime, ValueUnion.psz); break;
3800 case OPT_FILE_TYPE: rcExit2 = HandleOptFileType(&enmForceFileType, ValueUnion.psz); break;
3801 case OPT_IGNORED: break;
3802 case 'v': cVerbosity++; break;
3803 case 'V': return HandleVersion(cArgs, papszArgs);
3804 case 'h': return HelpSign(g_pStdOut, RTSIGNTOOLHELP_FULL);
3805
3806 case VINF_GETOPT_NOT_OPTION:
3807 /*
3808 * Do final certificate and key option processing (first file only).
3809 */
3810 rcExit2 = SigningCertKey.finalizeOptions(cVerbosity);
3811 if (rcExit2 == RTEXITCODE_SUCCESS)
3812 rcExit2 = TimestampCertKey.finalizeOptions(cVerbosity);
3813 if (rcExit2 == RTEXITCODE_SUCCESS)
3814 {
3815 /*
3816 * Detect file type.
3817 */
3818 RTSIGNTOOLFILETYPE enmFileType = DetectFileType(enmForceFileType, ValueUnion.psz);
3819 if (enmFileType == RTSIGNTOOLFILETYPE_EXE)
3820 {
3821 /*
3822 * Sign executable image.
3823 */
3824 SIGNTOOLPKCS7EXE Exe;
3825 rcExit2 = SignToolPkcs7Exe_InitFromFile(&Exe, ValueUnion.psz, cVerbosity,
3826 RTLDRARCH_WHATEVER, true /*fAllowUnsigned*/);
3827 if (rcExit2 == RTEXITCODE_SUCCESS)
3828 {
3829 rcExit2 = SignToolPkcs7_AddOrReplaceSignature(&Exe, cVerbosity, enmSigType, fReplaceExisting,
3830 fHashPages, fNoSigningTime, &SigningCertKey, hAddCerts,
3831 fTimestampTypeOld, SigningTime, &TimestampCertKey);
3832 if (rcExit2 == RTEXITCODE_SUCCESS)
3833 rcExit2 = SignToolPkcs7_Encode(&Exe, cVerbosity);
3834 if (rcExit2 == RTEXITCODE_SUCCESS)
3835 rcExit2 = SignToolPkcs7Exe_WriteSignatureToFile(&Exe, cVerbosity);
3836 SignToolPkcs7Exe_Delete(&Exe);
3837 }
3838 }
3839 else if (enmFileType == RTSIGNTOOLFILETYPE_CAT)
3840 {
3841 /*
3842 * Sign catalog file.
3843 */
3844 SIGNTOOLPKCS7 Cat;
3845 rcExit2 = SignToolPkcs7_InitFromFile(&Cat, ValueUnion.psz, cVerbosity);
3846 if (rcExit2 == RTEXITCODE_SUCCESS)
3847 {
3848 rcExit2 = SignToolPkcs7_AddOrReplaceCatSignature(&Cat, cVerbosity, enmSigType, fReplaceExisting,
3849 fNoSigningTime, &SigningCertKey, hAddCerts,
3850 fTimestampTypeOld, SigningTime, &TimestampCertKey);
3851 if (rcExit2 == RTEXITCODE_SUCCESS)
3852 rcExit2 = SignToolPkcs7_Encode(&Cat, cVerbosity);
3853 if (rcExit2 == RTEXITCODE_SUCCESS)
3854 rcExit2 = SignToolPkcs7_WriteSignatureToFile(&Cat, ValueUnion.psz, cVerbosity);
3855 SignToolPkcs7_Delete(&Cat);
3856 }
3857 }
3858 else
3859 rcExit2 = RTEXITCODE_FAILURE;
3860 if (rcExit2 != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
3861 rcExit = rcExit2;
3862 rcExit2 = RTEXITCODE_SUCCESS;
3863 }
3864 break;
3865
3866 default:
3867 return RTGetOptPrintError(ch, &ValueUnion);
3868 }
3869 if (rcExit2 != RTEXITCODE_SUCCESS)
3870 {
3871 rcExit = rcExit2;
3872 break;
3873 }
3874 }
3875
3876 if (hAddCerts != NIL_RTCRSTORE)
3877 RTCrStoreRelease(hAddCerts);
3878 return rcExit;
3879}
3880
3881#endif /*!IPRT_SIGNTOOL_NO_SIGNING */
3882
3883
3884/*********************************************************************************************************************************
3885* The 'verify-exe' command. *
3886*********************************************************************************************************************************/
3887#ifndef IPRT_IN_BUILD_TOOL
3888
3889static RTEXITCODE HelpVerifyExe(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
3890{
3891 RT_NOREF_PV(enmLevel);
3892 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
3893 "verify-exe [--verbose|--quiet] [--kernel] [--root <root-cert.der>] [--additional <supp-cert.der>] "
3894 "[--type <win|osx>] <exe1> [exe2 [..]]\n");
3895 return RTEXITCODE_SUCCESS;
3896}
3897
3898typedef struct VERIFYEXESTATE
3899{
3900 RTCRSTORE hRootStore;
3901 RTCRSTORE hKernelRootStore;
3902 RTCRSTORE hAdditionalStore;
3903 bool fKernel;
3904 int cVerbose;
3905 enum { kSignType_Windows, kSignType_OSX } enmSignType;
3906 RTLDRARCH enmLdrArch;
3907 uint32_t cBad;
3908 uint32_t cOkay;
3909 const char *pszFilename;
3910 RTTIMESPEC ValidationTime;
3911} VERIFYEXESTATE;
3912
3913# ifdef VBOX
3914/** Certificate store load set.
3915 * Declared outside HandleVerifyExe because of braindead gcc visibility crap. */
3916struct STSTORESET
3917{
3918 RTCRSTORE hStore;
3919 PCSUPTAENTRY paTAs;
3920 unsigned cTAs;
3921};
3922# endif
3923
3924/**
3925 * @callback_method_impl{FNRTCRPKCS7VERIFYCERTCALLBACK,
3926 * Standard code signing. Use this for Microsoft SPC.}
3927 */
3928static DECLCALLBACK(int) VerifyExecCertVerifyCallback(PCRTCRX509CERTIFICATE pCert, RTCRX509CERTPATHS hCertPaths, uint32_t fFlags,
3929 void *pvUser, PRTERRINFO pErrInfo)
3930{
3931 VERIFYEXESTATE *pState = (VERIFYEXESTATE *)pvUser;
3932 uint32_t cPaths = RTCrX509CertPathsGetPathCount(hCertPaths);
3933
3934 /*
3935 * Dump all the paths.
3936 */
3937 if (pState->cVerbose > 0)
3938 {
3939 for (uint32_t iPath = 0; iPath < cPaths; iPath++)
3940 {
3941 RTPrintf("---\n");
3942 RTCrX509CertPathsDumpOne(hCertPaths, iPath, pState->cVerbose, RTStrmDumpPrintfV, g_pStdOut);
3943 *pErrInfo->pszMsg = '\0';
3944 }
3945 RTPrintf("---\n");
3946 }
3947
3948 /*
3949 * Test signing certificates normally doesn't have all the necessary
3950 * features required below. So, treat them as special cases.
3951 */
3952 if ( hCertPaths == NIL_RTCRX509CERTPATHS
3953 && RTCrX509Name_Compare(&pCert->TbsCertificate.Issuer, &pCert->TbsCertificate.Subject) == 0)
3954 {
3955 RTMsgInfo("Test signed.\n");
3956 return VINF_SUCCESS;
3957 }
3958
3959 if (hCertPaths == NIL_RTCRX509CERTPATHS)
3960 RTMsgInfo("Signed by trusted certificate.\n");
3961
3962 /*
3963 * Standard code signing capabilites required.
3964 */
3965 int rc = RTCrPkcs7VerifyCertCallbackCodeSigning(pCert, hCertPaths, fFlags, NULL, pErrInfo);
3966 if ( RT_SUCCESS(rc)
3967 && (fFlags & RTCRPKCS7VCC_F_SIGNED_DATA))
3968 {
3969 /*
3970 * If windows kernel signing, a valid certificate path must be anchored
3971 * by the microsoft kernel signing root certificate. The only
3972 * alternative is test signing.
3973 */
3974 if ( pState->fKernel
3975 && hCertPaths != NIL_RTCRX509CERTPATHS
3976 && pState->enmSignType == VERIFYEXESTATE::kSignType_Windows)
3977 {
3978 uint32_t cFound = 0;
3979 uint32_t cValid = 0;
3980 for (uint32_t iPath = 0; iPath < cPaths; iPath++)
3981 {
3982 bool fTrusted;
3983 PCRTCRX509NAME pSubject;
3984 PCRTCRX509SUBJECTPUBLICKEYINFO pPublicKeyInfo;
3985 int rcVerify;
3986 rc = RTCrX509CertPathsQueryPathInfo(hCertPaths, iPath, &fTrusted, NULL /*pcNodes*/, &pSubject, &pPublicKeyInfo,
3987 NULL, NULL /*pCertCtx*/, &rcVerify);
3988 AssertRCBreak(rc);
3989
3990 if (RT_SUCCESS(rcVerify))
3991 {
3992 Assert(fTrusted);
3993 cValid++;
3994
3995 /* Search the kernel signing root store for a matching anchor. */
3996 RTCRSTORECERTSEARCH Search;
3997 rc = RTCrStoreCertFindBySubjectOrAltSubjectByRfc5280(pState->hKernelRootStore, pSubject, &Search);
3998 AssertRCBreak(rc);
3999 PCRTCRCERTCTX pCertCtx;
4000 while ((pCertCtx = RTCrStoreCertSearchNext(pState->hKernelRootStore, &Search)) != NULL)
4001 {
4002 PCRTCRX509SUBJECTPUBLICKEYINFO pPubKeyInfo;
4003 if (pCertCtx->pCert)
4004 pPubKeyInfo = &pCertCtx->pCert->TbsCertificate.SubjectPublicKeyInfo;
4005 else if (pCertCtx->pTaInfo)
4006 pPubKeyInfo = &pCertCtx->pTaInfo->PubKey;
4007 else
4008 pPubKeyInfo = NULL;
4009 if (RTCrX509SubjectPublicKeyInfo_Compare(pPubKeyInfo, pPublicKeyInfo) == 0)
4010 cFound++;
4011 RTCrCertCtxRelease(pCertCtx);
4012 }
4013
4014 int rc2 = RTCrStoreCertSearchDestroy(pState->hKernelRootStore, &Search); AssertRC(rc2);
4015 }
4016 }
4017 if (RT_SUCCESS(rc) && cFound == 0)
4018 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE, "Not valid kernel code signature.");
4019 if (RT_SUCCESS(rc) && cValid != 2)
4020 RTMsgWarning("%u valid paths, expected 2", cValid);
4021 }
4022 /*
4023 * For Mac OS X signing, check for special developer ID attributes.
4024 */
4025 else if (pState->enmSignType == VERIFYEXESTATE::kSignType_OSX)
4026 {
4027 uint32_t cDevIdApp = 0;
4028 uint32_t cDevIdKext = 0;
4029 uint32_t cDevIdMacDev = 0;
4030 for (uint32_t i = 0; i < pCert->TbsCertificate.T3.Extensions.cItems; i++)
4031 {
4032 PCRTCRX509EXTENSION pExt = pCert->TbsCertificate.T3.Extensions.papItems[i];
4033 if (RTAsn1ObjId_CompareWithString(&pExt->ExtnId, RTCR_APPLE_CS_DEVID_APPLICATION_OID) == 0)
4034 {
4035 cDevIdApp++;
4036 if (!pExt->Critical.fValue)
4037 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
4038 "Dev ID Application certificate extension is not flagged critical");
4039 }
4040 else if (RTAsn1ObjId_CompareWithString(&pExt->ExtnId, RTCR_APPLE_CS_DEVID_KEXT_OID) == 0)
4041 {
4042 cDevIdKext++;
4043 if (!pExt->Critical.fValue)
4044 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
4045 "Dev ID kext certificate extension is not flagged critical");
4046 }
4047 else if (RTAsn1ObjId_CompareWithString(&pExt->ExtnId, RTCR_APPLE_CS_DEVID_MAC_SW_DEV_OID) == 0)
4048 {
4049 cDevIdMacDev++;
4050 if (!pExt->Critical.fValue)
4051 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
4052 "Dev ID Mac SW dev certificate extension is not flagged critical");
4053 }
4054 }
4055 if (cDevIdApp == 0)
4056 {
4057 if (cDevIdMacDev == 0)
4058 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
4059 "Certificate is missing the 'Dev ID Application' extension");
4060 else
4061 RTMsgWarning("Mac SW dev certificate used to sign code.");
4062 }
4063 if (cDevIdKext == 0 && pState->fKernel)
4064 {
4065 if (cDevIdMacDev == 0)
4066 rc = RTErrInfoSetF(pErrInfo, VERR_GENERAL_FAILURE,
4067 "Certificate is missing the 'Dev ID kext' extension");
4068 else
4069 RTMsgWarning("Mac SW dev certificate used to sign kernel code.");
4070 }
4071 }
4072 }
4073
4074 return rc;
4075}
4076
4077/** @callback_method_impl{FNRTLDRVALIDATESIGNEDDATA} */
4078static DECLCALLBACK(int) VerifyExeCallback(RTLDRMOD hLdrMod, PCRTLDRSIGNATUREINFO pInfo, PRTERRINFO pErrInfo, void *pvUser)
4079{
4080 VERIFYEXESTATE *pState = (VERIFYEXESTATE *)pvUser;
4081 RT_NOREF_PV(hLdrMod);
4082
4083 switch (pInfo->enmType)
4084 {
4085 case RTLDRSIGNATURETYPE_PKCS7_SIGNED_DATA:
4086 {
4087 PCRTCRPKCS7CONTENTINFO pContentInfo = (PCRTCRPKCS7CONTENTINFO)pInfo->pvSignature;
4088
4089 /*
4090 * Dump the signed data if so requested and it's the first one, assuming that
4091 * additional signatures in contained wihtin the same ContentInfo structure.
4092 */
4093 if (pState->cVerbose && pInfo->iSignature == 0)
4094 RTAsn1Dump(&pContentInfo->SeqCore.Asn1Core, 0, 0, RTStrmDumpPrintfV, g_pStdOut);
4095
4096 /*
4097 * We'll try different alternative timestamps here.
4098 */
4099 struct { RTTIMESPEC TimeSpec; const char *pszDesc; } aTimes[3];
4100 unsigned cTimes = 0;
4101
4102 /* The specified timestamp. */
4103 if (RTTimeSpecGetSeconds(&pState->ValidationTime) != 0)
4104 {
4105 aTimes[cTimes].TimeSpec = pState->ValidationTime;
4106 aTimes[cTimes].pszDesc = "validation time";
4107 cTimes++;
4108 }
4109
4110 /* Linking timestamp: */
4111 uint64_t uLinkingTime = 0;
4112 int rc = RTLdrQueryProp(hLdrMod, RTLDRPROP_TIMESTAMP_SECONDS, &uLinkingTime, sizeof(uLinkingTime));
4113 if (RT_SUCCESS(rc))
4114 {
4115 RTTimeSpecSetSeconds(&aTimes[cTimes].TimeSpec, uLinkingTime);
4116 aTimes[cTimes].pszDesc = "at link time";
4117 cTimes++;
4118 }
4119 else if (rc != VERR_NOT_FOUND)
4120 RTMsgError("RTLdrQueryProp/RTLDRPROP_TIMESTAMP_SECONDS failed on '%s': %Rrc\n", pState->pszFilename, rc);
4121
4122 /* Now: */
4123 RTTimeNow(&aTimes[cTimes].TimeSpec);
4124 aTimes[cTimes].pszDesc = "now";
4125 cTimes++;
4126
4127 /*
4128 * Do the actual verification.
4129 */
4130 for (unsigned iTime = 0; iTime < cTimes; iTime++)
4131 {
4132 if (pInfo->pvExternalData)
4133 rc = RTCrPkcs7VerifySignedDataWithExternalData(pContentInfo,
4134 RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE_SIGNING_TIME_ONLY
4135 | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_SIGNING_TIME_IF_PRESENT
4136 | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_MS_TIMESTAMP_IF_PRESENT
4137 | RTCRPKCS7VERIFY_SD_F_CHECK_TRUST_ANCHORS,
4138 pState->hAdditionalStore, pState->hRootStore,
4139 &aTimes[iTime].TimeSpec,
4140 VerifyExecCertVerifyCallback, pState,
4141 pInfo->pvExternalData, pInfo->cbExternalData, pErrInfo);
4142 else
4143 rc = RTCrPkcs7VerifySignedData(pContentInfo,
4144 RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE_SIGNING_TIME_ONLY
4145 | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_SIGNING_TIME_IF_PRESENT
4146 | RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_MS_TIMESTAMP_IF_PRESENT
4147 | RTCRPKCS7VERIFY_SD_F_CHECK_TRUST_ANCHORS,
4148 pState->hAdditionalStore, pState->hRootStore,
4149 &aTimes[iTime].TimeSpec,
4150 VerifyExecCertVerifyCallback, pState, pErrInfo);
4151 if (RT_SUCCESS(rc))
4152 {
4153 Assert(rc == VINF_SUCCESS || rc == VINF_CR_DIGEST_DEPRECATED);
4154 const char *pszNote = rc == VINF_CR_DIGEST_DEPRECATED ? " (deprecated digest)" : "";
4155 if (pInfo->cSignatures == 1)
4156 RTMsgInfo("'%s' is valid %s%s.\n", pState->pszFilename, aTimes[iTime].pszDesc, pszNote);
4157 else
4158 RTMsgInfo("'%s' signature #%u is valid %s%s.\n",
4159 pState->pszFilename, pInfo->iSignature + 1, aTimes[iTime].pszDesc, pszNote);
4160 pState->cOkay++;
4161 return VINF_SUCCESS;
4162 }
4163 if (rc != VERR_CR_X509_CPV_NOT_VALID_AT_TIME)
4164 {
4165 if (pInfo->cSignatures == 1)
4166 RTMsgError("%s: Failed to verify signature: %Rrc%#RTeim\n", pState->pszFilename, rc, pErrInfo);
4167 else
4168 RTMsgError("%s: Failed to verify signature #%u: %Rrc%#RTeim\n",
4169 pState->pszFilename, pInfo->iSignature + 1, rc, pErrInfo);
4170 pState->cBad++;
4171 return VINF_SUCCESS;
4172 }
4173 }
4174
4175 if (pInfo->cSignatures == 1)
4176 RTMsgError("%s: Signature is not valid at present or link time.\n", pState->pszFilename);
4177 else
4178 RTMsgError("%s: Signature #%u is not valid at present or link time.\n",
4179 pState->pszFilename, pInfo->iSignature + 1);
4180 pState->cBad++;
4181 return VINF_SUCCESS;
4182 }
4183
4184 default:
4185 return RTErrInfoSetF(pErrInfo, VERR_NOT_SUPPORTED, "Unsupported signature type: %d", pInfo->enmType);
4186 }
4187}
4188
4189/**
4190 * Worker for HandleVerifyExe.
4191 */
4192static RTEXITCODE HandleVerifyExeWorker(VERIFYEXESTATE *pState, const char *pszFilename, PRTERRINFOSTATIC pStaticErrInfo)
4193{
4194 /*
4195 * Open the executable image and verify it.
4196 */
4197 RTLDRMOD hLdrMod;
4198 int rc = RTLdrOpen(pszFilename, RTLDR_O_FOR_VALIDATION, pState->enmLdrArch, &hLdrMod);
4199 if (RT_FAILURE(rc))
4200 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error opening executable image '%s': %Rrc", pszFilename, rc);
4201
4202 /* Reset the state. */
4203 pState->cBad = 0;
4204 pState->cOkay = 0;
4205 pState->pszFilename = pszFilename;
4206
4207 rc = RTLdrVerifySignature(hLdrMod, VerifyExeCallback, pState, RTErrInfoInitStatic(pStaticErrInfo));
4208 if (RT_FAILURE(rc))
4209 RTMsgError("RTLdrVerifySignature failed on '%s': %Rrc - %s\n", pszFilename, rc, pStaticErrInfo->szMsg);
4210
4211 int rc2 = RTLdrClose(hLdrMod);
4212 if (RT_FAILURE(rc2))
4213 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTLdrClose failed: %Rrc\n", rc2);
4214 if (RT_FAILURE(rc))
4215 return rc != VERR_LDRVI_NOT_SIGNED ? RTEXITCODE_FAILURE : RTEXITCODE_SKIPPED;
4216
4217 return pState->cOkay > 0 ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
4218}
4219
4220
4221static RTEXITCODE HandleVerifyExe(int cArgs, char **papszArgs)
4222{
4223 RTERRINFOSTATIC StaticErrInfo;
4224
4225 /* Note! This code does not try to clean up the crypto stores on failure.
4226 This is intentional as the code is only expected to be used in a
4227 one-command-per-process environment where we do exit() upon
4228 returning from this function. */
4229
4230 /*
4231 * Parse arguments.
4232 */
4233 static const RTGETOPTDEF s_aOptions[] =
4234 {
4235 { "--kernel", 'k', RTGETOPT_REQ_NOTHING },
4236 { "--root", 'r', RTGETOPT_REQ_STRING },
4237 { "--additional", 'a', RTGETOPT_REQ_STRING },
4238 { "--add", 'a', RTGETOPT_REQ_STRING },
4239 { "--type", 't', RTGETOPT_REQ_STRING },
4240 { "--validation-time", 'T', RTGETOPT_REQ_STRING },
4241 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
4242 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
4243 };
4244
4245 VERIFYEXESTATE State =
4246 {
4247 NIL_RTCRSTORE, NIL_RTCRSTORE, NIL_RTCRSTORE, false, 0,
4248 VERIFYEXESTATE::kSignType_Windows, RTLDRARCH_WHATEVER,
4249 0, 0, NULL
4250 };
4251 int rc = RTCrStoreCreateInMem(&State.hRootStore, 0);
4252 if (RT_SUCCESS(rc))
4253 rc = RTCrStoreCreateInMem(&State.hKernelRootStore, 0);
4254 if (RT_SUCCESS(rc))
4255 rc = RTCrStoreCreateInMem(&State.hAdditionalStore, 0);
4256 if (RT_FAILURE(rc))
4257 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error creating in-memory certificate store: %Rrc", rc);
4258 RTTimeSpecSetSeconds(&State.ValidationTime, 0);
4259
4260 RTGETOPTSTATE GetState;
4261 rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
4262 AssertRCReturn(rc, RTEXITCODE_FAILURE);
4263 RTGETOPTUNION ValueUnion;
4264 int ch;
4265 while ((ch = RTGetOpt(&GetState, &ValueUnion)) && ch != VINF_GETOPT_NOT_OPTION)
4266 {
4267 switch (ch)
4268 {
4269 case 'r': case 'a':
4270 rc = RTCrStoreCertAddFromFile(ch == 'r' ? State.hRootStore : State.hAdditionalStore,
4271 RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR,
4272 ValueUnion.psz, RTErrInfoInitStatic(&StaticErrInfo));
4273 if (RT_FAILURE(rc))
4274 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error loading certificate '%s': %Rrc - %s",
4275 ValueUnion.psz, rc, StaticErrInfo.szMsg);
4276 if (RTErrInfoIsSet(&StaticErrInfo.Core))
4277 RTMsgWarning("Warnings loading certificate '%s': %s", ValueUnion.psz, StaticErrInfo.szMsg);
4278 break;
4279
4280 case 't':
4281 if (!strcmp(ValueUnion.psz, "win") || !strcmp(ValueUnion.psz, "windows"))
4282 State.enmSignType = VERIFYEXESTATE::kSignType_Windows;
4283 else if (!strcmp(ValueUnion.psz, "osx") || !strcmp(ValueUnion.psz, "apple"))
4284 State.enmSignType = VERIFYEXESTATE::kSignType_OSX;
4285 else
4286 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown signing type: '%s'", ValueUnion.psz);
4287 break;
4288
4289 case 'T':
4290 if (!RTTimeSpecFromString(&State.ValidationTime, ValueUnion.psz))
4291 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Invalid validation time (%s): %Rrc", ValueUnion.psz, rc);
4292 break;
4293
4294 case 'k': State.fKernel = true; break;
4295 case 'v': State.cVerbose++; break;
4296 case 'q': State.cVerbose = 0; break;
4297 case 'V': return HandleVersion(cArgs, papszArgs);
4298 case 'h': return HelpVerifyExe(g_pStdOut, RTSIGNTOOLHELP_FULL);
4299 default: return RTGetOptPrintError(ch, &ValueUnion);
4300 }
4301 }
4302 if (ch != VINF_GETOPT_NOT_OPTION)
4303 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
4304
4305 /*
4306 * Populate the certificate stores according to the signing type.
4307 */
4308# ifdef VBOX
4309 unsigned cSets = 0;
4310 struct STSTORESET aSets[6];
4311 switch (State.enmSignType)
4312 {
4313 case VERIFYEXESTATE::kSignType_Windows:
4314 aSets[cSets].hStore = State.hRootStore;
4315 aSets[cSets].paTAs = g_aSUPTimestampTAs;
4316 aSets[cSets].cTAs = g_cSUPTimestampTAs;
4317 cSets++;
4318 aSets[cSets].hStore = State.hRootStore;
4319 aSets[cSets].paTAs = g_aSUPSpcRootTAs;
4320 aSets[cSets].cTAs = g_cSUPSpcRootTAs;
4321 cSets++;
4322 aSets[cSets].hStore = State.hRootStore;
4323 aSets[cSets].paTAs = g_aSUPNtKernelRootTAs;
4324 aSets[cSets].cTAs = g_cSUPNtKernelRootTAs;
4325 cSets++;
4326 aSets[cSets].hStore = State.hKernelRootStore;
4327 aSets[cSets].paTAs = g_aSUPNtKernelRootTAs;
4328 aSets[cSets].cTAs = g_cSUPNtKernelRootTAs;
4329 cSets++;
4330 break;
4331
4332 case VERIFYEXESTATE::kSignType_OSX:
4333 aSets[cSets].hStore = State.hRootStore;
4334 aSets[cSets].paTAs = g_aSUPAppleRootTAs;
4335 aSets[cSets].cTAs = g_cSUPAppleRootTAs;
4336 cSets++;
4337 break;
4338 }
4339 for (unsigned i = 0; i < cSets; i++)
4340 for (unsigned j = 0; j < aSets[i].cTAs; j++)
4341 {
4342 rc = RTCrStoreCertAddEncoded(aSets[i].hStore, RTCRCERTCTX_F_ENC_TAF_DER, aSets[i].paTAs[j].pch,
4343 aSets[i].paTAs[j].cb, RTErrInfoInitStatic(&StaticErrInfo));
4344 if (RT_FAILURE(rc))
4345 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTCrStoreCertAddEncoded failed (%u/%u): %s",
4346 i, j, StaticErrInfo.szMsg);
4347 }
4348# endif /* VBOX */
4349
4350 /*
4351 * Do it.
4352 */
4353 RTEXITCODE rcExit;
4354 for (;;)
4355 {
4356 rcExit = HandleVerifyExeWorker(&State, ValueUnion.psz, &StaticErrInfo);
4357 if (rcExit != RTEXITCODE_SUCCESS)
4358 break;
4359
4360 /*
4361 * Next file
4362 */
4363 ch = RTGetOpt(&GetState, &ValueUnion);
4364 if (ch == 0)
4365 break;
4366 if (ch != VINF_GETOPT_NOT_OPTION)
4367 {
4368 rcExit = RTGetOptPrintError(ch, &ValueUnion);
4369 break;
4370 }
4371 }
4372
4373 /*
4374 * Clean up.
4375 */
4376 uint32_t cRefs;
4377 cRefs = RTCrStoreRelease(State.hRootStore); Assert(cRefs == 0);
4378 cRefs = RTCrStoreRelease(State.hKernelRootStore); Assert(cRefs == 0);
4379 cRefs = RTCrStoreRelease(State.hAdditionalStore); Assert(cRefs == 0);
4380
4381 return rcExit;
4382}
4383
4384#endif /* !IPRT_IN_BUILD_TOOL */
4385
4386/*
4387 * common code for show-exe and show-cat:
4388 */
4389
4390/**
4391 * Display an object ID.
4392 *
4393 * @returns IPRT status code.
4394 * @param pThis The show exe instance data.
4395 * @param pObjId The object ID to display.
4396 * @param pszLabel The field label (prefixed by szPrefix).
4397 * @param pszPost What to print after the ID (typically newline).
4398 */
4399static void HandleShowExeWorkerDisplayObjId(PSHOWEXEPKCS7 pThis, PCRTASN1OBJID pObjId, const char *pszLabel, const char *pszPost)
4400{
4401 int rc = RTAsn1QueryObjIdName(pObjId, pThis->szTmp, sizeof(pThis->szTmp));
4402 if (RT_SUCCESS(rc))
4403 {
4404 if (pThis->cVerbosity > 1)
4405 RTPrintf("%s%s%s (%s)%s", pThis->szPrefix, pszLabel, pThis->szTmp, pObjId->szObjId, pszPost);
4406 else
4407 RTPrintf("%s%s%s%s", pThis->szPrefix, pszLabel, pThis->szTmp, pszPost);
4408 }
4409 else
4410 RTPrintf("%s%s%s%s", pThis->szPrefix, pszLabel, pObjId->szObjId, pszPost);
4411}
4412
4413
4414/**
4415 * Display an object ID, without prefix and label
4416 *
4417 * @returns IPRT status code.
4418 * @param pThis The show exe instance data.
4419 * @param pObjId The object ID to display.
4420 * @param pszPost What to print after the ID (typically newline).
4421 */
4422static void HandleShowExeWorkerDisplayObjIdSimple(PSHOWEXEPKCS7 pThis, PCRTASN1OBJID pObjId, const char *pszPost)
4423{
4424 int rc = RTAsn1QueryObjIdName(pObjId, pThis->szTmp, sizeof(pThis->szTmp));
4425 if (RT_SUCCESS(rc))
4426 {
4427 if (pThis->cVerbosity > 1)
4428 RTPrintf("%s (%s)%s", pThis->szTmp, pObjId->szObjId, pszPost);
4429 else
4430 RTPrintf("%s%s", pThis->szTmp, pszPost);
4431 }
4432 else
4433 RTPrintf("%s%s", pObjId->szObjId, pszPost);
4434}
4435
4436
4437/**
4438 * Display a signer info attribute.
4439 *
4440 * @returns IPRT status code.
4441 * @param pThis The show exe instance data.
4442 * @param offPrefix The current prefix offset.
4443 * @param pAttr The attribute to display.
4444 */
4445static int HandleShowExeWorkerPkcs7DisplayAttrib(PSHOWEXEPKCS7 pThis, size_t offPrefix, PCRTCRPKCS7ATTRIBUTE pAttr)
4446{
4447 HandleShowExeWorkerDisplayObjId(pThis, &pAttr->Type, "", ":\n");
4448 if (pThis->cVerbosity > 4 && pAttr->SeqCore.Asn1Core.uData.pu8)
4449 RTPrintf("%s uData.pu8=%p cb=%#x\n", pThis->szPrefix, pAttr->SeqCore.Asn1Core.uData.pu8, pAttr->SeqCore.Asn1Core.cb);
4450
4451 int rc = VINF_SUCCESS;
4452 switch (pAttr->enmType)
4453 {
4454 case RTCRPKCS7ATTRIBUTETYPE_UNKNOWN:
4455 if (pAttr->uValues.pCores->cItems <= 1)
4456 RTPrintf("%s %u bytes\n", pThis->szPrefix,pAttr->uValues.pCores->SetCore.Asn1Core.cb);
4457 else
4458 RTPrintf("%s %u bytes divided by %u items\n", pThis->szPrefix, pAttr->uValues.pCores->SetCore.Asn1Core.cb, pAttr->uValues.pCores->cItems);
4459 break;
4460
4461 /* Object IDs, use pObjIds. */
4462 case RTCRPKCS7ATTRIBUTETYPE_OBJ_IDS:
4463 if (pAttr->uValues.pObjIds->cItems != 1)
4464 RTPrintf("%s%u object IDs:", pThis->szPrefix, pAttr->uValues.pObjIds->cItems);
4465 for (unsigned i = 0; i < pAttr->uValues.pObjIds->cItems; i++)
4466 {
4467 if (pAttr->uValues.pObjIds->cItems == 1)
4468 RTPrintf("%s ", pThis->szPrefix);
4469 else
4470 RTPrintf("%s ObjId[%u]: ", pThis->szPrefix, i);
4471 HandleShowExeWorkerDisplayObjIdSimple(pThis, pAttr->uValues.pObjIds->papItems[i], "\n");
4472 }
4473 break;
4474
4475 /* Sequence of object IDs, use pObjIdSeqs. */
4476 case RTCRPKCS7ATTRIBUTETYPE_MS_STATEMENT_TYPE:
4477 if (pAttr->uValues.pObjIdSeqs->cItems != 1)
4478 RTPrintf("%s%u object IDs:", pThis->szPrefix, pAttr->uValues.pObjIdSeqs->cItems);
4479 for (unsigned i = 0; i < pAttr->uValues.pObjIdSeqs->cItems; i++)
4480 {
4481 uint32_t const cObjIds = pAttr->uValues.pObjIdSeqs->papItems[i]->cItems;
4482 for (unsigned j = 0; j < cObjIds; j++)
4483 {
4484 if (pAttr->uValues.pObjIdSeqs->cItems == 1)
4485 RTPrintf("%s ", pThis->szPrefix);
4486 else
4487 RTPrintf("%s ObjIdSeq[%u]: ", pThis->szPrefix, i);
4488 if (cObjIds != 1)
4489 RTPrintf(" ObjId[%u]: ", j);
4490 HandleShowExeWorkerDisplayObjIdSimple(pThis, pAttr->uValues.pObjIdSeqs->papItems[i]->papItems[i], "\n");
4491 }
4492 }
4493 break;
4494
4495 /* Octet strings, use pOctetStrings. */
4496 case RTCRPKCS7ATTRIBUTETYPE_OCTET_STRINGS:
4497 if (pAttr->uValues.pOctetStrings->cItems != 1)
4498 RTPrintf("%s%u octet strings:", pThis->szPrefix, pAttr->uValues.pOctetStrings->cItems);
4499 for (unsigned i = 0; i < pAttr->uValues.pOctetStrings->cItems; i++)
4500 {
4501 PCRTASN1OCTETSTRING pOctetString = pAttr->uValues.pOctetStrings->papItems[i];
4502 uint32_t cbContent = pOctetString->Asn1Core.cb;
4503 if (cbContent > 0 && (cbContent <= 128 || pThis->cVerbosity >= 2))
4504 {
4505 uint8_t const *pbContent = pOctetString->Asn1Core.uData.pu8;
4506 uint32_t off = 0;
4507 while (off < cbContent)
4508 {
4509 uint32_t cbNow = RT_MIN(cbContent - off, 16);
4510 if (pAttr->uValues.pOctetStrings->cItems == 1)
4511 RTPrintf("%s %#06x: %.*Rhxs\n", pThis->szPrefix, off, cbNow, &pbContent[off]);
4512 else
4513 RTPrintf("%s OctetString[%u]: %#06x: %.*Rhxs\n", pThis->szPrefix, i, off, cbNow, &pbContent[off]);
4514 off += cbNow;
4515 }
4516 }
4517 else
4518 RTPrintf("%s: OctetString[%u]: %u bytes\n", pThis->szPrefix, i, pOctetString->Asn1Core.cb);
4519 }
4520 break;
4521
4522 /* Counter signatures (PKCS \#9), use pCounterSignatures. */
4523 case RTCRPKCS7ATTRIBUTETYPE_COUNTER_SIGNATURES:
4524 RTPrintf("%s%u counter signatures, %u bytes in total\n", pThis->szPrefix,
4525 pAttr->uValues.pCounterSignatures->cItems, pAttr->uValues.pCounterSignatures->SetCore.Asn1Core.cb);
4526 for (uint32_t i = 0; i < pAttr->uValues.pCounterSignatures->cItems; i++)
4527 {
4528 size_t offPrefix2 = offPrefix;
4529 if (pAttr->uValues.pContentInfos->cItems > 1)
4530 offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, "CounterSig[%u]: ", i);
4531 else
4532 offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, " ");
4533
4534 int rc2 = HandleShowExeWorkerPkcs7DisplaySignerInfo(pThis, offPrefix2,
4535 pAttr->uValues.pCounterSignatures->papItems[i]);
4536 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
4537 rc = rc2;
4538 }
4539 break;
4540
4541 /* Signing time (PKCS \#9), use pSigningTime. */
4542 case RTCRPKCS7ATTRIBUTETYPE_SIGNING_TIME:
4543 for (uint32_t i = 0; i < pAttr->uValues.pSigningTime->cItems; i++)
4544 {
4545 PCRTASN1TIME pTime = pAttr->uValues.pSigningTime->papItems[i];
4546 char szTS[RTTIME_STR_LEN];
4547 RTTimeToString(&pTime->Time, szTS, sizeof(szTS));
4548 if (pAttr->uValues.pSigningTime->cItems == 1)
4549 RTPrintf("%s %s (%.*s)\n", pThis->szPrefix, szTS, pTime->Asn1Core.cb, pTime->Asn1Core.uData.pch);
4550 else
4551 RTPrintf("%s #%u: %s (%.*s)\n", pThis->szPrefix, i, szTS, pTime->Asn1Core.cb, pTime->Asn1Core.uData.pch);
4552 }
4553 break;
4554
4555 /* Microsoft timestamp info (RFC-3161) signed data, use pContentInfo. */
4556 case RTCRPKCS7ATTRIBUTETYPE_MS_TIMESTAMP:
4557 case RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE:
4558 if (pAttr->uValues.pContentInfos->cItems > 1)
4559 RTPrintf("%s%u nested signatures, %u bytes in total\n", pThis->szPrefix,
4560 pAttr->uValues.pContentInfos->cItems, pAttr->uValues.pContentInfos->SetCore.Asn1Core.cb);
4561 for (unsigned i = 0; i < pAttr->uValues.pContentInfos->cItems; i++)
4562 {
4563 size_t offPrefix2 = offPrefix;
4564 if (pAttr->uValues.pContentInfos->cItems > 1)
4565 offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, "NestedSig[%u]: ", i);
4566 else
4567 offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, " ");
4568 // offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, "NestedSig: ", i);
4569 PCRTCRPKCS7CONTENTINFO pContentInfo = pAttr->uValues.pContentInfos->papItems[i];
4570 int rc2;
4571 if (RTCrPkcs7ContentInfo_IsSignedData(pContentInfo))
4572 rc2 = HandleShowExeWorkerPkcs7Display(pThis, pContentInfo->u.pSignedData, offPrefix2, pContentInfo);
4573 else
4574 rc2 = RTMsgErrorRc(VERR_ASN1_UNEXPECTED_OBJ_ID, "%sPKCS#7 content in nested signature is not 'signedData': %s",
4575 pThis->szPrefix, pContentInfo->ContentType.szObjId);
4576 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
4577 rc = rc2;
4578 }
4579 break;
4580
4581 case RTCRPKCS7ATTRIBUTETYPE_APPLE_MULTI_CD_PLIST:
4582 if (pAttr->uValues.pContentInfos->cItems != 1)
4583 RTPrintf("%s%u plists, expected only 1.\n", pThis->szPrefix, pAttr->uValues.pOctetStrings->cItems);
4584 for (unsigned i = 0; i < pAttr->uValues.pOctetStrings->cItems; i++)
4585 {
4586 PCRTASN1OCTETSTRING pOctetString = pAttr->uValues.pOctetStrings->papItems[i];
4587 size_t cbContent = pOctetString->Asn1Core.cb;
4588 char const *pchContent = pOctetString->Asn1Core.uData.pch;
4589 rc = RTStrValidateEncodingEx(pchContent, cbContent, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
4590 if (RT_SUCCESS(rc))
4591 {
4592 while (cbContent > 0)
4593 {
4594 const char *pchNewLine = (const char *)memchr(pchContent, '\n', cbContent);
4595 size_t cchToWrite = pchNewLine ? pchNewLine - pchContent : cbContent;
4596 if (pAttr->uValues.pOctetStrings->cItems == 1)
4597 RTPrintf("%s %.*s\n", pThis->szPrefix, cchToWrite, pchContent);
4598 else
4599 RTPrintf("%s plist[%u]: %.*s\n", pThis->szPrefix, i, cchToWrite, pchContent);
4600 if (!pchNewLine)
4601 break;
4602 pchContent = pchNewLine + 1;
4603 cbContent -= cchToWrite + 1;
4604 }
4605 }
4606 else
4607 {
4608 if (pAttr->uValues.pContentInfos->cItems != 1)
4609 RTPrintf("%s: plist[%u]: Invalid UTF-8: %Rrc\n", pThis->szPrefix, i, rc);
4610 else
4611 RTPrintf("%s: Invalid UTF-8: %Rrc\n", pThis->szPrefix, rc);
4612 for (uint32_t off = 0; off < cbContent; off += 16)
4613 {
4614 size_t cbNow = RT_MIN(cbContent - off, 16);
4615 if (pAttr->uValues.pOctetStrings->cItems == 1)
4616 RTPrintf("%s %#06x: %.*Rhxs\n", pThis->szPrefix, off, cbNow, &pchContent[off]);
4617 else
4618 RTPrintf("%s plist[%u]: %#06x: %.*Rhxs\n", pThis->szPrefix, i, off, cbNow, &pchContent[off]);
4619 }
4620 }
4621 }
4622 break;
4623
4624 case RTCRPKCS7ATTRIBUTETYPE_INVALID:
4625 RTPrintf("%sINVALID!\n", pThis->szPrefix);
4626 break;
4627 case RTCRPKCS7ATTRIBUTETYPE_NOT_PRESENT:
4628 RTPrintf("%sNOT PRESENT!\n", pThis->szPrefix);
4629 break;
4630 default:
4631 RTPrintf("%senmType=%d!\n", pThis->szPrefix, pAttr->enmType);
4632 break;
4633 }
4634 return rc;
4635}
4636
4637
4638/**
4639 * Displays a SignerInfo structure.
4640 *
4641 * @returns IPRT status code.
4642 * @param pThis The show exe instance data.
4643 * @param offPrefix The current prefix offset.
4644 * @param pSignerInfo The structure to display.
4645 */
4646static int HandleShowExeWorkerPkcs7DisplaySignerInfo(PSHOWEXEPKCS7 pThis, size_t offPrefix, PCRTCRPKCS7SIGNERINFO pSignerInfo)
4647{
4648 int rc = RTAsn1Integer_ToString(&pSignerInfo->IssuerAndSerialNumber.SerialNumber,
4649 pThis->szTmp, sizeof(pThis->szTmp), 0 /*fFlags*/, NULL);
4650 if (RT_FAILURE(rc))
4651 RTStrPrintf(pThis->szTmp, sizeof(pThis->szTmp), "%Rrc", rc);
4652 RTPrintf("%s Serial No: %s\n", pThis->szPrefix, pThis->szTmp);
4653
4654 rc = RTCrX509Name_FormatAsString(&pSignerInfo->IssuerAndSerialNumber.Name, pThis->szTmp, sizeof(pThis->szTmp), NULL);
4655 if (RT_FAILURE(rc))
4656 RTStrPrintf(pThis->szTmp, sizeof(pThis->szTmp), "%Rrc", rc);
4657 RTPrintf("%s Issuer: %s\n", pThis->szPrefix, pThis->szTmp);
4658
4659 const char *pszType = RTCrDigestTypeToName(RTCrX509AlgorithmIdentifier_QueryDigestType(&pSignerInfo->DigestAlgorithm));
4660 if (!pszType)
4661 pszType = pSignerInfo->DigestAlgorithm.Algorithm.szObjId;
4662 RTPrintf("%s Digest Algorithm: %s", pThis->szPrefix, pszType);
4663 if (pThis->cVerbosity > 1)
4664 RTPrintf(" (%s)\n", pSignerInfo->DigestAlgorithm.Algorithm.szObjId);
4665 else
4666 RTPrintf("\n");
4667
4668 HandleShowExeWorkerDisplayObjId(pThis, &pSignerInfo->DigestEncryptionAlgorithm.Algorithm,
4669 "Digest Encryption Algorithm: ", "\n");
4670
4671 if (pSignerInfo->AuthenticatedAttributes.cItems == 0)
4672 RTPrintf("%s Authenticated Attributes: none\n", pThis->szPrefix);
4673 else
4674 {
4675 RTPrintf("%s Authenticated Attributes: %u item%s\n", pThis->szPrefix,
4676 pSignerInfo->AuthenticatedAttributes.cItems, pSignerInfo->AuthenticatedAttributes.cItems > 1 ? "s" : "");
4677 for (unsigned j = 0; j < pSignerInfo->AuthenticatedAttributes.cItems; j++)
4678 {
4679 PRTCRPKCS7ATTRIBUTE pAttr = pSignerInfo->AuthenticatedAttributes.papItems[j];
4680 size_t offPrefix3 = offPrefix+ RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix,
4681 " AuthAttrib[%u]: ", j);
4682 HandleShowExeWorkerPkcs7DisplayAttrib(pThis, offPrefix3, pAttr);
4683 }
4684 pThis->szPrefix[offPrefix] = '\0';
4685 }
4686
4687 if (pSignerInfo->UnauthenticatedAttributes.cItems == 0)
4688 RTPrintf("%s Unauthenticated Attributes: none\n", pThis->szPrefix);
4689 else
4690 {
4691 RTPrintf("%s Unauthenticated Attributes: %u item%s\n", pThis->szPrefix,
4692 pSignerInfo->UnauthenticatedAttributes.cItems, pSignerInfo->UnauthenticatedAttributes.cItems > 1 ? "s" : "");
4693 for (unsigned j = 0; j < pSignerInfo->UnauthenticatedAttributes.cItems; j++)
4694 {
4695 PRTCRPKCS7ATTRIBUTE pAttr = pSignerInfo->UnauthenticatedAttributes.papItems[j];
4696 size_t offPrefix3 = offPrefix + RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix,
4697 " UnauthAttrib[%u]: ", j);
4698 HandleShowExeWorkerPkcs7DisplayAttrib(pThis, offPrefix3, pAttr);
4699 }
4700 pThis->szPrefix[offPrefix] = '\0';
4701 }
4702
4703 /** @todo show the encrypted stuff (EncryptedDigest)? */
4704 return rc;
4705}
4706
4707
4708/**
4709 * Displays a Microsoft SPC indirect data structure.
4710 *
4711 * @returns IPRT status code.
4712 * @param pThis The show exe instance data.
4713 * @param offPrefix The current prefix offset.
4714 * @param pIndData The indirect data to display.
4715 */
4716static int HandleShowExeWorkerPkcs7DisplaySpcIdirectDataContent(PSHOWEXEPKCS7 pThis, size_t offPrefix,
4717 PCRTCRSPCINDIRECTDATACONTENT pIndData)
4718{
4719 /*
4720 * The image hash.
4721 */
4722 RTDIGESTTYPE const enmDigestType = RTCrX509AlgorithmIdentifier_QueryDigestType(&pIndData->DigestInfo.DigestAlgorithm);
4723 const char *pszDigestType = RTCrDigestTypeToName(enmDigestType);
4724 RTPrintf("%s Digest Type: %s", pThis->szPrefix, pszDigestType);
4725 if (pThis->cVerbosity > 1)
4726 RTPrintf(" (%s)\n", pIndData->DigestInfo.DigestAlgorithm.Algorithm.szObjId);
4727 else
4728 RTPrintf("\n");
4729 RTPrintf("%s Digest: %.*Rhxs\n",
4730 pThis->szPrefix, pIndData->DigestInfo.Digest.Asn1Core.cb, pIndData->DigestInfo.Digest.Asn1Core.uData.pu8);
4731
4732 /*
4733 * The data/file/url.
4734 */
4735 switch (pIndData->Data.enmType)
4736 {
4737 case RTCRSPCAAOVTYPE_PE_IMAGE_DATA:
4738 {
4739 RTPrintf("%s Data Type: PE Image Data\n", pThis->szPrefix);
4740 PRTCRSPCPEIMAGEDATA pPeImage = pIndData->Data.uValue.pPeImage;
4741 /** @todo display "Flags". */
4742
4743 switch (pPeImage->T0.File.enmChoice)
4744 {
4745 case RTCRSPCLINKCHOICE_MONIKER:
4746 {
4747 PRTCRSPCSERIALIZEDOBJECT pMoniker = pPeImage->T0.File.u.pMoniker;
4748 if (RTCrSpcSerializedObject_IsPresent(pMoniker))
4749 {
4750 if (RTUuidCompareStr(pMoniker->Uuid.Asn1Core.uData.pUuid, RTCRSPCSERIALIZEDOBJECT_UUID_STR) == 0)
4751 {
4752 RTPrintf("%s Moniker: SpcSerializedObject (%RTuuid)\n",
4753 pThis->szPrefix, pMoniker->Uuid.Asn1Core.uData.pUuid);
4754
4755 PCRTCRSPCSERIALIZEDOBJECTATTRIBUTES pData = pMoniker->u.pData;
4756 if (pData)
4757 for (uint32_t i = 0; i < pData->cItems; i++)
4758 {
4759 RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix,
4760 "MonikerAttrib[%u]: ", i);
4761
4762 switch (pData->papItems[i]->enmType)
4763 {
4764 case RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V2:
4765 case RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V1:
4766 {
4767 PCRTCRSPCSERIALIZEDPAGEHASHES pPgHashes = pData->papItems[i]->u.pPageHashes;
4768 uint32_t const cbHash = pData->papItems[i]->enmType
4769 == RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V1
4770 ? 160/8 /*SHA-1*/ : 256/8 /*SHA-256*/;
4771 uint32_t const cPages = pPgHashes->RawData.Asn1Core.cb / (cbHash + sizeof(uint32_t));
4772
4773 RTPrintf("%sPage Hashes version %u - %u pages (%u bytes total)\n", pThis->szPrefix,
4774 pData->papItems[i]->enmType
4775 == RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V1 ? 1 : 2,
4776 cPages, pPgHashes->RawData.Asn1Core.cb);
4777 if (pThis->cVerbosity > 0)
4778 {
4779 PCRTCRSPCPEIMAGEPAGEHASHES pPg = pPgHashes->pData;
4780 for (unsigned iPg = 0; iPg < cPages; iPg++)
4781 {
4782 uint32_t offHash = 0;
4783 do
4784 {
4785 if (offHash == 0)
4786 RTPrintf("%.*s Page#%04u/%#08x: ",
4787 offPrefix, pThis->szPrefix, iPg, pPg->Generic.offFile);
4788 else
4789 RTPrintf("%.*s ", offPrefix, pThis->szPrefix);
4790 uint32_t cbLeft = cbHash - offHash;
4791 if (cbLeft > 24)
4792 cbLeft = 16;
4793 RTPrintf("%.*Rhxs\n", cbLeft, &pPg->Generic.abHash[offHash]);
4794 offHash += cbLeft;
4795 } while (offHash < cbHash);
4796 pPg = (PCRTCRSPCPEIMAGEPAGEHASHES)&pPg->Generic.abHash[cbHash];
4797 }
4798
4799 if (pThis->cVerbosity > 3)
4800 RTPrintf("%.*Rhxd\n",
4801 pPgHashes->RawData.Asn1Core.cb,
4802 pPgHashes->RawData.Asn1Core.uData.pu8);
4803 }
4804 break;
4805 }
4806
4807 case RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_UNKNOWN:
4808 HandleShowExeWorkerDisplayObjIdSimple(pThis, &pData->papItems[i]->Type, "\n");
4809 break;
4810 case RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_NOT_PRESENT:
4811 RTPrintf("%sNot present!\n", pThis->szPrefix);
4812 break;
4813 default:
4814 RTPrintf("%senmType=%d!\n", pThis->szPrefix, pData->papItems[i]->enmType);
4815 break;
4816 }
4817 pThis->szPrefix[offPrefix] = '\0';
4818 }
4819 else
4820 RTPrintf("%s pData is NULL!\n", pThis->szPrefix);
4821 }
4822 else
4823 RTPrintf("%s Moniker: Unknown UUID: %RTuuid\n",
4824 pThis->szPrefix, pMoniker->Uuid.Asn1Core.uData.pUuid);
4825 }
4826 else
4827 RTPrintf("%s Moniker: not present\n", pThis->szPrefix);
4828 break;
4829 }
4830
4831 case RTCRSPCLINKCHOICE_URL:
4832 {
4833 const char *pszUrl = NULL;
4834 int rc = pPeImage->T0.File.u.pUrl
4835 ? RTAsn1String_QueryUtf8(pPeImage->T0.File.u.pUrl, &pszUrl, NULL)
4836 : VERR_NOT_FOUND;
4837 if (RT_SUCCESS(rc))
4838 RTPrintf("%s URL: '%s'\n", pThis->szPrefix, pszUrl);
4839 else
4840 RTPrintf("%s URL: rc=%Rrc\n", pThis->szPrefix, rc);
4841 break;
4842 }
4843
4844 case RTCRSPCLINKCHOICE_FILE:
4845 {
4846 const char *pszFile = NULL;
4847 int rc = pPeImage->T0.File.u.pT2 && pPeImage->T0.File.u.pT2->File.u.pAscii
4848 ? RTAsn1String_QueryUtf8(pPeImage->T0.File.u.pT2->File.u.pAscii, &pszFile, NULL)
4849 : VERR_NOT_FOUND;
4850 if (RT_SUCCESS(rc))
4851 RTPrintf("%s File: '%s'\n", pThis->szPrefix, pszFile);
4852 else
4853 RTPrintf("%s File: rc=%Rrc\n", pThis->szPrefix, rc);
4854 if (pThis->cVerbosity > 4 && pPeImage->T0.File.u.pT2 == NULL)
4855 RTPrintf("%s pT2=NULL\n", pThis->szPrefix);
4856 else if (pThis->cVerbosity > 4)
4857 {
4858 PCRTASN1STRING pStr = pPeImage->T0.File.u.pT2->File.u.pAscii;
4859 RTPrintf("%s pT2=%p/%p LB %#x fFlags=%#x pOps=%p (%s)\n"
4860 "%s enmChoice=%d pStr=%p/%p LB %#x fFlags=%#x\n",
4861 pThis->szPrefix,
4862 pPeImage->T0.File.u.pT2,
4863 pPeImage->T0.File.u.pT2->CtxTag2.Asn1Core.uData.pu8,
4864 pPeImage->T0.File.u.pT2->CtxTag2.Asn1Core.cb,
4865 pPeImage->T0.File.u.pT2->CtxTag2.Asn1Core.fFlags,
4866 pPeImage->T0.File.u.pT2->CtxTag2.Asn1Core.pOps,
4867 pPeImage->T0.File.u.pT2->CtxTag2.Asn1Core.pOps
4868 ? pPeImage->T0.File.u.pT2->CtxTag2.Asn1Core.pOps->pszName : "",
4869 pThis->szPrefix,
4870 pPeImage->T0.File.u.pT2->File.enmChoice,
4871 pStr,
4872 pStr ? pStr->Asn1Core.uData.pu8 : NULL,
4873 pStr ? pStr->Asn1Core.cb : 0,
4874 pStr ? pStr->Asn1Core.fFlags : 0);
4875 }
4876 break;
4877 }
4878
4879 case RTCRSPCLINKCHOICE_NOT_PRESENT:
4880 RTPrintf("%s File not present!\n", pThis->szPrefix);
4881 break;
4882 default:
4883 RTPrintf("%s enmChoice=%d!\n", pThis->szPrefix, pPeImage->T0.File.enmChoice);
4884 break;
4885 }
4886 break;
4887 }
4888
4889 case RTCRSPCAAOVTYPE_UNKNOWN:
4890 HandleShowExeWorkerDisplayObjId(pThis, &pIndData->Data.Type, " Data Type: ", "\n");
4891 break;
4892 case RTCRSPCAAOVTYPE_NOT_PRESENT:
4893 RTPrintf("%s Data Type: Not present!\n", pThis->szPrefix);
4894 break;
4895 default:
4896 RTPrintf("%s Data Type: enmType=%d!\n", pThis->szPrefix, pIndData->Data.enmType);
4897 break;
4898 }
4899
4900 return VINF_SUCCESS;
4901}
4902
4903
4904/**
4905 * Display an PKCS#7 signed data instance.
4906 *
4907 * @returns IPRT status code.
4908 * @param pThis The show exe instance data.
4909 * @param pSignedData The signed data to display.
4910 * @param offPrefix The current prefix offset.
4911 * @param pContentInfo The content info structure (for the size).
4912 */
4913static int HandleShowExeWorkerPkcs7Display(PSHOWEXEPKCS7 pThis, PRTCRPKCS7SIGNEDDATA pSignedData, size_t offPrefix,
4914 PCRTCRPKCS7CONTENTINFO pContentInfo)
4915{
4916 pThis->szPrefix[offPrefix] = '\0';
4917 RTPrintf("%sPKCS#7 signature: %u (%#x) bytes\n", pThis->szPrefix,
4918 RTASN1CORE_GET_RAW_ASN1_SIZE(&pContentInfo->SeqCore.Asn1Core),
4919 RTASN1CORE_GET_RAW_ASN1_SIZE(&pContentInfo->SeqCore.Asn1Core));
4920
4921 /*
4922 * Display list of signing algorithms.
4923 */
4924 RTPrintf("%sDigestAlgorithms: ", pThis->szPrefix);
4925 if (pSignedData->DigestAlgorithms.cItems == 0)
4926 RTPrintf("none");
4927 for (unsigned i = 0; i < pSignedData->DigestAlgorithms.cItems; i++)
4928 {
4929 PCRTCRX509ALGORITHMIDENTIFIER pAlgoId = pSignedData->DigestAlgorithms.papItems[i];
4930 const char *pszDigestType = RTCrDigestTypeToName(RTCrX509AlgorithmIdentifier_QueryDigestType(pAlgoId));
4931 if (!pszDigestType)
4932 pszDigestType = pAlgoId->Algorithm.szObjId;
4933 RTPrintf(i == 0 ? "%s" : ", %s", pszDigestType);
4934 if (pThis->cVerbosity > 1)
4935 RTPrintf(" (%s)", pAlgoId->Algorithm.szObjId);
4936 }
4937 RTPrintf("\n");
4938
4939 /*
4940 * Display the signed data content.
4941 */
4942 if (RTAsn1ObjId_CompareWithString(&pSignedData->ContentInfo.ContentType, RTCRSPCINDIRECTDATACONTENT_OID) == 0)
4943 {
4944 RTPrintf("%s ContentType: SpcIndirectDataContent (" RTCRSPCINDIRECTDATACONTENT_OID ")\n", pThis->szPrefix);
4945 size_t offPrefix2 = RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, " SPC Ind Data: ");
4946 HandleShowExeWorkerPkcs7DisplaySpcIdirectDataContent(pThis, offPrefix2 + offPrefix,
4947 pSignedData->ContentInfo.u.pIndirectDataContent);
4948 pThis->szPrefix[offPrefix] = '\0';
4949 }
4950 else
4951 {
4952 HandleShowExeWorkerDisplayObjId(pThis, &pSignedData->ContentInfo.ContentType, " ContentType: ", " - not implemented.\n");
4953 RTPrintf("%s %u (%#x) bytes\n", pThis->szPrefix,
4954 pSignedData->ContentInfo.Content.Asn1Core.cb, pSignedData->ContentInfo.Content.Asn1Core.cb);
4955 }
4956
4957 /*
4958 * Display certificates (Certificates).
4959 */
4960 if (pSignedData->Certificates.cItems > 0)
4961 {
4962 RTPrintf("%s Certificates: %u\n", pThis->szPrefix, pSignedData->Certificates.cItems);
4963 for (uint32_t i = 0; i < pSignedData->Certificates.cItems; i++)
4964 {
4965 PCRTCRPKCS7CERT pCert = pSignedData->Certificates.papItems[i];
4966 if (i != 0 && pThis->cVerbosity >= 2)
4967 RTPrintf("\n");
4968 switch (pCert->enmChoice)
4969 {
4970 case RTCRPKCS7CERTCHOICE_X509:
4971 {
4972 PCRTCRX509CERTIFICATE pX509Cert = pCert->u.pX509Cert;
4973 int rc2 = RTAsn1QueryObjIdName(&pX509Cert->SignatureAlgorithm.Algorithm, pThis->szTmp, sizeof(pThis->szTmp));
4974 RTPrintf("%s Certificate #%u: %s\n", pThis->szPrefix, i,
4975 RT_SUCCESS(rc2) ? pThis->szTmp : pX509Cert->SignatureAlgorithm.Algorithm.szObjId);
4976
4977 rc2 = RTCrX509Name_FormatAsString(&pX509Cert->TbsCertificate.Subject,
4978 pThis->szTmp, sizeof(pThis->szTmp), NULL);
4979 if (RT_FAILURE(rc2))
4980 RTStrPrintf(pThis->szTmp, sizeof(pThis->szTmp), "%Rrc", rc2);
4981 RTPrintf("%s Subject: %s\n", pThis->szPrefix, pThis->szTmp);
4982
4983 rc2 = RTCrX509Name_FormatAsString(&pX509Cert->TbsCertificate.Issuer,
4984 pThis->szTmp, sizeof(pThis->szTmp), NULL);
4985 if (RT_FAILURE(rc2))
4986 RTStrPrintf(pThis->szTmp, sizeof(pThis->szTmp), "%Rrc", rc2);
4987 RTPrintf("%s Issuer: %s\n", pThis->szPrefix, pThis->szTmp);
4988
4989
4990 char szNotAfter[RTTIME_STR_LEN];
4991 RTPrintf("%s Valid: %s thru %s\n", pThis->szPrefix,
4992 RTTimeToString(&pX509Cert->TbsCertificate.Validity.NotBefore.Time,
4993 pThis->szTmp, sizeof(pThis->szTmp)),
4994 RTTimeToString(&pX509Cert->TbsCertificate.Validity.NotAfter.Time,
4995 szNotAfter, sizeof(szNotAfter)));
4996 break;
4997 }
4998
4999 default:
5000 RTPrintf("%s Certificate #%u: Unsupported type\n", pThis->szPrefix, i);
5001 break;
5002 }
5003
5004
5005 if (pThis->cVerbosity >= 2)
5006 RTAsn1Dump(RTCrPkcs7Cert_GetAsn1Core(pSignedData->Certificates.papItems[i]), 0,
5007 ((uint32_t)offPrefix + 9) / 2, RTStrmDumpPrintfV, g_pStdOut);
5008 }
5009
5010 /** @todo display certificates properly. */
5011 }
5012
5013 if (pSignedData->Crls.cb > 0)
5014 RTPrintf("%s CRLs: %u bytes\n", pThis->szPrefix, pSignedData->Crls.cb);
5015
5016 /*
5017 * Show signatures (SignerInfos).
5018 */
5019 unsigned const cSigInfos = pSignedData->SignerInfos.cItems;
5020 if (cSigInfos != 1)
5021 RTPrintf("%s SignerInfos: %u signers\n", pThis->szPrefix, cSigInfos);
5022 else
5023 RTPrintf("%s SignerInfos:\n", pThis->szPrefix);
5024 int rc = VINF_SUCCESS;
5025 for (unsigned i = 0; i < cSigInfos; i++)
5026 {
5027 size_t offPrefix2 = offPrefix;
5028 if (cSigInfos != 1)
5029 offPrefix2 += RTStrPrintf(&pThis->szPrefix[offPrefix], sizeof(pThis->szPrefix) - offPrefix, "SignerInfo[%u]: ", i);
5030
5031 int rc2 = HandleShowExeWorkerPkcs7DisplaySignerInfo(pThis, offPrefix2, pSignedData->SignerInfos.papItems[i]);
5032 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
5033 rc = rc2;
5034 }
5035 pThis->szPrefix[offPrefix] = '\0';
5036
5037 return rc;
5038}
5039
5040
5041/*
5042 * The 'show-exe' command.
5043 */
5044static RTEXITCODE HelpShowExe(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
5045{
5046 RT_NOREF_PV(enmLevel);
5047 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT, "show-exe [--verbose|-v] [--quiet|-q] <exe1> [exe2 [..]]\n");
5048 return RTEXITCODE_SUCCESS;
5049}
5050
5051
5052static RTEXITCODE HandleShowExe(int cArgs, char **papszArgs)
5053{
5054 /*
5055 * Parse arguments.
5056 */
5057 static const RTGETOPTDEF s_aOptions[] =
5058 {
5059 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
5060 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
5061 };
5062
5063 unsigned cVerbosity = 0;
5064 RTLDRARCH enmLdrArch = RTLDRARCH_WHATEVER;
5065
5066 RTGETOPTSTATE GetState;
5067 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
5068 AssertRCReturn(rc, RTEXITCODE_FAILURE);
5069 RTGETOPTUNION ValueUnion;
5070 int ch;
5071 while ((ch = RTGetOpt(&GetState, &ValueUnion)) && ch != VINF_GETOPT_NOT_OPTION)
5072 {
5073 switch (ch)
5074 {
5075 case 'v': cVerbosity++; break;
5076 case 'q': cVerbosity = 0; break;
5077 case 'V': return HandleVersion(cArgs, papszArgs);
5078 case 'h': return HelpShowExe(g_pStdOut, RTSIGNTOOLHELP_FULL);
5079 default: return RTGetOptPrintError(ch, &ValueUnion);
5080 }
5081 }
5082 if (ch != VINF_GETOPT_NOT_OPTION)
5083 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
5084
5085 /*
5086 * Do it.
5087 */
5088 unsigned iFile = 0;
5089 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
5090 do
5091 {
5092 RTPrintf(iFile == 0 ? "%s:\n" : "\n%s:\n", ValueUnion.psz);
5093
5094 SHOWEXEPKCS7 This;
5095 RT_ZERO(This);
5096 This.cVerbosity = cVerbosity;
5097
5098 RTEXITCODE rcExitThis = SignToolPkcs7Exe_InitFromFile(&This, ValueUnion.psz, cVerbosity, enmLdrArch);
5099 if (rcExitThis == RTEXITCODE_SUCCESS)
5100 {
5101 rc = HandleShowExeWorkerPkcs7Display(&This, This.pSignedData, 0, &This.ContentInfo);
5102 if (RT_FAILURE(rc))
5103 rcExit = RTEXITCODE_FAILURE;
5104 SignToolPkcs7Exe_Delete(&This);
5105 }
5106 if (rcExitThis != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
5107 rcExit = rcExitThis;
5108
5109 iFile++;
5110 } while ((ch = RTGetOpt(&GetState, &ValueUnion)) == VINF_GETOPT_NOT_OPTION);
5111 if (ch != 0)
5112 return RTGetOptPrintError(ch, &ValueUnion);
5113
5114 return rcExit;
5115}
5116
5117
5118/*
5119 * The 'show-cat' command.
5120 */
5121static RTEXITCODE HelpShowCat(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
5122{
5123 RT_NOREF_PV(enmLevel);
5124 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT, "show-cat [--verbose|-v] [--quiet|-q] <cat1> [cat2 [..]]\n");
5125 return RTEXITCODE_SUCCESS;
5126}
5127
5128
5129static RTEXITCODE HandleShowCat(int cArgs, char **papszArgs)
5130{
5131 /*
5132 * Parse arguments.
5133 */
5134 static const RTGETOPTDEF s_aOptions[] =
5135 {
5136 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
5137 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
5138 };
5139
5140 unsigned cVerbosity = 0;
5141
5142 RTGETOPTSTATE GetState;
5143 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
5144 AssertRCReturn(rc, RTEXITCODE_FAILURE);
5145 RTGETOPTUNION ValueUnion;
5146 int ch;
5147 while ((ch = RTGetOpt(&GetState, &ValueUnion)) && ch != VINF_GETOPT_NOT_OPTION)
5148 {
5149 switch (ch)
5150 {
5151 case 'v': cVerbosity++; break;
5152 case 'q': cVerbosity = 0; break;
5153 case 'V': return HandleVersion(cArgs, papszArgs);
5154 case 'h': return HelpShowCat(g_pStdOut, RTSIGNTOOLHELP_FULL);
5155 default: return RTGetOptPrintError(ch, &ValueUnion);
5156 }
5157 }
5158 if (ch != VINF_GETOPT_NOT_OPTION)
5159 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No executable given.");
5160
5161 /*
5162 * Do it.
5163 */
5164 unsigned iFile = 0;
5165 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
5166 do
5167 {
5168 RTPrintf(iFile == 0 ? "%s:\n" : "\n%s:\n", ValueUnion.psz);
5169
5170 SHOWEXEPKCS7 This;
5171 RT_ZERO(This);
5172 This.cVerbosity = cVerbosity;
5173
5174 RTEXITCODE rcExitThis = SignToolPkcs7_InitFromFile(&This, ValueUnion.psz, cVerbosity);
5175 if (rcExitThis == RTEXITCODE_SUCCESS)
5176 {
5177 This.hLdrMod = NIL_RTLDRMOD;
5178
5179 rc = HandleShowExeWorkerPkcs7Display(&This, This.pSignedData, 0, &This.ContentInfo);
5180 if (RT_FAILURE(rc))
5181 rcExit = RTEXITCODE_FAILURE;
5182 SignToolPkcs7Exe_Delete(&This);
5183 }
5184 if (rcExitThis != RTEXITCODE_SUCCESS && rcExit == RTEXITCODE_SUCCESS)
5185 rcExit = rcExitThis;
5186
5187 iFile++;
5188 } while ((ch = RTGetOpt(&GetState, &ValueUnion)) == VINF_GETOPT_NOT_OPTION);
5189 if (ch != 0)
5190 return RTGetOptPrintError(ch, &ValueUnion);
5191
5192 return rcExit;
5193}
5194
5195
5196/*
5197 * The 'make-tainfo' command.
5198 */
5199static RTEXITCODE HelpMakeTaInfo(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
5200{
5201 RT_NOREF_PV(enmLevel);
5202 RTStrmWrappedPrintf(pStrm, RTSTRMWRAPPED_F_HANGING_INDENT,
5203 "make-tainfo [--verbose|--quiet] [--cert <cert.der>] [-o|--output] <tainfo.der>\n");
5204 return RTEXITCODE_SUCCESS;
5205}
5206
5207
5208typedef struct MAKETAINFOSTATE
5209{
5210 int cVerbose;
5211 const char *pszCert;
5212 const char *pszOutput;
5213} MAKETAINFOSTATE;
5214
5215
5216/** @callback_method_impl{FNRTASN1ENCODEWRITER} */
5217static DECLCALLBACK(int) handleMakeTaInfoWriter(const void *pvBuf, size_t cbToWrite, void *pvUser, PRTERRINFO pErrInfo)
5218{
5219 RT_NOREF_PV(pErrInfo);
5220 return RTStrmWrite((PRTSTREAM)pvUser, pvBuf, cbToWrite);
5221}
5222
5223
5224static RTEXITCODE HandleMakeTaInfo(int cArgs, char **papszArgs)
5225{
5226 /*
5227 * Parse arguments.
5228 */
5229 static const RTGETOPTDEF s_aOptions[] =
5230 {
5231 { "--cert", 'c', RTGETOPT_REQ_STRING },
5232 { "--output", 'o', RTGETOPT_REQ_STRING },
5233 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
5234 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
5235 };
5236
5237 MAKETAINFOSTATE State = { 0, NULL, NULL };
5238
5239 RTGETOPTSTATE GetState;
5240 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
5241 AssertRCReturn(rc, RTEXITCODE_FAILURE);
5242 RTGETOPTUNION ValueUnion;
5243 int ch;
5244 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
5245 {
5246 switch (ch)
5247 {
5248 case 'c':
5249 if (State.pszCert)
5250 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The --cert option can only be used once.");
5251 State.pszCert = ValueUnion.psz;
5252 break;
5253
5254 case 'o':
5255 case VINF_GETOPT_NOT_OPTION:
5256 if (State.pszOutput)
5257 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Multiple output files specified.");
5258 State.pszOutput = ValueUnion.psz;
5259 break;
5260
5261 case 'v': State.cVerbose++; break;
5262 case 'q': State.cVerbose = 0; break;
5263 case 'V': return HandleVersion(cArgs, papszArgs);
5264 case 'h': return HelpMakeTaInfo(g_pStdOut, RTSIGNTOOLHELP_FULL);
5265 default: return RTGetOptPrintError(ch, &ValueUnion);
5266 }
5267 }
5268 if (!State.pszCert)
5269 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No input certificate was specified.");
5270 if (!State.pszOutput)
5271 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No output file was specified.");
5272
5273 /*
5274 * Read the certificate.
5275 */
5276 RTERRINFOSTATIC StaticErrInfo;
5277 RTCRX509CERTIFICATE Certificate;
5278 rc = RTCrX509Certificate_ReadFromFile(&Certificate, State.pszCert, 0, &g_RTAsn1DefaultAllocator,
5279 RTErrInfoInitStatic(&StaticErrInfo));
5280 if (RT_FAILURE(rc))
5281 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error reading certificate from %s: %Rrc - %s",
5282 State.pszCert, rc, StaticErrInfo.szMsg);
5283 /*
5284 * Construct the trust anchor information.
5285 */
5286 RTCRTAFTRUSTANCHORINFO TrustAnchor;
5287 rc = RTCrTafTrustAnchorInfo_Init(&TrustAnchor, &g_RTAsn1DefaultAllocator);
5288 if (RT_SUCCESS(rc))
5289 {
5290 /* Public key. */
5291 Assert(RTCrX509SubjectPublicKeyInfo_IsPresent(&TrustAnchor.PubKey));
5292 RTCrX509SubjectPublicKeyInfo_Delete(&TrustAnchor.PubKey);
5293 rc = RTCrX509SubjectPublicKeyInfo_Clone(&TrustAnchor.PubKey, &Certificate.TbsCertificate.SubjectPublicKeyInfo,
5294 &g_RTAsn1DefaultAllocator);
5295 if (RT_FAILURE(rc))
5296 RTMsgError("RTCrX509SubjectPublicKeyInfo_Clone failed: %Rrc", rc);
5297 RTAsn1Core_ResetImplict(RTCrX509SubjectPublicKeyInfo_GetAsn1Core(&TrustAnchor.PubKey)); /* temporary hack. */
5298
5299 /* Key Identifier. */
5300 PCRTASN1OCTETSTRING pKeyIdentifier = NULL;
5301 if (Certificate.TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_SUBJECT_KEY_IDENTIFIER)
5302 pKeyIdentifier = Certificate.TbsCertificate.T3.pSubjectKeyIdentifier;
5303 else if ( (Certificate.TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_AUTHORITY_KEY_IDENTIFIER)
5304 && RTCrX509Certificate_IsSelfSigned(&Certificate)
5305 && RTAsn1OctetString_IsPresent(&Certificate.TbsCertificate.T3.pAuthorityKeyIdentifier->KeyIdentifier) )
5306 pKeyIdentifier = &Certificate.TbsCertificate.T3.pAuthorityKeyIdentifier->KeyIdentifier;
5307 else if ( (Certificate.TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_OLD_AUTHORITY_KEY_IDENTIFIER)
5308 && RTCrX509Certificate_IsSelfSigned(&Certificate)
5309 && RTAsn1OctetString_IsPresent(&Certificate.TbsCertificate.T3.pOldAuthorityKeyIdentifier->KeyIdentifier) )
5310 pKeyIdentifier = &Certificate.TbsCertificate.T3.pOldAuthorityKeyIdentifier->KeyIdentifier;
5311 if (pKeyIdentifier && pKeyIdentifier->Asn1Core.cb > 0)
5312 {
5313 Assert(RTAsn1OctetString_IsPresent(&TrustAnchor.KeyIdentifier));
5314 RTAsn1OctetString_Delete(&TrustAnchor.KeyIdentifier);
5315 rc = RTAsn1OctetString_Clone(&TrustAnchor.KeyIdentifier, pKeyIdentifier, &g_RTAsn1DefaultAllocator);
5316 if (RT_FAILURE(rc))
5317 RTMsgError("RTAsn1OctetString_Clone failed: %Rrc", rc);
5318 RTAsn1Core_ResetImplict(RTAsn1OctetString_GetAsn1Core(&TrustAnchor.KeyIdentifier)); /* temporary hack. */
5319 }
5320 else
5321 RTMsgWarning("No key identifier found or has zero length.");
5322
5323 /* Subject */
5324 if (RT_SUCCESS(rc))
5325 {
5326 Assert(!RTCrTafCertPathControls_IsPresent(&TrustAnchor.CertPath));
5327 rc = RTCrTafCertPathControls_Init(&TrustAnchor.CertPath, &g_RTAsn1DefaultAllocator);
5328 if (RT_SUCCESS(rc))
5329 {
5330 Assert(RTCrX509Name_IsPresent(&TrustAnchor.CertPath.TaName));
5331 RTCrX509Name_Delete(&TrustAnchor.CertPath.TaName);
5332 rc = RTCrX509Name_Clone(&TrustAnchor.CertPath.TaName, &Certificate.TbsCertificate.Subject,
5333 &g_RTAsn1DefaultAllocator);
5334 if (RT_SUCCESS(rc))
5335 {
5336 RTAsn1Core_ResetImplict(RTCrX509Name_GetAsn1Core(&TrustAnchor.CertPath.TaName)); /* temporary hack. */
5337 rc = RTCrX509Name_RecodeAsUtf8(&TrustAnchor.CertPath.TaName, &g_RTAsn1DefaultAllocator);
5338 if (RT_FAILURE(rc))
5339 RTMsgError("RTCrX509Name_RecodeAsUtf8 failed: %Rrc", rc);
5340 }
5341 else
5342 RTMsgError("RTCrX509Name_Clone failed: %Rrc", rc);
5343 }
5344 else
5345 RTMsgError("RTCrTafCertPathControls_Init failed: %Rrc", rc);
5346 }
5347
5348 /* Check that what we've constructed makes some sense. */
5349 if (RT_SUCCESS(rc))
5350 {
5351 rc = RTCrTafTrustAnchorInfo_CheckSanity(&TrustAnchor, 0, RTErrInfoInitStatic(&StaticErrInfo), "TAI");
5352 if (RT_FAILURE(rc))
5353 RTMsgError("RTCrTafTrustAnchorInfo_CheckSanity failed: %Rrc - %s", rc, StaticErrInfo.szMsg);
5354 }
5355
5356 if (RT_SUCCESS(rc))
5357 {
5358 /*
5359 * Encode it and write it to the output file.
5360 */
5361 uint32_t cbEncoded;
5362 rc = RTAsn1EncodePrepare(RTCrTafTrustAnchorInfo_GetAsn1Core(&TrustAnchor), RTASN1ENCODE_F_DER, &cbEncoded,
5363 RTErrInfoInitStatic(&StaticErrInfo));
5364 if (RT_SUCCESS(rc))
5365 {
5366 if (State.cVerbose >= 1)
5367 RTAsn1Dump(RTCrTafTrustAnchorInfo_GetAsn1Core(&TrustAnchor), 0, 0, RTStrmDumpPrintfV, g_pStdOut);
5368
5369 PRTSTREAM pStrm;
5370 rc = RTStrmOpen(State.pszOutput, "wb", &pStrm);
5371 if (RT_SUCCESS(rc))
5372 {
5373 rc = RTAsn1EncodeWrite(RTCrTafTrustAnchorInfo_GetAsn1Core(&TrustAnchor), RTASN1ENCODE_F_DER,
5374 handleMakeTaInfoWriter, pStrm, RTErrInfoInitStatic(&StaticErrInfo));
5375 if (RT_SUCCESS(rc))
5376 {
5377 rc = RTStrmClose(pStrm);
5378 if (RT_SUCCESS(rc))
5379 RTMsgInfo("Successfully wrote TrustedAnchorInfo to '%s'.", State.pszOutput);
5380 else
5381 RTMsgError("RTStrmClose failed: %Rrc", rc);
5382 }
5383 else
5384 {
5385 RTMsgError("RTAsn1EncodeWrite failed: %Rrc - %s", rc, StaticErrInfo.szMsg);
5386 RTStrmClose(pStrm);
5387 }
5388 }
5389 else
5390 RTMsgError("Error opening '%s' for writing: %Rrcs", State.pszOutput, rc);
5391 }
5392 else
5393 RTMsgError("RTAsn1EncodePrepare failed: %Rrc - %s", rc, StaticErrInfo.szMsg);
5394 }
5395
5396 RTCrTafTrustAnchorInfo_Delete(&TrustAnchor);
5397 }
5398 else
5399 RTMsgError("RTCrTafTrustAnchorInfo_Init failed: %Rrc", rc);
5400
5401 RTCrX509Certificate_Delete(&Certificate);
5402 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
5403}
5404
5405
5406
5407/*
5408 * The 'version' command.
5409 */
5410static RTEXITCODE HelpVersion(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
5411{
5412 RT_NOREF_PV(enmLevel);
5413 RTStrmPrintf(pStrm, "version\n");
5414 return RTEXITCODE_SUCCESS;
5415}
5416
5417static RTEXITCODE HandleVersion(int cArgs, char **papszArgs)
5418{
5419 RT_NOREF_PV(cArgs); RT_NOREF_PV(papszArgs);
5420#ifndef IN_BLD_PROG /* RTBldCfgVersion or RTBldCfgRevision in build time IPRT lib. */
5421 RTPrintf("%s\n", RTBldCfgVersion());
5422 return RTEXITCODE_SUCCESS;
5423#else
5424 return RTEXITCODE_FAILURE;
5425#endif
5426}
5427
5428
5429
5430/**
5431 * Command mapping.
5432 */
5433static struct
5434{
5435 /** The command. */
5436 const char *pszCmd;
5437 /**
5438 * Handle the command.
5439 * @returns Program exit code.
5440 * @param cArgs Number of arguments.
5441 * @param papszArgs The argument vector, starting with the command name.
5442 */
5443 RTEXITCODE (*pfnHandler)(int cArgs, char **papszArgs);
5444 /**
5445 * Produce help.
5446 * @returns RTEXITCODE_SUCCESS to simplify handling '--help' in the handler.
5447 * @param pStrm Where to send help text.
5448 * @param enmLevel The level of the help information.
5449 */
5450 RTEXITCODE (*pfnHelp)(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel);
5451}
5452/** Mapping commands to handler and helper functions. */
5453const g_aCommands[] =
5454{
5455 { "extract-exe-signer-cert", HandleExtractExeSignerCert, HelpExtractExeSignerCert },
5456 { "extract-exe-signature", HandleExtractExeSignature, HelpExtractExeSignature },
5457 { "add-nested-exe-signature", HandleAddNestedExeSignature, HelpAddNestedExeSignature },
5458 { "add-nested-cat-signature", HandleAddNestedCatSignature, HelpAddNestedCatSignature },
5459#ifndef IPRT_SIGNTOOL_NO_SIGNING
5460 { "add-timestamp-exe-signature", HandleAddTimestampExeSignature, HelpAddTimestampExeSignature },
5461 { "sign", HandleSign, HelpSign },
5462#endif
5463#ifndef IPRT_IN_BUILD_TOOL
5464 { "verify-exe", HandleVerifyExe, HelpVerifyExe },
5465#endif
5466 { "show-exe", HandleShowExe, HelpShowExe },
5467 { "show-cat", HandleShowCat, HelpShowCat },
5468 { "make-tainfo", HandleMakeTaInfo, HelpMakeTaInfo },
5469 { "help", HandleHelp, HelpHelp },
5470 { "--help", HandleHelp, NULL },
5471 { "-h", HandleHelp, NULL },
5472 { "version", HandleVersion, HelpVersion },
5473 { "--version", HandleVersion, NULL },
5474 { "-V", HandleVersion, NULL },
5475};
5476
5477
5478/*
5479 * The 'help' command.
5480 */
5481static RTEXITCODE HelpHelp(PRTSTREAM pStrm, RTSIGNTOOLHELP enmLevel)
5482{
5483 RT_NOREF_PV(enmLevel);
5484 RTStrmPrintf(pStrm, "help [cmd-patterns]\n");
5485 return RTEXITCODE_SUCCESS;
5486}
5487
5488static RTEXITCODE HandleHelp(int cArgs, char **papszArgs)
5489{
5490 RTSIGNTOOLHELP enmLevel = cArgs <= 1 ? RTSIGNTOOLHELP_USAGE : RTSIGNTOOLHELP_FULL;
5491 uint32_t cShowed = 0;
5492 uint32_t cchWidth;
5493 if (RT_FAILURE(RTStrmQueryTerminalWidth(g_pStdOut, &cchWidth)))
5494 cchWidth = 80;
5495 for (uint32_t iCmd = 0; iCmd < RT_ELEMENTS(g_aCommands); iCmd++)
5496 {
5497 if (g_aCommands[iCmd].pfnHelp)
5498 {
5499 bool fShow = false;
5500 if (cArgs <= 1)
5501 fShow = true;
5502 else
5503 for (int iArg = 1; iArg < cArgs; iArg++)
5504 if (RTStrSimplePatternMultiMatch(papszArgs[iArg], RTSTR_MAX, g_aCommands[iCmd].pszCmd, RTSTR_MAX, NULL))
5505 {
5506 fShow = true;
5507 break;
5508 }
5509 if (fShow)
5510 {
5511 if (cShowed && enmLevel == RTSIGNTOOLHELP_FULL)
5512 RTPrintf("%.*s\n", RT_MIN(cchWidth, 100),
5513 "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ");
5514 g_aCommands[iCmd].pfnHelp(g_pStdOut, enmLevel);
5515 cShowed++;
5516 }
5517 }
5518 }
5519 return cShowed ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
5520}
5521
5522
5523
5524int main(int argc, char **argv)
5525{
5526 int rc = RTR3InitExe(argc, &argv, 0);
5527 if (RT_FAILURE(rc))
5528 return RTMsgInitFailure(rc);
5529
5530 /*
5531 * Parse global arguments.
5532 */
5533 int iArg = 1;
5534 /* none presently. */
5535
5536 /*
5537 * Command dispatcher.
5538 */
5539 if (iArg < argc)
5540 {
5541 const char *pszCmd = argv[iArg];
5542 uint32_t i = RT_ELEMENTS(g_aCommands);
5543 while (i-- > 0)
5544 if (!strcmp(g_aCommands[i].pszCmd, pszCmd))
5545 return g_aCommands[i].pfnHandler(argc - iArg, &argv[iArg]);
5546 RTMsgError("Unknown command '%s'.", pszCmd);
5547 }
5548 else
5549 RTMsgError("No command given. (try --help)");
5550
5551 return RTEXITCODE_SYNTAX;
5552}
5553
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