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.

1091 lines
30 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Widows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1997.
  5. //
  6. // File: defcreds.c
  7. //
  8. // Contents: Routines for acquiring default credentials.
  9. //
  10. // Classes:
  11. //
  12. // Functions:
  13. //
  14. // History: 12-05-97 jbanes Created.
  15. //
  16. //----------------------------------------------------------------------------
  17. #include <spbase.h>
  18. #include <softpub.h>
  19. void
  20. GetImplementationType(
  21. PCCERT_CONTEXT pCertContext,
  22. PDWORD pdwImpType);
  23. NTSTATUS
  24. AssignNewClientCredential(
  25. PSPContext pContext,
  26. PCCERT_CONTEXT pCertContext,
  27. BOOL fPromptNow)
  28. {
  29. PSPCredential pCred = NULL;
  30. NTSTATUS Status;
  31. BOOL fEventLogged;
  32. LSA_SCHANNEL_SUB_CRED SubCred;
  33. //
  34. // Does this certificate have an acceptable public key type?
  35. //
  36. {
  37. BOOL fFound;
  38. DWORD dwKeyType;
  39. DWORD i;
  40. fFound = FALSE;
  41. dwKeyType = MapOidToCertType(pCertContext->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId);
  42. for(i = 0; i < pContext->cSsl3ClientCertTypes; i++)
  43. {
  44. if(pContext->Ssl3ClientCertTypes[i] == dwKeyType)
  45. {
  46. fFound = TRUE;
  47. break;
  48. }
  49. }
  50. if(!fFound)
  51. {
  52. // Don't use this certificate.
  53. Status = SP_LOG_RESULT(PCT_INT_UNKNOWN_CREDENTIAL);
  54. goto cleanup;
  55. }
  56. }
  57. //
  58. // Build a credential structure for the certificate.
  59. //
  60. pCred = SPExternalAlloc(sizeof(SPCredential));
  61. if(pCred == NULL)
  62. {
  63. Status = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
  64. goto cleanup;
  65. }
  66. memset(&SubCred, 0, sizeof(SubCred));
  67. SubCred.pCert = pCertContext;
  68. Status = SPCreateCred(pContext->dwProtocol,
  69. &SubCred,
  70. pCred,
  71. &fEventLogged);
  72. if(Status != PCT_ERR_OK)
  73. {
  74. goto cleanup;
  75. }
  76. //
  77. // Release the existing credential, if one exists.
  78. //
  79. if(pContext->RipeZombie->pClientCred)
  80. {
  81. SPDeleteCred(pContext->RipeZombie->pClientCred, TRUE);
  82. pContext->RipeZombie->pClientCred = NULL;
  83. }
  84. //
  85. // Assign the credential to the cache element.
  86. //
  87. pContext->RipeZombie->pClientCred = pCred;
  88. pContext->pActiveClientCred = pCred;
  89. if(fPromptNow == FALSE)
  90. {
  91. pContext->RipeZombie->dwFlags |= SP_CACHE_FLAG_USE_VALIDATED;
  92. }
  93. Status = PCT_ERR_OK;
  94. cleanup:
  95. if(pCred && Status != PCT_ERR_OK)
  96. {
  97. SPDeleteCred(pCred, TRUE);
  98. }
  99. return Status;
  100. }
  101. NTSTATUS
  102. QueryCredentialManagerForCert(
  103. PSPContext pContext,
  104. LPWSTR pszTarget)
  105. {
  106. PCCERT_CONTEXT pCertContext = NULL;
  107. LUID LogonId;
  108. PENCRYPTED_CREDENTIALW pCredential = NULL;
  109. BOOL fImpersonating = FALSE;
  110. CRYPT_HASH_BLOB HashBlob;
  111. NTSTATUS Status;
  112. HCERTSTORE hStore = 0;
  113. PCERT_CREDENTIAL_INFO pCertInfo = NULL;
  114. CRED_MARSHAL_TYPE CredType;
  115. //
  116. // Obtain client logon id.
  117. //
  118. Status = SslGetClientLogonId(&LogonId);
  119. if(!NT_SUCCESS(Status))
  120. {
  121. SP_LOG_RESULT(Status);
  122. goto cleanup;
  123. }
  124. fImpersonating = SslImpersonateClient();
  125. //
  126. // Query the credential manager for a certificate.
  127. //
  128. Status = LsaTable->CrediRead(&LogonId,
  129. CREDP_FLAGS_IN_PROCESS,
  130. pszTarget,
  131. CRED_TYPE_DOMAIN_CERTIFICATE,
  132. 0,
  133. &pCredential);
  134. if(!NT_SUCCESS(Status))
  135. {
  136. if(Status == STATUS_NOT_FOUND)
  137. {
  138. DebugLog((DEB_WARN, "No certificate found in credential manager.\n"));
  139. }
  140. else
  141. {
  142. SP_LOG_RESULT(Status);
  143. }
  144. goto cleanup;
  145. }
  146. //
  147. // Extract the certificate thumbprint and (optional) PIN.
  148. //
  149. if(!CredIsMarshaledCredentialW(pCredential->Cred.UserName))
  150. {
  151. Status = SP_LOG_RESULT(SEC_E_UNKNOWN_CREDENTIALS);
  152. goto cleanup;
  153. }
  154. if(!CredUnmarshalCredentialW(pCredential->Cred.UserName,
  155. &CredType,
  156. &pCertInfo))
  157. {
  158. Status = SP_LOG_RESULT(SEC_E_UNKNOWN_CREDENTIALS);
  159. goto cleanup;
  160. }
  161. if(CredType != CertCredential)
  162. {
  163. Status = SP_LOG_RESULT(SEC_E_UNKNOWN_CREDENTIALS);
  164. goto cleanup;
  165. }
  166. //
  167. // Look up the certificate in the MY certificate store.
  168. //
  169. hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_W,
  170. X509_ASN_ENCODING, 0,
  171. CERT_SYSTEM_STORE_CURRENT_USER |
  172. CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG,
  173. L"MY");
  174. if(!hStore)
  175. {
  176. SP_LOG_RESULT(GetLastError());
  177. Status = SEC_E_NO_CREDENTIALS;
  178. goto cleanup;
  179. }
  180. HashBlob.cbData = sizeof(pCertInfo->rgbHashOfCert);
  181. HashBlob.pbData = pCertInfo->rgbHashOfCert;
  182. pCertContext = CertFindCertificateInStore(hStore,
  183. X509_ASN_ENCODING,
  184. 0,
  185. CERT_FIND_HASH,
  186. &HashBlob,
  187. NULL);
  188. if(pCertContext == NULL)
  189. {
  190. DebugLog((DEB_ERROR, "Certificate designated by credential manager was not found in certificate store (0x%x).\n", GetLastError()));
  191. Status = SEC_E_NO_CREDENTIALS;
  192. goto cleanup;
  193. }
  194. //
  195. // Attempt to add this certificate context to the current credential.
  196. //
  197. Status = AssignNewClientCredential(pContext,
  198. pCertContext,
  199. pCredential->Cred.Flags & CRED_FLAGS_PROMPT_NOW);
  200. if(!NT_SUCCESS(Status))
  201. {
  202. SP_LOG_RESULT(Status);
  203. goto cleanup;
  204. }
  205. Status = STATUS_SUCCESS;
  206. cleanup:
  207. if(pCertContext)
  208. {
  209. CertFreeCertificateContext(pCertContext);
  210. }
  211. if(hStore)
  212. {
  213. CertCloseStore(hStore, 0);
  214. }
  215. if(pCredential)
  216. {
  217. LsaTable->FreeLsaHeap(pCredential);
  218. }
  219. if(pCertInfo)
  220. {
  221. CredFree(pCertInfo);
  222. }
  223. if(fImpersonating)
  224. {
  225. RevertToSelf();
  226. }
  227. return Status;
  228. }
  229. DWORD
  230. IsThreadLocalSystem(
  231. BOOL *pfIsLocalSystem)
  232. {
  233. DWORD Status;
  234. HANDLE hToken = 0;
  235. UCHAR InfoBuffer[1024];
  236. DWORD dwInfoBufferSize = sizeof(InfoBuffer);
  237. PTOKEN_USER SlowBuffer = NULL;
  238. PTOKEN_USER pTokenUser = (PTOKEN_USER)InfoBuffer;
  239. PSID psidLocalSystem = NULL;
  240. PSID psidNetworkService = NULL;
  241. SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
  242. *pfIsLocalSystem = FALSE;
  243. //
  244. // Get SID of calling thread.
  245. //
  246. if(!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken))
  247. {
  248. Status = GetLastError();
  249. goto cleanup;
  250. }
  251. if(!GetTokenInformation(hToken, TokenUser, pTokenUser,
  252. dwInfoBufferSize, &dwInfoBufferSize))
  253. {
  254. //
  255. // if fast buffer wasn't big enough, allocate enough storage
  256. // and try again.
  257. //
  258. Status = GetLastError();
  259. if(Status != ERROR_INSUFFICIENT_BUFFER)
  260. {
  261. goto cleanup;
  262. }
  263. SlowBuffer = (PTOKEN_USER)LocalAlloc(LPTR, dwInfoBufferSize);
  264. if(NULL == SlowBuffer)
  265. {
  266. Status = ERROR_NOT_ENOUGH_MEMORY;
  267. goto cleanup;
  268. }
  269. pTokenUser = SlowBuffer;
  270. if(!GetTokenInformation(hToken, TokenUser, pTokenUser,
  271. dwInfoBufferSize, &dwInfoBufferSize))
  272. {
  273. Status = GetLastError();
  274. goto cleanup;
  275. }
  276. }
  277. //
  278. // Check for local system.
  279. //
  280. if(!AllocateAndInitializeSid(&siaNtAuthority,
  281. 1,
  282. SECURITY_LOCAL_SYSTEM_RID,
  283. 0, 0, 0, 0, 0, 0, 0,
  284. &psidLocalSystem))
  285. {
  286. Status = GetLastError();
  287. goto cleanup;
  288. }
  289. if (EqualSid(psidLocalSystem, pTokenUser->User.Sid))
  290. {
  291. DebugLog((DEB_TRACE, "Client is using the LOCAL SYSTEM account.\n"));
  292. *pfIsLocalSystem = TRUE;
  293. Status = ERROR_SUCCESS;
  294. goto cleanup;
  295. }
  296. //
  297. // Check for network service.
  298. //
  299. if(!AllocateAndInitializeSid(&siaNtAuthority,
  300. 1,
  301. SECURITY_NETWORK_SERVICE_RID,
  302. 0, 0, 0, 0, 0, 0, 0,
  303. &psidNetworkService))
  304. {
  305. Status = GetLastError();
  306. goto cleanup;
  307. }
  308. if (EqualSid(psidNetworkService, pTokenUser->User.Sid))
  309. {
  310. DebugLog((DEB_TRACE, "Client is using the NETWORK SERVICE account.\n"));
  311. *pfIsLocalSystem = TRUE;
  312. Status = ERROR_SUCCESS;
  313. goto cleanup;
  314. }
  315. Status = ERROR_SUCCESS;
  316. cleanup:
  317. if(NULL != SlowBuffer)
  318. {
  319. LocalFree(SlowBuffer);
  320. }
  321. if(NULL != psidLocalSystem)
  322. {
  323. FreeSid(psidLocalSystem);
  324. }
  325. if(NULL != psidNetworkService)
  326. {
  327. FreeSid(psidNetworkService);
  328. }
  329. if(NULL != hToken)
  330. {
  331. CloseHandle(hToken);
  332. }
  333. return Status;
  334. }
  335. NTSTATUS
  336. FindClientCertificate(
  337. PSPContext pContext,
  338. HCERTSTORE hMyStore,
  339. CERT_CHAIN_FIND_BY_ISSUER_PARA *pFindByIssuerPara,
  340. BOOL fSkipExpiredCerts,
  341. BOOL fSoftwareCspOnly)
  342. {
  343. PCCERT_CHAIN_CONTEXT pChainContext = NULL;
  344. HTTPSPolicyCallbackData polHttps;
  345. CERT_CHAIN_POLICY_PARA PolicyPara;
  346. CERT_CHAIN_POLICY_STATUS PolicyStatus;
  347. PCCERT_CONTEXT pCertContext;
  348. NTSTATUS Status;
  349. ULONG j;
  350. pChainContext = NULL;
  351. while(TRUE)
  352. {
  353. // Find a certificate chain.
  354. pChainContext = CertFindChainInStore(hMyStore,
  355. X509_ASN_ENCODING,
  356. 0,
  357. CERT_CHAIN_FIND_BY_ISSUER,
  358. pFindByIssuerPara,
  359. pChainContext);
  360. if(pChainContext == NULL)
  361. {
  362. break;
  363. }
  364. // Make sure that every certificate in the chain either has the
  365. // client auth EKU or it has no EKUs at all.
  366. {
  367. PCERT_SIMPLE_CHAIN pSimpleChain;
  368. PCCERT_CONTEXT pCurrentCert;
  369. BOOL fIsAllowed = FALSE;
  370. pSimpleChain = pChainContext->rgpChain[0];
  371. for(j = 0; j < pSimpleChain->cElement; j++)
  372. {
  373. pCurrentCert = pSimpleChain->rgpElement[j]->pCertContext;
  374. Status = SPCheckKeyUsage(pCurrentCert,
  375. szOID_PKIX_KP_CLIENT_AUTH,
  376. TRUE,
  377. &fIsAllowed);
  378. if(Status != SEC_E_OK || !fIsAllowed)
  379. {
  380. fIsAllowed = FALSE;
  381. break;
  382. }
  383. }
  384. if(!fIsAllowed)
  385. {
  386. // skip this certificate chain.
  387. continue;
  388. }
  389. }
  390. // Set up validate chain structures.
  391. ZeroMemory(&polHttps, sizeof(HTTPSPolicyCallbackData));
  392. polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData);
  393. polHttps.dwAuthType = AUTHTYPE_CLIENT;
  394. polHttps.fdwChecks = 0;
  395. polHttps.pwszServerName = NULL;
  396. ZeroMemory(&PolicyStatus, sizeof(PolicyStatus));
  397. PolicyStatus.cbSize = sizeof(PolicyStatus);
  398. ZeroMemory(&PolicyPara, sizeof(PolicyPara));
  399. PolicyPara.cbSize = sizeof(PolicyPara);
  400. PolicyPara.pvExtraPolicyPara= &polHttps;
  401. PolicyPara.dwFlags = CERT_CHAIN_POLICY_IGNORE_WRONG_USAGE_FLAG |
  402. CERT_CHAIN_POLICY_ALLOW_UNKNOWN_CA_FLAG;
  403. if(!fSkipExpiredCerts)
  404. {
  405. PolicyPara.dwFlags |= CERT_CHAIN_POLICY_IGNORE_ALL_NOT_TIME_VALID_FLAGS;
  406. }
  407. // Validate chain
  408. if(!CertVerifyCertificateChainPolicy(
  409. CERT_CHAIN_POLICY_SSL,
  410. pChainContext,
  411. &PolicyPara,
  412. &PolicyStatus))
  413. {
  414. DebugLog((DEB_WARN,"Error 0x%x returned by CertVerifyCertificateChainPolicy!\n", GetLastError()));
  415. continue;
  416. }
  417. Status = MapWinTrustError(PolicyStatus.dwError, 0, 0);
  418. if(Status)
  419. {
  420. // Certificate did not validate, move on to the next one.
  421. DebugLog((DEB_WARN, "Client certificate failed validation with 0x%x\n", Status));
  422. continue;
  423. }
  424. // Get pointer to leaf certificate context.
  425. if(pChainContext->cChain == 0 || pChainContext->rgpChain[0] == NULL)
  426. {
  427. Status = SP_LOG_RESULT(SEC_E_INTERNAL_ERROR);
  428. goto cleanup;
  429. }
  430. if(pChainContext->rgpChain[0]->cElement == 0 ||
  431. pChainContext->rgpChain[0]->rgpElement == NULL)
  432. {
  433. Status = SP_LOG_RESULT(SEC_E_INTERNAL_ERROR);
  434. goto cleanup;
  435. }
  436. pCertContext = pChainContext->rgpChain[0]->rgpElement[0]->pCertContext;
  437. //
  438. // Is the private key stored in a software CSP?
  439. //
  440. if(fSoftwareCspOnly)
  441. {
  442. DWORD dwImpType;
  443. GetImplementationType(pCertContext, &dwImpType);
  444. if(dwImpType != CRYPT_IMPL_SOFTWARE)
  445. {
  446. // Skip this certificate
  447. continue;
  448. }
  449. }
  450. //
  451. // Assign the certificate to the current context.
  452. //
  453. Status = AssignNewClientCredential(pContext,
  454. pCertContext,
  455. FALSE);
  456. if(NT_SUCCESS(Status))
  457. {
  458. // Success! Our work here is done.
  459. goto cleanup;
  460. }
  461. }
  462. // No suitable client credential was found.
  463. Status = SP_LOG_RESULT(SEC_E_INCOMPLETE_CREDENTIALS);
  464. cleanup:
  465. if(pChainContext)
  466. {
  467. CertFreeCertificateChain(pChainContext);
  468. }
  469. return Status;
  470. }
  471. NTSTATUS
  472. AcquireDefaultClientCredential(
  473. PSPContext pContext,
  474. BOOL fCredManagerOnly)
  475. {
  476. CERT_CHAIN_FIND_BY_ISSUER_PARA FindByIssuerPara;
  477. CERT_NAME_BLOB * prgIssuers = NULL;
  478. DWORD cIssuers = 0;
  479. HCERTSTORE hStore = 0;
  480. NTSTATUS Status;
  481. BOOL fImpersonating = FALSE;
  482. BOOL fLocalSystem = FALSE;
  483. ULONG i;
  484. DebugLog((DEB_TRACE,"AcquireDefaultClientCredential\n"));
  485. //
  486. // Is the application running under local system?
  487. //
  488. fImpersonating = SslImpersonateClient();
  489. if(fImpersonating)
  490. {
  491. Status = IsThreadLocalSystem(&fLocalSystem);
  492. if(Status)
  493. {
  494. DebugLog((DEB_WARN, "IsThreadLocalSystem returned error 0x%x.\n", Status));
  495. }
  496. RevertToSelf();
  497. fImpersonating = FALSE;
  498. }
  499. //
  500. // Ask the credential manager to select a certificate for us.
  501. //
  502. Status = QueryCredentialManagerForCert(
  503. pContext,
  504. pContext->pszTarget);
  505. if(NT_SUCCESS(Status))
  506. {
  507. DebugLog((DEB_TRACE, "Credential manager found a certificate for us.\n"));
  508. goto cleanup;
  509. }
  510. if(fCredManagerOnly)
  511. {
  512. // No suitable client credential was found.
  513. Status = SP_LOG_RESULT(SEC_I_INCOMPLETE_CREDENTIALS);
  514. goto cleanup;
  515. }
  516. //
  517. // Get list of trusted issuers as a list of CERT_NAME_BLOBs.
  518. //
  519. if(pContext->pbIssuerList && pContext->cbIssuerList > 2)
  520. {
  521. PBYTE pbIssuerList = pContext->pbIssuerList + 2;
  522. DWORD cbIssuerList = pContext->cbIssuerList - 2;
  523. PBYTE pbIssuer;
  524. // Count issuers.
  525. cIssuers = 0;
  526. pbIssuer = pbIssuerList;
  527. while(pbIssuer + 1 < pbIssuerList + cbIssuerList)
  528. {
  529. pbIssuer += 2 + COMBINEBYTES(pbIssuer[0], pbIssuer[1]);
  530. cIssuers++;
  531. }
  532. // Allocate memory for list of blobs.
  533. prgIssuers = SPExternalAlloc(cIssuers * sizeof(CERT_NAME_BLOB));
  534. if(prgIssuers == NULL)
  535. {
  536. Status = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
  537. goto cleanup;
  538. }
  539. // Build the issuer blob list.
  540. pbIssuer = pbIssuerList;
  541. for(i = 0; i < cIssuers; i++)
  542. {
  543. prgIssuers[i].pbData = pbIssuer + 2;
  544. prgIssuers[i].cbData = COMBINEBYTES(pbIssuer[0], pbIssuer[1]);
  545. pbIssuer += 2 + prgIssuers[i].cbData;
  546. }
  547. }
  548. //
  549. // Enumerate the certificates in the MY store, looking for a suitable
  550. // client certificate.
  551. //
  552. fImpersonating = SslImpersonateClient();
  553. if(fLocalSystem)
  554. {
  555. hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_W,
  556. X509_ASN_ENCODING, 0,
  557. CERT_SYSTEM_STORE_LOCAL_MACHINE |
  558. CERT_STORE_READONLY_FLAG |
  559. CERT_STORE_OPEN_EXISTING_FLAG,
  560. L"MY");
  561. }
  562. else
  563. {
  564. hStore = CertOpenSystemStore(0, "MY");
  565. }
  566. if(!hStore)
  567. {
  568. SP_LOG_RESULT(GetLastError());
  569. Status = SEC_E_INTERNAL_ERROR;
  570. goto cleanup;
  571. }
  572. ZeroMemory(&FindByIssuerPara, sizeof(FindByIssuerPara));
  573. FindByIssuerPara.cbSize = sizeof(FindByIssuerPara);
  574. FindByIssuerPara.pszUsageIdentifier = szOID_PKIX_KP_CLIENT_AUTH;
  575. FindByIssuerPara.dwKeySpec = 0;
  576. FindByIssuerPara.cIssuer = cIssuers;
  577. FindByIssuerPara.rgIssuer = prgIssuers;
  578. //
  579. // Attempt to find a suitable certificate.
  580. //
  581. Status = FindClientCertificate(pContext,
  582. hStore,
  583. &FindByIssuerPara,
  584. TRUE, // skip expired certs
  585. TRUE); // software CSPs only
  586. if(NT_SUCCESS(Status))
  587. {
  588. // Success! Our work here is done.
  589. goto cleanup;
  590. }
  591. Status = FindClientCertificate(pContext,
  592. hStore,
  593. &FindByIssuerPara,
  594. TRUE, // skip expired certs
  595. FALSE); // software CSPs only
  596. if(NT_SUCCESS(Status))
  597. {
  598. // Success! Our work here is done.
  599. goto cleanup;
  600. }
  601. Status = FindClientCertificate(pContext,
  602. hStore,
  603. &FindByIssuerPara,
  604. FALSE, // skip expired certs
  605. TRUE); // software CSPs only
  606. if(NT_SUCCESS(Status))
  607. {
  608. // Success! Our work here is done.
  609. goto cleanup;
  610. }
  611. Status = FindClientCertificate(pContext,
  612. hStore,
  613. &FindByIssuerPara,
  614. FALSE, // skip expired certs
  615. FALSE); // software CSPs only
  616. if(NT_SUCCESS(Status))
  617. {
  618. // Success! Our work here is done.
  619. goto cleanup;
  620. }
  621. // No suitable client credential was found.
  622. Status = SP_LOG_RESULT(SEC_I_INCOMPLETE_CREDENTIALS);
  623. cleanup:
  624. if(hStore)
  625. {
  626. CertCloseStore(hStore, 0);
  627. }
  628. if(fImpersonating)
  629. {
  630. RevertToSelf();
  631. }
  632. if(prgIssuers)
  633. {
  634. SPExternalFree(prgIssuers);
  635. }
  636. DebugLog((DEB_TRACE,"AcquireDefaultClientCredential returned 0x%x\n", Status));
  637. return Status;
  638. }
  639. NTSTATUS
  640. FindDefaultMachineCred(
  641. PSPCredentialGroup *ppCred,
  642. DWORD dwProtocol)
  643. {
  644. HTTPSPolicyCallbackData polHttps;
  645. CERT_CHAIN_POLICY_PARA PolicyPara;
  646. CERT_CHAIN_POLICY_STATUS PolicyStatus;
  647. CERT_CHAIN_PARA ChainPara;
  648. PCCERT_CHAIN_CONTEXT pChainContext = NULL;
  649. PCCERT_CONTEXT pCertContext = NULL;
  650. LSA_SCHANNEL_CRED SchannelCred;
  651. LSA_SCHANNEL_SUB_CRED SubCred;
  652. HCERTSTORE hStore = 0;
  653. #define SERVER_USAGE_COUNT 3
  654. LPSTR rgszUsages[SERVER_USAGE_COUNT] = {
  655. szOID_PKIX_KP_SERVER_AUTH,
  656. szOID_SERVER_GATED_CRYPTO,
  657. szOID_SGC_NETSCAPE };
  658. LPWSTR pwszMachineName = NULL;
  659. DWORD cchMachineName;
  660. SP_STATUS Status;
  661. DWORD i;
  662. // Get the machine name
  663. cchMachineName = 0;
  664. if(!GetComputerNameExW(ComputerNameDnsFullyQualified, NULL, &cchMachineName))
  665. {
  666. if(GetLastError() != ERROR_MORE_DATA)
  667. {
  668. DebugLog((DEB_ERROR,"Failed to get computer name size: 0x%x\n",GetLastError()));
  669. Status = SP_LOG_RESULT(SEC_E_WRONG_PRINCIPAL);
  670. goto cleanup;
  671. }
  672. }
  673. pwszMachineName = SPExternalAlloc(cchMachineName * sizeof(WCHAR));
  674. if(pwszMachineName == NULL)
  675. {
  676. Status = SP_LOG_RESULT(SEC_E_INSUFFICIENT_MEMORY);
  677. goto cleanup;
  678. }
  679. if(!GetComputerNameExW(ComputerNameDnsFullyQualified, pwszMachineName, &cchMachineName))
  680. {
  681. DebugLog((DEB_ERROR,"Failed to get computer name: 0x%x\n",GetLastError()));
  682. Status = SP_LOG_RESULT(SEC_E_WRONG_PRINCIPAL);
  683. goto cleanup;
  684. }
  685. // Remove the trailing "." if any. This can happen in the stand-alone
  686. // server case.
  687. cchMachineName = lstrlenW(pwszMachineName);
  688. if(cchMachineName > 0 && pwszMachineName[cchMachineName - 1] == L'.')
  689. {
  690. pwszMachineName[cchMachineName - 1] = L'\0';
  691. }
  692. DebugLog((DEB_TRACE,"Computer name: %ls\n",pwszMachineName));
  693. // Open the system MY store.
  694. hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_W,
  695. X509_ASN_ENCODING, 0,
  696. CERT_SYSTEM_STORE_LOCAL_MACHINE |
  697. CERT_STORE_READONLY_FLAG |
  698. CERT_STORE_OPEN_EXISTING_FLAG,
  699. L"MY");
  700. if(hStore == NULL)
  701. {
  702. SP_LOG_RESULT(GetLastError());
  703. Status = SEC_E_NO_CREDENTIALS;
  704. goto cleanup;
  705. }
  706. //
  707. // Enumerate the certificates in the MY store, looking for a suitable
  708. // server certificate. Do this twice, the first time looking for the
  709. // perfect certificate, and if this fails then look again, this time
  710. // being a little less picky.
  711. //
  712. for(i = 0; i < 2; i++)
  713. {
  714. pCertContext = NULL;
  715. while(TRUE)
  716. {
  717. // Get leaf certificate in the MY store.
  718. pCertContext = CertEnumCertificatesInStore(hStore, pCertContext);
  719. if(pCertContext == NULL)
  720. {
  721. // No more certificates.
  722. break;
  723. }
  724. //
  725. // Build a certificate chain from the leaf certificate.
  726. //
  727. ZeroMemory(&ChainPara, sizeof(ChainPara));
  728. ChainPara.cbSize = sizeof(ChainPara);
  729. ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
  730. ChainPara.RequestedUsage.Usage.cUsageIdentifier = SERVER_USAGE_COUNT;
  731. ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages;
  732. if(!CertGetCertificateChain(
  733. NULL,
  734. pCertContext,
  735. NULL,
  736. 0,
  737. &ChainPara,
  738. CERT_CHAIN_REVOCATION_CHECK_END_CERT,
  739. NULL,
  740. &pChainContext))
  741. {
  742. DebugLog((DEB_WARN, "Error 0x%x returned by CertGetCertificateChain!\n", GetLastError()));
  743. continue;
  744. }
  745. // Set up validate chain structures.
  746. ZeroMemory(&polHttps, sizeof(HTTPSPolicyCallbackData));
  747. polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData);
  748. polHttps.dwAuthType = AUTHTYPE_SERVER;
  749. polHttps.fdwChecks = 0;
  750. polHttps.pwszServerName = pwszMachineName;
  751. ZeroMemory(&PolicyStatus, sizeof(PolicyStatus));
  752. PolicyStatus.cbSize = sizeof(PolicyStatus);
  753. ZeroMemory(&PolicyPara, sizeof(PolicyPara));
  754. PolicyPara.cbSize = sizeof(PolicyPara);
  755. PolicyPara.pvExtraPolicyPara= &polHttps;
  756. if(i == 0)
  757. {
  758. // Look for the perfect certificate.
  759. PolicyPara.dwFlags = 0;
  760. }
  761. else
  762. {
  763. // Ignore expiration.
  764. PolicyPara.dwFlags = CERT_CHAIN_POLICY_IGNORE_ALL_NOT_TIME_VALID_FLAGS;
  765. }
  766. // Validate chain
  767. if(!CertVerifyCertificateChainPolicy(
  768. CERT_CHAIN_POLICY_SSL,
  769. pChainContext,
  770. &PolicyPara,
  771. &PolicyStatus))
  772. {
  773. SP_LOG_RESULT(GetLastError());
  774. CertFreeCertificateChain(pChainContext);
  775. continue;
  776. }
  777. Status = MapWinTrustError(PolicyStatus.dwError,
  778. 0,
  779. CRED_FLAG_IGNORE_NO_REVOCATION_CHECK | CRED_FLAG_IGNORE_REVOCATION_OFFLINE);
  780. if(Status)
  781. {
  782. // Certificate did not validate, move on to the next one.
  783. DebugLog((DEB_WARN, "Machine certificate failed validation with 0x%x\n", Status));
  784. CertFreeCertificateChain(pChainContext);
  785. continue;
  786. }
  787. CertFreeCertificateChain(pChainContext);
  788. // Build an schannel credential.
  789. ZeroMemory(&SchannelCred, sizeof(SchannelCred));
  790. SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
  791. SchannelCred.cSubCreds = 1;
  792. SchannelCred.paSubCred = &SubCred;
  793. ZeroMemory(&SubCred, sizeof(SubCred));
  794. SubCred.pCert = pCertContext;
  795. Status = SPCreateCredential(ppCred,
  796. dwProtocol,
  797. &SchannelCred);
  798. if(Status != PCT_ERR_OK)
  799. {
  800. // Don't use this certificate.
  801. continue;
  802. }
  803. // We have a winner!
  804. DebugLog((DEB_TRACE, "Machine certificate automatically acquired\n"));
  805. Status = PCT_ERR_OK;
  806. goto cleanup;
  807. }
  808. }
  809. // No suitable machine credential was found.
  810. Status = SP_LOG_RESULT(SEC_E_NO_CREDENTIALS);
  811. cleanup:
  812. if(Status != PCT_ERR_OK)
  813. {
  814. LogNoDefaultServerCredEvent();
  815. }
  816. if(pwszMachineName)
  817. {
  818. SPExternalFree(pwszMachineName);
  819. }
  820. if(pCertContext)
  821. {
  822. CertFreeCertificateContext(pCertContext);
  823. }
  824. if(hStore)
  825. {
  826. CertCloseStore(hStore, 0);
  827. }
  828. return Status;
  829. }
  830. void
  831. GetImplementationType(
  832. PCCERT_CONTEXT pCertContext,
  833. PDWORD pdwImpType)
  834. {
  835. PCRYPT_KEY_PROV_INFO pProvInfo = NULL;
  836. HCRYPTPROV hProv = 0;
  837. DWORD cbSize;
  838. DWORD dwImpType;
  839. *pdwImpType = CRYPT_IMPL_UNKNOWN;
  840. if(!CertGetCertificateContextProperty(pCertContext,
  841. CERT_KEY_PROV_INFO_PROP_ID,
  842. NULL,
  843. &cbSize))
  844. {
  845. goto cleanup;
  846. }
  847. pProvInfo = SPExternalAlloc(cbSize);
  848. if(pProvInfo == NULL)
  849. {
  850. goto cleanup;
  851. }
  852. if(!CertGetCertificateContextProperty(pCertContext,
  853. CERT_KEY_PROV_INFO_PROP_ID,
  854. pProvInfo,
  855. &cbSize))
  856. {
  857. goto cleanup;
  858. }
  859. // HACKHACK - clear the smart-card specific flag.
  860. pProvInfo->dwFlags &= ~CERT_SET_KEY_CONTEXT_PROP_ID;
  861. if(!CryptAcquireContextW(&hProv,
  862. pProvInfo->pwszContainerName,
  863. pProvInfo->pwszProvName,
  864. pProvInfo->dwProvType,
  865. pProvInfo->dwFlags | CRYPT_SILENT))
  866. {
  867. goto cleanup;
  868. }
  869. cbSize = sizeof(dwImpType);
  870. if(!CryptGetProvParam(hProv,
  871. PP_IMPTYPE,
  872. (PBYTE)&dwImpType,
  873. &cbSize,
  874. 0))
  875. {
  876. goto cleanup;
  877. }
  878. *pdwImpType = dwImpType;
  879. cleanup:
  880. if(pProvInfo)
  881. {
  882. SPExternalFree(pProvInfo);
  883. }
  884. if(hProv)
  885. {
  886. CryptReleaseContext(hProv, 0);
  887. }
  888. }