VirtualBox

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

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

RTSignTool: Added --input argument aliases for --exe to simplify certificate extraction for the GA installer. Corrected --help for the two new root extraction commands. bugref:8691.

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