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.

2034 lines
55 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1995.
  5. //
  6. // File: cred.c
  7. //
  8. // Contents: Schannel credential management routines.
  9. //
  10. // Classes:
  11. //
  12. // Functions:
  13. //
  14. // History: 09-23-97 jbanes LSA integration stuff.
  15. // 03-15-99 jbanes Remove dead code, fix legacy SGC.
  16. //
  17. //----------------------------------------------------------------------------
  18. #include <spbase.h>
  19. #include <wincrypt.h>
  20. #include <oidenc.h>
  21. #include <mapper.h>
  22. #include <userenv.h>
  23. #include <lsasecpk.h>
  24. RTL_CRITICAL_SECTION g_SslCredLock;
  25. LIST_ENTRY g_SslCredList;
  26. HANDLE g_GPEvent;
  27. HCERTSTORE g_hMyCertStore;
  28. HANDLE g_hMyCertStoreEvent;
  29. SP_STATUS
  30. GetPrivateFromCert(
  31. PSPCredential pCred,
  32. DWORD dwProtocol,
  33. PLSA_SCHANNEL_SUB_CRED pSubCred);
  34. BOOL
  35. SslInitCredentialManager(VOID)
  36. {
  37. NTSTATUS Status;
  38. //
  39. // Initialize synchronization objects.
  40. //
  41. Status = RtlInitializeCriticalSection( &g_SslCredLock );
  42. if (!NT_SUCCESS(Status))
  43. {
  44. return FALSE;
  45. }
  46. InitializeListHead( &g_SslCredList );
  47. //
  48. // Register for group policy notifications. Servers rebuild their list
  49. // of trusted CAs each time this happens (every 8 hours) in case these
  50. // have changed.
  51. //
  52. g_GPEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  53. if(g_GPEvent)
  54. {
  55. if(!RegisterGPNotification(g_GPEvent, TRUE))
  56. {
  57. DebugLog((DEB_ERROR, "Error 0x%x registering for machine GP notification\n", GetLastError()));
  58. }
  59. }
  60. //
  61. // Register for changes to the local machine MY store. Each time a change
  62. // is detected, we check to see if any of our certificates have been renewed.
  63. //
  64. g_hMyCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM,
  65. X509_ASN_ENCODING,
  66. 0,
  67. CERT_SYSTEM_STORE_LOCAL_MACHINE,
  68. L"MY");
  69. if(g_hMyCertStore)
  70. {
  71. g_hMyCertStoreEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  72. if(g_hMyCertStoreEvent)
  73. {
  74. if(!CertControlStore(g_hMyCertStore,
  75. 0,
  76. CERT_STORE_CTRL_NOTIFY_CHANGE,
  77. &g_hMyCertStoreEvent))
  78. {
  79. DebugLog((DEB_ERROR, "Error 0x%x registering for machine MY store change notification\n", GetLastError()));
  80. }
  81. }
  82. }
  83. return( TRUE );
  84. }
  85. BOOL
  86. SslFreeCredentialManager(VOID)
  87. {
  88. if(g_GPEvent)
  89. {
  90. if(!UnregisterGPNotification(g_GPEvent))
  91. {
  92. DebugLog((DEB_ERROR, "Error 0x%x unregistering for machine GP notification\n", GetLastError()));
  93. }
  94. CloseHandle(g_GPEvent);
  95. g_GPEvent = NULL;
  96. }
  97. if(g_hMyCertStore)
  98. {
  99. if(g_hMyCertStoreEvent)
  100. {
  101. CertControlStore(g_hMyCertStore,
  102. 0,
  103. CERT_STORE_CTRL_CANCEL_NOTIFY,
  104. &g_hMyCertStoreEvent);
  105. CloseHandle(g_hMyCertStoreEvent);
  106. }
  107. CertCloseStore(g_hMyCertStore, 0);
  108. }
  109. RtlDeleteCriticalSection( &g_SslCredLock );
  110. return TRUE;
  111. }
  112. BOOL
  113. SslCheckForGPEvent(void)
  114. {
  115. PLIST_ENTRY pList;
  116. PSPCredentialGroup pCredGroup;
  117. DWORD Status;
  118. if(g_GPEvent)
  119. {
  120. Status = WaitForSingleObjectEx(g_GPEvent, 0, FALSE);
  121. if(Status == WAIT_OBJECT_0)
  122. {
  123. DebugLog((DEB_WARN, "GP event detected, so download new trusted issuer list\n"));
  124. RtlEnterCriticalSection( &g_SslCredLock );
  125. pList = g_SslCredList.Flink ;
  126. while ( pList != &g_SslCredList )
  127. {
  128. pCredGroup = CONTAINING_RECORD( pList, SPCredentialGroup, GlobalCredList.Flink );
  129. pList = pList->Flink ;
  130. pCredGroup->dwFlags |= CRED_FLAG_UPDATE_ISSUER_LIST;
  131. }
  132. RtlLeaveCriticalSection( &g_SslCredLock );
  133. return TRUE;
  134. }
  135. }
  136. return FALSE;
  137. }
  138. SP_STATUS
  139. IsCredentialInGroup(
  140. PSPCredentialGroup pCredGroup,
  141. PCCERT_CONTEXT pCertContext,
  142. PBOOL pfInGroup)
  143. {
  144. PSPCredential pCred;
  145. BYTE rgbThumbprint[20];
  146. DWORD cbThumbprint;
  147. BYTE rgbHash[20];
  148. DWORD cbHash;
  149. PLIST_ENTRY pList;
  150. SP_STATUS pctRet = PCT_ERR_OK;
  151. *pfInGroup = FALSE;
  152. if(pCredGroup->CredCount == 0)
  153. {
  154. return PCT_ERR_OK;
  155. }
  156. // Get thumbprint of certificate.
  157. cbThumbprint = sizeof(rgbThumbprint);
  158. if(!CertGetCertificateContextProperty(pCertContext,
  159. CERT_MD5_HASH_PROP_ID,
  160. rgbThumbprint,
  161. &cbThumbprint))
  162. {
  163. pctRet = SP_LOG_RESULT(GetLastError());
  164. goto cleanup;
  165. }
  166. LockCredentialShared(pCredGroup);
  167. pList = pCredGroup->CredList.Flink ;
  168. while ( pList != &pCredGroup->CredList )
  169. {
  170. pCred = CONTAINING_RECORD( pList, SPCredential, ListEntry.Flink );
  171. pList = pList->Flink ;
  172. // Get thumbprint of certificate.
  173. cbHash = sizeof(rgbHash);
  174. if(!CertGetCertificateContextProperty(pCred->pCert,
  175. CERT_MD5_HASH_PROP_ID,
  176. rgbHash,
  177. &cbHash))
  178. {
  179. SP_LOG_RESULT(GetLastError());
  180. pctRet = PCT_INT_UNKNOWN_CREDENTIAL;
  181. goto cleanup;
  182. }
  183. if(memcmp(rgbThumbprint, rgbHash, cbThumbprint) == 0)
  184. {
  185. *pfInGroup = TRUE;
  186. break;
  187. }
  188. }
  189. cleanup:
  190. UnlockCredential(pCredGroup);
  191. return pctRet;
  192. }
  193. BOOL
  194. IsValidThumbprint(
  195. PCRED_THUMBPRINT Thumbprint)
  196. {
  197. if(Thumbprint->LowPart == 0 && Thumbprint->HighPart == 0)
  198. {
  199. return FALSE;
  200. }
  201. return TRUE;
  202. }
  203. BOOL
  204. IsSameThumbprint(
  205. PCRED_THUMBPRINT Thumbprint1,
  206. PCRED_THUMBPRINT Thumbprint2)
  207. {
  208. if(Thumbprint1->LowPart == Thumbprint2->LowPart &&
  209. Thumbprint1->HighPart == Thumbprint2->HighPart)
  210. {
  211. return TRUE;
  212. }
  213. return FALSE;
  214. }
  215. void
  216. GenerateCertThumbprint(
  217. PCCERT_CONTEXT pCertContext,
  218. PCRED_THUMBPRINT Thumbprint)
  219. {
  220. MD5_CTX Md5Hash;
  221. MD5Init(&Md5Hash);
  222. MD5Update(&Md5Hash,
  223. pCertContext->pbCertEncoded,
  224. pCertContext->cbCertEncoded);
  225. MD5Final(&Md5Hash);
  226. CopyMemory((PBYTE)Thumbprint,
  227. Md5Hash.digest,
  228. sizeof(CRED_THUMBPRINT));
  229. }
  230. NTSTATUS
  231. GenerateRandomThumbprint(
  232. PCRED_THUMBPRINT Thumbprint)
  233. {
  234. return GenerateRandomBits((PBYTE)Thumbprint, sizeof(CRED_THUMBPRINT));
  235. }
  236. BOOL
  237. DoesCredThumbprintMatch(
  238. PSPCredentialGroup pCredGroup,
  239. PCRED_THUMBPRINT pThumbprint)
  240. {
  241. PSPCredential pCurrentCred;
  242. BOOL fFound = FALSE;
  243. PLIST_ENTRY pList;
  244. if(pCredGroup->CredCount == 0)
  245. {
  246. return FALSE;
  247. }
  248. LockCredentialShared(pCredGroup);
  249. pList = pCredGroup->CredList.Flink ;
  250. while ( pList != &pCredGroup->CredList )
  251. {
  252. pCurrentCred = CONTAINING_RECORD( pList, SPCredential, ListEntry.Flink );
  253. pList = pList->Flink ;
  254. if(IsSameThumbprint(pThumbprint, &pCurrentCred->CertThumbprint))
  255. {
  256. fFound = TRUE;
  257. break;
  258. }
  259. }
  260. UnlockCredential(pCredGroup);
  261. return fFound;
  262. }
  263. void
  264. ComputeCredExpiry(
  265. PSPCredentialGroup pCredGroup,
  266. PTimeStamp ptsExpiry)
  267. {
  268. PSPCredential pCurrentCred;
  269. PLIST_ENTRY pList;
  270. if(ptsExpiry == NULL)
  271. return;
  272. // Default to maximum timeout.
  273. ptsExpiry->QuadPart = MAXTIMEQUADPART;
  274. if(pCredGroup->CredCount == 0)
  275. return;
  276. LockCredentialShared(pCredGroup);
  277. pList = pCredGroup->CredList.Flink ;
  278. while ( pList != &pCredGroup->CredList )
  279. {
  280. pCurrentCred = CONTAINING_RECORD( pList, SPCredential, ListEntry.Flink );
  281. pList = pList->Flink ;
  282. if(pCurrentCred->pCert)
  283. {
  284. ptsExpiry->QuadPart = *((LONGLONG *)&pCurrentCred->pCert->pCertInfo->NotAfter);
  285. break;
  286. }
  287. }
  288. UnlockCredential(pCredGroup);
  289. }
  290. SP_STATUS
  291. SPCreateCred(
  292. DWORD dwProtocol,
  293. PLSA_SCHANNEL_SUB_CRED pSubCred,
  294. PSPCredential pCurrentCred,
  295. BOOL * pfEventLogged)
  296. {
  297. SP_STATUS pctRet;
  298. BOOL fRenewed;
  299. PCCERT_CONTEXT pNewCertificate = NULL;
  300. //
  301. // Check to see if the certificate has been renewed.
  302. //
  303. fRenewed = CheckForCertificateRenewal(dwProtocol,
  304. pSubCred->pCert,
  305. &pNewCertificate);
  306. if(fRenewed)
  307. {
  308. pCurrentCred->pCert = pNewCertificate;
  309. pSubCred->hRemoteProv = 0;
  310. }
  311. else
  312. {
  313. pCurrentCred->pCert = CertDuplicateCertificateContext(pSubCred->pCert);
  314. if(pCurrentCred->pCert == NULL)
  315. {
  316. pctRet = SP_LOG_RESULT(SEC_E_CERT_UNKNOWN);
  317. goto error;
  318. }
  319. }
  320. //
  321. // Obtain the public and private keys for the credential.
  322. //
  323. pctRet = SPPublicKeyFromCert(pCurrentCred->pCert,
  324. &pCurrentCred->pPublicKey,
  325. &pCurrentCred->dwExchSpec);
  326. if(pctRet != PCT_ERR_OK)
  327. {
  328. goto error;
  329. }
  330. pctRet = GetPrivateFromCert(pCurrentCred, dwProtocol, pSubCred);
  331. if(pctRet != PCT_ERR_OK)
  332. {
  333. *pfEventLogged = TRUE;
  334. goto error;
  335. }
  336. pCurrentCred->dwCertFlags = CF_EXPORT;
  337. pCurrentCred->dwCertFlags |= CF_DOMESTIC;
  338. // Generate the credential thumbprint. This is computed by
  339. // taking the hash of the certificate.
  340. GenerateCertThumbprint(pCurrentCred->pCert,
  341. &pCurrentCred->CertThumbprint);
  342. DebugLog((DEB_TRACE, "Credential thumbprint: %x %x\n",
  343. pCurrentCred->CertThumbprint.LowPart,
  344. pCurrentCred->CertThumbprint.HighPart));
  345. // Read list of supported algorithms.
  346. if((dwProtocol & SP_PROT_SERVERS) && pCurrentCred->hProv)
  347. {
  348. GetSupportedCapiAlgs(pCurrentCred->hProv,
  349. &pCurrentCred->pCapiAlgs,
  350. &pCurrentCred->cCapiAlgs);
  351. }
  352. // Build SSL3 serialized certificate chain. This is an optimization
  353. // so that we won't have to build it for each connection.
  354. pctRet = SPSerializeCertificate(
  355. SP_PROT_SSL3,
  356. TRUE,
  357. &pCurrentCred->pbSsl3SerializedChain,
  358. &pCurrentCred->cbSsl3SerializedChain,
  359. pCurrentCred->pCert,
  360. CERT_CHAIN_CACHE_ONLY_URL_RETRIEVAL);
  361. if(pctRet != PCT_ERR_OK)
  362. {
  363. goto error;
  364. }
  365. error:
  366. return pctRet;
  367. }
  368. SP_STATUS
  369. SPCreateCredential(
  370. PSPCredentialGroup *ppCred,
  371. DWORD grbitProtocol,
  372. PLSA_SCHANNEL_CRED pSchannelCred)
  373. {
  374. PSPCredentialGroup pCred = NULL;
  375. PSPCredential pCurrentCred = NULL;
  376. SECPKG_CALL_INFO CallInfo;
  377. SP_STATUS pctRet = PCT_ERR_OK;
  378. DWORD i;
  379. BOOL fImpersonating = FALSE;
  380. BOOL fEventLogged = FALSE;
  381. SP_BEGIN("SPCreateCredential");
  382. DebugLog((DEB_TRACE, " dwVersion: %d\n", pSchannelCred->dwVersion));
  383. DebugLog((DEB_TRACE, " cCreds: %d\n", pSchannelCred->cSubCreds));
  384. DebugLog((DEB_TRACE, " paCred: 0x%p\n", pSchannelCred->paSubCred));
  385. DebugLog((DEB_TRACE, " hRootStore: 0x%p\n", pSchannelCred->hRootStore));
  386. DebugLog((DEB_TRACE, " cMappers: %d\n", pSchannelCred->cMappers));
  387. DebugLog((DEB_TRACE, " aphMappers: 0x%p\n", pSchannelCred->aphMappers));
  388. DebugLog((DEB_TRACE, " cSupportedAlgs: %d\n", pSchannelCred->cSupportedAlgs));
  389. DebugLog((DEB_TRACE, " palgSupportedAlgs: 0x%p\n", pSchannelCred->palgSupportedAlgs));
  390. DebugLog((DEB_TRACE, " grbitEnabledProtocols: 0x%x\n", pSchannelCred->grbitEnabledProtocols));
  391. DebugLog((DEB_TRACE, " dwMinimumCipherStrength:%d\n", pSchannelCred->dwMinimumCipherStrength));
  392. DebugLog((DEB_TRACE, " dwMaximumCipherStrength:%d\n", pSchannelCred->dwMaximumCipherStrength));
  393. DebugLog((DEB_TRACE, " dwSessionLifespan: %d\n", pSchannelCred->dwSessionLifespan));
  394. DebugLog((DEB_TRACE, " dwFlags: 0x%x\n", pSchannelCred->dwFlags));
  395. DebugLog((DEB_TRACE, " reserved: 0x%x\n", pSchannelCred->reserved));
  396. LogCreateCredEvent(grbitProtocol, pSchannelCred);
  397. //
  398. // Allocate the internal credential structure and perform
  399. // basic initialization.
  400. //
  401. pCred = SPExternalAlloc(sizeof(SPCredentialGroup));
  402. if(pCred == NULL)
  403. {
  404. SP_RETURN(SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY));
  405. }
  406. DebugLog((DEB_TRACE, "New cred:%p, Protocol:%x\n", pCred, grbitProtocol));
  407. pCred->Magic = PCT_CRED_MAGIC;
  408. pCred->grbitProtocol = grbitProtocol;
  409. pCred->RefCount = 0;
  410. pCred->cMappers = 0;
  411. pCred->pahMappers = NULL;
  412. pCred->dwFlags = 0;
  413. // Initialize this early so that if a failure occurs, the cleanup
  414. // code won't try to release a non-initialized resource.
  415. __try {
  416. RtlInitializeResource(&pCred->csCredListLock);
  417. } __except(EXCEPTION_EXECUTE_HANDLER)
  418. {
  419. pctRet = STATUS_INSUFFICIENT_RESOURCES;
  420. SPExternalFree(pCred);
  421. pCred = NULL;
  422. goto error;
  423. }
  424. pctRet = GenerateRandomThumbprint(&pCred->CredThumbprint);
  425. if(!NT_SUCCESS(pctRet))
  426. {
  427. goto error;
  428. }
  429. if((grbitProtocol & SP_PROT_SERVERS) && (pSchannelCred->cSubCreds == 0))
  430. {
  431. pctRet = SP_LOG_RESULT(SEC_E_NO_CREDENTIALS);
  432. goto error;
  433. }
  434. if(LsaTable->GetCallInfo(&CallInfo))
  435. {
  436. pCred->ProcessId = CallInfo.ProcessId;
  437. }
  438. //
  439. // Walk through and initialize all certs and keys.
  440. //
  441. InitializeListHead( &pCred->CredList );
  442. pCred->CredCount = 0;
  443. if(pSchannelCred->cSubCreds)
  444. {
  445. for(i = 0; i < pSchannelCred->cSubCreds; i++)
  446. {
  447. pCurrentCred = SPExternalAlloc(sizeof(SPCredential));
  448. if(pCurrentCred == NULL)
  449. {
  450. pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
  451. goto error;
  452. }
  453. InsertTailList( &pCred->CredList, &pCurrentCred->ListEntry );
  454. pCred->CredCount++;
  455. pctRet = SPCreateCred(grbitProtocol,
  456. pSchannelCred->paSubCred + i,
  457. pCurrentCred,
  458. &fEventLogged);
  459. if(pctRet != PCT_ERR_OK)
  460. {
  461. goto error;
  462. }
  463. }
  464. }
  465. //
  466. // Determine which protocols are to be supported.
  467. //
  468. if(pSchannelCred->grbitEnabledProtocols == 0)
  469. {
  470. pCred->grbitEnabledProtocols = g_ProtEnabled;
  471. if(g_PctClientDisabledByDefault)
  472. {
  473. pCred->grbitEnabledProtocols &= ~SP_PROT_PCT1_CLIENT;
  474. }
  475. if(g_Ssl2ClientDisabledByDefault)
  476. {
  477. pCred->grbitEnabledProtocols &= ~SP_PROT_SSL2_CLIENT;
  478. }
  479. }
  480. else
  481. {
  482. pCred->grbitEnabledProtocols = pSchannelCred->grbitEnabledProtocols & g_ProtEnabled;
  483. }
  484. // Force credential to client-only or server only.
  485. if(grbitProtocol & SP_PROT_SERVERS)
  486. {
  487. pCred->grbitEnabledProtocols &= SP_PROT_SERVERS;
  488. }
  489. else
  490. {
  491. pCred->grbitEnabledProtocols &= SP_PROT_CLIENTS;
  492. }
  493. //
  494. // Propagate flags from SCHANNEL_CRED structure.
  495. //
  496. if(pSchannelCred->dwFlags & SCH_CRED_NO_SYSTEM_MAPPER)
  497. {
  498. pCred->dwFlags |= CRED_FLAG_NO_SYSTEM_MAPPER;
  499. }
  500. if(pSchannelCred->dwFlags & SCH_CRED_NO_SERVERNAME_CHECK)
  501. {
  502. pCred->dwFlags |= CRED_FLAG_NO_SERVERNAME_CHECK;
  503. }
  504. if(pSchannelCred->dwFlags & SCH_CRED_MANUAL_CRED_VALIDATION)
  505. {
  506. pCred->dwFlags |= CRED_FLAG_MANUAL_CRED_VALIDATION;
  507. }
  508. if(pSchannelCred->dwFlags & SCH_CRED_NO_DEFAULT_CREDS)
  509. {
  510. pCred->dwFlags |= CRED_FLAG_NO_DEFAULT_CREDS;
  511. }
  512. if(pSchannelCred->dwFlags & SCH_CRED_AUTO_CRED_VALIDATION)
  513. {
  514. // Automatically validate server credentials.
  515. pCred->dwFlags &= ~CRED_FLAG_MANUAL_CRED_VALIDATION;
  516. }
  517. if(pSchannelCred->dwFlags & SCH_CRED_USE_DEFAULT_CREDS)
  518. {
  519. // Use default client credentials.
  520. pCred->dwFlags &= ~CRED_FLAG_NO_DEFAULT_CREDS;
  521. }
  522. if(pSchannelCred->dwFlags & SCH_CRED_DISABLE_RECONNECTS)
  523. {
  524. // Disable reconnects.
  525. pCred->dwFlags |= CRED_FLAG_DISABLE_RECONNECTS;
  526. }
  527. // set revocation flags
  528. if(pSchannelCred->dwFlags & SCH_CRED_REVOCATION_CHECK_END_CERT)
  529. pCred->dwFlags |= CRED_FLAG_REVCHECK_END_CERT;
  530. if(pSchannelCred->dwFlags & SCH_CRED_REVOCATION_CHECK_CHAIN)
  531. pCred->dwFlags |= CRED_FLAG_REVCHECK_CHAIN;
  532. if(pSchannelCred->dwFlags & SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT)
  533. pCred->dwFlags |= CRED_FLAG_REVCHECK_CHAIN_EXCLUDE_ROOT;
  534. if(pSchannelCred->dwFlags & SCH_CRED_IGNORE_NO_REVOCATION_CHECK)
  535. pCred->dwFlags |= CRED_FLAG_IGNORE_NO_REVOCATION_CHECK;
  536. if(pSchannelCred->dwFlags & SCH_CRED_IGNORE_REVOCATION_OFFLINE)
  537. pCred->dwFlags |= CRED_FLAG_IGNORE_REVOCATION_OFFLINE;
  538. // set up the min and max strength
  539. GetBaseCipherSizes(&pCred->dwMinStrength, &pCred->dwMaxStrength);
  540. if(pSchannelCred->dwMinimumCipherStrength == 0)
  541. {
  542. pCred->dwMinStrength = max(40, pCred->dwMinStrength);
  543. }
  544. else if(pSchannelCred->dwMinimumCipherStrength == (DWORD)(-1))
  545. {
  546. // Turn on NULL cipher.
  547. pCred->dwMinStrength = 0;
  548. }
  549. else
  550. {
  551. pCred->dwMinStrength = pSchannelCred->dwMinimumCipherStrength;
  552. }
  553. if(pSchannelCred->dwMaximumCipherStrength == (DWORD)(-1))
  554. {
  555. // NULL cipher only.
  556. pCred->dwMaxStrength = 0;
  557. }
  558. else if(pSchannelCred->dwMaximumCipherStrength != 0)
  559. {
  560. pCred->dwMaxStrength = pSchannelCred->dwMaximumCipherStrength;
  561. }
  562. // set up the allowed ciphers
  563. BuildAlgList(pCred, pSchannelCred->palgSupportedAlgs, pSchannelCred->cSupportedAlgs);
  564. //
  565. // Set up the system certificate mapper.
  566. //
  567. pCred->cMappers = 1;
  568. pCred->pahMappers = SPExternalAlloc(sizeof(HMAPPER *));
  569. if(pCred->pahMappers == NULL)
  570. {
  571. pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
  572. goto error;
  573. }
  574. pCred->pahMappers[0] = SslGetMapper(TRUE);
  575. if(pCred->dwFlags & CRED_FLAG_REVCHECK_END_CERT)
  576. pCred->pahMappers[0]->m_dwFlags |= SCH_FLAG_REVCHECK_END_CERT;
  577. if(pCred->dwFlags & CRED_FLAG_REVCHECK_CHAIN)
  578. pCred->pahMappers[0]->m_dwFlags |= SCH_FLAG_REVCHECK_CHAIN;
  579. if(pCred->dwFlags & CRED_FLAG_REVCHECK_CHAIN_EXCLUDE_ROOT)
  580. pCred->pahMappers[0]->m_dwFlags |= SCH_FLAG_REVCHECK_CHAIN_EXCLUDE_ROOT;
  581. if(pCred->dwFlags & CRED_FLAG_IGNORE_NO_REVOCATION_CHECK)
  582. pCred->pahMappers[0]->m_dwFlags |= SCH_FLAG_IGNORE_NO_REVOCATION_CHECK;
  583. if(pCred->dwFlags & CRED_FLAG_IGNORE_REVOCATION_OFFLINE)
  584. pCred->pahMappers[0]->m_dwFlags |= SCH_FLAG_IGNORE_REVOCATION_OFFLINE;
  585. SslReferenceMapper(pCred->pahMappers[0]);
  586. // set up timeouts.
  587. if(pSchannelCred->dwSessionLifespan == 0)
  588. {
  589. if(grbitProtocol & SP_PROT_CLIENTS)
  590. {
  591. pCred->dwSessionLifespan = SchannelCache.dwClientLifespan;
  592. }
  593. else
  594. {
  595. pCred->dwSessionLifespan = SchannelCache.dwServerLifespan;
  596. }
  597. }
  598. else if(pSchannelCred->dwSessionLifespan == (DWORD)(-1))
  599. {
  600. pCred->dwSessionLifespan = 0;
  601. }
  602. else
  603. {
  604. pCred->dwSessionLifespan = pSchannelCred->dwSessionLifespan;
  605. }
  606. //
  607. // Add credential to global list of credentials.
  608. //
  609. RtlEnterCriticalSection( &g_SslCredLock );
  610. InsertTailList( &g_SslCredList, &pCred->GlobalCredList );
  611. RtlLeaveCriticalSection( &g_SslCredLock );
  612. //
  613. // Get list of trusted issuers.
  614. //
  615. if(grbitProtocol & SP_PROT_SERVERS)
  616. {
  617. if(pSchannelCred->hRootStore)
  618. {
  619. pCred->hApplicationRoots = CertDuplicateStore(pSchannelCred->hRootStore);
  620. if(!pCred->hApplicationRoots)
  621. {
  622. DebugLog((DEB_ERROR, "Error 0x%x duplicating app root store\n", GetLastError()));
  623. }
  624. }
  625. fImpersonating = SslImpersonateClient();
  626. pCred->hUserRoots = CertOpenSystemStore(0, "ROOT");
  627. if(!pCred->hUserRoots)
  628. {
  629. DebugLog((DEB_ERROR, "Error 0x%x opening user root store\n", GetLastError()));
  630. }
  631. else
  632. {
  633. if(!CertControlStore(pCred->hUserRoots,
  634. 0,
  635. CERT_STORE_CTRL_NOTIFY_CHANGE,
  636. &g_GPEvent))
  637. {
  638. DebugLog((DEB_ERROR, "Error 0x%x registering user root change notification\n", GetLastError()));
  639. }
  640. }
  641. if(fImpersonating)
  642. {
  643. RevertToSelf();
  644. fImpersonating = FALSE;
  645. }
  646. }
  647. SPReferenceCredential(pCred);
  648. *ppCred = pCred;
  649. SP_RETURN(PCT_ERR_OK);
  650. error:
  651. if(fEventLogged == FALSE)
  652. {
  653. LogCreateCredFailedEvent(grbitProtocol);
  654. }
  655. // Error case, free the credential
  656. if(pCred)
  657. {
  658. SPDeleteCredential(pCred, TRUE);
  659. }
  660. SP_RETURN(pctRet);
  661. }
  662. BOOL
  663. SPDeleteCredential(
  664. PSPCredentialGroup pCred,
  665. BOOL fFreeRemoteHandle)
  666. {
  667. DWORD i;
  668. SP_BEGIN("SPDeleteCredential");
  669. if(pCred == NULL)
  670. {
  671. SP_RETURN(TRUE);
  672. }
  673. if(pCred->Magic != PCT_CRED_MAGIC)
  674. {
  675. DebugLog((SP_LOG_ERROR, "Attempting to delete invalid credential!\n"));
  676. SP_RETURN (FALSE);
  677. }
  678. LockCredentialExclusive(pCred);
  679. if(pCred->CredCount)
  680. {
  681. PLIST_ENTRY pList;
  682. PSPCredential pCurrentCred;
  683. pList = pCred->CredList.Flink ;
  684. while ( pList != &pCred->CredList )
  685. {
  686. pCurrentCred = CONTAINING_RECORD( pList, SPCredential, ListEntry.Flink );
  687. pList = pList->Flink ;
  688. SPDeleteCred(pCurrentCred, fFreeRemoteHandle);
  689. SPExternalFree(pCurrentCred);
  690. }
  691. pCred->CredCount = 0;
  692. pCred->CredList.Flink = NULL;
  693. pCred->CredList.Blink = NULL;
  694. }
  695. if(pCred->cMappers && pCred->pahMappers)
  696. {
  697. for(i=0; i < (DWORD)pCred->cMappers; i++)
  698. {
  699. SslDereferenceMapper(pCred->pahMappers[i]);
  700. }
  701. SPExternalFree(pCred->pahMappers);
  702. }
  703. if(pCred->palgSupportedAlgs)
  704. {
  705. SPExternalFree(pCred->palgSupportedAlgs);
  706. }
  707. pCred->Magic = PCT_INVALID_MAGIC;
  708. if(pCred->GlobalCredList.Flink)
  709. {
  710. RtlEnterCriticalSection( &g_SslCredLock );
  711. RemoveEntryList( &pCred->GlobalCredList );
  712. RtlLeaveCriticalSection( &g_SslCredLock );
  713. }
  714. if(pCred->pbTrustedIssuers)
  715. {
  716. // LocalFree is used for the issuer list because realloc
  717. // is used when building the list and the LSA doesn't
  718. // provide a realloc helper function.
  719. LocalFree(pCred->pbTrustedIssuers);
  720. }
  721. if(pCred->hApplicationRoots)
  722. {
  723. CertCloseStore(pCred->hApplicationRoots, 0);
  724. }
  725. if(pCred->hUserRoots)
  726. {
  727. BOOL fImpersonating = SslImpersonateClient();
  728. CertCloseStore(pCred->hUserRoots, 0);
  729. if(fImpersonating) RevertToSelf();
  730. }
  731. UnlockCredential(pCred);
  732. RtlDeleteResource(&pCred->csCredListLock);
  733. ZeroMemory(pCred, sizeof(SPCredentialGroup));
  734. SPExternalFree(pCred);
  735. SP_RETURN(TRUE);
  736. }
  737. void
  738. SPDeleteCred(
  739. PSPCredential pCred,
  740. BOOL fFreeRemoteHandle)
  741. {
  742. BOOL fImpersonating = FALSE;
  743. if(pCred == NULL)
  744. {
  745. return;
  746. }
  747. if(pCred->pPublicKey)
  748. {
  749. SPExternalFree(pCred->pPublicKey);
  750. pCred->pPublicKey = NULL;
  751. }
  752. if(pCred->pCert)
  753. {
  754. CertFreeCertificateContext(pCred->pCert);
  755. pCred->pCert = NULL;
  756. }
  757. if(pCred->hTek)
  758. {
  759. if(!CryptDestroyKey(pCred->hTek))
  760. {
  761. SP_LOG_RESULT(GetLastError());
  762. }
  763. pCred->hTek = 0;
  764. }
  765. if(pCred->hProv)
  766. {
  767. fImpersonating = SslImpersonateClient();
  768. if(!CryptReleaseContext(pCred->hProv, 0))
  769. {
  770. SP_LOG_RESULT(GetLastError());
  771. }
  772. pCred->hProv = 0;
  773. if(fImpersonating)
  774. {
  775. RevertToSelf();
  776. fImpersonating = FALSE;
  777. }
  778. }
  779. if(pCred->pCapiAlgs)
  780. {
  781. SPExternalFree(pCred->pCapiAlgs);
  782. pCred->pCapiAlgs = NULL;
  783. }
  784. if(fFreeRemoteHandle)
  785. {
  786. if(pCred->hRemoteProv && !pCred->fAppRemoteProv)
  787. {
  788. if(!RemoteCryptReleaseContext(pCred->hRemoteProv, 0))
  789. {
  790. SP_LOG_RESULT(GetLastError());
  791. }
  792. pCred->hRemoteProv = 0;
  793. }
  794. }
  795. if(pCred->hEphem512Prov)
  796. {
  797. if(!CryptReleaseContext(pCred->hEphem512Prov, 0))
  798. {
  799. SP_LOG_RESULT(GetLastError());
  800. }
  801. pCred->hEphem512Prov = 0;
  802. }
  803. if(pCred->pbSsl3SerializedChain)
  804. {
  805. SPExternalFree(pCred->pbSsl3SerializedChain);
  806. }
  807. }
  808. // Reference a credential.
  809. // Note: This should only be called by someone who already
  810. // has a reference to the credential, or by the CreateCredential
  811. // call.
  812. BOOL
  813. SPReferenceCredential(
  814. PSPCredentialGroup pCred)
  815. {
  816. BOOL fRet = FALSE;
  817. fRet = (InterlockedIncrement(&pCred->RefCount) > 0);
  818. DebugLog((SP_LOG_TRACE, "Reference Cred %lx: %d\n", pCred, pCred->RefCount));
  819. return fRet;
  820. }
  821. BOOL
  822. SPDereferenceCredential(
  823. PSPCredentialGroup pCred,
  824. BOOL fFreeRemoteHandle)
  825. {
  826. BOOL fRet = FALSE;
  827. if(pCred == NULL)
  828. {
  829. return FALSE;
  830. }
  831. if(pCred->Magic != PCT_CRED_MAGIC)
  832. {
  833. DebugLog((SP_LOG_ERROR, "Attempting to dereference invalid credential!\n"));
  834. return FALSE;
  835. }
  836. fRet = TRUE;
  837. DebugLog((SP_LOG_TRACE, "Dereference Cred %lx: %d\n", pCred, pCred->RefCount-1));
  838. if(0 == InterlockedDecrement(&pCred->RefCount))
  839. {
  840. fRet = SPDeleteCredential(pCred, fFreeRemoteHandle);
  841. }
  842. return fRet;
  843. }
  844. SECURITY_STATUS
  845. UpdateCredentialFormat(
  846. PSCH_CRED pSchCred, // in
  847. PLSA_SCHANNEL_CRED pSchannelCred) // out
  848. {
  849. DWORD dwType;
  850. SP_STATUS pctRet;
  851. DWORD i;
  852. PBYTE pbChain;
  853. DWORD cbChain;
  854. PSCH_CRED_PUBLIC_CERTCHAIN pCertChain;
  855. SP_BEGIN("UpdateCredentialFormat");
  856. //
  857. // Initialize the output structure to null credential.
  858. //
  859. if(pSchannelCred == NULL)
  860. {
  861. SP_RETURN(SP_LOG_RESULT(SEC_E_INTERNAL_ERROR));
  862. }
  863. memset(pSchannelCred, 0, sizeof(LSA_SCHANNEL_CRED));
  864. pSchannelCred->dwVersion = SCHANNEL_CRED_VERSION;
  865. //
  866. // If input buffer is empty then we're done.
  867. //
  868. if(pSchCred == NULL)
  869. {
  870. SP_RETURN(SEC_E_OK);
  871. }
  872. //
  873. // Convert the certificates and private keys.
  874. //
  875. if(pSchCred->cCreds == 0)
  876. {
  877. SP_RETURN(SEC_E_OK);
  878. }
  879. pSchannelCred->cSubCreds = pSchCred->cCreds;
  880. pSchannelCred->paSubCred = SPExternalAlloc(sizeof(LSA_SCHANNEL_SUB_CRED) * pSchannelCred->cSubCreds);
  881. if(pSchannelCred->paSubCred == NULL)
  882. {
  883. pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
  884. goto error;
  885. }
  886. // Loop through each of the creds, and convert them into something we know
  887. for(i = 0; i < pSchannelCred->cSubCreds; i++)
  888. {
  889. PLSA_SCHANNEL_SUB_CRED pSubCred = pSchannelCred->paSubCred + i;
  890. //
  891. // Decode the certificate.
  892. //
  893. dwType = *(PDWORD)pSchCred->paPublic[i];
  894. if(dwType != SCH_CRED_X509_CERTCHAIN)
  895. {
  896. pctRet = SP_LOG_RESULT(SEC_E_UNKNOWN_CREDENTIALS);
  897. goto error;
  898. }
  899. pCertChain = (PSCH_CRED_PUBLIC_CERTCHAIN)pSchCred->paPublic[i];
  900. pbChain = pCertChain->pCertChain;
  901. cbChain = pCertChain->cbCertChain;
  902. // Decode the credential
  903. pctRet = SPLoadCertificate(0,
  904. X509_ASN_ENCODING,
  905. pbChain,
  906. cbChain,
  907. &pSubCred->pCert);
  908. if(pctRet != PCT_ERR_OK)
  909. {
  910. pctRet = SP_LOG_RESULT(SEC_E_UNKNOWN_CREDENTIALS);
  911. goto error;
  912. }
  913. //
  914. // Now deal with the private key.
  915. //
  916. dwType = *(DWORD *)pSchCred->paSecret[i];
  917. if(dwType == SCHANNEL_SECRET_PRIVKEY)
  918. {
  919. PSCH_CRED_SECRET_PRIVKEY pPrivKey;
  920. DWORD Size;
  921. pPrivKey = (PSCH_CRED_SECRET_PRIVKEY)pSchCred->paSecret[i];
  922. pSubCred->pPrivateKey = SPExternalAlloc(pPrivKey->cbPrivateKey);
  923. if(pSubCred->pPrivateKey == NULL)
  924. {
  925. pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
  926. goto error;
  927. }
  928. memcpy(pSubCred->pPrivateKey, pPrivKey->pPrivateKey, pPrivKey->cbPrivateKey);
  929. pSubCred->cbPrivateKey = pPrivKey->cbPrivateKey;
  930. Size = lstrlen(pPrivKey->pszPassword) + sizeof(CHAR);
  931. pSubCred->pszPassword = SPExternalAlloc(Size);
  932. if(pSubCred->pszPassword == NULL)
  933. {
  934. pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
  935. goto error;
  936. }
  937. memcpy(pSubCred->pszPassword, pPrivKey->pszPassword, Size);
  938. }
  939. else if(dwType == SCHANNEL_SECRET_TYPE_CAPI)
  940. {
  941. PSCH_CRED_SECRET_CAPI pCapiKey;
  942. pCapiKey = (PSCH_CRED_SECRET_CAPI)pSchCred->paSecret[i];
  943. pSubCred->hRemoteProv = pCapiKey->hProv;
  944. }
  945. else
  946. {
  947. pctRet = SP_LOG_RESULT(SEC_E_UNKNOWN_CREDENTIALS);
  948. goto error;
  949. }
  950. }
  951. SP_RETURN(SEC_E_OK);
  952. error:
  953. if(pSchannelCred->paSubCred)
  954. {
  955. SPExternalFree((PVOID)pSchannelCred->paSubCred);
  956. pSchannelCred->paSubCred = NULL;
  957. }
  958. SP_RETURN(pctRet);
  959. }
  960. SP_STATUS
  961. GetIisPrivateFromCert(
  962. PSPCredential pCred,
  963. PLSA_SCHANNEL_SUB_CRED pSubCred)
  964. {
  965. PBYTE pbPrivate = NULL;
  966. DWORD cbPrivate;
  967. PBYTE pbPassword = NULL;
  968. DWORD cbPassword;
  969. PPRIVATE_KEY_FILE_ENCODE pPrivateFile = NULL;
  970. DWORD cbPrivateFile;
  971. BLOBHEADER *pPrivateBlob = NULL;
  972. DWORD cbPrivateBlob;
  973. HCRYPTKEY hPrivateKey;
  974. HCRYPTPROV hProv = 0;
  975. SP_STATUS pctRet;
  976. MD5_CTX md5Ctx;
  977. struct RC4_KEYSTRUCT rc4Key;
  978. if(pSubCred->cbPrivateKey == 0 ||
  979. pSubCred->pPrivateKey == NULL ||
  980. pSubCred->pszPassword == NULL)
  981. {
  982. return SP_LOG_RESULT(SEC_E_NO_CREDENTIALS);
  983. }
  984. pbPrivate = pSubCred->pPrivateKey;
  985. cbPrivate = pSubCred->cbPrivateKey;
  986. pbPassword = (PBYTE)pSubCred->pszPassword;
  987. cbPassword = lstrlen(pSubCred->pszPassword);
  988. // We have to do a little fixup here. Old versions of
  989. // schannel wrote the wrong header data into the ASN
  990. // for private key files, so we must fix the size data.
  991. pbPrivate[2] = MSBOF(cbPrivate - 4);
  992. pbPrivate[3] = LSBOF(cbPrivate - 4);
  993. // ASN.1 decode the private key.
  994. if(!CryptDecodeObject(X509_ASN_ENCODING,
  995. szPrivateKeyFileEncode,
  996. pbPrivate,
  997. cbPrivate,
  998. 0,
  999. NULL,
  1000. &cbPrivateFile))
  1001. {
  1002. DebugLog((SP_LOG_ERROR, "Error 0x%x decoding the private key\n",
  1003. GetLastError()));
  1004. pctRet = SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
  1005. goto error;
  1006. }
  1007. pPrivateFile = SPExternalAlloc(cbPrivateFile);
  1008. if(pPrivateFile == NULL)
  1009. {
  1010. pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
  1011. goto error;
  1012. }
  1013. if(!CryptDecodeObject(X509_ASN_ENCODING,
  1014. szPrivateKeyFileEncode,
  1015. pbPrivate,
  1016. cbPrivate,
  1017. 0,
  1018. pPrivateFile,
  1019. &cbPrivateFile))
  1020. {
  1021. DebugLog((SP_LOG_ERROR, "Error 0x%x decoding the private key\n",
  1022. GetLastError()));
  1023. pctRet = SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
  1024. goto error;
  1025. }
  1026. // Decrypt the decoded private key using the password.
  1027. MD5Init(&md5Ctx);
  1028. MD5Update(&md5Ctx, pbPassword, cbPassword);
  1029. MD5Final(&md5Ctx);
  1030. rc4_key(&rc4Key, 16, md5Ctx.digest);
  1031. rc4(&rc4Key,
  1032. pPrivateFile->EncryptedBlob.cbData,
  1033. pPrivateFile->EncryptedBlob.pbData);
  1034. // Build a PRIVATEKEYBLOB from the decrypted private key.
  1035. if(!CryptDecodeObject(X509_ASN_ENCODING,
  1036. szPrivateKeyInfoEncode,
  1037. pPrivateFile->EncryptedBlob.pbData,
  1038. pPrivateFile->EncryptedBlob.cbData,
  1039. 0,
  1040. NULL,
  1041. &cbPrivateBlob))
  1042. {
  1043. // Maybe this was a SGC style key.
  1044. // Re-encrypt it, and build the SGC decrypting
  1045. // key, and re-decrypt it.
  1046. BYTE md5Digest[MD5DIGESTLEN];
  1047. rc4_key(&rc4Key, 16, md5Ctx.digest);
  1048. rc4(&rc4Key,
  1049. pPrivateFile->EncryptedBlob.cbData,
  1050. pPrivateFile->EncryptedBlob.pbData);
  1051. CopyMemory(md5Digest, md5Ctx.digest, MD5DIGESTLEN);
  1052. MD5Init(&md5Ctx);
  1053. MD5Update(&md5Ctx, md5Digest, MD5DIGESTLEN);
  1054. MD5Update(&md5Ctx, (PBYTE)SGC_KEY_SALT, lstrlen(SGC_KEY_SALT));
  1055. MD5Final(&md5Ctx);
  1056. rc4_key(&rc4Key, 16, md5Ctx.digest);
  1057. rc4(&rc4Key,
  1058. pPrivateFile->EncryptedBlob.cbData,
  1059. pPrivateFile->EncryptedBlob.pbData);
  1060. // Try again...
  1061. if(!CryptDecodeObject(X509_ASN_ENCODING,
  1062. szPrivateKeyInfoEncode,
  1063. pPrivateFile->EncryptedBlob.pbData,
  1064. pPrivateFile->EncryptedBlob.cbData,
  1065. 0,
  1066. NULL,
  1067. &cbPrivateBlob))
  1068. {
  1069. DebugLog((SP_LOG_ERROR, "Error 0x%x building PRIVATEKEYBLOB\n",
  1070. GetLastError()));
  1071. ZeroMemory(&md5Ctx, sizeof(md5Ctx));
  1072. pctRet = SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
  1073. goto error;
  1074. }
  1075. }
  1076. ZeroMemory(&md5Ctx, sizeof(md5Ctx));
  1077. pPrivateBlob = SPExternalAlloc(cbPrivateBlob);
  1078. if(pPrivateBlob == NULL)
  1079. {
  1080. pctRet = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
  1081. goto error;
  1082. }
  1083. if(!CryptDecodeObject(X509_ASN_ENCODING,
  1084. szPrivateKeyInfoEncode,
  1085. pPrivateFile->EncryptedBlob.pbData,
  1086. pPrivateFile->EncryptedBlob.cbData,
  1087. 0,
  1088. pPrivateBlob,
  1089. &cbPrivateBlob))
  1090. {
  1091. DebugLog((SP_LOG_ERROR, "Error 0x%x building PRIVATEKEYBLOB\n",
  1092. GetLastError()));
  1093. pctRet = SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
  1094. goto error;
  1095. }
  1096. // HACKHACK - Make sure that the key contained within the private
  1097. // key blob is marked for "key exchange".
  1098. pPrivateBlob->aiKeyAlg = CALG_RSA_KEYX;
  1099. // Create an in-memory key container.
  1100. if(!CryptAcquireContext(&hProv,
  1101. NULL,
  1102. NULL,
  1103. PROV_RSA_SCHANNEL,
  1104. CRYPT_VERIFYCONTEXT))
  1105. {
  1106. DebugLog((SP_LOG_ERROR, "Couldn't Acquire RSA Provider %lx\n", GetLastError()));
  1107. pctRet = SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
  1108. goto error;
  1109. }
  1110. // Import the private key blob into the key container.
  1111. if(!CryptImportKey(hProv,
  1112. (PBYTE)pPrivateBlob,
  1113. cbPrivateBlob,
  1114. 0, 0,
  1115. &hPrivateKey))
  1116. {
  1117. DebugLog((SP_LOG_ERROR, "Error 0x%x importing PRIVATEKEYBLOB\n",
  1118. GetLastError()));
  1119. pctRet = SP_LOG_RESULT(PCT_INT_INTERNAL_ERROR);
  1120. goto error;
  1121. }
  1122. CryptDestroyKey(hPrivateKey);
  1123. // Obtain a matching CSP handle in the application process.
  1124. pctRet = RemoteCryptAcquireContextW(
  1125. &pCred->hRemoteProv,
  1126. NULL,
  1127. NULL,
  1128. PROV_RSA_SCHANNEL,
  1129. CRYPT_VERIFYCONTEXT);
  1130. if(!NT_SUCCESS(pctRet))
  1131. {
  1132. pCred->hRemoteProv = 0;
  1133. SP_LOG_RESULT(pctRet);
  1134. goto error;
  1135. }
  1136. pCred->hProv = hProv;
  1137. pCred->dwKeySpec = AT_KEYEXCHANGE;
  1138. pctRet = PCT_ERR_OK;
  1139. error:
  1140. if(pPrivateFile) SPExternalFree(pPrivateFile);
  1141. if(pPrivateBlob) SPExternalFree(pPrivateBlob);
  1142. return pctRet;
  1143. }
  1144. SP_STATUS
  1145. LocalCryptAcquireContext(
  1146. HCRYPTPROV * phProv,
  1147. PCRYPT_KEY_PROV_INFO pProvInfo,
  1148. DWORD dwProtocol,
  1149. BOOL * pfEventLogged)
  1150. {
  1151. BOOL fImpersonating = FALSE;
  1152. BOOL fSuccess;
  1153. SP_STATUS Status;
  1154. HCRYPTPROV hProv;
  1155. // If the private key belongs to one of the Microsoft PROV_RSA_FULL
  1156. // CSPs, then manually divert it to the Microsoft PROV_RSA_SCHANNEL
  1157. // CSP. This works because both CSP types use the same private key
  1158. // storage scheme.
  1159. if(pProvInfo->dwProvType == PROV_RSA_FULL)
  1160. {
  1161. if(lstrcmpW(pProvInfo->pwszProvName, MS_DEF_PROV_W) == 0 ||
  1162. lstrcmpW(pProvInfo->pwszProvName, MS_STRONG_PROV_W) == 0 ||
  1163. lstrcmpW(pProvInfo->pwszProvName, MS_ENHANCED_PROV_W) == 0)
  1164. {
  1165. DebugLog((DEB_WARN, "Force CSP type to PROV_RSA_SCHANNEL.\n"));
  1166. pProvInfo->pwszProvName = MS_DEF_RSA_SCHANNEL_PROV_W;
  1167. pProvInfo->dwProvType = PROV_RSA_SCHANNEL;
  1168. }
  1169. }
  1170. if(pProvInfo->dwProvType != PROV_RSA_SCHANNEL &&
  1171. pProvInfo->dwProvType != PROV_DH_SCHANNEL)
  1172. {
  1173. DebugLog((SP_LOG_ERROR, "Bad server CSP type:%d\n", pProvInfo->dwProvType));
  1174. return SP_LOG_RESULT(PCT_ERR_UNKNOWN_CREDENTIAL);
  1175. }
  1176. fImpersonating = SslImpersonateClient();
  1177. fSuccess = CryptAcquireContextW(&hProv,
  1178. pProvInfo->pwszContainerName,
  1179. pProvInfo->pwszProvName,
  1180. pProvInfo->dwProvType,
  1181. pProvInfo->dwFlags | CRYPT_SILENT);
  1182. if(fImpersonating)
  1183. {
  1184. RevertToSelf();
  1185. fImpersonating = FALSE;
  1186. }
  1187. if(!fSuccess)
  1188. {
  1189. Status = GetLastError();
  1190. DebugLog((SP_LOG_ERROR, "Error 0x%x calling CryptAcquireContextW\n", Status));
  1191. LogCredAcquireContextFailedEvent(dwProtocol, Status);
  1192. *pfEventLogged = TRUE;
  1193. return SP_LOG_RESULT(PCT_ERR_UNKNOWN_CREDENTIAL);
  1194. }
  1195. DebugLog((SP_LOG_TRACE, "Local CSP handle acquired (0x%p)\n", hProv));
  1196. *phProv = hProv;
  1197. return PCT_ERR_OK;
  1198. }
  1199. //+---------------------------------------------------------------------------
  1200. //
  1201. // Function: GetPrivateFromCert
  1202. //
  1203. // Synopsis: Given a certificate context, somehow obtain a handle to the
  1204. // corresponding key container. Determine the key spec of the
  1205. // private key.
  1206. //
  1207. // Arguments: [pCred] -- Pointer to the credential.
  1208. //
  1209. // History: 09-24-96 jbanes Hacked for LSA integration.
  1210. //
  1211. // Notes: The private key often lives in a CSP. In this case, a handle
  1212. // to the CSP context is obtained by either reading the
  1213. // CERT_KEY_REMOTE_PROV_HANDLE_PROP_ID property, or by reading
  1214. // the CERT_KEY_PROV_INFO_PROP_ID property and then calling
  1215. // CryptAcquireContext.
  1216. //
  1217. // If this fails, then check and see if the private key is
  1218. // stored by IIS. If this is the case, then the encrypted
  1219. // private key is obtained by reading the
  1220. //
  1221. //----------------------------------------------------------------------------
  1222. SP_STATUS
  1223. GetPrivateFromCert(
  1224. PSPCredential pCred,
  1225. DWORD dwProtocol,
  1226. PLSA_SCHANNEL_SUB_CRED pSubCred)
  1227. {
  1228. PCRYPT_KEY_PROV_INFO pProvInfo = NULL;
  1229. HCRYPTPROV hProv;
  1230. DWORD cbSize;
  1231. BOOL fRemoteProvider = FALSE;
  1232. NTSTATUS Status;
  1233. BOOL fEventLogged = FALSE;
  1234. //
  1235. // Set the output fields to default values.
  1236. //
  1237. pCred->hProv = 0;
  1238. pCred->hRemoteProv = 0;
  1239. pCred->dwKeySpec = AT_KEYEXCHANGE;
  1240. if(dwProtocol & SP_PROT_CLIENTS)
  1241. {
  1242. // Access the CSP from the application process.
  1243. fRemoteProvider = TRUE;
  1244. }
  1245. //
  1246. // Check to see if the application called CryptAcquireContext. If so then
  1247. // we don't have to. This will typically not be the case.
  1248. //
  1249. if(fRemoteProvider && pSubCred->hRemoteProv)
  1250. {
  1251. DebugLog((SP_LOG_TRACE, "Application provided CSP handle (0x%p)\n", pSubCred->hRemoteProv));
  1252. pCred->hRemoteProv = pSubCred->hRemoteProv;
  1253. pCred->fAppRemoteProv = TRUE;
  1254. }
  1255. //
  1256. // Read the certificate context's "key info" property.
  1257. //
  1258. if(CertGetCertificateContextProperty(pCred->pCert,
  1259. CERT_KEY_PROV_INFO_PROP_ID,
  1260. NULL,
  1261. &cbSize))
  1262. {
  1263. SafeAllocaAllocate(pProvInfo, cbSize);
  1264. if(pProvInfo == NULL)
  1265. {
  1266. Status = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
  1267. goto cleanup;
  1268. }
  1269. if(!CertGetCertificateContextProperty(pCred->pCert,
  1270. CERT_KEY_PROV_INFO_PROP_ID,
  1271. pProvInfo,
  1272. &cbSize))
  1273. {
  1274. DebugLog((SP_LOG_ERROR, "Error 0x%x reading CERT_KEY_PROV_INFO_PROP_ID\n",GetLastError()));
  1275. SafeAllocaFree(pProvInfo);
  1276. pProvInfo = NULL;
  1277. }
  1278. else
  1279. {
  1280. // Success.
  1281. pCred->dwKeySpec = pProvInfo->dwKeySpec;
  1282. DebugLog((SP_LOG_TRACE, "Container:%ls\n", pProvInfo->pwszContainerName));
  1283. DebugLog((SP_LOG_TRACE, "Provider: %ls\n", pProvInfo->pwszProvName));
  1284. DebugLog((SP_LOG_TRACE, "Type: 0x%8.8x\n", pProvInfo->dwProvType));
  1285. DebugLog((SP_LOG_TRACE, "Flags: 0x%8.8x\n", pProvInfo->dwFlags));
  1286. DebugLog((SP_LOG_TRACE, "Key spec: %d\n", pProvInfo->dwKeySpec));
  1287. LogCredPropertiesEvent(dwProtocol, pProvInfo, pCred->pCert);
  1288. }
  1289. }
  1290. if(pCred->hRemoteProv)
  1291. {
  1292. // The application supplied an hProv for us to use.
  1293. Status = PCT_ERR_OK;
  1294. goto cleanup;
  1295. }
  1296. if(pProvInfo)
  1297. {
  1298. //
  1299. // We read the "key info" property successfully, so call
  1300. // CryptAcquireContext in order to get a handle to the appropriate
  1301. // key container.
  1302. //
  1303. if(!fRemoteProvider)
  1304. {
  1305. // Call CryptAcquireContext from the LSA process.
  1306. Status = LocalCryptAcquireContext(&hProv, pProvInfo, dwProtocol, &fEventLogged);
  1307. if(Status != PCT_ERR_OK)
  1308. {
  1309. goto cleanup;
  1310. }
  1311. pCred->hProv = hProv;
  1312. }
  1313. // Obtain a matching CSP handle in the application process.
  1314. Status = RemoteCryptAcquireContextW(
  1315. &pCred->hRemoteProv,
  1316. pProvInfo->pwszContainerName,
  1317. pProvInfo->pwszProvName,
  1318. pProvInfo->dwProvType,
  1319. pProvInfo->dwFlags);
  1320. if(!NT_SUCCESS(Status))
  1321. {
  1322. LogCredAcquireContextFailedEvent(dwProtocol, Status);
  1323. fEventLogged = TRUE;
  1324. Status = SP_LOG_RESULT(PCT_ERR_UNKNOWN_CREDENTIAL);
  1325. goto cleanup;
  1326. }
  1327. }
  1328. else
  1329. {
  1330. //
  1331. // We weren't able to read the "key info" property, so attempt to
  1332. // read the "iis private key" property, and build the private key
  1333. // up from that.
  1334. //
  1335. DebugLog((SP_LOG_TRACE, "Attempt IIS 4.0 compatibility hack.\n"));
  1336. Status = GetIisPrivateFromCert(pCred, pSubCred);
  1337. if(Status != PCT_ERR_OK)
  1338. {
  1339. SP_LOG_RESULT(Status);
  1340. goto cleanup;
  1341. }
  1342. }
  1343. Status = PCT_ERR_OK;
  1344. cleanup:
  1345. if(Status != PCT_ERR_OK && fEventLogged == FALSE)
  1346. {
  1347. if(pProvInfo == NULL)
  1348. {
  1349. LogNoPrivateKeyEvent(dwProtocol);
  1350. }
  1351. else
  1352. {
  1353. LogCreateCredFailedEvent(dwProtocol);
  1354. }
  1355. }
  1356. if(pProvInfo)
  1357. {
  1358. SafeAllocaFree(pProvInfo);
  1359. }
  1360. return Status;
  1361. }
  1362. void
  1363. GlobalCheckForCertificateRenewal(void)
  1364. {
  1365. PSPCredentialGroup pCredGroup;
  1366. PLIST_ENTRY pList;
  1367. DWORD Status;
  1368. static LONG ReentryCount = 0;
  1369. //
  1370. // This routine gets called every 5 minutes or so. Don't allow resync
  1371. // operations to get queued up in the rare case where a resync takes
  1372. // longer than that.
  1373. //
  1374. if(InterlockedIncrement(&ReentryCount) > 1)
  1375. {
  1376. goto cleanup;
  1377. }
  1378. //
  1379. // Has the MY certificate store been updated recently?
  1380. //
  1381. if(g_hMyCertStoreEvent == NULL)
  1382. {
  1383. goto cleanup;
  1384. }
  1385. Status = WaitForSingleObjectEx(g_hMyCertStoreEvent, 0, FALSE);
  1386. if(Status != WAIT_OBJECT_0)
  1387. {
  1388. goto cleanup;
  1389. }
  1390. DebugLog((DEB_WARN, "The MY store has been updated, so check for certificate renewal.\n"));
  1391. //
  1392. // Resync the MY certificate store, and reregister for event notification.
  1393. //
  1394. if(!CertControlStore(g_hMyCertStore,
  1395. 0, // dwFlags
  1396. CERT_STORE_CTRL_RESYNC,
  1397. &g_hMyCertStoreEvent))
  1398. {
  1399. DebugLog((DEB_ERROR, "Error 0x%x resyncing machine MY store.\n", GetLastError()));
  1400. goto cleanup;
  1401. }
  1402. //
  1403. // Enumerate through each credential, and see if any of the
  1404. // certificates in them have been renewed.
  1405. //
  1406. RtlEnterCriticalSection( &g_SslCredLock );
  1407. pList = g_SslCredList.Flink ;
  1408. while ( pList != &g_SslCredList )
  1409. {
  1410. pCredGroup = CONTAINING_RECORD( pList, SPCredentialGroup, GlobalCredList.Flink );
  1411. pList = pList->Flink ;
  1412. pCredGroup->dwFlags |= CRED_FLAG_CHECK_FOR_RENEWAL;
  1413. }
  1414. RtlLeaveCriticalSection( &g_SslCredLock );
  1415. cleanup:
  1416. InterlockedDecrement(&ReentryCount);
  1417. }
  1418. void
  1419. CheckForCredentialRenewal(
  1420. PSPCredentialGroup pCredGroup)
  1421. {
  1422. PLIST_ENTRY pList;
  1423. PSPCredential pCred;
  1424. PSPCredential pNewCred;
  1425. PCCERT_CONTEXT pNewCert = NULL;
  1426. LSA_SCHANNEL_SUB_CRED SubCred;
  1427. BOOL fEventLogged;
  1428. SP_STATUS pctRet;
  1429. //
  1430. // Only dynamically check for the renewal of server certificates.
  1431. // Reacquiring client certificates can involve UI and other
  1432. // messy stuff like that, so we'll punt on this for now.
  1433. //
  1434. if((pCredGroup->grbitProtocol & SP_PROT_SERVERS) == 0)
  1435. {
  1436. return;
  1437. }
  1438. LockCredentialExclusive(pCredGroup);
  1439. //
  1440. // Check to see if we've already checked out this credential.
  1441. // It's common to get this routine called simultaneously on
  1442. // several threads when the MY store is updated.
  1443. //
  1444. if((pCredGroup->dwFlags & CRED_FLAG_CHECK_FOR_RENEWAL) == 0)
  1445. {
  1446. // We've already checked out this credential.
  1447. UnlockCredential(pCredGroup);
  1448. return;
  1449. }
  1450. pCredGroup->dwFlags &= ~CRED_FLAG_CHECK_FOR_RENEWAL;
  1451. //
  1452. // Enumerate through each certificate in the credential.
  1453. //
  1454. pList = pCredGroup->CredList.Flink ;
  1455. while ( pList != &pCredGroup->CredList )
  1456. {
  1457. pCred = CONTAINING_RECORD( pList, SPCredential, ListEntry.Flink );
  1458. pList = pList->Flink ;
  1459. //
  1460. // Has this certificate already been replaced?
  1461. //
  1462. if(pCred->dwCertFlags & CF_RENEWED)
  1463. {
  1464. continue;
  1465. }
  1466. //
  1467. // Has this certificate been renewed?
  1468. //
  1469. if(!CheckForCertificateRenewal(pCredGroup->grbitProtocol,
  1470. pCred->pCert,
  1471. &pNewCert))
  1472. {
  1473. continue;
  1474. }
  1475. pCred->dwCertFlags |= CF_RENEWED;
  1476. //
  1477. // Attempt to build a credential around the new
  1478. // certificate.
  1479. //
  1480. pNewCred = SPExternalAlloc(sizeof(SPCredential));
  1481. if(pNewCred != NULL)
  1482. {
  1483. memset(&SubCred, 0, sizeof(SubCred));
  1484. SubCred.pCert = pNewCert;
  1485. pctRet = SPCreateCred(pCredGroup->grbitProtocol,
  1486. &SubCred,
  1487. pNewCred,
  1488. &fEventLogged);
  1489. CertFreeCertificateContext(pNewCert);
  1490. if(pctRet == PCT_ERR_OK)
  1491. {
  1492. // Insert the new certificate at the head of the list,
  1493. // so that it will be picked up in preference to the
  1494. // old one.
  1495. InsertHeadList( &pCredGroup->CredList, &pNewCred->ListEntry );
  1496. pCredGroup->CredCount++;
  1497. pNewCred = NULL;
  1498. }
  1499. if(pNewCred)
  1500. {
  1501. SPExternalFree(pNewCred);
  1502. }
  1503. }
  1504. }
  1505. UnlockCredential(pCredGroup);
  1506. }
  1507. BOOL
  1508. CheckForCertificateRenewal(
  1509. DWORD dwProtocol,
  1510. PCCERT_CONTEXT pCertContext,
  1511. PCCERT_CONTEXT *ppNewCertificate)
  1512. {
  1513. BYTE rgbThumbprint[CB_SHA_DIGEST_LEN];
  1514. DWORD cbThumbprint = sizeof(rgbThumbprint);
  1515. CRYPT_HASH_BLOB HashBlob;
  1516. PCCERT_CONTEXT pNewCert;
  1517. BOOL fMachineCert;
  1518. PCRYPT_KEY_PROV_INFO pProvInfo = NULL;
  1519. DWORD cbSize;
  1520. HCERTSTORE hMyCertStore = 0;
  1521. BOOL fImpersonating = FALSE;
  1522. BOOL fRenewed = FALSE;
  1523. if(dwProtocol & SP_PROT_SERVERS)
  1524. {
  1525. fMachineCert = TRUE;
  1526. }
  1527. else
  1528. {
  1529. fMachineCert = FALSE;
  1530. }
  1531. //
  1532. // Loop through the linked list of renewed certificates, looking
  1533. // for the last one.
  1534. //
  1535. while(TRUE)
  1536. {
  1537. //
  1538. // Check for renewal property.
  1539. //
  1540. if(!CertGetCertificateContextProperty(pCertContext,
  1541. CERT_RENEWAL_PROP_ID,
  1542. rgbThumbprint,
  1543. &cbThumbprint))
  1544. {
  1545. // Certificate has not been renewed.
  1546. break;
  1547. }
  1548. DebugLog((DEB_TRACE, "Certificate has renewal property\n"));
  1549. //
  1550. // Determine whether to look in the local machine MY store
  1551. // or the current user MY store.
  1552. //
  1553. if(!hMyCertStore)
  1554. {
  1555. if(CertGetCertificateContextProperty(pCertContext,
  1556. CERT_KEY_PROV_INFO_PROP_ID,
  1557. NULL,
  1558. &cbSize))
  1559. {
  1560. SafeAllocaAllocate(pProvInfo, cbSize);
  1561. if(pProvInfo == NULL)
  1562. {
  1563. break;
  1564. }
  1565. if(CertGetCertificateContextProperty(pCertContext,
  1566. CERT_KEY_PROV_INFO_PROP_ID,
  1567. pProvInfo,
  1568. &cbSize))
  1569. {
  1570. if(pProvInfo->dwFlags & CRYPT_MACHINE_KEYSET)
  1571. {
  1572. fMachineCert = TRUE;
  1573. }
  1574. else
  1575. {
  1576. fMachineCert = FALSE;
  1577. }
  1578. }
  1579. SafeAllocaFree(pProvInfo);
  1580. }
  1581. }
  1582. //
  1583. // Open up the appropriate MY store, and attempt to find
  1584. // the new certificate.
  1585. //
  1586. if(!hMyCertStore)
  1587. {
  1588. if(fMachineCert)
  1589. {
  1590. hMyCertStore = g_hMyCertStore;
  1591. }
  1592. else
  1593. {
  1594. fImpersonating = SslImpersonateClient();
  1595. hMyCertStore = CertOpenSystemStore(0, "MY");
  1596. }
  1597. if(!hMyCertStore)
  1598. {
  1599. DebugLog((DEB_ERROR, "Error 0x%x opening %s MY certificate store!\n",
  1600. GetLastError(),
  1601. (fMachineCert ? "local machine" : "current user") ));
  1602. break;
  1603. }
  1604. }
  1605. HashBlob.cbData = cbThumbprint;
  1606. HashBlob.pbData = rgbThumbprint;
  1607. pNewCert = CertFindCertificateInStore(hMyCertStore,
  1608. X509_ASN_ENCODING,
  1609. 0,
  1610. CERT_FIND_HASH,
  1611. &HashBlob,
  1612. NULL);
  1613. if(pNewCert == NULL)
  1614. {
  1615. // Certificate has been renewed, but the new certificate
  1616. // cannot be found.
  1617. DebugLog((DEB_ERROR, "New certificate cannot be found: 0x%x\n", GetLastError()));
  1618. break;
  1619. }
  1620. //
  1621. // Return the new certificate, but first loop back and see if it's been
  1622. // renewed itself.
  1623. //
  1624. pCertContext = pNewCert;
  1625. *ppNewCertificate = pNewCert;
  1626. DebugLog((DEB_TRACE, "Certificate has been renewed\n"));
  1627. fRenewed = TRUE;
  1628. }
  1629. //
  1630. // Cleanup.
  1631. //
  1632. if(hMyCertStore && hMyCertStore != g_hMyCertStore)
  1633. {
  1634. CertCloseStore(hMyCertStore, 0);
  1635. }
  1636. if(fImpersonating)
  1637. {
  1638. RevertToSelf();
  1639. fImpersonating = FALSE;
  1640. }
  1641. return fRenewed;
  1642. }