VirtualBox

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

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

RTSignTool: Search the user/system stores for intermedate CAs to the signing certificate and automatically include them in the signing output. [build fix] bugref:8691

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