VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use