VirtualBox

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

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

RTSignTool: Added some more detail to the 'help sign' output. bugref:8691

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

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