VirtualBox

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

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

RTSignTool: Handle the /d and /du in the sign command for signtool compatibility. Need to implement packing the info, though. bugref:8691

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette