VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/tools/VBoxCertUtil.cpp@ 106501

Last change on this file since 106501 was 106302, checked in by vboxsync, 4 months ago

WINNT/tools/VBoxCertUtil: Show vendor / copyright header, as it's a tool we ship officially.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 39.8 KB
Line 
1/* $Id: VBoxCertUtil.cpp 106302 2024-10-14 09:15:17Z vboxsync $ */
2/** @file
3 * VBoxCertUtil - VBox Certificate Utility - Windows Only.
4 */
5
6/*
7 * Copyright (C) 2012-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#include <iprt/win/windows.h>
33#include <Wincrypt.h>
34
35#include <iprt/buildconfig.h>
36#include <iprt/errcore.h>
37#include <iprt/file.h>
38#include <iprt/getopt.h>
39#include <iprt/initterm.h>
40#include <iprt/message.h>
41#include <iprt/path.h>
42#include <iprt/process.h>
43#include <iprt/stream.h>
44#include <iprt/string.h>
45#include <iprt/time.h>
46#include <iprt/utf16.h>
47
48#include <package-generated.h>
49#include "product-generated.h"
50
51#include <VBox/version.h>
52
53
54/*********************************************************************************************************************************
55* Defined Constants And Macros *
56*********************************************************************************************************************************/
57#define VCU_COMMON_OPTION_DEFINITIONS() \
58 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }, \
59 { "--quiet", 'q', RTGETOPT_REQ_NOTHING }
60
61#define VCU_COMMON_OPTION_HANDLING() \
62 case 'v': \
63 g_cVerbosityLevel++; \
64 break; \
65 \
66 case 'q': \
67 if (g_cVerbosityLevel > 0) \
68 g_cVerbosityLevel--; \
69 break; \
70 \
71 case 'V': \
72 return displayVersion()
73
74
75
76/*********************************************************************************************************************************
77* Global Variables *
78*********************************************************************************************************************************/
79/** The verbosity level. */
80static unsigned g_cVerbosityLevel = 1;
81
82
83static const char *errorToString(DWORD dwErr)
84{
85 switch (dwErr)
86 {
87#define MY_CASE(a_uConst) case a_uConst: return #a_uConst;
88 MY_CASE(CRYPT_E_MSG_ERROR);
89 MY_CASE(CRYPT_E_UNKNOWN_ALGO);
90 MY_CASE(CRYPT_E_OID_FORMAT);
91 MY_CASE(CRYPT_E_INVALID_MSG_TYPE);
92 MY_CASE(CRYPT_E_UNEXPECTED_ENCODING);
93 MY_CASE(CRYPT_E_AUTH_ATTR_MISSING);
94 MY_CASE(CRYPT_E_HASH_VALUE);
95 MY_CASE(CRYPT_E_INVALID_INDEX);
96 MY_CASE(CRYPT_E_ALREADY_DECRYPTED);
97 MY_CASE(CRYPT_E_NOT_DECRYPTED);
98 MY_CASE(CRYPT_E_RECIPIENT_NOT_FOUND);
99 MY_CASE(CRYPT_E_CONTROL_TYPE);
100 MY_CASE(CRYPT_E_ISSUER_SERIALNUMBER);
101 MY_CASE(CRYPT_E_SIGNER_NOT_FOUND);
102 MY_CASE(CRYPT_E_ATTRIBUTES_MISSING);
103 MY_CASE(CRYPT_E_STREAM_MSG_NOT_READY);
104 MY_CASE(CRYPT_E_STREAM_INSUFFICIENT_DATA);
105 MY_CASE(CRYPT_I_NEW_PROTECTION_REQUIRED);
106 MY_CASE(CRYPT_E_BAD_LEN);
107 MY_CASE(CRYPT_E_BAD_ENCODE);
108 MY_CASE(CRYPT_E_FILE_ERROR);
109 MY_CASE(CRYPT_E_NOT_FOUND);
110 MY_CASE(CRYPT_E_EXISTS);
111 MY_CASE(CRYPT_E_NO_PROVIDER);
112 MY_CASE(CRYPT_E_SELF_SIGNED);
113 MY_CASE(CRYPT_E_DELETED_PREV);
114 MY_CASE(CRYPT_E_NO_MATCH);
115 MY_CASE(CRYPT_E_UNEXPECTED_MSG_TYPE);
116 MY_CASE(CRYPT_E_NO_KEY_PROPERTY);
117 MY_CASE(CRYPT_E_NO_DECRYPT_CERT);
118 MY_CASE(CRYPT_E_BAD_MSG);
119 MY_CASE(CRYPT_E_NO_SIGNER);
120 MY_CASE(CRYPT_E_PENDING_CLOSE);
121 MY_CASE(CRYPT_E_REVOKED);
122 MY_CASE(CRYPT_E_NO_REVOCATION_DLL);
123 MY_CASE(CRYPT_E_NO_REVOCATION_CHECK);
124 MY_CASE(CRYPT_E_REVOCATION_OFFLINE);
125 MY_CASE(CRYPT_E_NOT_IN_REVOCATION_DATABASE);
126 MY_CASE(CRYPT_E_INVALID_NUMERIC_STRING);
127 MY_CASE(CRYPT_E_INVALID_PRINTABLE_STRING);
128 MY_CASE(CRYPT_E_INVALID_IA5_STRING);
129 MY_CASE(CRYPT_E_INVALID_X500_STRING);
130 MY_CASE(CRYPT_E_NOT_CHAR_STRING);
131 MY_CASE(CRYPT_E_FILERESIZED);
132 MY_CASE(CRYPT_E_SECURITY_SETTINGS);
133 MY_CASE(CRYPT_E_NO_VERIFY_USAGE_DLL);
134 MY_CASE(CRYPT_E_NO_VERIFY_USAGE_CHECK);
135 MY_CASE(CRYPT_E_VERIFY_USAGE_OFFLINE);
136 MY_CASE(CRYPT_E_NOT_IN_CTL);
137 MY_CASE(CRYPT_E_NO_TRUSTED_SIGNER);
138 MY_CASE(CRYPT_E_MISSING_PUBKEY_PARA);
139 MY_CASE(CRYPT_E_OSS_ERROR);
140 default:
141 {
142 static char s_szErr[80];
143 if (RTErrWinQueryDefine(dwErr, s_szErr, sizeof(s_szErr), true /*fFailIfUnknown*/) == VERR_NOT_FOUND)
144 RTStrPrintf(s_szErr, sizeof(s_szErr), "%#x (%d)", dwErr, dwErr);
145 return s_szErr;
146 }
147 }
148}
149
150
151/**
152 * Deals with -V and --version.
153 */
154static RTEXITCODE displayVersion(void)
155{
156 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
157 return RTEXITCODE_SUCCESS;
158}
159
160
161#if 0 /* hacking */
162static RTEXITCODE addToStore(const char *pszFilename, PCRTUTF16 pwszStore)
163{
164 /*
165 * Open the source.
166 */
167 void *pvFile;
168 size_t cbFile;
169 int rc = RTFileReadAll(pszFilename, &pvFile, &cbFile);
170 if (RT_FAILURE(rc))
171 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTFileReadAll failed on '%s': %Rrc", pszFilename, rc);
172
173 RTEXITCODE rcExit = RTEXITCODE_FAILURE;
174
175 PCCERT_CONTEXT pCertCtx = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
176 (PBYTE)pvFile,
177 (DWORD)cbFile);
178 if (pCertCtx)
179 {
180 /*
181 * Open the destination.
182 */
183 HCERTSTORE hDstStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_W,
184 PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
185 NULL /* hCryptProv = default */,
186 /*CERT_SYSTEM_STORE_LOCAL_MACHINE*/ CERT_SYSTEM_STORE_CURRENT_USER | CERT_STORE_OPEN_EXISTING_FLAG,
187 pwszStore);
188 if (hDstStore != NULL)
189 {
190#if 0
191 DWORD dwContextType;
192 if (CertAddSerializedElementToStore(hDstStore,
193 pCertCtx->pbCertEncoded,
194 pCertCtx->cbCertEncoded,
195 CERT_STORE_ADD_NEW,
196 0 /* dwFlags (reserved) */,
197 CERT_STORE_ALL_CONTEXT_FLAG,
198 &dwContextType,
199 NULL))
200 {
201 RTMsgInfo("Successfully added '%s' to the '%ls' store (ctx type %u)", pszFilename, pwszStore, dwContextType);
202 rcExit = RTEXITCODE_SUCCESS;
203 }
204 else
205 RTMsgError("CertAddSerializedElementToStore returned %s", errorToString(GetLastError()));
206#else
207 if (CertAddCertificateContextToStore(hDstStore, pCertCtx, CERT_STORE_ADD_NEW, NULL))
208 {
209 RTMsgInfo("Successfully added '%s' to the '%ls' store", pszFilename, pwszStore);
210 rcExit = RTEXITCODE_SUCCESS;
211 }
212 else
213 RTMsgError("CertAddCertificateContextToStore returned %s", errorToString(GetLastError()));
214#endif
215
216 CertCloseStore(hDstStore, CERT_CLOSE_STORE_CHECK_FLAG);
217 }
218 else
219 RTMsgError("CertOpenStore returned %s", errorToString(GetLastError()));
220 CertFreeCertificateContext(pCertCtx);
221 }
222 else
223 RTMsgError("CertCreateCertificateContext returned %s", errorToString(GetLastError()));
224 RTFileReadAllFree(pvFile, cbFile);
225 return rcExit;
226
227#if 0
228
229 CRYPT_DATA_BLOB Blob;
230 Blob.cbData = (DWORD)cbData;
231 Blob.pbData = (PBYTE)pvData;
232 HCERTSTORE hSrcStore = PFXImportCertStore(&Blob, L"", )
233
234#endif
235}
236#endif /* hacking */
237
238
239/**
240 * Reads a certificate from a file, returning a context or a the handle to a
241 * temporary memory store.
242 *
243 * @returns true on success, false on failure (error message written).
244 * @param pszCertFile The name of the file containing the
245 * certificates.
246 * @param ppOutCtx Where to return the certificate context.
247 * @param phSrcStore Where to return the handle to the temporary
248 * memory store.
249 */
250static bool readCertFile(const char *pszCertFile, PCCERT_CONTEXT *ppOutCtx, HCERTSTORE *phSrcStore)
251{
252 *ppOutCtx = NULL;
253 *phSrcStore = NULL;
254
255 bool fRc = false;
256 void *pvFile;
257 size_t cbFile;
258 int rc = RTFileReadAll(pszCertFile, &pvFile, &cbFile);
259 if (RT_SUCCESS(rc))
260 {
261 *ppOutCtx = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
262 (PBYTE)pvFile, (DWORD)cbFile);
263 if (*ppOutCtx)
264 fRc = true;
265 else
266 {
267 /** @todo figure out if it's some other format... */
268 RTMsgError("CertCreateCertificateContext returned %s parsing the content of '%s'",
269 errorToString(GetLastError()), pszCertFile);
270 }
271 RTFileReadAllFree(pvFile, cbFile);
272 }
273 else
274 RTMsgError("RTFileReadAll failed on '%s': %Rrc", pszCertFile, rc);
275 return fRc;
276}
277
278
279/**
280 * Opens a certificate store.
281 *
282 * @returns true on success, false on failure (error message written).
283 * @param dwDst The destination, like
284 * CERT_SYSTEM_STORE_LOCAL_MACHINE or
285 * CERT_SYSTEM_STORE_CURRENT_USER.
286 * @param pszStoreNm The store name.
287 */
288static HCERTSTORE openCertStore(DWORD dwDst, const char *pszStoreNm)
289{
290 HCERTSTORE hStore = NULL;
291 PRTUTF16 pwszStoreNm;
292 int rc = RTStrToUtf16(pszStoreNm, &pwszStoreNm);
293 if (RT_SUCCESS(rc))
294 {
295 if (g_cVerbosityLevel > 1)
296 RTMsgInfo("Opening store %#x:'%s'", dwDst, pszStoreNm);
297
298 /*
299 * Make sure CERT_STORE_OPEN_EXISTING_FLAG is not set. This causes Windows XP
300 * to return ACCESS_DENIED when installing TrustedPublisher certificates via
301 * CertAddCertificateContextToStore() if the TrustedPublisher store never has
302 * been used (through certmgr.exe and friends) yet.
303 *
304 * According to MSDN, if neither CERT_STORE_OPEN_EXISTING_FLAG nor
305 * CERT_STORE_CREATE_NEW_FLAG is set, the store will be either opened or
306 * created accordingly.
307 */
308 dwDst &= ~CERT_STORE_OPEN_EXISTING_FLAG;
309
310 hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_W,
311 PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
312 NULL /* hCryptProv = default */,
313 dwDst,
314 pwszStoreNm);
315 if (hStore == NULL)
316 RTMsgError("CertOpenStore failed opening %#x:'%s': %s",
317 dwDst, pszStoreNm, errorToString(GetLastError()));
318
319 RTUtf16Free(pwszStoreNm);
320 }
321 return hStore;
322}
323
324
325/**
326 * Worker for 'root-exists', searching by exact relative distinguished name.
327 */
328static RTEXITCODE checkIfCertExistsInStoreByRdn(DWORD dwStore, const char *pszStoreNm, const char *pszStoreDesc,
329 const char *pszName, RTEXITCODE rcExit, uint32_t *pcFound)
330{
331 /*
332 * Convert the name into something that can be searched for.
333 */
334 PRTUTF16 pwszName = NULL;
335 int rc = RTStrToUtf16(pszName, &pwszName);
336 if (RT_FAILURE(rc))
337 return RTMsgErrorExitFailure("RTStrToUtf16 failed: %Rrc", rc);
338
339
340 BYTE abNameBuf[16384]; /* this should be more than sufficient... */
341 CERT_NAME_BLOB NameBlob = { sizeof(abNameBuf), abNameBuf };
342 PCRTUTF16 pwszErr = NULL;
343 if (CertStrToNameW(X509_ASN_ENCODING, pwszName, CERT_X500_NAME_STR | CERT_NAME_STR_SEMICOLON_FLAG, NULL /*pvReserved*/,
344 NameBlob.pbData, &NameBlob.cbData, &pwszErr))
345 {
346 /*
347 * Now perform the search.
348 */
349 HCERTSTORE hDstStore = openCertStore(dwStore, pszStoreNm);
350 if (hDstStore)
351 {
352 uint32_t cFound = 0;
353 uint32_t idxCur = 0;
354 PCCERT_CONTEXT pCurCtx = NULL;
355 while ((pCurCtx = CertEnumCertificatesInStore(hDstStore, pCurCtx)) != NULL)
356 {
357 if (pCurCtx->pCertInfo)
358 {
359 if (g_cVerbosityLevel > 1)
360 {
361 WCHAR wszCurName[1024];
362 if (CertNameToStrW(X509_ASN_ENCODING, &pCurCtx->pCertInfo->Subject,
363 CERT_X500_NAME_STR | CERT_NAME_STR_SEMICOLON_FLAG,
364 wszCurName, sizeof(wszCurName)))
365 RTMsgInfo("Considering #%u: '%ls' ...", idxCur, wszCurName);
366 else
367 RTMsgInfo("Considering #%u: CertNameToStrW -> %u ...", idxCur, GetLastError());
368 }
369
370 if (CertCompareCertificateName(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &pCurCtx->pCertInfo->Subject, &NameBlob))
371 {
372 if (g_cVerbosityLevel > 0)
373 RTMsgInfo("Found '%ls' in the %s store...", pwszName, pszStoreDesc);
374 cFound++;
375 }
376 }
377 idxCur++;
378 }
379
380 *pcFound += cFound;
381 if (!cFound && g_cVerbosityLevel > 0)
382 RTMsgInfo("Certificate with subject '%ls' was _NOT_ found in the %s store.", pwszName, pszStoreDesc);
383
384 CertCloseStore(hDstStore, CERT_CLOSE_STORE_CHECK_FLAG);
385 }
386 else
387 rcExit = RTEXITCODE_FAILURE;
388 }
389 else
390 rcExit = RTMsgErrorExitFailure("CertStrToNameW failed at position %zu: %s\n"
391 " '%ls'\n"
392 " %*s",
393 pwszErr - pwszName, errorToString(GetLastError()), pwszName, pwszErr - pwszName, "^");
394 RTUtf16Free(pwszName);
395 return rcExit;
396}
397
398
399/**
400 * Removes a certificate, given by file, from a store
401 *
402 * @returns Command exit code.
403 * @param dwDst The destination, like
404 * CERT_SYSTEM_STORE_LOCAL_MACHINE or
405 * CERT_SYSTEM_STORE_CURRENT_USER.
406 * @param pszStoreNm The store name.
407 * @param pszStoreDesc The store descriptor (all lower case).
408 * @param pszCertFile The file containing the certificate to add.
409 * @param rcExit Incoming exit status.
410 */
411static RTEXITCODE removeCertFromStoreByFile(DWORD dwDst, const char *pszStoreNm, const char *pszStoreDesc,
412 const char *pszCertFile, RTEXITCODE rcExit)
413{
414 /*
415 * Read the certificate file first and get the certificate name from it.
416 */
417 PCCERT_CONTEXT pSrcCtx = NULL;
418 HCERTSTORE hSrcStore = NULL;
419 if (!readCertFile(pszCertFile, &pSrcCtx, &hSrcStore))
420 return RTEXITCODE_FAILURE;
421
422 WCHAR wszName[1024];
423 if (!CertGetNameStringW(pSrcCtx, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0 /*dwFlags*/, NULL /*pvTypePara*/,
424 wszName, sizeof(wszName)))
425 {
426 RTMsgError("CertGetNameStringW(Subject) failed: %s\n", errorToString(GetLastError()));
427 wszName[0] = '\0';
428 }
429
430 /*
431 * Open the destination store.
432 */
433 HCERTSTORE hDstStore = openCertStore(dwDst, pszStoreNm);
434 if (hDstStore)
435 {
436 if (pSrcCtx)
437 {
438 unsigned cDeleted = 0;
439 PCCERT_CONTEXT pCurCtx = NULL;
440 while ((pCurCtx = CertEnumCertificatesInStore(hDstStore, pCurCtx)) != NULL)
441 {
442 if (CertCompareCertificate(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pCurCtx->pCertInfo, pSrcCtx->pCertInfo))
443 {
444 if (g_cVerbosityLevel > 1)
445 RTMsgInfo("Removing '%ls'...", wszName);
446 PCCERT_CONTEXT pDeleteCtx = CertDuplicateCertificateContext(pCurCtx);
447 if (pDeleteCtx)
448 {
449 if (CertDeleteCertificateFromStore(pDeleteCtx))
450 {
451 cDeleted++;
452 if (g_cVerbosityLevel > 0)
453 RTMsgInfo("Successfully removed '%s' ('%ls') from the %s store", pszCertFile, wszName, pszStoreDesc);
454 }
455 else
456 rcExit = RTMsgErrorExitFailure("CertDeleteFromStore('%ls') failed: %s\n",
457 wszName, errorToString(GetLastError()));
458 }
459 else
460 rcExit = RTMsgErrorExitFailure("CertDuplicateCertificateContext('%ls') failed: %s\n",
461 wszName, errorToString(GetLastError()));
462 }
463 }
464
465 if (!cDeleted)
466 RTMsgInfo("Found no matching certificates to remove.");
467 }
468 else
469 rcExit = RTMsgErrorExitFailure("Code path not implemented at line %d\n", __LINE__);
470
471
472 CertCloseStore(hDstStore, CERT_CLOSE_STORE_CHECK_FLAG);
473 }
474 else
475 rcExit = RTEXITCODE_FAILURE;
476 if (pSrcCtx)
477 CertFreeCertificateContext(pSrcCtx);
478 if (hSrcStore)
479 CertCloseStore(hSrcStore, CERT_CLOSE_STORE_CHECK_FLAG);
480 return rcExit;
481}
482
483
484/**
485 * Adds a certificate to a store.
486 *
487 * @returns true on success, false on failure (error message written).
488 * @param dwDst The destination, like
489 * CERT_SYSTEM_STORE_LOCAL_MACHINE or
490 * CERT_SYSTEM_STORE_CURRENT_USER.
491 * @param pszStoreNm The store name.
492 * @param pszCertFile The file containing the certificate to add.
493 * @param dwDisposition The disposition towards existing certificates when
494 * adding it. CERT_STORE_ADD_NEW is a safe one.
495 * @param pfAlreadyExists Where to indicate whether the certificate was
496 * already present and not replaced.
497 */
498static bool addCertToStoreByFile(DWORD dwDst, const char *pszStoreNm, const char *pszCertFile, DWORD dwDisposition,
499 bool *pfAlreadyExists)
500{
501 *pfAlreadyExists = false;
502
503 /*
504 * Read the certificate file first.
505 */
506 PCCERT_CONTEXT pSrcCtx = NULL;
507 HCERTSTORE hSrcStore = NULL;
508 if (!readCertFile(pszCertFile, &pSrcCtx, &hSrcStore))
509 return false;
510
511 /*
512 * Open the destination store.
513 */
514 bool fRc = false;
515 HCERTSTORE hDstStore = openCertStore(dwDst, pszStoreNm);
516 if (hDstStore)
517 {
518 if (pSrcCtx)
519 {
520 if (g_cVerbosityLevel > 1)
521 RTMsgInfo("Adding '%s' to %#x:'%s'... (disp %d)", pszCertFile, dwDst, pszStoreNm, dwDisposition);
522
523 if (CertAddCertificateContextToStore(hDstStore, pSrcCtx, dwDisposition, NULL))
524 fRc = true;
525 else
526 {
527 DWORD const dwErr = GetLastError();
528 *pfAlreadyExists = fRc = dwErr == CRYPT_E_EXISTS;
529 if (!fRc)
530 RTMsgError("CertAddCertificateContextToStore returned %s", errorToString(dwErr));
531 }
532 }
533 else
534 RTMsgError("Code path not implemented at line %d\n", __LINE__);
535
536 CertCloseStore(hDstStore, CERT_CLOSE_STORE_CHECK_FLAG);
537 }
538 if (pSrcCtx)
539 CertFreeCertificateContext(pSrcCtx);
540 if (hSrcStore)
541 CertCloseStore(hSrcStore, CERT_CLOSE_STORE_CHECK_FLAG);
542 return fRc;
543}
544
545
546/**
547 * Handle adding one or more certificates to a store.
548 */
549static RTEXITCODE addCertToStoreByFilePattern(DWORD dwDst, const char *pszStoreNm, const char *pszStoreDesc,
550 const char *pszFilePattern, bool fForce, RTEXITCODE rcExit, uint32_t *pcImports)
551{
552 PCRTPATHGLOBENTRY pResultHead;
553 int rc = RTPathGlob(pszFilePattern, RTPATHGLOB_F_NO_DIRS, &pResultHead, NULL);
554 if (RT_SUCCESS(rc))
555 {
556 for (PCRTPATHGLOBENTRY pCur = pResultHead; pCur; pCur = pCur->pNext)
557 {
558 bool fAlreadyExists = false;
559 if (addCertToStoreByFile(dwDst, pszStoreNm, pCur->szPath,
560 !fForce ? CERT_STORE_ADD_NEW : CERT_STORE_ADD_REPLACE_EXISTING,
561 &fAlreadyExists))
562 {
563 if (!fAlreadyExists)
564 RTMsgInfo("Successfully added '%s' to the %s store", pCur->szPath, pszStoreDesc);
565 else
566 RTMsgInfo("Certificate '%s' is already present in the %s store and was not re-added or updated.",
567 pCur->szPath, pszStoreNm);
568 }
569 else
570 rcExit = RTEXITCODE_FAILURE;
571 *pcImports += 1;
572 }
573 RTPathGlobFree(pResultHead);
574 }
575 else
576 {
577 rcExit = RTMsgErrorExitFailure("glob failed on '%s': %Rrc", pszFilePattern, rc);
578 *pcImports += 1;
579 }
580 return rcExit;
581}
582
583
584/**
585 * Worker for cmdDisplayAll.
586 */
587static BOOL WINAPI displaySystemStoreCallback(const void *pvSystemStore, DWORD dwFlags, PCERT_SYSTEM_STORE_INFO pStoreInfo,
588 void *pvReserved, void *pvArg) RT_NOTHROW_DEF
589{
590 RT_NOREF(pvArg);
591 if (g_cVerbosityLevel > 1)
592 RTPrintf(" pvSystemStore=%p dwFlags=%#x pStoreInfo=%p pvReserved=%p\n", pvSystemStore, dwFlags, pStoreInfo, pvReserved);
593 LPCWSTR pwszStoreNm = NULL;
594 if (dwFlags & CERT_SYSTEM_STORE_RELOCATE_FLAG)
595 {
596 const CERT_SYSTEM_STORE_RELOCATE_PARA *pRelPara = (const CERT_SYSTEM_STORE_RELOCATE_PARA *)pvSystemStore;
597 pwszStoreNm = pRelPara->pwszSystemStore;
598 RTPrintf(" %#010x '%ls' hKeyBase=%p\n", dwFlags, pwszStoreNm, pRelPara->hKeyBase);
599 }
600 else
601 {
602 pwszStoreNm = (LPCWSTR)pvSystemStore;
603 RTPrintf(" %#010x '%ls'\n", dwFlags, pwszStoreNm);
604 }
605
606 /*
607 * Open the store and list the certificates within.
608 */
609 DWORD dwDst = (dwFlags & CERT_SYSTEM_STORE_LOCATION_MASK);
610 HCERTSTORE hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_W,
611 PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
612 NULL /* hCryptProv = default */,
613 dwDst | CERT_STORE_OPEN_EXISTING_FLAG,
614 pwszStoreNm);
615 if (hStore)
616 {
617 PCCERT_CONTEXT pCertCtx = NULL;
618 while ((pCertCtx = CertEnumCertificatesInStore(hStore, pCertCtx)) != NULL)
619 {
620 if (g_cVerbosityLevel > 1)
621 RTPrintf(" pCertCtx=%p dwCertEncodingType=%#x cbCertEncoded=%#x pCertInfo=%p\n",
622 pCertCtx, pCertCtx->dwCertEncodingType, pCertCtx->cbCertEncoded, pCertCtx->pCertInfo);
623 WCHAR wszName[1024];
624 if (CertGetNameStringW(pCertCtx, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0 /*dwFlags*/, NULL /*pvTypePara*/,
625 wszName, sizeof(wszName)))
626 {
627 RTPrintf(" '%ls'\n", wszName);
628 if (pCertCtx->pCertInfo)
629 {
630 RTTIMESPEC TmpTS;
631 char szNotBefore[80];
632 RTTimeSpecToString(RTTimeSpecSetNtFileTime(&TmpTS, &pCertCtx->pCertInfo->NotBefore),
633 szNotBefore, sizeof(szNotBefore));
634 char szNotAfter[80];
635 RTTimeSpecToString(RTTimeSpecSetNtFileTime(&TmpTS, &pCertCtx->pCertInfo->NotAfter),
636 szNotAfter, sizeof(szNotAfter));
637
638 RTPrintf(" NotBefore='%s'\n", szNotBefore);
639 RTPrintf(" NotAfter ='%s'\n", szNotAfter);
640 if (pCertCtx->pCertInfo->Issuer.cbData)
641 {
642 if (CertGetNameStringW(pCertCtx, CERT_NAME_SIMPLE_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, NULL /*pvTypePara*/,
643 wszName, sizeof(wszName)))
644 RTPrintf(" Issuer='%ls'\n", wszName);
645 else
646 RTMsgError("CertGetNameStringW(Issuer) failed: %s\n", errorToString(GetLastError()));
647 }
648 }
649 }
650 else
651 RTMsgError("CertGetNameStringW(Subject) failed: %s\n", errorToString(GetLastError()));
652
653 }
654
655 CertCloseStore(hStore, CERT_CLOSE_STORE_CHECK_FLAG);
656 }
657 else
658 RTMsgError("CertOpenStore failed opening %#x:'%ls': %s\n", dwDst, pwszStoreNm, errorToString(GetLastError()));
659
660 return TRUE;
661}
662
663
664/**
665 * Worker for cmdDisplayAll.
666 */
667static BOOL WINAPI
668displaySystemStoreLocation(LPCWSTR pwszStoreLocation, DWORD dwFlags, void *pvReserved, void *pvArg) RT_NOTHROW_DEF
669{
670 NOREF(pvReserved); NOREF(pvArg);
671 RTPrintf("System store location: %#010x '%ls'\n", dwFlags, pwszStoreLocation);
672 if (!CertEnumSystemStore(dwFlags, NULL, NULL /*pvArg*/, displaySystemStoreCallback))
673 RTMsgError("CertEnumSystemStore failed on %#x:'%ls': %s\n",
674 dwFlags, pwszStoreLocation, errorToString(GetLastError()));
675
676 return TRUE;
677}
678
679
680/**
681 * Handler for the 'display-all' command.
682 */
683static RTEXITCODE cmdDisplayAll(int argc, char **argv)
684{
685 /*
686 * Parse arguments.
687 */
688 static const RTGETOPTDEF s_aOptions[] =
689 {
690 VCU_COMMON_OPTION_DEFINITIONS(),
691 };
692
693 int rc;
694 RTGETOPTUNION ValueUnion;
695 RTGETOPTSTATE GetState;
696 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
697 while ((rc = RTGetOpt(&GetState, &ValueUnion)))
698 {
699 switch (rc)
700 {
701 VCU_COMMON_OPTION_HANDLING();
702
703 case 'h':
704 RTStrmWrappedPrintf(g_pStdOut, RTSTRMWRAPPED_F_HANGING_INDENT,
705 "Usage: VBoxCertUtil display-all [-v|--verbose] [-q|--quiet]\n");
706 return RTEXITCODE_SUCCESS;
707
708 default:
709 return RTGetOptPrintError(rc, &ValueUnion);
710 }
711 }
712
713 /*
714 * Do the enumerating.
715 */
716 if (!CertEnumSystemStoreLocation(0, NULL /*pvArg*/, displaySystemStoreLocation))
717 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "CertEnumSystemStoreLocation failed: %s\n", errorToString(GetLastError()));
718 return RTEXITCODE_SUCCESS;
719}
720
721
722/**
723 * Handler for the 'root-exists' command.
724 */
725static RTEXITCODE cmdRootExists(int argc, char **argv)
726{
727 /*
728 * Parse arguments.
729 */
730 static const RTGETOPTDEF s_aOptions[] =
731 {
732 VCU_COMMON_OPTION_DEFINITIONS(),
733 };
734
735 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
736 uint32_t cFound = 0;
737 uint32_t cSearched = 0;
738
739 int rc;
740 RTGETOPTUNION ValueUnion;
741 RTGETOPTSTATE GetState;
742 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
743 while ((rc = RTGetOpt(&GetState, &ValueUnion)))
744 {
745 switch (rc)
746 {
747 VCU_COMMON_OPTION_HANDLING();
748
749 case 'h':
750 RTStrmWrappedPrintf(g_pStdOut, RTSTRMWRAPPED_F_HANGING_INDENT,
751 "Usage: VBoxCertUtil root-exists <full-subject-name> [alternative-subject-name [...]]\n");
752 RTStrmWrappedPrintf(g_pStdOut, 0,
753 "\n"
754 "Exit code: 10 if not found, 0 if found.\n"
755 "\n"
756 "The names are on the form 'C=US; O=Company; OU=some unit; CN=a cert name' "
757 "where semi-colon is the X.500 attribute separator and spaces surrounding it "
758 "the type (CN, OU, ) and '=' are generally ignored.\n"
759 "\n"
760 "At verbosity level 2, the full subject name of each certificate in the store "
761 "will be listed as the search progresses. These can be used as search input.\n"
762 );
763 return RTEXITCODE_SUCCESS;
764
765 case VINF_GETOPT_NOT_OPTION:
766 rcExit = checkIfCertExistsInStoreByRdn(CERT_SYSTEM_STORE_LOCAL_MACHINE, "Root", "root",
767 ValueUnion.psz, rcExit, &cFound);
768 cSearched++;
769 break;
770
771 default:
772 return RTGetOptPrintError(rc, &ValueUnion);
773 }
774 }
775
776 if (!cSearched)
777 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No certificate name specified.");
778 return cFound ? RTEXITCODE_SUCCESS : rcExit == RTEXITCODE_SUCCESS ? (RTEXITCODE)10 : rcExit;
779}
780
781
782
783/**
784 * Handler for the 'remove-root' command.
785 */
786static RTEXITCODE cmdRemoveRoot(int argc, char **argv)
787{
788 /*
789 * Parse arguments.
790 */
791 static const RTGETOPTDEF s_aOptions[] =
792 {
793 VCU_COMMON_OPTION_DEFINITIONS(),
794 };
795
796 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
797 uint32_t cRemoved = 0;
798
799 int rc;
800 RTGETOPTUNION ValueUnion;
801 RTGETOPTSTATE GetState;
802 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
803 while ((rc = RTGetOpt(&GetState, &ValueUnion)))
804 {
805 switch (rc)
806 {
807 VCU_COMMON_OPTION_HANDLING();
808
809 case 'h':
810 RTStrmWrappedPrintf(g_pStdOut, RTSTRMWRAPPED_F_HANGING_INDENT,
811 "Usage: VBoxCertUtil remove-root <root-cert-file>\n");
812 return RTEXITCODE_SUCCESS;
813
814 case VINF_GETOPT_NOT_OPTION:
815 rcExit = removeCertFromStoreByFile(CERT_SYSTEM_STORE_LOCAL_MACHINE, "Root", "root", ValueUnion.psz, rcExit);
816 cRemoved++;
817 break;
818
819 default:
820 return RTGetOptPrintError(rc, &ValueUnion);
821 }
822 }
823 if (!cRemoved)
824 return RTMsgErrorExit(RTEXITCODE_SUCCESS, "No certificate specified.");
825 return rcExit;
826}
827
828
829/**
830 * Handler for the 'remove-trusted-publisher' command.
831 */
832static RTEXITCODE cmdRemoveTrustedPublisher(int argc, char **argv)
833{
834 /*
835 * Parse arguments.
836 */
837 static const RTGETOPTDEF s_aOptions[] =
838 {
839 { "--root", 'r', RTGETOPT_REQ_STRING },
840 VCU_COMMON_OPTION_DEFINITIONS(),
841 };
842
843 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
844 uint32_t cRemoved = 0;
845
846 int rc;
847 RTGETOPTUNION ValueUnion;
848 RTGETOPTSTATE GetState;
849 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
850 while ((rc = RTGetOpt(&GetState, &ValueUnion)))
851 {
852 switch (rc)
853 {
854 VCU_COMMON_OPTION_HANDLING();
855
856 case 'h':
857 RTStrmWrappedPrintf(g_pStdOut, RTSTRMWRAPPED_F_HANGING_INDENT,
858 "Usage: VBoxCertUtil remove-trusted-publisher [--root <root-cert>] <trusted-cert>\n");
859 return RTEXITCODE_SUCCESS;
860
861 case 'r':
862 rcExit = removeCertFromStoreByFile(CERT_SYSTEM_STORE_LOCAL_MACHINE, "Root", "root", ValueUnion.psz, rcExit);
863 cRemoved++;
864 break;
865
866 case VINF_GETOPT_NOT_OPTION:
867 rcExit = removeCertFromStoreByFile(CERT_SYSTEM_STORE_LOCAL_MACHINE, "TrustedPublisher", "trusted publisher",
868 ValueUnion.psz, rcExit);
869 cRemoved++;
870 break;
871
872 default:
873 return RTGetOptPrintError(rc, &ValueUnion);
874 }
875 }
876 if (!cRemoved)
877 return RTMsgErrorExit(RTEXITCODE_SUCCESS, "No certificate specified.");
878 return rcExit;
879}
880
881
882/**
883 * Handler for the 'add-root' command.
884 */
885static RTEXITCODE cmdAddRoot(int argc, char **argv)
886{
887 /*
888 * Parse arguments and execute imports as we move along.
889 */
890 static const RTGETOPTDEF s_aOptions[] =
891 {
892 { "--add-if-new", 'a', RTGETOPT_REQ_NOTHING },
893 { "--force", 'f', RTGETOPT_REQ_NOTHING },
894 VCU_COMMON_OPTION_DEFINITIONS(),
895 };
896
897 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
898 unsigned cImports = 0;
899 bool fForce = false;
900 RTGETOPTUNION ValueUnion;
901 RTGETOPTSTATE GetState;
902 int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
903 AssertRC(rc);
904 while ((rc = RTGetOpt(&GetState, &ValueUnion)))
905 {
906 switch (rc)
907 {
908 VCU_COMMON_OPTION_HANDLING();
909
910 case 'a':
911 fForce = false;
912 break;
913
914 case 'f':
915 fForce = false;
916 break;
917
918 case 'h':
919 RTStrmWrappedPrintf(g_pStdOut, RTSTRMWRAPPED_F_HANGING_INDENT,
920 "Usage: VBoxCertUtil add-root [--force|--add-if-new] <root-cert>\n");
921 return RTEXITCODE_SUCCESS;
922
923 case VINF_GETOPT_NOT_OPTION:
924 rcExit = addCertToStoreByFilePattern(CERT_SYSTEM_STORE_LOCAL_MACHINE, "Root", "root",
925 ValueUnion.psz, fForce, rcExit, &cImports);
926 break;
927
928 default:
929 return RTGetOptPrintError(rc, &ValueUnion);
930 }
931 }
932 if (cImports == 0)
933 return RTMsgErrorExit(RTEXITCODE_SUCCESS, "No trusted or root certificates specified.");
934 return rcExit;
935}
936
937
938/**
939 * Handler for the 'add-trusted-publisher' command.
940 */
941static RTEXITCODE cmdAddTrustedPublisher(int argc, char **argv)
942{
943 /*
944 * Parse arguments and execute imports as we move along.
945 */
946 static const RTGETOPTDEF s_aOptions[] =
947 {
948 { "--root", 'r', RTGETOPT_REQ_STRING },
949 { "--add-if-new", 'a', RTGETOPT_REQ_NOTHING },
950 { "--force", 'f', RTGETOPT_REQ_NOTHING },
951 VCU_COMMON_OPTION_DEFINITIONS(),
952 };
953
954 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
955 bool fForce = false;
956 unsigned cImports = 0;
957 RTGETOPTUNION ValueUnion;
958 RTGETOPTSTATE GetState;
959 int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
960 AssertRC(rc);
961 while ((rc = RTGetOpt(&GetState, &ValueUnion)))
962 {
963 switch (rc)
964 {
965 VCU_COMMON_OPTION_HANDLING();
966
967 case 'a':
968 fForce = false;
969 break;
970
971 case 'f':
972 fForce = false;
973 break;
974
975 case 'h':
976 RTStrmWrappedPrintf(g_pStdOut, RTSTRMWRAPPED_F_HANGING_INDENT,
977 "Usage: VBoxCertUtil add-trusted-publisher [--force|--add-if-new] "
978 "[--root <root-cert>] <trusted-cert>\n");
979 return RTEXITCODE_SUCCESS;
980
981 case 'r':
982 rcExit = addCertToStoreByFilePattern(CERT_SYSTEM_STORE_LOCAL_MACHINE, "Root", "root",
983 ValueUnion.psz, fForce, rcExit, &cImports);
984 break;
985
986 case VINF_GETOPT_NOT_OPTION:
987 rcExit = addCertToStoreByFilePattern(CERT_SYSTEM_STORE_LOCAL_MACHINE, "TrustedPublisher", "trusted publisher",
988 ValueUnion.psz, fForce, rcExit, &cImports);
989 break;
990
991 default:
992 return RTGetOptPrintError(rc, &ValueUnion);
993 }
994 }
995 if (cImports == 0)
996 return RTMsgErrorExit(RTEXITCODE_SUCCESS, "No trusted or root certificates specified.");
997 return rcExit;
998}
999
1000
1001/**
1002 * Shows the logo.
1003 *
1004 * @param pStream Output stream to show logo on.
1005 */
1006static void showLogo(PRTSTREAM pStream)
1007{
1008 RTStrmPrintf(pStream, VBOX_PRODUCT " VBoxCertUtil Version " VBOX_VERSION_STRING " - r%s\n"
1009 "Copyright (C) " VBOX_C_YEAR " " VBOX_VENDOR "\n\n", RTBldCfgRevisionStr());
1010}
1011
1012
1013/**
1014 * Displays the usage info.
1015 */
1016static void showUsage(void)
1017{
1018 const char * const pszShortNm = RTProcShortName();
1019 RTPrintf("Usage: %Rbn [-v[v]|--verbose] [-q[q]|--quiet] <command>\n"
1020 " or %Rbn <-V|--version>\n"
1021 " or %Rbn <-h|--help>\n"
1022 "\n"
1023 "Available commands:\n"
1024 " add-trusted-publisher\n"
1025 " add-root\n"
1026 " remove-trusted-publisher\n"
1027 " remove-root\n"
1028 " display-all\n"
1029 , pszShortNm, pszShortNm, pszShortNm);
1030}
1031
1032
1033int main(int argc, char **argv)
1034{
1035 int rc = RTR3InitExe(argc, &argv, 0);
1036 if (RT_FAILURE(rc))
1037 return RTMsgInitFailure(rc);
1038
1039 /*
1040 * Parse arguments up to the command and pass it on to the command handlers.
1041 */
1042 typedef enum
1043 {
1044 VCUACTION_ADD_TRUSTED_PUBLISHER = 1000,
1045 VCUACTION_ADD_ROOT,
1046 VCUACTION_REMOVE_TRUSTED_PUBLISHER,
1047 VCUACTION_REMOVE_ROOT,
1048 VCUACTION_ROOT_EXISTS,
1049 VCUACTION_DISPLAY_ALL,
1050 VCUACTION_END
1051 } VCUACTION;
1052
1053 static const RTGETOPTDEF s_aOptions[] =
1054 {
1055 { "add-trusted-publisher", VCUACTION_ADD_TRUSTED_PUBLISHER, RTGETOPT_REQ_NOTHING },
1056 { "add-root", VCUACTION_ADD_ROOT, RTGETOPT_REQ_NOTHING },
1057 { "remove-trusted-publisher", VCUACTION_REMOVE_TRUSTED_PUBLISHER, RTGETOPT_REQ_NOTHING },
1058 { "remove-root", VCUACTION_REMOVE_ROOT, RTGETOPT_REQ_NOTHING },
1059 { "root-exists", VCUACTION_ROOT_EXISTS, RTGETOPT_REQ_NOTHING },
1060 { "display-all", VCUACTION_DISPLAY_ALL, RTGETOPT_REQ_NOTHING },
1061 VCU_COMMON_OPTION_DEFINITIONS(),
1062 };
1063
1064 showLogo(g_pStdOut);
1065
1066 RTGETOPTUNION ValueUnion;
1067 RTGETOPTSTATE GetState;
1068 RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
1069 while ((rc = RTGetOpt(&GetState, &ValueUnion)))
1070 {
1071 switch (rc)
1072 {
1073 case VCUACTION_ADD_TRUSTED_PUBLISHER:
1074 return cmdAddTrustedPublisher(argc - GetState.iNext + 1, argv + GetState.iNext - 1);
1075
1076 case VCUACTION_ADD_ROOT:
1077 return cmdAddRoot(argc - GetState.iNext + 1, argv + GetState.iNext - 1);
1078
1079 case VCUACTION_REMOVE_TRUSTED_PUBLISHER:
1080 return cmdRemoveTrustedPublisher(argc - GetState.iNext + 1, argv + GetState.iNext - 1);
1081
1082 case VCUACTION_REMOVE_ROOT:
1083 return cmdRemoveRoot(argc - GetState.iNext + 1, argv + GetState.iNext - 1);
1084
1085 case VCUACTION_ROOT_EXISTS:
1086 return cmdRootExists(argc - GetState.iNext + 1, argv + GetState.iNext - 1);
1087
1088 case VCUACTION_DISPLAY_ALL:
1089 return cmdDisplayAll(argc - GetState.iNext + 1, argv + GetState.iNext - 1);
1090
1091 case 'h':
1092 showUsage();
1093 return RTEXITCODE_SUCCESS;
1094
1095 VCU_COMMON_OPTION_HANDLING();
1096
1097 default:
1098 return RTGetOptPrintError(rc, &ValueUnion);
1099 }
1100 }
1101
1102 RTMsgError("Missing command...");
1103 showUsage();
1104 return RTEXITCODE_SYNTAX;
1105}
1106
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