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.

1442 lines
38 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1995.
  5. //
  6. // File: cert.c
  7. //
  8. // Contents:
  9. //
  10. // Classes:
  11. //
  12. // Functions:
  13. //
  14. // History: 09-23-97 jbanes LSA integration stuff.
  15. // 01-05-98 jbanes Use WinVerifyTrust to validate certs.
  16. // 03-26-99 jbanes Fix CTL support, bug #303246
  17. //
  18. //----------------------------------------------------------------------------
  19. #include <stdlib.h>
  20. #include <spbase.h>
  21. #include <ssl2msg.h>
  22. #include <ssl3msg.h>
  23. #include <wincrypt.h>
  24. #include <oidenc.h>
  25. #include <softpub.h>
  26. #define CERT_HEADER_CONST "certificate"
  27. #define CERT_HEADER_OFFSET 6
  28. SP_STATUS
  29. SchGetTrustedRoots(
  30. HCERTSTORE *phClientRootStore);
  31. BOOL
  32. WINAPI
  33. SchCreateWorldStore (
  34. IN HCERTSTORE hRoot,
  35. IN DWORD cAdditionalStore,
  36. IN HCERTSTORE* rghAdditionalStore,
  37. OUT HCERTSTORE* phWorld);
  38. BOOL
  39. IsCertSelfSigned(PCCERT_CONTEXT pCertContext);
  40. // typedef struct _OIDPROVMAP
  41. // {
  42. // LPSTR szOid;
  43. // DWORD dwExchSpec;
  44. // DWORD dwCertType; // used for SSL 3.0 client auth
  45. // } OIDPROVMAP, *POIDPROVMAP;
  46. OIDPROVMAP g_CertTypes[] =
  47. {
  48. { szOID_RSA_RSA, SP_EXCH_RSA_PKCS1, SSL3_CERTTYPE_RSA_SIGN},
  49. { szOID_RSA_MD2RSA, SP_EXCH_RSA_PKCS1, SSL3_CERTTYPE_RSA_SIGN},
  50. { szOID_RSA_MD4RSA, SP_EXCH_RSA_PKCS1, SSL3_CERTTYPE_RSA_SIGN},
  51. { szOID_RSA_MD5RSA, SP_EXCH_RSA_PKCS1, SSL3_CERTTYPE_RSA_SIGN},
  52. { szOID_RSA_SHA1RSA, SP_EXCH_RSA_PKCS1, SSL3_CERTTYPE_RSA_SIGN},
  53. { szOID_OIWSEC_dsa, SP_EXCH_DH_PKCS3, SSL3_CERTTYPE_DSS_SIGN},
  54. { szOID_X957_DSA, SP_EXCH_DH_PKCS3, SSL3_CERTTYPE_DSS_SIGN},
  55. };
  56. DWORD g_cCertTypes = sizeof(g_CertTypes)/sizeof(OIDPROVMAP);
  57. DWORD
  58. MapOidToKeyExch(LPSTR szOid)
  59. {
  60. DWORD i;
  61. for(i = 0; i < g_cCertTypes; i++)
  62. {
  63. if(strcmp(szOid, g_CertTypes[i].szOid) == 0)
  64. {
  65. return g_CertTypes[i].dwExchSpec;
  66. }
  67. }
  68. return 0;
  69. }
  70. DWORD
  71. MapOidToCertType(LPSTR szOid)
  72. {
  73. DWORD i;
  74. for(i = 0; i < g_cCertTypes; i++)
  75. {
  76. if(strcmp(szOid, g_CertTypes[i].szOid) == 0)
  77. {
  78. return g_CertTypes[i].dwCertType;
  79. }
  80. }
  81. return 0;
  82. }
  83. // SPLoadCertificate takes a string of encoded cert bytes
  84. // and decodes them into the local certificate cache. It
  85. // then returns the first certificate of the group.
  86. SP_STATUS
  87. SPLoadCertificate(
  88. DWORD fProtocol,
  89. DWORD dwCertEncodingType,
  90. PUCHAR pCertificate,
  91. DWORD cbCertificate,
  92. PCCERT_CONTEXT *ppCertContext)
  93. {
  94. HCERTSTORE hCertStore = NULL;
  95. PCCERT_CONTEXT pCertContext = NULL;
  96. PBYTE pbCurrentRaw;
  97. DWORD cbCurrentRaw;
  98. BOOL fLeafCert;
  99. SP_STATUS pctRet;
  100. //
  101. // Dereference the cert that we are replacing.
  102. //
  103. if(ppCertContext == NULL)
  104. {
  105. return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
  106. }
  107. if(*ppCertContext != NULL)
  108. {
  109. CertFreeCertificateContext(*ppCertContext);
  110. }
  111. *ppCertContext = NULL;
  112. //
  113. // Create an in-memory certificate store.
  114. //
  115. hCertStore = CertOpenStore(CERT_STORE_PROV_MEMORY,
  116. 0, 0,
  117. CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG,
  118. 0);
  119. if(hCertStore == NULL)
  120. {
  121. SP_LOG_RESULT(GetLastError());
  122. return SEC_E_INSUFFICIENT_MEMORY;
  123. }
  124. fLeafCert = TRUE;
  125. pbCurrentRaw = pCertificate;
  126. cbCurrentRaw = cbCertificate;
  127. do
  128. {
  129. //
  130. // Skip to beginning of certificate.
  131. //
  132. if((fProtocol & SP_PROT_SSL3TLS1) && cbCurrentRaw > 3)
  133. {
  134. // SSL3 style cert chain, where the length
  135. // of each cert is prepended.
  136. pbCurrentRaw += 3;
  137. cbCurrentRaw -= 3;
  138. }
  139. // Skip past the "certificate" header
  140. if((cbCurrentRaw > (CERT_HEADER_OFFSET + strlen(CERT_HEADER_CONST))) &&
  141. (memcmp(pbCurrentRaw + CERT_HEADER_OFFSET, CERT_HEADER_CONST, strlen(CERT_HEADER_CONST)) == 0))
  142. {
  143. pbCurrentRaw += CERT_HEADER_OFFSET + strlen(CERT_HEADER_CONST);
  144. cbCurrentRaw -= CERT_HEADER_OFFSET + strlen(CERT_HEADER_CONST);
  145. }
  146. //
  147. // Decode this certificate context.
  148. //
  149. if(!CertAddEncodedCertificateToStore(hCertStore,
  150. dwCertEncodingType,
  151. pbCurrentRaw,
  152. cbCurrentRaw,
  153. CERT_STORE_ADD_USE_EXISTING,
  154. &pCertContext))
  155. {
  156. SP_LOG_RESULT(GetLastError());
  157. pctRet = PCT_ERR_BAD_CERTIFICATE;
  158. goto cleanup;
  159. }
  160. pbCurrentRaw += pCertContext->cbCertEncoded;
  161. if(cbCurrentRaw < pCertContext->cbCertEncoded)
  162. {
  163. pctRet = SP_LOG_RESULT(PCT_ERR_BAD_CERTIFICATE);
  164. goto cleanup;
  165. }
  166. cbCurrentRaw -= pCertContext->cbCertEncoded;
  167. if(fLeafCert)
  168. {
  169. fLeafCert = FALSE;
  170. *ppCertContext = pCertContext;
  171. }
  172. else
  173. {
  174. CertFreeCertificateContext(pCertContext);
  175. }
  176. pCertContext = NULL;
  177. } while(cbCurrentRaw);
  178. pctRet = PCT_ERR_OK;
  179. cleanup:
  180. CertCloseStore(hCertStore, 0);
  181. if(pctRet != PCT_ERR_OK)
  182. {
  183. if(pCertContext)
  184. {
  185. CertFreeCertificateContext(pCertContext);
  186. }
  187. if(*ppCertContext)
  188. {
  189. CertFreeCertificateContext(*ppCertContext);
  190. *ppCertContext = NULL;
  191. }
  192. }
  193. return pctRet;
  194. }
  195. SP_STATUS
  196. SPPublicKeyFromCert(
  197. PCCERT_CONTEXT pCert,
  198. PUBLICKEY ** ppKey,
  199. ExchSpec * pdwExchSpec)
  200. {
  201. PCERT_PUBLIC_KEY_INFO pPubKeyInfo;
  202. PUBLICKEY * pPublicKey;
  203. DWORD dwExchSpec;
  204. DWORD cbBlob;
  205. SP_STATUS pctRet;
  206. //
  207. // Log the subject and issuer names.
  208. //
  209. LogDistinguishedName(DEB_TRACE,
  210. "Subject: %s\n",
  211. pCert->pCertInfo->Subject.pbData,
  212. pCert->pCertInfo->Subject.cbData);
  213. LogDistinguishedName(DEB_TRACE,
  214. "Issuer: %s\n",
  215. pCert->pCertInfo->Issuer.pbData,
  216. pCert->pCertInfo->Issuer.cbData);
  217. //
  218. // Determine type of public key embedded in the certificate.
  219. //
  220. pPubKeyInfo = &pCert->pCertInfo->SubjectPublicKeyInfo;
  221. if(pPubKeyInfo == NULL)
  222. {
  223. return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
  224. }
  225. dwExchSpec = MapOidToKeyExch(pPubKeyInfo->Algorithm.pszObjId);
  226. if(dwExchSpec == 0)
  227. {
  228. return PCT_INT_UNKNOWN_CREDENTIAL;
  229. }
  230. //
  231. // Build public key blob from encoded public key.
  232. //
  233. switch(dwExchSpec)
  234. {
  235. case SP_EXCH_RSA_PKCS1:
  236. pctRet = RsaPublicKeyFromCert(pPubKeyInfo,
  237. NULL,
  238. &cbBlob);
  239. if(pctRet != PCT_ERR_OK)
  240. {
  241. return pctRet;
  242. }
  243. pPublicKey = SPExternalAlloc(sizeof(PUBLICKEY) + cbBlob);
  244. if(pPublicKey == NULL)
  245. {
  246. return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
  247. }
  248. pPublicKey->pPublic = (BLOBHEADER *)(pPublicKey + 1);
  249. pPublicKey->cbPublic = cbBlob;
  250. pctRet = RsaPublicKeyFromCert(pPubKeyInfo,
  251. pPublicKey->pPublic,
  252. &pPublicKey->cbPublic);
  253. if(pctRet != PCT_ERR_OK)
  254. {
  255. SPExternalFree(pPublicKey);
  256. return pctRet;
  257. }
  258. break;
  259. case SP_EXCH_DH_PKCS3:
  260. pctRet = DssPublicKeyFromCert(pPubKeyInfo,
  261. NULL,
  262. &cbBlob);
  263. if(pctRet != PCT_ERR_OK)
  264. {
  265. return pctRet;
  266. }
  267. pPublicKey = SPExternalAlloc(sizeof(PUBLICKEY) + cbBlob);
  268. if(pPublicKey == NULL)
  269. {
  270. return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
  271. }
  272. pPublicKey->pPublic = (BLOBHEADER *)(pPublicKey + 1);
  273. pPublicKey->cbPublic = cbBlob;
  274. pctRet = DssPublicKeyFromCert(pPubKeyInfo,
  275. pPublicKey->pPublic,
  276. &pPublicKey->cbPublic);
  277. if(pctRet != PCT_ERR_OK)
  278. {
  279. SPExternalFree(pPublicKey);
  280. return pctRet;
  281. }
  282. break;
  283. default:
  284. return SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
  285. }
  286. //
  287. // Set function outputs.
  288. //
  289. *ppKey = pPublicKey;
  290. if(pdwExchSpec)
  291. {
  292. *pdwExchSpec = dwExchSpec;
  293. }
  294. return PCT_ERR_OK;
  295. }
  296. SP_STATUS
  297. SPSerializeCertificate(
  298. DWORD dwProtocol, // in
  299. BOOL fBuildChain, // in
  300. PBYTE * ppCertChain, // out
  301. DWORD * pcbCertChain, // out
  302. PCCERT_CONTEXT pCertContext, // in
  303. DWORD dwChainingFlags) // in
  304. {
  305. PCCERT_CHAIN_CONTEXT pChainContext = NULL;
  306. CERT_CHAIN_PARA ChainPara;
  307. PCERT_SIMPLE_CHAIN pSimpleChain;
  308. PCCERT_CONTEXT pCurrentCert;
  309. BOOL fSuccess = FALSE;
  310. PBYTE pbCertChain;
  311. DWORD cbCertChain;
  312. DWORD i;
  313. SP_STATUS pctRet;
  314. BOOL fImpersonating = FALSE;
  315. SP_BEGIN("SPSerializeCertificate");
  316. if(pcbCertChain == NULL)
  317. {
  318. SP_RETURN( SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR));
  319. }
  320. if(fBuildChain)
  321. {
  322. ZeroMemory(&ChainPara, sizeof(ChainPara));
  323. ChainPara.cbSize = sizeof(ChainPara);
  324. fImpersonating = SslImpersonateClient();
  325. if(!(fSuccess = CertGetCertificateChain(
  326. NULL,
  327. pCertContext,
  328. NULL,
  329. NULL,
  330. &ChainPara,
  331. dwChainingFlags,
  332. NULL,
  333. &pChainContext)))
  334. {
  335. DebugLog((DEB_WARN, "Error 0x%x returned by CertGetCertificateChain!\n", GetLastError()));
  336. pChainContext = NULL;
  337. }
  338. if(fImpersonating)
  339. {
  340. RevertToSelf();
  341. fImpersonating = FALSE;
  342. }
  343. }
  344. if(!fSuccess)
  345. {
  346. //
  347. // Send the leaf certificate only.
  348. //
  349. // Compute size of chain.
  350. cbCertChain = pCertContext->cbCertEncoded;
  351. if(dwProtocol & SP_PROT_SSL3TLS1)
  352. {
  353. cbCertChain += CB_SSL3_CERT_VECTOR;
  354. }
  355. // Allocate memory for chain.
  356. if(ppCertChain == NULL)
  357. {
  358. *pcbCertChain = cbCertChain;
  359. pctRet = PCT_ERR_OK;
  360. goto cleanup;
  361. }
  362. else if(*ppCertChain == NULL)
  363. {
  364. *ppCertChain = SPExternalAlloc(cbCertChain);
  365. if(*ppCertChain == NULL)
  366. {
  367. pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
  368. goto cleanup;
  369. }
  370. }
  371. else if(*pcbCertChain < cbCertChain)
  372. {
  373. pctRet = SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL);
  374. goto cleanup;
  375. }
  376. *pcbCertChain = cbCertChain;
  377. // Place chain in output buffer.
  378. pbCertChain = *ppCertChain;
  379. if(dwProtocol & SP_PROT_SSL3TLS1)
  380. {
  381. pbCertChain[0] = MS24BOF(pCertContext->cbCertEncoded);
  382. pbCertChain[1] = MSBOF(pCertContext->cbCertEncoded);
  383. pbCertChain[2] = LSBOF(pCertContext->cbCertEncoded);
  384. pbCertChain += CB_SSL3_CERT_VECTOR;
  385. }
  386. CopyMemory(pbCertChain, pCertContext->pbCertEncoded, pCertContext->cbCertEncoded);
  387. pctRet = PCT_ERR_OK;
  388. goto cleanup;
  389. }
  390. //
  391. // Compute size of chain.
  392. //
  393. pSimpleChain = pChainContext->rgpChain[0];
  394. cbCertChain = 0;
  395. for(i = 0; i < pSimpleChain->cElement; i++)
  396. {
  397. pCurrentCert = pSimpleChain->rgpElement[i]->pCertContext;
  398. if(i > 0)
  399. {
  400. // Verify that this is not a root certificate.
  401. if(CertCompareCertificateName(pCurrentCert->dwCertEncodingType,
  402. &pCurrentCert->pCertInfo->Issuer,
  403. &pCurrentCert->pCertInfo->Subject))
  404. {
  405. break;
  406. }
  407. }
  408. cbCertChain += pCurrentCert->cbCertEncoded;
  409. if(dwProtocol & SP_PROT_SSL3TLS1)
  410. {
  411. cbCertChain += CB_SSL3_CERT_VECTOR;
  412. }
  413. }
  414. //
  415. // Allocate memory for chain.
  416. //
  417. if(ppCertChain == NULL)
  418. {
  419. *pcbCertChain = cbCertChain;
  420. pctRet = PCT_ERR_OK;
  421. goto cleanup;
  422. }
  423. else if(*ppCertChain == NULL)
  424. {
  425. *ppCertChain = SPExternalAlloc(cbCertChain);
  426. if(*ppCertChain == NULL)
  427. {
  428. pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
  429. goto cleanup;
  430. }
  431. }
  432. else if(*pcbCertChain < cbCertChain)
  433. {
  434. pctRet = SP_LOG_RESULT(PCT_INT_BUFF_TOO_SMALL);
  435. goto cleanup;
  436. }
  437. *pcbCertChain = cbCertChain;
  438. //
  439. // Place chain in output buffer.
  440. //
  441. pbCertChain = *ppCertChain;
  442. for(i = 0; i < pSimpleChain->cElement; i++)
  443. {
  444. pCurrentCert = pSimpleChain->rgpElement[i]->pCertContext;
  445. if(i > 0)
  446. {
  447. // Verify that this is not a root certificate.
  448. if(CertCompareCertificateName(pCurrentCert->dwCertEncodingType,
  449. &pCurrentCert->pCertInfo->Issuer,
  450. &pCurrentCert->pCertInfo->Subject))
  451. {
  452. break;
  453. }
  454. }
  455. if(dwProtocol & SP_PROT_SSL3TLS1)
  456. {
  457. pbCertChain[0] = MS24BOF(pCurrentCert->cbCertEncoded);
  458. pbCertChain[1] = MSBOF(pCurrentCert->cbCertEncoded);
  459. pbCertChain[2] = LSBOF(pCurrentCert->cbCertEncoded);
  460. pbCertChain += CB_SSL3_CERT_VECTOR;
  461. }
  462. CopyMemory(pbCertChain, pCurrentCert->pbCertEncoded, pCurrentCert->cbCertEncoded);
  463. pbCertChain += pCurrentCert->cbCertEncoded;
  464. }
  465. SP_ASSERT(*ppCertChain + cbCertChain == pbCertChain);
  466. pctRet = PCT_ERR_OK;
  467. cleanup:
  468. if(pChainContext)
  469. {
  470. CertFreeCertificateChain(pChainContext);
  471. }
  472. SP_RETURN(pctRet);
  473. }
  474. /*****************************************************************************/
  475. SP_STATUS
  476. ExtractIssuerNamesFromStore(
  477. HCERTSTORE hStore, // in
  478. PBYTE pbIssuers, // out
  479. DWORD *pcbIssuers) // in, out
  480. {
  481. DWORD cbCurIssuerLen = 0;
  482. DWORD cbIssuerLen = *pcbIssuers;
  483. PBYTE pbCurIssuer = pbIssuers;
  484. PCCERT_CONTEXT pCurrent = NULL;
  485. SECURITY_STATUS scRet;
  486. BOOL fIsAllowed;
  487. // Initialize output to zero.
  488. *pcbIssuers = 0;
  489. while(TRUE)
  490. {
  491. pCurrent = CertEnumCertificatesInStore(hStore, pCurrent);
  492. if(pCurrent == NULL) break;
  493. // Is this a client-auth certificate?
  494. scRet = SPCheckKeyUsage(pCurrent,
  495. szOID_PKIX_KP_CLIENT_AUTH,
  496. FALSE,
  497. &fIsAllowed);
  498. if(scRet != SEC_E_OK)
  499. {
  500. continue;
  501. }
  502. if(!fIsAllowed)
  503. {
  504. continue;
  505. }
  506. cbCurIssuerLen += 2 + pCurrent->pCertInfo->Subject.cbData;
  507. // Are we writing?
  508. if(pbIssuers)
  509. {
  510. if(cbCurIssuerLen > cbIssuerLen)
  511. {
  512. // Memory overrun
  513. CertFreeCertificateContext(pCurrent);
  514. return SP_LOG_RESULT(PCT_INT_DATA_OVERFLOW);
  515. }
  516. pbCurIssuer[0] = MSBOF(pCurrent->pCertInfo->Subject.cbData);
  517. pbCurIssuer[1] = LSBOF(pCurrent->pCertInfo->Subject.cbData);
  518. pbCurIssuer += 2;
  519. CopyMemory(pbCurIssuer, pCurrent->pCertInfo->Subject.pbData,
  520. pCurrent->pCertInfo->Subject.cbData);
  521. pbCurIssuer += pCurrent->pCertInfo->Subject.cbData;
  522. }
  523. }
  524. *pcbIssuers = cbCurIssuerLen;
  525. return PCT_ERR_OK;
  526. }
  527. /*****************************************************************************/
  528. SP_STATUS
  529. GetDefaultIssuers(
  530. PBYTE pbIssuers, // out
  531. DWORD *pcbIssuers) // in, out
  532. {
  533. HCERTSTORE hStore;
  534. SP_STATUS pctRet;
  535. pctRet = SchGetTrustedRoots(&hStore);
  536. if(pctRet != PCT_ERR_OK)
  537. {
  538. return pctRet;
  539. }
  540. pctRet = ExtractIssuerNamesFromStore(hStore, pbIssuers, pcbIssuers);
  541. if(pctRet != PCT_ERR_OK)
  542. {
  543. CertCloseStore(hStore, 0);
  544. return pctRet;
  545. }
  546. CertCloseStore(hStore, 0);
  547. return PCT_ERR_OK;
  548. }
  549. SP_STATUS
  550. SchGetTrustedRoots(
  551. HCERTSTORE *phClientRootStore)
  552. {
  553. HTTPSPolicyCallbackData polHttps;
  554. CERT_CHAIN_POLICY_PARA PolicyPara;
  555. CERT_CHAIN_POLICY_STATUS PolicyStatus;
  556. CERT_CHAIN_PARA ChainPara;
  557. PCCERT_CHAIN_CONTEXT pChainContext = NULL;
  558. LPSTR pszUsage;
  559. PCCERT_CONTEXT pCertContext;
  560. HCERTSTORE hClientRootStore = 0;
  561. HCERTSTORE hRootStore = 0;
  562. HCERTSTORE hWorldStore = 0;
  563. DWORD Status = SEC_E_OK;
  564. BOOL fImpersonating = FALSE;
  565. // Open output store.
  566. hClientRootStore = CertOpenStore(CERT_STORE_PROV_MEMORY,
  567. 0, 0,
  568. CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG,
  569. 0);
  570. if(hClientRootStore == NULL)
  571. {
  572. //SP_LOG_RESULT(GetLastError());
  573. Status = SEC_E_INSUFFICIENT_MEMORY;
  574. goto cleanup;
  575. }
  576. fImpersonating = SslImpersonateClient();
  577. // Open root store.
  578. hRootStore = CertOpenSystemStore(0, "ROOT");
  579. if(hRootStore == NULL)
  580. {
  581. DebugLog((DEB_WARN, "Error 0x%x opening root store\n", GetLastError()));
  582. }
  583. // Create world store.
  584. if(!SchCreateWorldStore(hRootStore,
  585. 0, NULL,
  586. &hWorldStore))
  587. {
  588. DebugLog((DEB_ERROR, "Error 0x%x creating world store\n", GetLastError()));
  589. goto cleanup;
  590. }
  591. // Enumerate the certificates in the world store, looking
  592. // for trusted roots. This approach will automatically take
  593. // advantage of any CTLs that are installed on the system.
  594. pCertContext = NULL;
  595. while(TRUE)
  596. {
  597. pCertContext = CertEnumCertificatesInStore(hWorldStore, pCertContext);
  598. if(pCertContext == NULL) break;
  599. if(!IsCertSelfSigned(pCertContext))
  600. {
  601. continue;
  602. }
  603. pszUsage = szOID_PKIX_KP_CLIENT_AUTH;
  604. ZeroMemory(&ChainPara, sizeof(ChainPara));
  605. ChainPara.cbSize = sizeof(ChainPara);
  606. ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
  607. ChainPara.RequestedUsage.Usage.cUsageIdentifier = 1;
  608. ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = &pszUsage;
  609. if(!CertGetCertificateChain(
  610. NULL,
  611. pCertContext,
  612. NULL,
  613. 0,
  614. &ChainPara,
  615. 0,
  616. NULL,
  617. &pChainContext))
  618. {
  619. SP_LOG_RESULT(GetLastError());
  620. continue;
  621. }
  622. // Set up validate chain structures.
  623. ZeroMemory(&polHttps, sizeof(HTTPSPolicyCallbackData));
  624. polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData);
  625. polHttps.dwAuthType = AUTHTYPE_CLIENT;
  626. polHttps.fdwChecks = 0;
  627. polHttps.pwszServerName = NULL;
  628. ZeroMemory(&PolicyStatus, sizeof(PolicyStatus));
  629. PolicyStatus.cbSize = sizeof(PolicyStatus);
  630. ZeroMemory(&PolicyPara, sizeof(PolicyPara));
  631. PolicyPara.cbSize = sizeof(PolicyPara);
  632. PolicyPara.pvExtraPolicyPara= &polHttps;
  633. PolicyPara.dwFlags = CERT_CHAIN_POLICY_IGNORE_ALL_NOT_TIME_VALID_FLAGS;
  634. // Validate chain
  635. if(!CertVerifyCertificateChainPolicy(
  636. CERT_CHAIN_POLICY_SSL,
  637. pChainContext,
  638. &PolicyPara,
  639. &PolicyStatus))
  640. {
  641. SP_LOG_RESULT(GetLastError());
  642. CertFreeCertificateChain(pChainContext);
  643. continue;
  644. }
  645. if(PolicyStatus.dwError)
  646. {
  647. // Certificate did not validate, move on to the next one.
  648. CertFreeCertificateChain(pChainContext);
  649. continue;
  650. }
  651. CertFreeCertificateChain(pChainContext);
  652. // Add the root certificate to the list of trusted ones.
  653. if(!CertAddCertificateContextToStore(hClientRootStore,
  654. pCertContext,
  655. CERT_STORE_ADD_USE_EXISTING,
  656. NULL))
  657. {
  658. SP_LOG_RESULT(GetLastError());
  659. }
  660. }
  661. cleanup:
  662. if(hRootStore)
  663. {
  664. CertCloseStore(hRootStore, 0);
  665. }
  666. if(hWorldStore)
  667. {
  668. CertCloseStore(hWorldStore, 0);
  669. }
  670. if(fImpersonating)
  671. {
  672. RevertToSelf();
  673. }
  674. if(Status == SEC_E_OK)
  675. {
  676. *phClientRootStore = hClientRootStore;
  677. }
  678. return Status;
  679. }
  680. //+---------------------------------------------------------------------------
  681. //
  682. // Function: ChainCreateCollectionIncludingCtlCertificates
  683. //
  684. // Synopsis: create a collection which includes the source store hStore and
  685. // any CTL certificates from it
  686. //
  687. //----------------------------------------------------------------------------
  688. BOOL WINAPI
  689. ChainCreateCollectionIncludingCtlCertificates (
  690. IN HCERTSTORE hStore,
  691. OUT HCERTSTORE* phCollection
  692. )
  693. {
  694. BOOL fResult = FALSE;
  695. HCERTSTORE hCollection;
  696. PCCTL_CONTEXT pCtlContext = NULL;
  697. HCERTSTORE hCtlStore;
  698. hCollection = CertOpenStore(
  699. CERT_STORE_PROV_COLLECTION,
  700. X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  701. 0,
  702. CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG,
  703. NULL
  704. );
  705. if ( hCollection == NULL )
  706. {
  707. return( FALSE );
  708. }
  709. fResult = CertAddStoreToCollection( hCollection, hStore, 0, 0 );
  710. while ( ( fResult == TRUE ) &&
  711. ( ( pCtlContext = CertEnumCTLsInStore(
  712. hStore,
  713. pCtlContext
  714. ) ) != NULL ) )
  715. {
  716. hCtlStore = CertOpenStore(
  717. CERT_STORE_PROV_MSG,
  718. X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  719. 0,
  720. 0,
  721. pCtlContext->hCryptMsg
  722. );
  723. if ( hCtlStore != NULL )
  724. {
  725. fResult = CertAddStoreToCollection(
  726. hCollection,
  727. hCtlStore,
  728. 0,
  729. 0
  730. );
  731. CertCloseStore( hCtlStore, 0 );
  732. }
  733. }
  734. if ( fResult == TRUE )
  735. {
  736. *phCollection = hCollection;
  737. }
  738. else
  739. {
  740. CertCloseStore( hCollection, 0 );
  741. }
  742. return( fResult );
  743. }
  744. BOOL
  745. WINAPI
  746. SchCreateWorldStore (
  747. IN HCERTSTORE hRoot,
  748. IN DWORD cAdditionalStore,
  749. IN HCERTSTORE* rghAdditionalStore,
  750. OUT HCERTSTORE* phWorld)
  751. {
  752. BOOL fResult;
  753. HCERTSTORE hWorld;
  754. HCERTSTORE hStore, hCtl;
  755. DWORD cCount;
  756. hWorld = CertOpenStore(
  757. CERT_STORE_PROV_COLLECTION,
  758. X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  759. 0,
  760. CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG,
  761. NULL
  762. );
  763. if ( hWorld == NULL )
  764. {
  765. return( FALSE );
  766. }
  767. fResult = CertAddStoreToCollection( hWorld, hRoot, 0, 0 );
  768. for ( cCount = 0;
  769. ( cCount < cAdditionalStore ) && ( fResult == TRUE );
  770. cCount++ )
  771. {
  772. fResult = CertAddStoreToCollection(
  773. hWorld,
  774. rghAdditionalStore[ cCount ],
  775. 0,
  776. 0
  777. );
  778. }
  779. if ( fResult == TRUE )
  780. {
  781. hStore = CertOpenSystemStore(0, "trust");
  782. if( hStore != NULL )
  783. {
  784. if(ChainCreateCollectionIncludingCtlCertificates(hStore, &hCtl))
  785. {
  786. if(!CertAddStoreToCollection( hWorld, hCtl, 0, 0 ))
  787. {
  788. DebugLog((DEB_WARN, "Error 0x%x adding CTL collection\n", GetLastError()));
  789. }
  790. CertCloseStore( hCtl, 0 );
  791. }
  792. else
  793. {
  794. DebugLog((DEB_WARN, "Error 0x%x creating CTL collection\n", GetLastError()));
  795. }
  796. CertCloseStore( hStore, 0 );
  797. }
  798. }
  799. if ( fResult == TRUE )
  800. {
  801. hStore = CertOpenSystemStore(0, "ca");
  802. if ( hStore != NULL )
  803. {
  804. fResult = CertAddStoreToCollection( hWorld, hStore, 0, 0 );
  805. CertCloseStore( hStore, 0 );
  806. }
  807. else
  808. {
  809. fResult = FALSE;
  810. }
  811. }
  812. if ( fResult == TRUE )
  813. {
  814. hStore = CertOpenSystemStore(0, "my");
  815. if ( hStore != NULL )
  816. {
  817. fResult = CertAddStoreToCollection( hWorld, hStore, 0, 0 );
  818. CertCloseStore( hStore, 0 );
  819. }
  820. else
  821. {
  822. fResult = FALSE;
  823. }
  824. }
  825. if ( fResult == TRUE )
  826. {
  827. *phWorld = hWorld;
  828. }
  829. else
  830. {
  831. CertCloseStore( hWorld, 0 );
  832. }
  833. return( fResult );
  834. }
  835. BOOL
  836. IsCertSelfSigned(PCCERT_CONTEXT pCertContext)
  837. {
  838. // Compare subject and issuer names.
  839. if(pCertContext->pCertInfo->Subject.cbData == pCertContext->pCertInfo->Issuer.cbData)
  840. {
  841. if(memcmp(pCertContext->pCertInfo->Subject.pbData,
  842. pCertContext->pCertInfo->Issuer.pbData,
  843. pCertContext->pCertInfo->Issuer.cbData) == 0)
  844. {
  845. return TRUE;
  846. }
  847. }
  848. return FALSE;
  849. }
  850. SP_STATUS
  851. MapWinTrustError(DWORD Status, DWORD DefaultError, DWORD dwIgnoreErrors)
  852. {
  853. if((Status == CRYPT_E_NO_REVOCATION_CHECK) &&
  854. (dwIgnoreErrors & CRED_FLAG_IGNORE_NO_REVOCATION_CHECK))
  855. {
  856. DebugLog((DEB_WARN, "MapWinTrustError: Ignoring CRYPT_E_NO_REVOCATION_CHECK\n"));
  857. Status = STATUS_SUCCESS;
  858. }
  859. if((Status == CRYPT_E_REVOCATION_OFFLINE) &&
  860. (dwIgnoreErrors & CRED_FLAG_IGNORE_REVOCATION_OFFLINE))
  861. {
  862. DebugLog((DEB_WARN, "MapWinTrustError: Ignoring CRYPT_E_REVOCATION_OFFLINE\n"));
  863. Status = STATUS_SUCCESS;
  864. }
  865. if(HRESULT_FACILITY(Status) == FACILITY_SECURITY)
  866. {
  867. return (Status);
  868. }
  869. switch(Status)
  870. {
  871. case ERROR_SUCCESS:
  872. return SEC_E_OK;
  873. // Expired certificate.
  874. case CERT_E_EXPIRED:
  875. case CERT_E_VALIDITYPERIODNESTING:
  876. return SEC_E_CERT_EXPIRED;
  877. // Unknown CA
  878. case CERT_E_UNTRUSTEDROOT:
  879. case CERT_E_UNTRUSTEDCA:
  880. return SEC_E_UNTRUSTED_ROOT;
  881. // Certificate revoked.
  882. case CERT_E_REVOKED:
  883. return CRYPT_E_REVOKED;
  884. // Target name doesn't match name in certificate.
  885. case CERT_E_CN_NO_MATCH:
  886. return SEC_E_WRONG_PRINCIPAL;
  887. // Some other error.
  888. default:
  889. if(DefaultError)
  890. {
  891. return DefaultError;
  892. }
  893. else
  894. {
  895. return SEC_E_CERT_UNKNOWN;
  896. }
  897. }
  898. }
  899. NTSTATUS
  900. VerifyClientCertificate(
  901. PCCERT_CONTEXT pCertContext,
  902. DWORD dwCertFlags,
  903. DWORD dwIgnoreErrors,
  904. LPCSTR pszPolicyOID,
  905. PCCERT_CHAIN_CONTEXT *ppChainContext) // optional
  906. {
  907. HTTPSPolicyCallbackData polHttps;
  908. CERT_CHAIN_POLICY_PARA PolicyPara;
  909. CERT_CHAIN_POLICY_STATUS PolicyStatus;
  910. CERT_CHAIN_PARA ChainPara;
  911. PCCERT_CHAIN_CONTEXT pChainContext = NULL;
  912. DWORD Status;
  913. LPSTR pszUsage;
  914. BOOL fImpersonating = FALSE;
  915. //
  916. // Build certificate chain.
  917. //
  918. fImpersonating = SslImpersonateClient();
  919. pszUsage = szOID_PKIX_KP_CLIENT_AUTH;
  920. ZeroMemory(&ChainPara, sizeof(ChainPara));
  921. ChainPara.cbSize = sizeof(ChainPara);
  922. ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
  923. ChainPara.RequestedUsage.Usage.cUsageIdentifier = 1;
  924. ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = &pszUsage;
  925. if(!CertGetCertificateChain(
  926. NULL, // hChainEngine
  927. pCertContext, // pCertContext
  928. NULL, // pTime
  929. pCertContext->hCertStore, // hAdditionalStore
  930. &ChainPara, // pChainPara
  931. dwCertFlags, // dwFlags
  932. NULL, // pvReserved
  933. &pChainContext)) // ppChainContext
  934. {
  935. Status = SP_LOG_RESULT(GetLastError());
  936. goto cleanup;
  937. }
  938. //
  939. // Validate certificate chain.
  940. //
  941. if(pszPolicyOID == CERT_CHAIN_POLICY_NT_AUTH)
  942. {
  943. ZeroMemory(&PolicyPara, sizeof(PolicyPara));
  944. PolicyPara.cbSize = sizeof(PolicyPara);
  945. PolicyPara.dwFlags = BASIC_CONSTRAINTS_CERT_CHAIN_POLICY_END_ENTITY_FLAG;
  946. }
  947. else
  948. {
  949. ZeroMemory(&polHttps, sizeof(HTTPSPolicyCallbackData));
  950. polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData);
  951. polHttps.dwAuthType = AUTHTYPE_CLIENT;
  952. polHttps.fdwChecks = 0;
  953. ZeroMemory(&PolicyPara, sizeof(PolicyPara));
  954. PolicyPara.cbSize = sizeof(PolicyPara);
  955. PolicyPara.pvExtraPolicyPara = &polHttps;
  956. }
  957. ZeroMemory(&PolicyStatus, sizeof(PolicyStatus));
  958. PolicyStatus.cbSize = sizeof(PolicyStatus);
  959. if(!CertVerifyCertificateChainPolicy(
  960. pszPolicyOID,
  961. pChainContext,
  962. &PolicyPara,
  963. &PolicyStatus))
  964. {
  965. Status = SP_LOG_RESULT(GetLastError());
  966. goto cleanup;
  967. }
  968. #if DBG
  969. if(PolicyStatus.dwError)
  970. {
  971. DebugLog((DEB_WARN, "CertVerifyCertificateChainPolicy returned 0x%x\n", PolicyStatus.dwError));
  972. }
  973. #endif
  974. Status = MapWinTrustError(PolicyStatus.dwError, 0, dwIgnoreErrors);
  975. if(Status)
  976. {
  977. DebugLog((DEB_ERROR, "MapWinTrustError returned 0x%x\n", Status));
  978. goto cleanup;
  979. }
  980. Status = STATUS_SUCCESS;
  981. if(ppChainContext != NULL)
  982. {
  983. *ppChainContext = pChainContext;
  984. pChainContext = NULL;
  985. }
  986. cleanup:
  987. if(pChainContext)
  988. {
  989. CertFreeCertificateChain(pChainContext);
  990. }
  991. if(fImpersonating) RevertToSelf();
  992. return Status;
  993. }
  994. NTSTATUS
  995. AutoVerifyServerCertificate(PSPContext pContext)
  996. {
  997. PSPCredentialGroup pCredGroup;
  998. DWORD dwCertFlags = 0;
  999. DWORD dwIgnoreErrors = 0;
  1000. if(pContext->Flags & CONTEXT_FLAG_MANUAL_CRED_VALIDATION)
  1001. {
  1002. return STATUS_SUCCESS;
  1003. }
  1004. pCredGroup = pContext->pCredGroup;
  1005. if(pCredGroup == NULL)
  1006. {
  1007. return SP_LOG_RESULT(SEC_E_INTERNAL_ERROR);
  1008. }
  1009. if(pCredGroup->dwFlags & CRED_FLAG_REVCHECK_END_CERT)
  1010. dwCertFlags |= CERT_CHAIN_REVOCATION_CHECK_END_CERT;
  1011. if(pCredGroup->dwFlags & CRED_FLAG_REVCHECK_CHAIN)
  1012. dwCertFlags |= CERT_CHAIN_REVOCATION_CHECK_CHAIN;
  1013. if(pCredGroup->dwFlags & CRED_FLAG_REVCHECK_CHAIN_EXCLUDE_ROOT)
  1014. dwCertFlags |= CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
  1015. if(pCredGroup->dwFlags & CRED_FLAG_IGNORE_NO_REVOCATION_CHECK)
  1016. dwIgnoreErrors |= CRED_FLAG_IGNORE_NO_REVOCATION_CHECK;
  1017. if(pCredGroup->dwFlags & CRED_FLAG_IGNORE_REVOCATION_OFFLINE)
  1018. dwIgnoreErrors |= CRED_FLAG_IGNORE_REVOCATION_OFFLINE;
  1019. return VerifyServerCertificate(pContext, dwCertFlags, dwIgnoreErrors);
  1020. }
  1021. NTSTATUS
  1022. VerifyServerCertificate(
  1023. PSPContext pContext,
  1024. DWORD dwCertFlags,
  1025. DWORD dwIgnoreErrors)
  1026. {
  1027. HTTPSPolicyCallbackData polHttps;
  1028. CERT_CHAIN_POLICY_PARA PolicyPara;
  1029. CERT_CHAIN_POLICY_STATUS PolicyStatus;
  1030. CERT_CHAIN_PARA ChainPara;
  1031. PCCERT_CHAIN_CONTEXT pChainContext = NULL;
  1032. #define SERVER_USAGE_COUNT 3
  1033. LPSTR rgszUsages[SERVER_USAGE_COUNT] = {
  1034. szOID_PKIX_KP_SERVER_AUTH,
  1035. szOID_SERVER_GATED_CRYPTO,
  1036. szOID_SGC_NETSCAPE };
  1037. DWORD Status;
  1038. PWSTR pwszServerName = NULL;
  1039. PSPCredentialGroup pCred;
  1040. PCCERT_CONTEXT pCertContext;
  1041. BOOL fImpersonating = FALSE;
  1042. pCred = pContext->pCredGroup;
  1043. if(pCred == NULL)
  1044. {
  1045. return SEC_E_INTERNAL_ERROR;
  1046. }
  1047. pCertContext = pContext->RipeZombie->pRemoteCert;
  1048. if(pCertContext == NULL)
  1049. {
  1050. return SEC_E_INTERNAL_ERROR;
  1051. }
  1052. //
  1053. // Build certificate chain.
  1054. //
  1055. fImpersonating = SslImpersonateClient();
  1056. ZeroMemory(&ChainPara, sizeof(ChainPara));
  1057. ChainPara.cbSize = sizeof(ChainPara);
  1058. ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
  1059. ChainPara.RequestedUsage.Usage.cUsageIdentifier = SERVER_USAGE_COUNT;
  1060. ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages;
  1061. if(!CertGetCertificateChain(
  1062. NULL, // hChainEngine
  1063. pCertContext, // pCertContext
  1064. NULL, // pTime
  1065. pCertContext->hCertStore, // hAdditionalStore
  1066. &ChainPara, // pChainPara
  1067. dwCertFlags, // dwFlags
  1068. NULL, // pvReserved
  1069. &pChainContext)) // ppChainContext
  1070. {
  1071. Status = SP_LOG_RESULT(GetLastError());
  1072. goto cleanup;
  1073. }
  1074. //
  1075. // Validate certificate chain.
  1076. //
  1077. if(!(pCred->dwFlags & CRED_FLAG_NO_SERVERNAME_CHECK))
  1078. {
  1079. pwszServerName = pContext->RipeZombie->szCacheID;
  1080. if(pwszServerName == NULL || lstrlenW(pwszServerName) == 0)
  1081. {
  1082. Status = SP_LOG_RESULT(SEC_E_WRONG_PRINCIPAL);
  1083. goto cleanup;
  1084. }
  1085. }
  1086. ZeroMemory(&polHttps, sizeof(HTTPSPolicyCallbackData));
  1087. polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData);
  1088. polHttps.dwAuthType = AUTHTYPE_SERVER;
  1089. polHttps.fdwChecks = 0;
  1090. polHttps.pwszServerName = pwszServerName;
  1091. ZeroMemory(&PolicyPara, sizeof(PolicyPara));
  1092. PolicyPara.cbSize = sizeof(PolicyPara);
  1093. PolicyPara.pvExtraPolicyPara = &polHttps;
  1094. ZeroMemory(&PolicyStatus, sizeof(PolicyStatus));
  1095. PolicyStatus.cbSize = sizeof(PolicyStatus);
  1096. if(!CertVerifyCertificateChainPolicy(
  1097. CERT_CHAIN_POLICY_SSL,
  1098. pChainContext,
  1099. &PolicyPara,
  1100. &PolicyStatus))
  1101. {
  1102. Status = SP_LOG_RESULT(GetLastError());
  1103. goto cleanup;
  1104. }
  1105. #if DBG
  1106. if(PolicyStatus.dwError)
  1107. {
  1108. DebugLog((DEB_WARN, "CertVerifyCertificateChainPolicy returned 0x%x\n", PolicyStatus.dwError));
  1109. }
  1110. #endif
  1111. Status = MapWinTrustError(PolicyStatus.dwError, 0, dwIgnoreErrors);
  1112. if(Status)
  1113. {
  1114. DebugLog((DEB_ERROR, "MapWinTrustError returned 0x%x\n", Status));
  1115. LogBogusServerCertEvent(pCertContext, pwszServerName, Status);
  1116. goto cleanup;
  1117. }
  1118. Status = STATUS_SUCCESS;
  1119. cleanup:
  1120. if(pChainContext)
  1121. {
  1122. CertFreeCertificateChain(pChainContext);
  1123. }
  1124. if(fImpersonating) RevertToSelf();
  1125. return Status;
  1126. }
  1127. SECURITY_STATUS
  1128. SPCheckKeyUsage(
  1129. PCCERT_CONTEXT pCertContext,
  1130. PSTR pszUsage,
  1131. BOOL fOnCertOnly,
  1132. PBOOL pfIsAllowed)
  1133. {
  1134. PCERT_ENHKEY_USAGE pKeyUsage;
  1135. DWORD cbKeyUsage;
  1136. DWORD j;
  1137. BOOL fFound;
  1138. DWORD dwFlags = 0;
  1139. if(fOnCertOnly)
  1140. {
  1141. dwFlags = CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG;
  1142. }
  1143. // Determine size of usage information.
  1144. if(!CertGetEnhancedKeyUsage(pCertContext,
  1145. dwFlags,
  1146. NULL,
  1147. &cbKeyUsage))
  1148. {
  1149. // No usage information exists.
  1150. *pfIsAllowed = TRUE;
  1151. return SEC_E_OK;
  1152. }
  1153. pKeyUsage = SPExternalAlloc(cbKeyUsage);
  1154. if(pKeyUsage == NULL)
  1155. {
  1156. *pfIsAllowed = FALSE;
  1157. return SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
  1158. }
  1159. // Read key usage information.
  1160. if(!CertGetEnhancedKeyUsage(pCertContext,
  1161. dwFlags,
  1162. pKeyUsage,
  1163. &cbKeyUsage))
  1164. {
  1165. // No usage information exists.
  1166. SPExternalFree(pKeyUsage);
  1167. *pfIsAllowed = TRUE;
  1168. return SEC_E_OK;
  1169. }
  1170. if(pKeyUsage->cUsageIdentifier == 0 && GetLastError() == CRYPT_E_NOT_FOUND)
  1171. {
  1172. // No usage information exists.
  1173. SPExternalFree(pKeyUsage);
  1174. *pfIsAllowed = TRUE;
  1175. return SEC_E_OK;
  1176. }
  1177. // See if requested usage is in list of supported usages.
  1178. fFound = FALSE;
  1179. for(j = 0; j < pKeyUsage->cUsageIdentifier; j++)
  1180. {
  1181. if(strcmp(pszUsage, pKeyUsage->rgpszUsageIdentifier[j]) == 0)
  1182. {
  1183. fFound = TRUE;
  1184. break;
  1185. }
  1186. }
  1187. SPExternalFree(pKeyUsage);
  1188. if(!fFound)
  1189. {
  1190. // Usage extensions found, but doesn't list ours.
  1191. *pfIsAllowed = FALSE;
  1192. }
  1193. else
  1194. {
  1195. *pfIsAllowed = TRUE;
  1196. }
  1197. return SEC_E_OK;
  1198. }