Leaked source code of windows server 2003
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.

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