Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

650 lines
18 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1997 - 1999
  6. //
  7. // File: usecert.cpp
  8. //
  9. // Contents: cert store and file operations
  10. //
  11. //--------------------------------------------------------------------------
  12. #include "pch.cpp"
  13. #pragma hdrstop
  14. #include <wincrypt.h>
  15. #include "initcert.h"
  16. #include "cscsp.h"
  17. #include "cspenum.h"
  18. #include "wizpage.h"
  19. #include "usecert.h"
  20. #define __dwFILE__ __dwFILE_OCMSETUP_USECERT_CPP__
  21. typedef struct _EXISTING_CA_IDINFO {
  22. LPSTR pszObjId;
  23. WCHAR **ppwszIdInfo;
  24. } EXISTING_CA_IDINFO;
  25. /*
  26. EXISTING_CA_IDINFO g_ExistingCAIdInfo[] = {
  27. {szOID_COMMON_NAME, NULL},
  28. {szOID_ORGANIZATION_NAME, NULL},
  29. {szOID_ORGANIZATIONAL_UNIT_NAME, NULL},
  30. {szOID_LOCALITY_NAME, NULL},
  31. {szOID_STATE_OR_PROVINCE_NAME, NULL},
  32. {szOID_RSA_emailAddr, NULL},
  33. {szOID_COUNTRY_NAME, NULL},
  34. {NULL, NULL},
  35. };
  36. */
  37. HRESULT
  38. myMakeExprValidity(
  39. IN FILETIME const *pft,
  40. OUT LONG *plDayCount)
  41. {
  42. HRESULT hr;
  43. FILETIME ft;
  44. LONGLONG llDelta;
  45. *plDayCount = 0;
  46. // get current time
  47. GetSystemTimeAsFileTime(&ft);
  48. llDelta = mySubtractFileTimes(pft, &ft);
  49. llDelta /= 1000 * 1000 * 10;
  50. llDelta += 12 * 60 * 60; // half day more to avoid truncation
  51. llDelta /= 24 * 60 * 60;
  52. *plDayCount = (LONG) llDelta;
  53. if (0 > *plDayCount)
  54. {
  55. *plDayCount = 0;
  56. }
  57. hr = S_OK;
  58. //error:
  59. return hr;
  60. }
  61. //--------------------------------------------------------------------
  62. // returns true if the CA type is root and the cert is self-signed,
  63. // or the CA type is subordinate and the cert is no self-signed
  64. HRESULT
  65. IsCertSelfSignedForCAType(
  66. IN CASERVERSETUPINFO * pServer,
  67. IN CERT_CONTEXT const * pccCert,
  68. OUT BOOL * pbOK)
  69. {
  70. CSASSERT(NULL!=pccCert);
  71. CSASSERT(NULL!=pServer);
  72. CSASSERT(NULL!=pbOK);
  73. HRESULT hr;
  74. DWORD dwVerificationFlags;
  75. BOOL bRetVal;
  76. // See if this cert is self-signed or not.
  77. // First, we flag what we want to check: "Use the public
  78. // key in the issuer's certificate to verify the signature on
  79. // the subject certificate."
  80. // We use the same certificate as issuer and subject
  81. dwVerificationFlags=CERT_STORE_SIGNATURE_FLAG;
  82. // perform the checks
  83. bRetVal=CertVerifySubjectCertificateContext(
  84. pccCert,
  85. pccCert, // issuer same as subject
  86. &dwVerificationFlags);
  87. if (FALSE==bRetVal) {
  88. hr=myHLastError();
  89. _JumpError(hr, error, "CertVerifySubjectCertificateContext");
  90. }
  91. // Every check that passed had its flag zeroed. See if our check passed
  92. if (CERT_STORE_SIGNATURE_FLAG&dwVerificationFlags){
  93. // This cert is not self-signed.
  94. if (IsRootCA(pServer->CAType)) {
  95. // A root CA cert must be self-signed.
  96. *pbOK=FALSE;
  97. } else {
  98. // A subordinate CA cert must not be self-signed.
  99. *pbOK=TRUE;
  100. }
  101. } else {
  102. // This cert is self-signed.
  103. if (IsSubordinateCA(pServer->CAType)) {
  104. // A subordinate CA cert must not be self-signed.
  105. *pbOK=FALSE;
  106. } else {
  107. // A root CA cert must be self-signed.
  108. *pbOK=TRUE;
  109. }
  110. }
  111. hr=S_OK;
  112. error:
  113. return hr;
  114. }
  115. //--------------------------------------------------------------------
  116. // find a certificate's hash algorithm in a CSP's list of hash algorithms
  117. HRESULT
  118. FindHashAlgorithm(
  119. IN CERT_CONTEXT const * pccCert,
  120. IN CSP_INFO * pCSPInfo,
  121. OUT CSP_HASH ** ppHash)
  122. {
  123. CSASSERT(NULL!=pccCert);
  124. CSASSERT(NULL!=pCSPInfo);
  125. CSASSERT(NULL!=ppHash);
  126. HRESULT hr;
  127. unsigned int nIndex;
  128. CSP_HASH * phTravel;
  129. const CRYPT_OID_INFO * pOIDInfo;
  130. // Initialize out param
  131. *ppHash=NULL;
  132. // get the AlgID from the hash algorithm OID
  133. // (returned pointer must not be freed)
  134. pOIDInfo=CryptFindOIDInfo(
  135. CRYPT_OID_INFO_OID_KEY,
  136. pccCert->pCertInfo->SignatureAlgorithm.pszObjId,
  137. CRYPT_SIGN_ALG_OID_GROUP_ID
  138. );
  139. if (NULL==pOIDInfo) {
  140. // function is not doc'd to set GetLastError()
  141. hr=CRYPT_E_NOT_FOUND;
  142. _JumpError(hr, error, "Signature algorithm not found");
  143. }
  144. // find the hash algorithm in the list of hash algorithms the CSP supports
  145. for (phTravel=pCSPInfo->pHashList; NULL!=phTravel; phTravel=phTravel->next) {
  146. if (pOIDInfo->Algid==phTravel->idAlg) {
  147. *ppHash=phTravel;
  148. break;
  149. }
  150. }
  151. if (NULL==phTravel) {
  152. hr=CRYPT_E_NOT_FOUND;
  153. _JumpError(hr, error, "CSP does not support hash algorithm");
  154. }
  155. hr=S_OK;
  156. error:
  157. return hr;
  158. }
  159. /*
  160. HRESULT
  161. HookExistingIdInfoData(
  162. CASERVERSETUPINFO *pServer)
  163. {
  164. HRESULT hr;
  165. int i = 0;
  166. while (NULL != g_ExistingCAIdInfo[i].pszObjId)
  167. {
  168. if (0 == strcmp(szOID_COMMON_NAME,
  169. g_ExistingCAIdInfo[i].pszObjId))
  170. {
  171. g_ExistingCAIdInfo[i].ppwszIdInfo = &pServer->pwszCACommonName;
  172. }
  173. else if (0 == strcmp(szOID_ORGANIZATION_NAME,
  174. g_ExistingCAIdInfo[i].pszObjId))
  175. {
  176. // dead
  177. }
  178. else if (0 == strcmp(szOID_ORGANIZATIONAL_UNIT_NAME,
  179. g_ExistingCAIdInfo[i].pszObjId))
  180. {
  181. // dead
  182. }
  183. else if (0 == strcmp(szOID_LOCALITY_NAME,
  184. g_ExistingCAIdInfo[i].pszObjId))
  185. {
  186. // dead
  187. }
  188. else if (0 == strcmp(szOID_STATE_OR_PROVINCE_NAME,
  189. g_ExistingCAIdInfo[i].pszObjId))
  190. {
  191. // dead
  192. }
  193. else if (0 == strcmp(szOID_COUNTRY_NAME,
  194. g_ExistingCAIdInfo[i].pszObjId))
  195. {
  196. // dead
  197. }
  198. else if (0 == strcmp(szOID_RSA_emailAddr,
  199. g_ExistingCAIdInfo[i].pszObjId))
  200. {
  201. // dead
  202. }
  203. else
  204. {
  205. hr = E_INVALIDARG;
  206. _JumpError(hr, error, "unsupported name");
  207. }
  208. ++i;
  209. }
  210. hr = S_OK;
  211. error:
  212. return hr;
  213. }
  214. */
  215. HRESULT
  216. DetermineExistingCAIdInfo(
  217. IN OUT CASERVERSETUPINFO *pServer,
  218. OPTIONAL IN CERT_CONTEXT const *pUpgradeCert)
  219. {
  220. CERT_NAME_INFO *pCertNameInfo = NULL;
  221. DWORD cbCertNameInfo;
  222. WCHAR const *pwszNameProp;
  223. WCHAR *pwszExisting;
  224. int i;
  225. HRESULT hr = E_FAIL;
  226. SYSTEMTIME sysTime;
  227. FILETIME fileTime;
  228. CERT_CONTEXT const *pCert = pServer->pccExistingCert;
  229. CSASSERT(NULL!=pServer->pccExistingCert ||
  230. NULL != pUpgradeCert);
  231. if (NULL == pUpgradeCert)
  232. {
  233. myMakeExprValidity(
  234. &pServer->pccExistingCert->pCertInfo->NotAfter,
  235. &pServer->lExistingValidity);
  236. pServer->NotBefore = pServer->pccExistingCert->pCertInfo->NotBefore;
  237. pServer->NotAfter = pServer->pccExistingCert->pCertInfo->NotAfter;
  238. }
  239. if (NULL != pUpgradeCert)
  240. {
  241. pCert = pUpgradeCert;
  242. }
  243. if (!myDecodeName(X509_ASN_ENCODING,
  244. X509_UNICODE_NAME,
  245. pCert->pCertInfo->Subject.pbData,
  246. pCert->pCertInfo->Subject.cbData,
  247. CERTLIB_USE_LOCALALLOC,
  248. &pCertNameInfo,
  249. &cbCertNameInfo))
  250. {
  251. hr = myHLastError();
  252. _JumpError(hr, error, "myDecodeName");
  253. }
  254. /*
  255. // fill a data structure for existing key id info
  256. hr = HookExistingIdInfoData(pServer);
  257. _JumpIfError(hr, error, "HookExistingIdInfoData");
  258. // load names from cert to the the data structure
  259. i = 0;
  260. while (NULL != g_ExistingCAIdInfo[i].pszObjId)
  261. {
  262. if (S_OK == myGetCertNameProperty(
  263. pCertNameInfo,
  264. g_ExistingCAIdInfo[i].pszObjId,
  265. &pwszNameProp))
  266. {
  267. pwszExisting = (WCHAR*)LocalAlloc(LPTR,
  268. (wcslen(pwszNameProp) + 1) * sizeof(WCHAR));
  269. _JumpIfOutOfMemory(hr, error, pwszExisting);
  270. // get name
  271. wcscpy(pwszExisting, pwszNameProp);
  272. // make sure free old
  273. if (NULL != *(g_ExistingCAIdInfo[i].ppwszIdInfo))
  274. {
  275. LocalFree(*(g_ExistingCAIdInfo[i].ppwszIdInfo));
  276. }
  277. *(g_ExistingCAIdInfo[i].ppwszIdInfo) = pwszExisting;
  278. }
  279. ++i;
  280. }
  281. */
  282. hr = myGetCertNameProperty( pCertNameInfo,
  283. szOID_COMMON_NAME,
  284. &pwszNameProp);
  285. if (hr == S_OK)
  286. {
  287. // Common name exists, copy it out
  288. hr = myDupString(pwszNameProp, &(pServer->pwszCACommonName));
  289. _JumpIfError(hr, error, "myDupString");
  290. }
  291. // now get everything else
  292. hr = myCertNameToStr(
  293. X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  294. &pCert->pCertInfo->Subject,
  295. CERT_X500_NAME_STR | CERT_NAME_STR_COMMA_FLAG | CERT_NAME_STR_REVERSE_FLAG,
  296. &pServer->pwszFullCADN);
  297. _JumpIfError(hr, error, "myCertNameToStr");
  298. // No need for a DN suffix, the full DN is already in the cert
  299. hr = myDupString(L"", &(pServer->pwszDNSuffix));
  300. _JumpIfError(hr, error, "myDupString");
  301. hr = S_OK;
  302. error:
  303. if (NULL != pCertNameInfo)
  304. {
  305. LocalFree(pCertNameInfo);
  306. }
  307. return hr;
  308. }
  309. //--------------------------------------------------------------------
  310. // find a cert that matches the currently selected CSP and key container name
  311. // returns CRYPT_E_NOT_FOUND if no certificate. Caller MUST free the returned
  312. // context.
  313. // Note: IT IS VERY IMPORTANT that pfx import maintains all the
  314. // invariants about CSP, key container, hash, cert validity, etc.
  315. // that the rest of the UI (including this function) maintains.
  316. HRESULT
  317. FindCertificateByKey(
  318. IN CASERVERSETUPINFO * pServer,
  319. OUT CERT_CONTEXT const ** ppccCertificateCtx)
  320. {
  321. CSASSERT(NULL!=pServer);
  322. CSASSERT(NULL!=ppccCertificateCtx);
  323. HRESULT hr;
  324. DWORD dwPublicKeySize;
  325. BOOL bRetVal;
  326. DWORD dwVerificationFlags;
  327. BOOL bSelfSigned;
  328. CSP_HASH * pHash;
  329. CERT_CONTEXT const *pccFound = NULL;
  330. // variables that must be cleaned up
  331. HCRYPTPROV hProv=NULL;
  332. CERT_PUBLIC_KEY_INFO * pcpkiKeyInfo=NULL;
  333. CERT_CONTEXT const * pccCurrentCert=NULL;
  334. // initialize out param
  335. *ppccCertificateCtx=NULL;
  336. // open certificate store if it is not already open
  337. if (NULL==pServer->hMyStore) {
  338. pServer->hMyStore=CertOpenStore(
  339. CERT_STORE_PROV_SYSTEM_W,
  340. X509_ASN_ENCODING,
  341. NULL, // hProv
  342. CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_ENUM_ARCHIVED_FLAG,
  343. wszMY_CERTSTORE);
  344. if (NULL==pServer->hMyStore) {
  345. hr=myHLastError();
  346. _JumpError(hr, error, "CertOpenStore");
  347. }
  348. }
  349. //
  350. // Get public key blob from key container
  351. // Note: This may fail if key is not
  352. // AT_SIGNATURE, but we will never actually use the key in
  353. // this case anyway so it's ok to not find any certs
  354. //
  355. DBGPRINT((
  356. DBG_SS_CERTOCM,
  357. "FindCertificateByKey: key=%ws\n",
  358. pServer->pwszKeyContainerName));
  359. // first, open the key container
  360. bRetVal=myCertSrvCryptAcquireContext(
  361. &hProv,
  362. pServer->pwszKeyContainerName,
  363. pServer->pCSPInfo->pwszProvName,
  364. pServer->pCSPInfo->dwProvType,
  365. CRYPT_SILENT, // we should never have to ask anything to get this info
  366. pServer->pCSPInfo->fMachineKeyset);
  367. if (FALSE==bRetVal) {
  368. hr=myHLastError();
  369. _JumpError(hr, error, "myCertSrvCryptAcquireContext");
  370. }
  371. // get the size of the blob
  372. bRetVal=CryptExportPublicKeyInfo(
  373. hProv,
  374. AT_SIGNATURE,
  375. X509_ASN_ENCODING,
  376. NULL, //determine size
  377. &dwPublicKeySize);
  378. if (FALSE==bRetVal) {
  379. hr=myHLastError();
  380. _JumpError(hr, error, "CryptExportPublicKeyInfo (get data size)");
  381. }
  382. // allocate the blob
  383. pcpkiKeyInfo=(CERT_PUBLIC_KEY_INFO *)LocalAlloc(LMEM_FIXED, dwPublicKeySize);
  384. _JumpIfOutOfMemory(hr, error, pcpkiKeyInfo);
  385. // get the public key info blob
  386. bRetVal=CryptExportPublicKeyInfo(
  387. hProv,
  388. AT_SIGNATURE,
  389. X509_ASN_ENCODING,
  390. pcpkiKeyInfo,
  391. &dwPublicKeySize);
  392. if (FALSE==bRetVal) {
  393. hr=myHLastError();
  394. _JumpError(hr, error, "CryptExportPublicKeyInfo (get data)");
  395. }
  396. //
  397. // Find a certificate that has a matching key, has not expired,
  398. // and is self-signed or not self-signed depending upon
  399. // the CA type we are trying to install
  400. //
  401. while (true) {
  402. // find the next cert that has this public key
  403. // Note: the function will free the previously
  404. // used context when we pass it back
  405. pccCurrentCert=CertFindCertificateInStore(
  406. pServer->hMyStore,
  407. X509_ASN_ENCODING,
  408. 0, // flags
  409. CERT_FIND_PUBLIC_KEY,
  410. pcpkiKeyInfo,
  411. pccCurrentCert);
  412. // exit the loop when we can find no more matching certs
  413. if (NULL == pccCurrentCert)
  414. {
  415. hr = myHLastError();
  416. if (NULL != pccFound)
  417. {
  418. break;
  419. }
  420. _JumpError(hr, error, "CertFindCertificateInStore");
  421. }
  422. // check to make sure that the cert has not expired
  423. // first, we flag what we want to check
  424. dwVerificationFlags = CERT_STORE_TIME_VALIDITY_FLAG;
  425. // perform the checks
  426. bRetVal=CertVerifySubjectCertificateContext(
  427. pccCurrentCert,
  428. NULL, // issuer; not needed
  429. &dwVerificationFlags);
  430. if (FALSE==bRetVal) {
  431. _PrintError(myHLastError(), "CertVerifySubjectCertificateContext");
  432. // this should not fail, but maybe we got a bad cert. Keep looking.
  433. continue;
  434. }
  435. // Every check that passed had its flag zeroed. See if our check passed
  436. if (CERT_STORE_TIME_VALIDITY_FLAG&dwVerificationFlags){
  437. // This cert is expired and we can't use it. Keep looking.
  438. continue;
  439. }
  440. // verify to make sure no cert in chain is revoked, but don't kill
  441. // yourself if can't connect
  442. // allow untrusted cert if installing a root
  443. hr = myVerifyCertContext(
  444. pccCurrentCert,
  445. CA_VERIFY_FLAGS_IGNORE_OFFLINE |
  446. (IsRootCA(pServer->CAType)?
  447. CA_VERIFY_FLAGS_ALLOW_UNTRUSTED_ROOT : 0),
  448. 0,
  449. NULL,
  450. HCCE_LOCAL_MACHINE,
  451. NULL,
  452. NULL);
  453. if (S_OK != hr)
  454. {
  455. // At least one cert is revoked in chain
  456. _PrintError(hr, "myVerifyCertContext");
  457. continue;
  458. }
  459. // See if this cert appropriately is self-signed or not.
  460. // A root CA cert must be self-signed, while
  461. // a subordinate CA cert must not be self-signed.
  462. hr=IsCertSelfSignedForCAType(pServer, pccCurrentCert, &bRetVal);
  463. if (FAILED(hr)) {
  464. // this should not fail, but maybe we got a bad cert. Keep looking.
  465. _PrintError(hr, "IsCertSelfSignedForCAType");
  466. continue;
  467. }
  468. if (FALSE==bRetVal) {
  469. // this cert is not appropriate for this CA type
  470. _PrintError(S_FALSE, "bad CA Type");
  471. continue;
  472. }
  473. // If we got here, the cert we have is a good one.
  474. // If we already found a good cert and this one expires later,
  475. // toss the old one and save this one.
  476. if (NULL != pccFound)
  477. {
  478. if (0 > CompareFileTime(
  479. &pccCurrentCert->pCertInfo->NotAfter,
  480. &pccFound->pCertInfo->NotAfter))
  481. {
  482. continue; // old one is newer -- keep it
  483. }
  484. CertFreeCertificateContext(pccFound);
  485. pccFound = NULL;
  486. }
  487. pccFound = CertDuplicateCertificateContext(pccCurrentCert);
  488. if (NULL == pccFound)
  489. {
  490. hr = myHLastError();
  491. _JumpError(hr, error, "CertDuplicateCertificateContext");
  492. }
  493. } // <- End certificate finding loop
  494. CSASSERT(NULL != pccFound);
  495. *ppccCertificateCtx = pccFound;
  496. pccFound = NULL;
  497. hr = S_OK;
  498. error:
  499. if (NULL!=hProv) {
  500. CryptReleaseContext(hProv, 0);
  501. }
  502. if (NULL!=pcpkiKeyInfo) {
  503. LocalFree(pcpkiKeyInfo);
  504. }
  505. if (NULL != pccFound)
  506. {
  507. CertFreeCertificateContext(pccFound);
  508. }
  509. if (NULL!=pccCurrentCert) {
  510. CertFreeCertificateContext(pccCurrentCert);
  511. }
  512. if (S_OK!=hr && CRYPT_E_NOT_FOUND!=hr) {
  513. _PrintError(hr, "Ignoring error in FindCertificateByKey, returning CRYPT_E_NOT_FOUND")
  514. hr=CRYPT_E_NOT_FOUND;
  515. }
  516. return hr;
  517. }
  518. //--------------------------------------------------------------------
  519. // Set which existing certificate we want to use
  520. HRESULT
  521. SetExistingCertToUse(
  522. IN CASERVERSETUPINFO * pServer,
  523. IN CERT_CONTEXT const * pccCertCtx)
  524. {
  525. CSASSERT(NULL!=pServer);
  526. CSASSERT(NULL!=pccCertCtx);
  527. HRESULT hr;
  528. CSP_HASH * pHash;
  529. // to use an existing cert, we must use an existing key
  530. CSASSERT(NULL!=pServer->pwszKeyContainerName);
  531. // find the hash algorithm that matches this cert, and use it if possible
  532. // otherwise, stick with what we are currently using.
  533. hr=FindHashAlgorithm(pccCertCtx, pServer->pCSPInfo, &pHash);
  534. if (S_OK==hr) {
  535. pServer->pHashInfo = pHash;
  536. }
  537. hr = myGetNameId(pccCertCtx, &pServer->dwCertNameId);
  538. _PrintIfError(hr, "myGetNameId");
  539. if (MAXDWORD == pServer->dwCertNameId)
  540. {
  541. pServer->dwCertNameId = 0;
  542. }
  543. ClearExistingCertToUse(pServer);
  544. pServer->pccExistingCert=pccCertCtx;
  545. // We could assume that everything will work, but it doesn't take long to check
  546. //pServer->fValidatedHashAndKey=TRUE;
  547. hr=S_OK;
  548. //error:
  549. return hr;
  550. }
  551. //--------------------------------------------------------------------
  552. // stop using an existing certificate
  553. void
  554. ClearExistingCertToUse(
  555. IN CASERVERSETUPINFO * pServer)
  556. {
  557. CSASSERT(NULL!=pServer);
  558. if (NULL!=pServer->pccExistingCert) {
  559. CertFreeCertificateContext(pServer->pccExistingCert);
  560. pServer->pccExistingCert=NULL;
  561. }
  562. }