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.

2776 lines
70 KiB

  1. //+-----------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (c) Microsoft Corporation 2000
  6. //
  7. // File: credman.cxx
  8. //
  9. // Contents: Code for credentials APIs for the Kerberos package
  10. //
  11. //
  12. // History: 23-Feb-2000 Created Jeffspel
  13. //
  14. //------------------------------------------------------------------------
  15. #include <kerb.hxx>
  16. #include <kerbp.h>
  17. #if DBG
  18. static TCHAR THIS_FILE[]=TEXT(__FILE__);
  19. #endif
  20. extern "C"
  21. {
  22. #include <des.h>
  23. }
  24. //+-------------------------------------------------------------------------
  25. //
  26. // Function: KerbFreeCredmanCred
  27. //
  28. // Synopsis: Frees credman cred
  29. //
  30. // Arguments:
  31. //
  32. // Requires:
  33. //
  34. // Returns: NTSTATUS, typically ignored, as failure to update the credman
  35. // should not be fatal.
  36. //
  37. // Notes:
  38. //
  39. //
  40. //--------------------------------------------------------------------------
  41. VOID
  42. KerbFreeCredmanCred(
  43. IN PKERB_CREDMAN_CRED CredToFree
  44. )
  45. {
  46. DsysAssert(CredToFree);
  47. KerbFreePrimaryCredentials(CredToFree->SuppliedCredentials, TRUE);
  48. KerbFreeString(&CredToFree->CredmanDomainName);
  49. KerbFreeString(&CredToFree->CredmanUserName);
  50. KerbFree(CredToFree);
  51. }
  52. //+-------------------------------------------------------------------------
  53. //
  54. // Function: KerbReferenceCredmanCred
  55. //
  56. // Synopsis: Frees credman cred
  57. //
  58. // Arguments:
  59. //
  60. // Requires:
  61. //
  62. //
  63. // Notes:
  64. //
  65. //
  66. //--------------------------------------------------------------------------
  67. VOID
  68. KerbReferenceCredmanCred(
  69. IN PKERB_CREDMAN_CRED Cred,
  70. IN PKERB_LOGON_SESSION LogonSession,
  71. IN BOOLEAN Unlink
  72. )
  73. {
  74. KerbReferenceListEntry(
  75. &LogonSession->CredmanCredentials,
  76. &Cred->ListEntry,
  77. Unlink
  78. );
  79. }
  80. //+-------------------------------------------------------------------------
  81. //
  82. // Function: KerbDereferenceCredmanCred
  83. //
  84. // Synopsis: Frees credman cred
  85. //
  86. // Arguments:
  87. //
  88. // Requires:
  89. //
  90. // Returns: NTSTATUS, typically ignored, as failure to update the credman
  91. // should not be fatal.
  92. //
  93. // Notes:
  94. //
  95. //
  96. //--------------------------------------------------------------------------
  97. VOID
  98. KerbDereferenceCredmanCred(
  99. IN PKERB_CREDMAN_CRED Cred,
  100. IN PKERBEROS_LIST CredmanList
  101. )
  102. {
  103. if (KerbDereferenceListEntry(
  104. &Cred->ListEntry,
  105. CredmanList
  106. ))
  107. {
  108. KerbFreeCredmanCred(Cred);
  109. }
  110. }
  111. //+-------------------------------------------------------------------------
  112. //
  113. // Function: KerbFreeCredmanList
  114. //
  115. // Synopsis: Free a credman list from a logon session...
  116. //
  117. // Arguments:
  118. //
  119. // Requires:
  120. //
  121. // Returns: NTSTATUS, typically ignored, as failure to update the credman
  122. // should not be fatal.
  123. //
  124. // Notes:
  125. //
  126. //
  127. //--------------------------------------------------------------------------
  128. VOID
  129. KerbFreeCredmanList(
  130. KERBEROS_LIST CredmanList
  131. )
  132. {
  133. PKERB_CREDMAN_CRED Cred;
  134. KerbLockList(&CredmanList);
  135. //
  136. // Go through the list of credman creds and dereferences them all
  137. //
  138. while (!IsListEmpty(&CredmanList.List))
  139. {
  140. Cred = CONTAINING_RECORD(
  141. CredmanList.List.Flink,
  142. KERB_CREDMAN_CRED,
  143. ListEntry.Next
  144. );
  145. // unlink cred from list
  146. KerbReferenceListEntry(
  147. &CredmanList,
  148. &Cred->ListEntry,
  149. TRUE
  150. );
  151. KerbDereferenceCredmanCred(
  152. Cred,
  153. &CredmanList
  154. );
  155. }
  156. SafeDeleteCriticalSection(&CredmanList.Lock);
  157. }
  158. //+-------------------------------------------------------------------------
  159. //
  160. // Function: KerbNotifyCredentialManager
  161. //
  162. // Synopsis: This function is used to notify the credential manager of a
  163. // password change event. Note: This will always be a MIT
  164. // session.
  165. //
  166. // Arguments:
  167. //
  168. // Requires:
  169. //
  170. // Returns: NTSTATUS, typically ignored, as failure to update the credman
  171. // should not be fatal.
  172. //
  173. // Notes:
  174. //
  175. //
  176. //--------------------------------------------------------------------------
  177. VOID
  178. KerbNotifyCredentialManager(
  179. IN PKERB_LOGON_SESSION LogonSession,
  180. IN PKERB_CHANGEPASSWORD_REQUEST ChangeRequest,
  181. IN PKERB_INTERNAL_NAME ClientName,
  182. IN PUNICODE_STRING RealmName
  183. )
  184. {
  185. UNICODE_STRING ClientNameU = {0};
  186. KERBERR KerbErr;
  187. // FESTER:
  188. // We should only expect to get pwd change notification on
  189. // an MIT Realm pwd change, in which case, there isn't a concept of a
  190. // Netbios name ....
  191. KerbErr = KerbConvertKdcNameToString(
  192. &ClientNameU,
  193. ClientName,
  194. NULL
  195. );
  196. if (!KERB_SUCCESS(KerbErr))
  197. {
  198. return;
  199. }
  200. LsaINotifyPasswordChanged(
  201. NULL,
  202. &ClientNameU,
  203. RealmName,
  204. NULL,
  205. &ChangeRequest->OldPassword,
  206. &ChangeRequest->NewPassword,
  207. ChangeRequest->Impersonating
  208. );
  209. KerbFreeString(&ClientNameU);
  210. }
  211. //+-------------------------------------------------------------------------
  212. //
  213. // Function: KerbComparePasswords
  214. //
  215. // Synopsis: Verifies that two stored credentials are identical, simply
  216. // through comparison of KERB_ETYPE_RC4_HMAC_NT keys
  217. //
  218. // Arguments:
  219. //
  220. // Requires:
  221. //
  222. // Returns:
  223. // NULL if the user name is not a marshalled cert, a pointer
  224. // to the
  225. //
  226. // Notes:
  227. //
  228. //
  229. //--------------------------------------------------------------------------
  230. BOOL
  231. KerbComparePasswords(
  232. IN PKERB_STORED_CREDENTIAL PwdList1,
  233. IN PKERB_STORED_CREDENTIAL PwdList2
  234. )
  235. {
  236. PKERB_ENCRYPTION_KEY Key1 = NULL;
  237. PKERB_ENCRYPTION_KEY Key2 = NULL;
  238. ULONG Etype = KERB_ETYPE_RC4_HMAC_NT;
  239. Key1 = KerbGetKeyFromList(
  240. PwdList1,
  241. KERB_ETYPE_RC4_HMAC_NT
  242. );
  243. if (NULL == Key1)
  244. {
  245. Etype = KERB_ETYPE_DES_CBC_MD5;
  246. Key1 = KerbGetKeyFromList(
  247. PwdList1,
  248. Etype
  249. );
  250. if (NULL == Key1)
  251. {
  252. D_DebugLog((DEB_ERROR, "Cred1 missing DES and RC4 key!\n"));
  253. DsysAssert(FALSE);
  254. return FALSE;
  255. }
  256. }
  257. Key2 = KerbGetKeyFromList(
  258. PwdList2,
  259. Etype
  260. );
  261. if (NULL == Key2)
  262. {
  263. D_DebugLog((DEB_ERROR, "Cred2 missing %x key!\n", Etype));
  264. DsysAssert(FALSE);
  265. return FALSE;
  266. }
  267. return (RtlEqualMemory(
  268. Key1->keyvalue.value,
  269. Key2->keyvalue.value,
  270. Key1->keyvalue.length
  271. ));
  272. }
  273. //+-------------------------------------------------------------------------
  274. //
  275. // Function: KerbLogCredmanError
  276. //
  277. // Synopsis: Create an event log entry to help the user fixup their
  278. // credman credential.
  279. //
  280. // Arguments:
  281. //
  282. // Requires:
  283. //
  284. // Returns:
  285. // NULL if the user name is not a marshalled cert, a pointer
  286. // to the
  287. //
  288. // Notes:
  289. //
  290. //
  291. //--------------------------------------------------------------------------
  292. VOID
  293. KerbLogCredmanError(
  294. IN PKERB_CREDMAN_CRED Cred,
  295. IN NTSTATUS Status
  296. )
  297. {
  298. BOOLEAN CardError = FALSE;
  299. BOOLEAN Pkinit = (Cred->SuppliedCredentials->PublicKeyCreds != NULL);
  300. switch ( Status )
  301. {
  302. case STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED:
  303. case STATUS_SMARTCARD_SUBSYSTEM_FAILURE:
  304. case STATUS_SMARTCARD_SILENT_CONTEXT:
  305. CardError = TRUE;
  306. case STATUS_NO_SUCH_USER:
  307. case STATUS_SMARTCARD_WRONG_PIN:
  308. case STATUS_WRONG_PASSWORD:
  309. break;
  310. default:
  311. return;
  312. }
  313. //
  314. // If this is a *Session, e.g. RAS connection, and we have a card error,
  315. // ask the user to reconnect.
  316. //
  317. if ((( Cred->CredentialFlags & RAS_CREDENTIAL ) != 0) &&
  318. ( CardError ))
  319. {
  320. KerbReportRasCardError(Status);
  321. return;
  322. }
  323. KerbReportCredmanError(
  324. &Cred->SuppliedCredentials->UserName,
  325. &Cred->SuppliedCredentials->DomainName,
  326. Pkinit,
  327. Status
  328. );
  329. }
  330. //+-------------------------------------------------------------------------
  331. //
  332. // Function: KerbCheckUserNameForCert
  333. //
  334. // Synopsis: Looks at the passed in user name and determines if that
  335. // user name is a marshalled cert. If it is the function
  336. // opens the user cert store and then attempts to find the
  337. // cert in the store.
  338. //
  339. // Arguments:
  340. //
  341. // Requires:
  342. //
  343. // Returns:
  344. // NULL if the user name is not a marshalled cert, a pointer
  345. // to the
  346. //
  347. // Notes:
  348. //
  349. //
  350. //--------------------------------------------------------------------------
  351. NTSTATUS
  352. KerbCheckUserNameForCert(
  353. IN PLUID ClientLogonId,
  354. IN BOOLEAN fImpersonateClient,
  355. IN UNICODE_STRING *pUserName,
  356. OUT PCERT_CONTEXT *ppCertContext
  357. )
  358. {
  359. CRED_MARSHAL_TYPE MarshalType;
  360. PCERT_CREDENTIAL_INFO pCertCredInfo = NULL;
  361. HCERTSTORE hCertStore = NULL;
  362. CRYPT_HASH_BLOB HashBlob;
  363. LPWSTR rgwszUserName;
  364. WCHAR FastUserName[(UNLEN + 1) * sizeof(WCHAR)];
  365. LPWSTR SlowUserName = NULL;
  366. BOOLEAN fImpersonating = FALSE;
  367. HANDLE ClientTokenHandle = NULL;
  368. NTSTATUS Status = STATUS_SUCCESS;
  369. *ppCertContext = NULL;
  370. // Switch to stackalloc routine when available.
  371. if( pUserName->Length+sizeof(WCHAR) <= sizeof(FastUserName) )
  372. {
  373. rgwszUserName = FastUserName;
  374. }
  375. else
  376. {
  377. SafeAllocaAllocate(SlowUserName, pUserName->Length+sizeof(WCHAR));
  378. if( SlowUserName == NULL )
  379. {
  380. Status = STATUS_INSUFFICIENT_RESOURCES;
  381. goto Cleanup;
  382. }
  383. rgwszUserName = SlowUserName;
  384. }
  385. RtlCopyMemory(
  386. rgwszUserName,
  387. pUserName->Buffer,
  388. pUserName->Length);
  389. rgwszUserName[pUserName->Length / sizeof(WCHAR)] = L'\0';
  390. //
  391. // unmarshall the cert cred info from the user name field
  392. // of the cred man cred
  393. //
  394. if (!CredUnmarshalCredentialW(
  395. rgwszUserName,
  396. &MarshalType,
  397. (void**)&pCertCredInfo
  398. ))
  399. {
  400. goto Cleanup;
  401. }
  402. if (CertCredential != MarshalType)
  403. {
  404. goto Cleanup;
  405. }
  406. // first need to impersonate the user so that we can call the
  407. // credential manager as that user
  408. // TODO: check if this fails.
  409. // don't do this until new ImpersonateLuid() is available.
  410. //
  411. if (NULL == ClientLogonId)
  412. {
  413. if (fImpersonateClient)
  414. {
  415. Status = LsaFunctions->ImpersonateClient();
  416. if (!NT_SUCCESS (Status))
  417. {
  418. goto Cleanup;
  419. }
  420. }
  421. else
  422. {
  423. goto Cleanup;
  424. }
  425. }
  426. else
  427. {
  428. Status = LsaFunctions->OpenTokenByLogonId(
  429. ClientLogonId,
  430. &ClientTokenHandle
  431. );
  432. if (!NT_SUCCESS(Status))
  433. {
  434. D_DebugLog((DEB_ERROR,"Unable to get the client token handle.\n"));
  435. goto Cleanup;
  436. }
  437. if(!SetThreadToken(NULL, ClientTokenHandle))
  438. {
  439. D_DebugLog((DEB_ERROR,"Unable to impersonate the client token handle.\n"));
  440. Status = STATUS_CANNOT_IMPERSONATE;
  441. goto Cleanup;
  442. }
  443. }
  444. fImpersonating = TRUE;
  445. // open a cert store if necessary
  446. if (NULL == hCertStore)
  447. {
  448. hCertStore = CertOpenStore(
  449. CERT_STORE_PROV_SYSTEM_W,
  450. 0,
  451. 0,
  452. CERT_SYSTEM_STORE_CURRENT_USER,
  453. L"MY");
  454. if (NULL == hCertStore)
  455. {
  456. Status = SEC_E_NO_CREDENTIALS;
  457. D_DebugLog((DEB_ERROR,"Failed to open the user cert store even though a cert cred was found.\n"));
  458. goto Cleanup;
  459. }
  460. }
  461. // find the cert in the store which meets this hash
  462. HashBlob.cbData = sizeof(pCertCredInfo->rgbHashOfCert);
  463. HashBlob.pbData = pCertCredInfo->rgbHashOfCert;
  464. *ppCertContext = (PCERT_CONTEXT)CertFindCertificateInStore(
  465. hCertStore,
  466. X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  467. 0,
  468. CERT_FIND_HASH,
  469. &HashBlob,
  470. NULL);
  471. if (NULL == *ppCertContext)
  472. {
  473. Status = SEC_E_NO_CREDENTIALS;
  474. D_DebugLog((DEB_ERROR,"Failed to find cert in store even though a cert cred was found.\n"));
  475. goto Cleanup;
  476. }
  477. Cleanup:
  478. if (NULL != hCertStore)
  479. {
  480. CertCloseStore(hCertStore, 0);
  481. }
  482. if (fImpersonating)
  483. {
  484. RevertToSelf();
  485. }
  486. if (NULL != pCertCredInfo)
  487. {
  488. CredFree (pCertCredInfo);
  489. }
  490. if(ClientTokenHandle != NULL)
  491. {
  492. CloseHandle( ClientTokenHandle );
  493. }
  494. SafeAllocaFree(SlowUserName);
  495. return Status;
  496. }
  497. // check username for domain/ or @ format
  498. NTSTATUS
  499. CredpParseUserName(
  500. IN OUT LPWSTR ParseName,
  501. OUT PUNICODE_STRING pUserName,
  502. OUT PUNICODE_STRING pDomainName
  503. )
  504. /*++
  505. Routine Description:
  506. This routine separates a passed in user name into domain and username. A user name must have one
  507. of the following two syntaxes:
  508. <DomainName>\<UserName>
  509. <UserName>@<DnsDomainName>
  510. The name is considered to have the first syntax if the string contains an \.
  511. A string containing a @ is ambiguous since <UserName> may contain an @.
  512. For the second syntax, the last @ in the string is used since <UserName> may
  513. contain an @ but <DnsDomainName> cannot.
  514. NOTE - The function does not allocate the UNICODE_STRING buffers
  515. so these should not be freed (RtlInitUnicodeString is used)
  516. Arguments:
  517. ParseName - Name of user to validate - will be modified
  518. pUserName - Returned pointing to canonical name inside of ParseName
  519. pDomainName - Returned pointing to domain name inside of ParseName
  520. Return Values:
  521. The following status codes may be returned:
  522. STATUS_INVALID_ACCOUNT_NAME - The user name is not valid.
  523. --*/
  524. {
  525. NTSTATUS Status;
  526. LPWSTR SlashPointer;
  527. LPWSTR AtPointer;
  528. LPWSTR pTmpUserName = NULL;
  529. LPWSTR pTmpDomainName = NULL;
  530. //
  531. // NULL is invalid
  532. //
  533. if ( ParseName == NULL ) {
  534. Status = STATUS_INVALID_ACCOUNT_NAME;
  535. goto Cleanup;
  536. }
  537. //
  538. // Classify the input account name.
  539. //
  540. // The name is considered to be <DomainName>\<UserName> if the string
  541. // contains an \.
  542. //
  543. SlashPointer = wcsrchr( ParseName, L'\\' );
  544. if ( SlashPointer != NULL )
  545. {
  546. //
  547. // point the output strings
  548. //
  549. pTmpDomainName = ParseName;
  550. //
  551. // Skip the backslash
  552. //
  553. *SlashPointer = L'\0';
  554. SlashPointer ++;
  555. pTmpUserName = SlashPointer;
  556. //
  557. // Otherwise the name must be a UPN
  558. //
  559. }
  560. else
  561. {
  562. //
  563. // A UPN has the syntax <AccountName>@<DnsDomainName>.
  564. // If there are multiple @ signs,
  565. // use the last one since an AccountName can have an @ in it.
  566. //
  567. //
  568. AtPointer = wcsrchr( ParseName, L'@' );
  569. if ( AtPointer == NULL )
  570. {
  571. // must be just <username>
  572. pTmpUserName = ParseName;
  573. }
  574. else
  575. {
  576. pTmpUserName = ParseName;
  577. *AtPointer = L'\0';
  578. AtPointer ++;
  579. pTmpDomainName = AtPointer;
  580. }
  581. }
  582. RtlInitUnicodeString( pUserName, pTmpUserName );
  583. RtlInitUnicodeString( pDomainName, pTmpDomainName );
  584. Status = STATUS_SUCCESS;
  585. //
  586. // Cleanup
  587. //
  588. Cleanup:
  589. return Status;
  590. }
  591. NTSTATUS
  592. CredpExtractMarshalledTargetInfo(
  593. IN PUNICODE_STRING TargetServerName,
  594. OUT CREDENTIAL_TARGET_INFORMATIONW **pTargetInfo
  595. )
  596. {
  597. NTSTATUS Status;
  598. //
  599. // LSA will set Length to include only the non-marshalled portion,
  600. // with MaximumLength trailing data to include marshalled portion.
  601. //
  602. if( (TargetServerName == NULL) ||
  603. (TargetServerName->Buffer == NULL) ||
  604. (TargetServerName->Length >= TargetServerName->MaximumLength) ||
  605. ((TargetServerName->MaximumLength - TargetServerName->Length) < CRED_MARSHALED_TI_SIZE_SIZE )
  606. )
  607. {
  608. return STATUS_SUCCESS;
  609. }
  610. //
  611. // Unmarshal the target info
  612. //
  613. Status = CredUnmarshalTargetInfo (
  614. TargetServerName->Buffer,
  615. TargetServerName->MaximumLength,
  616. pTargetInfo,
  617. NULL );
  618. if( !NT_SUCCESS(Status) )
  619. {
  620. if( Status == STATUS_INVALID_PARAMETER )
  621. {
  622. Status = STATUS_SUCCESS;
  623. }
  624. }
  625. return Status ;
  626. }
  627. //+-------------------------------------------------------------------------
  628. //
  629. // Function: KerbCheckForPKINITEnhKeyUsage
  630. //
  631. // Synopsis: Checks if the passed in cert context contains the
  632. // PKINIT enhanced key usage.
  633. //
  634. // Arguments: pCertContext - cert context to check for enh key usage
  635. //
  636. // Requires:
  637. //
  638. // Returns: TRUE is success, FALSE is failure
  639. //
  640. // Notes:
  641. //
  642. //
  643. //--------------------------------------------------------------------------
  644. BOOL
  645. KerbCheckForPKINITEnhKeyUsage(
  646. IN PCERT_CONTEXT pCertContext
  647. )
  648. {
  649. LPSTR pszClientAuthUsage = KERB_PKINIT_CLIENT_CERT_TYPE;
  650. PCERT_ENHKEY_USAGE pEnhKeyUsage = NULL;
  651. ULONG cbEnhKeyUsage = 0;
  652. ULONG i;
  653. BOOLEAN fRet = FALSE;
  654. if ( pCertContext == NULL )
  655. {
  656. return FALSE;
  657. }
  658. if (!CertGetEnhancedKeyUsage(
  659. pCertContext,
  660. CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG,
  661. NULL,
  662. &cbEnhKeyUsage))
  663. {
  664. goto Cleanup;
  665. }
  666. //
  667. // Allocate space for the key usage structure
  668. //
  669. SafeAllocaAllocate(pEnhKeyUsage, cbEnhKeyUsage);
  670. if (NULL == pEnhKeyUsage)
  671. {
  672. goto Cleanup;
  673. }
  674. if (!CertGetEnhancedKeyUsage(
  675. pCertContext,
  676. CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG,
  677. pEnhKeyUsage,
  678. &cbEnhKeyUsage))
  679. {
  680. goto Cleanup;
  681. }
  682. //
  683. // Enumerate through the enh key usages looking for the PKINIT one
  684. //
  685. for (i=0;i<pEnhKeyUsage->cUsageIdentifier;i++)
  686. {
  687. if (0 == strcmp(pszClientAuthUsage, pEnhKeyUsage->rgpszUsageIdentifier[i]))
  688. {
  689. fRet = TRUE;
  690. goto Cleanup;
  691. }
  692. }
  693. Cleanup:
  694. SafeAllocaFree(pEnhKeyUsage);
  695. return fRet;
  696. }
  697. //+-------------------------------------------------------------------------
  698. //
  699. // Function: KerbAddCertCredToPrimaryCredential
  700. //
  701. // Synopsis: Adds cert context and Pin info to the kerb credential
  702. // structure.
  703. //
  704. // Arguments: pCertContext - logon session
  705. // pCertCredInfo - cert cred manager info
  706. // pKerbCred - credential to be updated
  707. //
  708. // Requires:
  709. //
  710. // Returns:
  711. //
  712. // Notes:
  713. //
  714. //
  715. //--------------------------------------------------------------------------
  716. NTSTATUS
  717. KerbAddCertCredToPrimaryCredential(
  718. IN PKERB_LOGON_SESSION pLogonSession,
  719. IN PUNICODE_STRING pTargetName,
  720. IN PCERT_CONTEXT pCertContext,
  721. IN PUNICODE_STRING pPin,
  722. IN ULONG CredFlags,
  723. IN OUT PKERB_PRIMARY_CREDENTIAL *ppCredMgrCred
  724. )
  725. {
  726. UNICODE_STRING UserName = {0};
  727. UNICODE_STRING DomainName = {0}; // get the domain from the UPN in the cert
  728. PKERB_PRIMARY_CREDENTIAL pOldCred;
  729. PKERB_PRIMARY_CREDENTIAL pNewCred = NULL;
  730. NTSTATUS Status = STATUS_SUCCESS;
  731. //
  732. // Get the client name from the cert.
  733. // Place it in the return location
  734. //
  735. Status = KerbGetPrincipalNameFromCertificate(pCertContext, &UserName);
  736. if (!NT_SUCCESS(Status))
  737. {
  738. goto Cleanup;
  739. }
  740. //
  741. // Initialize the primary credentials structure
  742. //
  743. Status = KerbInitPrimaryCreds(
  744. pLogonSession,
  745. &UserName,
  746. &DomainName,
  747. pTargetName,
  748. pPin,
  749. TRUE,
  750. pCertContext,
  751. &pNewCred
  752. );
  753. if (!NT_SUCCESS(Status))
  754. {
  755. goto Cleanup;
  756. }
  757. pNewCred->PublicKeyCreds->InitializationInfo |= CredFlags;
  758. Status = KerbInitializePkCreds(
  759. pNewCred->PublicKeyCreds
  760. );
  761. if (!NT_SUCCESS(Status))
  762. {
  763. goto Cleanup;
  764. }
  765. pOldCred = *ppCredMgrCred;
  766. *ppCredMgrCred = pNewCred;
  767. pNewCred = NULL;
  768. KerbFreePrimaryCredentials(pOldCred, TRUE);
  769. Cleanup:
  770. KerbFreeString(&UserName);
  771. KerbFreePrimaryCredentials(pNewCred, TRUE);
  772. return Status;
  773. }
  774. //+-------------------------------------------------------------------------
  775. //
  776. // Function: KerbAddPasswordCredToPrimaryCredential
  777. //
  778. // Synopsis: Adds cert context and Pin info to the kerb credential
  779. // structure.
  780. //
  781. // Arguments: pCertContext - logon session
  782. // pCertCredInfo - cert cred manager info
  783. // pKerbCred - credential to be updated
  784. //
  785. // Requires:
  786. //
  787. // Returns:
  788. //
  789. // Notes:
  790. //
  791. //
  792. //--------------------------------------------------------------------------
  793. NTSTATUS
  794. KerbAddPasswordCredToPrimaryCredential(
  795. IN PKERB_LOGON_SESSION pLogonSession,
  796. IN PUNICODE_STRING pUserName,
  797. IN PUNICODE_STRING pTargetDomainName,
  798. IN PUNICODE_STRING pPassword,
  799. IN OUT PKERB_PRIMARY_CREDENTIAL *ppCredMgrCred
  800. )
  801. {
  802. PKERB_PRIMARY_CREDENTIAL pOldCred;
  803. PKERB_PRIMARY_CREDENTIAL pNewCred = NULL;
  804. UNICODE_STRING RevealedPassword;
  805. NTSTATUS Status = STATUS_SUCCESS;
  806. RtlZeroMemory(&RevealedPassword, sizeof(RevealedPassword));
  807. Status = KerbDuplicatePassword(
  808. &RevealedPassword,
  809. pPassword
  810. );
  811. if (!NT_SUCCESS(Status))
  812. {
  813. goto Cleanup;
  814. }
  815. KerbRevealPassword( &RevealedPassword );
  816. //
  817. // Initialize the primary credentials structure
  818. //
  819. Status = KerbInitPrimaryCreds(
  820. pLogonSession,
  821. pUserName,
  822. pTargetDomainName,
  823. NULL,
  824. &RevealedPassword,
  825. FALSE,
  826. NULL,
  827. &pNewCred
  828. );
  829. if (!NT_SUCCESS(Status))
  830. {
  831. goto Cleanup;
  832. }
  833. pOldCred = *ppCredMgrCred;
  834. *ppCredMgrCred = pNewCred;
  835. pNewCred = NULL;
  836. KerbFreePrimaryCredentials(pOldCred, TRUE);
  837. Cleanup:
  838. if ((0 != RevealedPassword.Length) && (NULL != RevealedPassword.Buffer))
  839. {
  840. RtlSecureZeroMemory(RevealedPassword.Buffer, RevealedPassword.Length);
  841. KerbFreeString(&RevealedPassword);
  842. }
  843. //
  844. // Don't leak password length
  845. //
  846. RevealedPassword.Length = RevealedPassword.MaximumLength = 0;
  847. KerbFreePrimaryCredentials(pNewCred, TRUE);
  848. return Status;
  849. }
  850. //+-------------------------------------------------------------------------
  851. //
  852. // Function: KerbCreateCredmanCred
  853. //
  854. // Synopsis: Goes to the credential manager to try and find
  855. // credentials for the specific target
  856. //
  857. // Arguments:
  858. // CredToAdd - PrimaryCredential to add to credman cred
  859. // ppNewCred - IN OUT built cred, free w/ KerbFreeCredmanCred
  860. //
  861. // Requires:
  862. //
  863. // Returns:
  864. //
  865. // Notes:
  866. //
  867. //
  868. //--------------------------------------------------------------------------
  869. NTSTATUS
  870. KerbCreateCredmanCred(
  871. IN PKERB_PRIMARY_CREDENTIAL CredToAdd,
  872. IN ULONG AdditionalCredFlags,
  873. IN OUT PKERB_CREDMAN_CRED * ppNewCred
  874. )
  875. {
  876. NTSTATUS Status = STATUS_SUCCESS;
  877. *ppNewCred = NULL;
  878. *ppNewCred = (PKERB_CREDMAN_CRED) KerbAllocate(sizeof(KERB_CREDMAN_CRED));
  879. if (NULL == *ppNewCred)
  880. {
  881. return STATUS_INSUFFICIENT_RESOURCES;
  882. }
  883. Status = KerbDuplicateStringEx(
  884. &(*ppNewCred)->CredmanUserName,
  885. &CredToAdd->UserName,
  886. FALSE
  887. );
  888. if (!NT_SUCCESS(Status))
  889. {
  890. goto Cleanup;
  891. }
  892. Status = KerbDuplicateStringEx(
  893. &(*ppNewCred)->CredmanDomainName,
  894. &CredToAdd->DomainName,
  895. FALSE
  896. );
  897. if (!NT_SUCCESS(Status))
  898. {
  899. goto Cleanup;
  900. }
  901. (*ppNewCred)->SuppliedCredentials = CredToAdd;
  902. (*ppNewCred)->CredentialFlags |= AdditionalCredFlags;
  903. Cleanup:
  904. if (!NT_SUCCESS(Status))
  905. {
  906. KerbFreeCredmanCred(*ppNewCred);
  907. *ppNewCred = NULL;
  908. }
  909. return (Status);
  910. }
  911. //+-------------------------------------------------------------------------
  912. //
  913. // Function: KerbAddCredmanCredToLogonSession
  914. //
  915. // Synopsis: Goes to the credential manager to try and find
  916. // credentials for the specific target
  917. //
  918. // Arguments: pLogonSession - logon session
  919. // CredToMatch - PrimaryCredential to look for in logon session
  920. //
  921. // Requires: Hold logon session lock...
  922. //
  923. // Returns:
  924. //
  925. // Notes: CredToMatch freed in this function...
  926. //
  927. //
  928. //--------------------------------------------------------------------------
  929. NTSTATUS
  930. KerbAddCredmanCredToLogonSession(
  931. IN PKERB_LOGON_SESSION pLogonSession,
  932. IN PKERB_PRIMARY_CREDENTIAL CredToMatch,
  933. IN ULONG AdditionalCredFlags,
  934. IN OUT PKERB_CREDMAN_CRED *NewCred
  935. )
  936. {
  937. NTSTATUS Status = STATUS_SUCCESS;
  938. PKERB_CREDMAN_CRED CredmanCred = NULL;
  939. PLIST_ENTRY ListEntry;
  940. BOOLEAN PublicKeyCred = FALSE;
  941. BOOLEAN Found = FALSE;
  942. *NewCred = NULL;
  943. //
  944. // First, make a determination if the cred's already listed
  945. // Replace w/ new one if password has changed.
  946. //
  947. KerbLockList(&pLogonSession->CredmanCredentials);
  948. //
  949. // Go through the list of logon sessions looking for the correct
  950. // credentials, if they exist...
  951. //
  952. for (ListEntry = pLogonSession->CredmanCredentials.List.Flink ;
  953. (ListEntry != &pLogonSession->CredmanCredentials.List && !Found);
  954. ListEntry = ListEntry->Flink )
  955. {
  956. CredmanCred = CONTAINING_RECORD(ListEntry, KERB_CREDMAN_CRED, ListEntry.Next);
  957. // We only match on UserName / DomainName for credman creds
  958. if(!RtlEqualUnicodeString(
  959. &CredToMatch->UserName,
  960. &CredmanCred->CredmanUserName,
  961. TRUE
  962. ))
  963. {
  964. continue;
  965. }
  966. if(!RtlEqualUnicodeString(
  967. &CredToMatch->DomainName,
  968. &CredmanCred->CredmanDomainName,
  969. TRUE
  970. ))
  971. {
  972. continue;
  973. }
  974. //
  975. // Differentiate between pkiint & password based structures
  976. //
  977. if ((CredmanCred->SuppliedCredentials->PublicKeyCreds != NULL) &&
  978. (CredToMatch->PublicKeyCreds != NULL))
  979. {
  980. if (!KerbComparePublicKeyCreds(
  981. CredToMatch->PublicKeyCreds,
  982. CredmanCred->SuppliedCredentials->PublicKeyCreds
  983. ))
  984. {
  985. continue;
  986. }
  987. PublicKeyCred = TRUE;
  988. }
  989. Found = TRUE;
  990. *NewCred = CredmanCred;
  991. } // FOR
  992. if (Found)
  993. {
  994. KerbReferenceCredmanCred(
  995. *NewCred,
  996. pLogonSession,
  997. FALSE
  998. );
  999. D_DebugLog((DEB_TRACE_CRED, "Found match %p\n", (*NewCred)));
  1000. //
  1001. // Found one. Now we've got to compare the pwd information, and
  1002. // change it, if needed...
  1003. //
  1004. if (!PublicKeyCred)
  1005. {
  1006. //
  1007. // Compare the password list, as the pwd may have changed...
  1008. // Note: This has the by-product of tossing old tickets, but
  1009. // that's desirable if the pwd's changed, so user knows the creds
  1010. // are bogus.
  1011. //
  1012. if (!KerbComparePasswords(
  1013. (*NewCred)->SuppliedCredentials->Passwords,
  1014. CredToMatch->Passwords
  1015. ))
  1016. {
  1017. D_DebugLog((DEB_ERROR, "Changing credman cred password\n"));
  1018. PKERB_PRIMARY_CREDENTIAL OldPwds = (*NewCred)->SuppliedCredentials;
  1019. (*NewCred)->SuppliedCredentials = CredToMatch;
  1020. KerbFreePrimaryCredentials(OldPwds, TRUE);
  1021. (*NewCred)->CredentialFlags &= ~KERB_CRED_TGT_AVAIL;
  1022. }
  1023. else
  1024. {
  1025. KerbFreePrimaryCredentials(CredToMatch, TRUE);
  1026. }
  1027. }
  1028. else
  1029. {
  1030. //
  1031. // Free up the cred to match, since we already have a copy stored w/ our credential
  1032. //
  1033. KerbFreePrimaryCredentials(CredToMatch, TRUE);
  1034. }
  1035. }
  1036. else // new cred, so prepare CredmanCred to add to list...
  1037. {
  1038. Status = KerbCreateCredmanCred(
  1039. CredToMatch,
  1040. AdditionalCredFlags,
  1041. NewCred
  1042. );
  1043. if (!NT_SUCCESS(Status))
  1044. {
  1045. goto Cleanup;
  1046. }
  1047. KerbInsertListEntryTail(
  1048. &((*NewCred)->ListEntry),
  1049. &pLogonSession->CredmanCredentials
  1050. );
  1051. // add a ref for caller of this function.
  1052. KerbReferenceCredmanCred(
  1053. (*NewCred),
  1054. pLogonSession,
  1055. FALSE
  1056. );
  1057. }
  1058. //
  1059. // We need an initial TGT for this cred
  1060. //
  1061. if (((*NewCred)->CredentialFlags & KERB_CRED_TGT_AVAIL) == 0)
  1062. {
  1063. //
  1064. // Get an initial TGT for this cred.
  1065. //
  1066. Status = KerbGetTicketGrantingTicket(
  1067. pLogonSession,
  1068. NULL,
  1069. (*NewCred),
  1070. NULL,
  1071. NULL,
  1072. NULL
  1073. );
  1074. if (!NT_SUCCESS(Status))
  1075. {
  1076. DebugLog((DEB_ERROR, "Failed to get TGT for credman cred - %x\n",Status));
  1077. if( Status == STATUS_NO_LOGON_SERVERS )
  1078. {
  1079. //
  1080. // negotiate treats NO_LOGON_SERVERS as a downgrade.
  1081. // Nego allows downgrade for explicit creds, but not default creds.
  1082. // Credman is basically explicit creds. So over-ride the error code.
  1083. //
  1084. Status = SEC_E_TARGET_UNKNOWN;
  1085. }
  1086. goto Cleanup;
  1087. }
  1088. (*NewCred)->CredentialFlags |= KERB_CRED_TGT_AVAIL;
  1089. }
  1090. Cleanup:
  1091. KerbUnlockList(&pLogonSession->CredmanCredentials);
  1092. return (Status);
  1093. }
  1094. //+-------------------------------------------------------------------------
  1095. //
  1096. // Function: KerbConvertCertCredential
  1097. //
  1098. // Synopsis: Converts a cert cred to a primary cred
  1099. //
  1100. // Arguments: pLogonSession - logon session
  1101. // pTargetName - service name
  1102. // pTargetDomainName - domain name
  1103. // pTargetForestName - forest name
  1104. // pKerbCred - credential to be allocated
  1105. //
  1106. // Requires: You've got to be impersonating when making this call.
  1107. //
  1108. // Returns:
  1109. //
  1110. // Notes:
  1111. //
  1112. //
  1113. //--------------------------------------------------------------------------
  1114. NTSTATUS
  1115. KerbConvertCertCredential(
  1116. IN PKERB_LOGON_SESSION LogonSession,
  1117. IN LPCWSTR MarshalledCredential,
  1118. IN PUNICODE_STRING TargetName,
  1119. IN OUT PKERB_PRIMARY_CREDENTIAL * PrimaryCredential
  1120. )
  1121. {
  1122. NTSTATUS Status;
  1123. CRED_MARSHAL_TYPE MarshalType;
  1124. PCERT_CREDENTIAL_INFO pCertCredInfo = NULL;
  1125. PKERB_PRIMARY_CREDENTIAL LocalCredential = NULL;
  1126. HCERTSTORE hCertStore = NULL;
  1127. CRYPT_HASH_BLOB HashBlob;
  1128. PCERT_CONTEXT pCertContext = NULL;
  1129. UNICODE_STRING Pin = {0};
  1130. *PrimaryCredential = NULL;
  1131. //
  1132. // unmarshal the cert cred info from the user name field
  1133. // of the cred man cred
  1134. //
  1135. if (!CredUnmarshalCredentialW(
  1136. MarshalledCredential,
  1137. &MarshalType,
  1138. (void**)&pCertCredInfo
  1139. ))
  1140. {
  1141. Status = STATUS_LOGON_FAILURE;
  1142. goto Cleanup;
  1143. }
  1144. if (CertCredential != MarshalType)
  1145. {
  1146. Status = STATUS_LOGON_FAILURE;
  1147. goto Cleanup;
  1148. }
  1149. // open a cert store if necessary
  1150. hCertStore = CertOpenStore(
  1151. CERT_STORE_PROV_SYSTEM_W,
  1152. 0,
  1153. 0,
  1154. CERT_SYSTEM_STORE_CURRENT_USER,
  1155. L"MY"
  1156. );
  1157. if (NULL == hCertStore)
  1158. {
  1159. D_DebugLog((DEB_ERROR,"Failed to open the user cert store even though a cert cred was found.\n"));
  1160. Status = STATUS_LOGON_FAILURE;
  1161. goto Cleanup;
  1162. }
  1163. // find the cert in the store which meets this hash
  1164. HashBlob.cbData = sizeof(pCertCredInfo->rgbHashOfCert);
  1165. HashBlob.pbData = pCertCredInfo->rgbHashOfCert;
  1166. pCertContext = (PCERT_CONTEXT)CertFindCertificateInStore(
  1167. hCertStore,
  1168. X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  1169. 0,
  1170. CERT_FIND_HASH,
  1171. &HashBlob,
  1172. pCertContext);
  1173. if ( KerbCheckForPKINITEnhKeyUsage( pCertContext ) )
  1174. {
  1175. //
  1176. // add the cert credential to the Kerb credential
  1177. //
  1178. // Cred man will no longer give us a pin, only the CSP
  1179. // knows that information...
  1180. //
  1181. Status = KerbAddCertCredToPrimaryCredential(
  1182. LogonSession,
  1183. TargetName,
  1184. pCertContext,
  1185. &Pin, // essentially, a NULL string
  1186. CONTEXT_INITIALIZED_WITH_CRED_MAN_CREDS,
  1187. &LocalCredential
  1188. );
  1189. if (!NT_SUCCESS(Status))
  1190. {
  1191. D_DebugLog((DEB_WARN,"Failed to add the cert cred to the credential.\n"));
  1192. goto Cleanup;
  1193. }
  1194. }
  1195. else
  1196. {
  1197. //
  1198. // Can't find the certificate
  1199. //
  1200. DebugLog((DEB_ERROR, "Can't find cert from credman\n"));
  1201. //
  1202. // TBD:
  1203. // Log Event
  1204. /*
  1205. KerbReportCredmanError(ID_MISSING_CERT);
  1206. */
  1207. Status = STATUS_LOGON_FAILURE;
  1208. goto Cleanup;
  1209. }
  1210. *PrimaryCredential = LocalCredential;
  1211. LocalCredential = NULL;
  1212. Cleanup:
  1213. if (NULL != pCertCredInfo)
  1214. {
  1215. CredFree(pCertCredInfo);
  1216. }
  1217. if (NULL != pCertContext)
  1218. {
  1219. CertFreeCertificateContext(pCertContext);
  1220. }
  1221. if ( LocalCredential )
  1222. {
  1223. KerbFree( LocalCredential );
  1224. }
  1225. if (NULL != hCertStore)
  1226. {
  1227. CertCloseStore(hCertStore, 0);
  1228. }
  1229. return (Status);
  1230. }
  1231. //+-------------------------------------------------------------------------
  1232. //
  1233. // Function: KerbCheckCredMgrForGivenTarget
  1234. //
  1235. // Synopsis: Goes to the credential manager to try and find
  1236. // credentials for the specific target
  1237. //
  1238. // Arguments: pLogonSession - logon session
  1239. // pTargetName - service name
  1240. // pTargetDomainName - domain name
  1241. // pTargetForestName - forest name
  1242. // pKerbCred - credential to be allocated
  1243. //
  1244. // Requires:
  1245. //
  1246. // Returns:
  1247. //
  1248. // Notes:
  1249. //
  1250. //
  1251. //--------------------------------------------------------------------------
  1252. NTSTATUS
  1253. KerbCheckCredMgrForGivenTarget(
  1254. IN PKERB_LOGON_SESSION LogonSession,
  1255. IN PKERB_CREDENTIAL Credential,
  1256. IN PUNICODE_STRING SuppliedTargetName,
  1257. IN PKERB_INTERNAL_NAME pTargetName,
  1258. IN ULONG TargetInfoFlags,
  1259. IN PUNICODE_STRING pTargetDomainName,
  1260. IN PUNICODE_STRING pTargetForestName,
  1261. IN OUT PKERB_CREDMAN_CRED *CredmanCred,
  1262. IN OUT PBYTE *pbMarshalledTargetInfo,
  1263. IN OUT ULONG *cbMarshalledTargetInfo
  1264. )
  1265. {
  1266. CREDENTIAL_TARGET_INFORMATIONW CredTargetInfo;
  1267. ULONG cCreds = 0;
  1268. PCREDENTIALW *rgpCreds = NULL;
  1269. PCREDENTIALW CertCred = NULL;
  1270. PCREDENTIALW PasswordCred = NULL;
  1271. PENCRYPTED_CREDENTIALW *rgpEncryptedCreds = NULL;
  1272. LPWSTR pwszTargetName = NULL;
  1273. LPWSTR pwszDomainName = NULL;
  1274. LPWSTR pwszForestName = NULL;
  1275. BOOLEAN Impersonating = FALSE;
  1276. ULONG i;
  1277. UNICODE_STRING CredManUserName = {0};
  1278. UNICODE_STRING CredManDomainName = {0};
  1279. UNICODE_STRING CredManTargetName = {0};
  1280. UNICODE_STRING Password = {0};
  1281. UNICODE_STRING RevealedPassword;
  1282. HANDLE ClientTokenHandle = NULL;
  1283. CREDENTIAL_TARGET_INFORMATIONW *pTargetInfo = NULL;
  1284. NTSTATUS Status = STATUS_SUCCESS;
  1285. PKERB_PRIMARY_CREDENTIAL pCredMgrCred = NULL;
  1286. HANDLE ImpersonationToken = NULL;
  1287. USHORT ClearBlobSize = 0;
  1288. ULONG AdditionalCredFlags = 0;
  1289. SECPKG_CALL_INFO CallInfo = {0};
  1290. RtlZeroMemory(&CredTargetInfo, sizeof(CredTargetInfo));
  1291. RtlZeroMemory(&RevealedPassword, sizeof(RevealedPassword));
  1292. *CredmanCred = NULL;
  1293. Status = CredpExtractMarshalledTargetInfo(
  1294. SuppliedTargetName,
  1295. &pTargetInfo
  1296. );
  1297. if (!NT_SUCCESS(Status))
  1298. {
  1299. goto Cleanup;
  1300. }
  1301. if (!LsaFunctions->GetCallInfo(&CallInfo))
  1302. {
  1303. Status = STATUS_INTERNAL_ERROR;
  1304. goto Cleanup;
  1305. }
  1306. //
  1307. // Allocate space for the names
  1308. //
  1309. if (NULL != pTargetName)
  1310. {
  1311. //
  1312. // want to use the second part of the SPN
  1313. //
  1314. if (pTargetName->NameCount > 1)
  1315. {
  1316. SafeAllocaAllocate(pwszTargetName, pTargetName->Names[1].Length + sizeof(WCHAR));
  1317. if (NULL == pwszTargetName)
  1318. {
  1319. Status = STATUS_INSUFFICIENT_RESOURCES;
  1320. goto Cleanup;
  1321. }
  1322. RtlCopyMemory(
  1323. (PUCHAR)pwszTargetName,
  1324. pTargetName->Names[1].Buffer,
  1325. pTargetName->Names[1].Length);
  1326. pwszTargetName[pTargetName->Names[1].Length / sizeof(WCHAR)] = L'\0';
  1327. CredTargetInfo.DnsServerName = pwszTargetName;
  1328. RtlInitUnicodeString(&CredManTargetName, pwszTargetName);
  1329. }
  1330. }
  1331. if ((NULL != pTargetDomainName) && (0 != pTargetDomainName->Length) &&
  1332. (NULL != pTargetDomainName->Buffer))
  1333. {
  1334. SafeAllocaAllocate(pwszDomainName, pTargetDomainName->Length + sizeof(WCHAR));
  1335. if (NULL == pwszDomainName)
  1336. {
  1337. Status = STATUS_INSUFFICIENT_RESOURCES;
  1338. goto Cleanup;
  1339. }
  1340. RtlCopyMemory(
  1341. (PUCHAR)pwszDomainName,
  1342. pTargetDomainName->Buffer,
  1343. pTargetDomainName->Length);
  1344. pwszDomainName[pTargetDomainName->Length / sizeof(WCHAR)] = L'\0';
  1345. CredTargetInfo.DnsDomainName = pwszDomainName;
  1346. }
  1347. if ((NULL != pTargetForestName) && (0 != pTargetForestName->Length) &&
  1348. (NULL != pTargetForestName->Buffer))
  1349. {
  1350. SafeAllocaAllocate(pwszForestName, pTargetForestName->Length + sizeof(WCHAR));
  1351. if (NULL == pwszForestName)
  1352. {
  1353. Status = STATUS_INSUFFICIENT_RESOURCES;
  1354. goto Cleanup;
  1355. }
  1356. RtlCopyMemory(
  1357. (PUCHAR)pwszForestName,
  1358. pTargetForestName->Buffer,
  1359. pTargetForestName->Length);
  1360. pwszForestName[pTargetForestName->Length / sizeof(WCHAR)] = L'\0';
  1361. CredTargetInfo.DnsTreeName = pwszForestName;
  1362. }
  1363. CredTargetInfo.PackageName = KERBEROS_PACKAGE_NAME;
  1364. //
  1365. // if marshalled targetinfo supplied, use it instead.
  1366. //
  1367. if ( pTargetInfo )
  1368. {
  1369. CredTargetInfo.TargetName = pTargetInfo->TargetName;
  1370. CredTargetInfo.NetbiosServerName = pTargetInfo->NetbiosServerName;
  1371. CredTargetInfo.DnsServerName = pTargetInfo->DnsServerName;
  1372. CredTargetInfo.NetbiosDomainName = pTargetInfo->NetbiosDomainName;
  1373. CredTargetInfo.DnsDomainName = pTargetInfo->DnsDomainName;
  1374. CredTargetInfo.DnsTreeName = pTargetInfo->DnsTreeName;
  1375. CredTargetInfo.Flags |= pTargetInfo->Flags;
  1376. }
  1377. else
  1378. {
  1379. //
  1380. // copy the names in to the memory and set the names
  1381. // in the PCREDENTIAL_TARGET_INFORMATIONW struct
  1382. //
  1383. if (pwszTargetName)
  1384. {
  1385. CredTargetInfo.Flags |= CRED_TI_SERVER_FORMAT_UNKNOWN;
  1386. }
  1387. if (pwszDomainName)
  1388. {
  1389. CredTargetInfo.Flags |= CRED_TI_DOMAIN_FORMAT_UNKNOWN;
  1390. }
  1391. CredTargetInfo.Flags |= TargetInfoFlags;
  1392. }
  1393. // need to specify a flag to indicate that we don't know what we are
  1394. // doing and both types of names should be checked.
  1395. Status = LsaFunctions->CrediReadDomainCredentials(
  1396. &LogonSession->LogonId,
  1397. CREDP_FLAGS_IN_PROCESS, // Allow password to be returned
  1398. &CredTargetInfo,
  1399. 0,
  1400. &cCreds,
  1401. &rgpEncryptedCreds );
  1402. rgpCreds = (PCREDENTIALW *) rgpEncryptedCreds;
  1403. //
  1404. // return a copy of the credential target info for kernel callers (MUP/DFS/RDR).
  1405. //
  1406. if (NT_SUCCESS(Status) || (CallInfo.Attributes & SECPKG_CALL_KERNEL_MODE))
  1407. {
  1408. CredMarshalTargetInfo(
  1409. &CredTargetInfo,
  1410. (PUSHORT*)pbMarshalledTargetInfo,
  1411. cbMarshalledTargetInfo
  1412. );
  1413. }
  1414. if (!NT_SUCCESS(Status))
  1415. {
  1416. // quiet these.
  1417. if ((Status == STATUS_NOT_FOUND) ||(Status == STATUS_NO_SUCH_LOGON_SESSION) )
  1418. {
  1419. D_DebugLog((DEB_TRACE, "No credentials from the cred mgr!\n", Status));
  1420. }
  1421. else
  1422. {
  1423. DebugLog((DEB_WARN, "Failed to read credentials from the cred mgr 0x%x.\n", Status));
  1424. }
  1425. // indicate success so we proceed with default creds
  1426. Status = STATUS_SUCCESS;
  1427. goto Cleanup;
  1428. }
  1429. //
  1430. // Look for cred types we understand.
  1431. //
  1432. for (i = 0; i < cCreds; i++)
  1433. {
  1434. if ((rgpCreds[i])->Type == CRED_TYPE_DOMAIN_CERTIFICATE)
  1435. {
  1436. CertCred = rgpCreds[i];
  1437. ClearBlobSize = (USHORT) (rgpEncryptedCreds[i])->ClearCredentialBlobSize;
  1438. }
  1439. else if ((rgpCreds[i])->Type == CRED_TYPE_DOMAIN_PASSWORD)
  1440. {
  1441. PasswordCred = rgpCreds[i];
  1442. ClearBlobSize = (USHORT) (rgpEncryptedCreds[i])->ClearCredentialBlobSize;
  1443. }
  1444. }
  1445. if (!(CertCred || PasswordCred))
  1446. {
  1447. DebugLog((DEB_ERROR, "Found no credman creds we understand\n"));
  1448. // indicate success so we proceed with default creds
  1449. Status = STATUS_SUCCESS;
  1450. goto Cleanup;
  1451. }
  1452. //
  1453. // now evaluate the creds which were returned to determine
  1454. // which one we should use.
  1455. //
  1456. // First choice is a certificate which may be
  1457. // used for PKINIT.
  1458. //
  1459. if ( CertCred )
  1460. {
  1461. // check for the prompt now flag
  1462. if (CertCred->Flags & CRED_FLAGS_PROMPT_NOW)
  1463. {
  1464. DebugLog((DEB_ERROR, "Asking for prompt on credman cred \n"));
  1465. Status = STATUS_SMARTCARD_SILENT_CONTEXT;
  1466. goto Cleanup;
  1467. }
  1468. if (!lstrcmpW(CRED_SESSION_WILDCARD_NAME_W, CertCred->TargetName))
  1469. {
  1470. AdditionalCredFlags |= RAS_CREDENTIAL;
  1471. }
  1472. if( !Impersonating )
  1473. {
  1474. //
  1475. // Save off the old token, if it exists.
  1476. //
  1477. Status = NtOpenThreadToken(
  1478. NtCurrentThread(),
  1479. TOKEN_QUERY | TOKEN_IMPERSONATE,
  1480. TRUE,
  1481. &ImpersonationToken
  1482. );
  1483. if (!NT_SUCCESS( Status ) && Status != STATUS_NO_TOKEN )
  1484. {
  1485. DebugLog((DEB_ERROR, "NtOpenThreadToken failed %x\n", Status));
  1486. goto Cleanup;
  1487. }
  1488. Status = LsaFunctions->OpenTokenByLogonId(
  1489. &LogonSession->LogonId,
  1490. &ClientTokenHandle
  1491. );
  1492. if (!NT_SUCCESS(Status))
  1493. {
  1494. D_DebugLog((DEB_ERROR,"Unable to get the client token handle.\n"));
  1495. goto Cleanup;
  1496. }
  1497. if(!SetThreadToken(NULL, ClientTokenHandle))
  1498. {
  1499. D_DebugLog((DEB_ERROR,"Unable to impersonate the client token handle.\n"));
  1500. Status = STATUS_CANNOT_IMPERSONATE;
  1501. goto Cleanup;
  1502. }
  1503. Impersonating = TRUE;
  1504. }
  1505. Status = KerbConvertCertCredential(
  1506. LogonSession,
  1507. CertCred->UserName,
  1508. &CredManTargetName,
  1509. &pCredMgrCred
  1510. );
  1511. if (!NT_SUCCESS( Status ))
  1512. {
  1513. DebugLog((DEB_ERROR, "KerbConvertCertCredential failed %x\n", Status));
  1514. goto Cleanup;
  1515. }
  1516. }
  1517. else if ( PasswordCred )
  1518. {
  1519. // check for the prompt now flag
  1520. if ( PasswordCred->Flags & CRED_FLAGS_PROMPT_NOW)
  1521. {
  1522. DebugLog((DEB_ERROR, "Asking for prompt on credman cred \n"));
  1523. Status = SEC_E_LOGON_DENIED;
  1524. goto Cleanup;
  1525. }
  1526. if (!lstrcmpW(CRED_SESSION_WILDCARD_NAME_W, PasswordCred->TargetName))
  1527. {
  1528. AdditionalCredFlags |= RAS_CREDENTIAL;
  1529. }
  1530. //
  1531. // get the user name and domain name from the credential manager info
  1532. //
  1533. // NOTE - CredpParseUserName does not allocate the UNICODE_STRING
  1534. // buffers so these should not be freed (RtlInitUnicodeString is used)
  1535. //
  1536. Status = CredpParseUserName(
  1537. PasswordCred->UserName,
  1538. &CredManUserName,
  1539. &CredManDomainName);
  1540. if (!NT_SUCCESS(Status))
  1541. {
  1542. D_DebugLog((DEB_WARN,"Failed to parse the add the cert cred to the credential.\n"));
  1543. goto Cleanup;
  1544. }
  1545. Password.Buffer = (LPWSTR)(PasswordCred->CredentialBlob);
  1546. Password.MaximumLength = (USHORT)PasswordCred->CredentialBlobSize;
  1547. Password.Length = ClearBlobSize;
  1548. // add the cert credential to the Kerb credential
  1549. Status = KerbAddPasswordCredToPrimaryCredential(
  1550. LogonSession,
  1551. &CredManUserName,
  1552. &CredManDomainName,
  1553. &Password,
  1554. &pCredMgrCred
  1555. );
  1556. if (!NT_SUCCESS(Status))
  1557. {
  1558. DebugLog((DEB_WARN,"Failed to add the cred mgr password to the credential.\n"));
  1559. goto Cleanup;
  1560. }
  1561. }
  1562. else
  1563. {
  1564. //
  1565. // NO creds found we can use.
  1566. //
  1567. DebugLog((DEB_ERROR, "No valid creds in credman\n"));
  1568. Status = STATUS_NOT_FOUND;
  1569. goto Cleanup;
  1570. }
  1571. //
  1572. // We've built the credman cred, now go ahead and add it to the logon.
  1573. //
  1574. Status = KerbAddCredmanCredToLogonSession(
  1575. LogonSession,
  1576. pCredMgrCred, // note: freed by this fn
  1577. AdditionalCredFlags,
  1578. CredmanCred
  1579. );
  1580. if (!NT_SUCCESS(Status))
  1581. {
  1582. DebugLog((DEB_ERROR, "Failed to add credman cred to logon session\n"));
  1583. goto Cleanup;
  1584. }
  1585. Cleanup:
  1586. if (Impersonating)
  1587. {
  1588. if (ImpersonationToken != NULL)
  1589. {
  1590. SetThreadToken(NULL, ImpersonationToken);
  1591. }
  1592. else
  1593. {
  1594. RevertToSelf();
  1595. }
  1596. }
  1597. if ( ImpersonationToken )
  1598. {
  1599. CloseHandle(ImpersonationToken);
  1600. }
  1601. if ( ClientTokenHandle )
  1602. {
  1603. CloseHandle( ClientTokenHandle );
  1604. }
  1605. if( pTargetInfo != NULL )
  1606. {
  1607. LocalFree( pTargetInfo );
  1608. }
  1609. SafeAllocaFree(pwszTargetName);
  1610. SafeAllocaFree(pwszDomainName);
  1611. SafeAllocaFree(pwszForestName);
  1612. if (NULL != rgpCreds)
  1613. {
  1614. //
  1615. // Free the returned credentials
  1616. //
  1617. LsaFunctions->CrediFreeCredentials(
  1618. cCreds,
  1619. rgpEncryptedCreds );
  1620. }
  1621. return Status;
  1622. }
  1623. NTSTATUS
  1624. CopyCredManCredentials(
  1625. IN PLUID LogonId,
  1626. CREDENTIAL_TARGET_INFORMATIONW* pTargetInfo,
  1627. IN OUT PUNICODE_STRING pUserName,
  1628. IN OUT PUNICODE_STRING pDomainName,
  1629. IN OUT PUNICODE_STRING pPassword
  1630. )
  1631. /*++
  1632. Routine Description:
  1633. Look for a keyring credential entry for the specified domain, and copy to Context handle if found
  1634. Arguments:
  1635. LogonId -- LogonId of the calling process.
  1636. pTargetInfo -- Information on target to search for creds.
  1637. Context - Points to the ContextHandle of the Context
  1638. to be referenced.
  1639. Return Value:
  1640. STATUS_SUCCESS -- All OK
  1641. STATUS_NOT_FOUND - Credential couldn't be found.
  1642. All others are real failures and should be returned to the caller.
  1643. --*/
  1644. {
  1645. NTSTATUS Status;
  1646. PCREDENTIALW *Credentials = NULL;
  1647. PENCRYPTED_CREDENTIALW *EncryptedCredentials = NULL;
  1648. ULONG CredentialCount;
  1649. ULONG CredIndex;
  1650. RtlInitUnicodeString(pUserName, NULL);
  1651. RtlInitUnicodeString(pDomainName, NULL);
  1652. RtlInitUnicodeString(pPassword, NULL);
  1653. Status = LsaFunctions->CrediReadDomainCredentials(
  1654. LogonId,
  1655. CREDP_FLAGS_IN_PROCESS, // Allow password to be returned
  1656. pTargetInfo,
  1657. 0, // no flags
  1658. &CredentialCount,
  1659. &EncryptedCredentials );
  1660. Credentials = (PCREDENTIALW *) EncryptedCredentials;
  1661. if(!NT_SUCCESS(Status))
  1662. {
  1663. //
  1664. // Ideally, only STATUS_NO_SUCH_LOGON_SESSION should be converted to
  1665. // STATUS_NOT_FOUND. However, swallowing all failures and asserting
  1666. // these specific two works around a bug in CrediReadDomainCredentials
  1667. // which returns invalid parameter if the target is a user account name.
  1668. // Eventually, CrediReadDomainCredentials should return a more appropriate
  1669. // error in this case.
  1670. //
  1671. return STATUS_NOT_FOUND;
  1672. }
  1673. //
  1674. // Loop through the list of credentials
  1675. //
  1676. for ( CredIndex=0; CredIndex<CredentialCount; CredIndex++ ) {
  1677. UNICODE_STRING UserName;
  1678. UNICODE_STRING DomainName;
  1679. UNICODE_STRING TempString;
  1680. //
  1681. // only supports password credentials
  1682. //
  1683. if ( Credentials[CredIndex]->Type != CRED_TYPE_DOMAIN_PASSWORD ) {
  1684. continue;
  1685. }
  1686. if ( Credentials[CredIndex]->Flags & CRED_FLAGS_PROMPT_NOW ) {
  1687. Status = SEC_E_LOGON_DENIED;
  1688. goto Cleanup;
  1689. }
  1690. //
  1691. // Sanity check the credential
  1692. //
  1693. if ( Credentials[CredIndex]->UserName == NULL ) {
  1694. Status = STATUS_NOT_FOUND;
  1695. goto Cleanup;
  1696. }
  1697. //
  1698. // Convert the UserName to domain name and user name
  1699. //
  1700. Status = CredpParseUserName(
  1701. Credentials[CredIndex]->UserName,
  1702. &UserName,
  1703. &DomainName
  1704. );
  1705. if(!NT_SUCCESS(Status))
  1706. {
  1707. goto Cleanup;
  1708. }
  1709. if( DomainName.Buffer )
  1710. {
  1711. Status = KerbDuplicateString(pDomainName, &DomainName);
  1712. if ( !NT_SUCCESS( Status ) )
  1713. {
  1714. goto Cleanup;
  1715. }
  1716. }
  1717. if( UserName.Buffer )
  1718. {
  1719. Status = KerbDuplicateString(pUserName, &UserName);
  1720. if ( !NT_SUCCESS( Status ) )
  1721. {
  1722. goto Cleanup;
  1723. }
  1724. }
  1725. //
  1726. // Free the existing password and add the new one
  1727. //
  1728. TempString.Buffer = (LPWSTR)Credentials[CredIndex]->CredentialBlob;
  1729. TempString.MaximumLength = (USHORT) Credentials[CredIndex]->CredentialBlobSize;
  1730. TempString.Length = (USHORT) EncryptedCredentials[CredIndex]->ClearCredentialBlobSize;
  1731. // zero length password must be treated as blank or will assume it should use the
  1732. // password of the currently logged in user.
  1733. if ( TempString.Length == 0 )
  1734. {
  1735. TempString.Buffer = L"";
  1736. }
  1737. Status = KerbDuplicatePassword(pPassword, &TempString);
  1738. if ( !NT_SUCCESS( Status ) )
  1739. {
  1740. goto Cleanup;
  1741. }
  1742. goto Cleanup;
  1743. }
  1744. Status = STATUS_NOT_FOUND;
  1745. Cleanup:
  1746. if(!NT_SUCCESS(Status))
  1747. {
  1748. KerbFreeString( pUserName );
  1749. KerbFreeString( pDomainName );
  1750. KerbFreeString( pPassword );
  1751. pUserName->Buffer = NULL;
  1752. pDomainName->Buffer = NULL;
  1753. pPassword->Buffer = NULL;
  1754. }
  1755. //
  1756. // Free the returned credentials
  1757. //
  1758. LsaFunctions->CrediFreeCredentials(
  1759. CredentialCount,
  1760. EncryptedCredentials );
  1761. return Status;
  1762. }
  1763. NTSTATUS
  1764. KerbProcessUserNameCredential(
  1765. IN PUNICODE_STRING MarshalledUserName,
  1766. OUT PUNICODE_STRING UserName,
  1767. OUT PUNICODE_STRING DomainName,
  1768. OUT PUNICODE_STRING Password
  1769. )
  1770. {
  1771. WCHAR FastUserName[ UNLEN+1 ];
  1772. LPWSTR SlowUserName = NULL;
  1773. LPWSTR TempUserName;
  1774. CRED_MARSHAL_TYPE CredMarshalType;
  1775. PUSERNAME_TARGET_CREDENTIAL_INFO pCredentialUserName = NULL;
  1776. CREDENTIAL_TARGET_INFORMATIONW TargetInfo;
  1777. ULONG CredTypes;
  1778. SECPKG_CLIENT_INFO ClientInfo;
  1779. NTSTATUS Status = STATUS_NOT_FOUND;
  1780. if( (MarshalledUserName->Length+sizeof(WCHAR)) <= sizeof(FastUserName) )
  1781. {
  1782. TempUserName = FastUserName;
  1783. }
  1784. else
  1785. {
  1786. SafeAllocaAllocate(SlowUserName, MarshalledUserName->Length + sizeof(WCHAR));
  1787. if( SlowUserName == NULL )
  1788. {
  1789. return STATUS_INSUFFICIENT_RESOURCES;
  1790. }
  1791. TempUserName = SlowUserName;
  1792. }
  1793. //
  1794. // copy the input to a NULL terminated string, then attempt to unmarshal it.
  1795. //
  1796. RtlCopyMemory( TempUserName,
  1797. MarshalledUserName->Buffer,
  1798. MarshalledUserName->Length
  1799. );
  1800. TempUserName[ MarshalledUserName->Length / sizeof(WCHAR) ] = L'\0';
  1801. if(!CredUnmarshalCredentialW(
  1802. TempUserName,
  1803. &CredMarshalType,
  1804. (VOID**)&pCredentialUserName
  1805. ))
  1806. {
  1807. goto Cleanup;
  1808. }
  1809. if( (CredMarshalType != UsernameTargetCredential) )
  1810. {
  1811. goto Cleanup;
  1812. }
  1813. //
  1814. // now query credential manager for a match.
  1815. //
  1816. Status = LsaFunctions->GetClientInfo(&ClientInfo);
  1817. if(!NT_SUCCESS(Status))
  1818. {
  1819. goto Cleanup;
  1820. }
  1821. ZeroMemory( &TargetInfo, sizeof(TargetInfo) );
  1822. CredTypes = CRED_TYPE_DOMAIN_PASSWORD;
  1823. TargetInfo.Flags = CRED_TI_USERNAME_TARGET;
  1824. TargetInfo.TargetName = pCredentialUserName->UserName;
  1825. TargetInfo.PackageName = KERBEROS_PACKAGE_NAME;
  1826. TargetInfo.CredTypeCount = 1;
  1827. TargetInfo.CredTypes = &CredTypes;
  1828. Status = CopyCredManCredentials(
  1829. &ClientInfo.LogonId,
  1830. &TargetInfo,
  1831. UserName,
  1832. DomainName,
  1833. Password
  1834. );
  1835. if(!NT_SUCCESS(Status))
  1836. {
  1837. goto Cleanup;
  1838. }
  1839. KerbRevealPassword( Password );
  1840. Cleanup:
  1841. if( pCredentialUserName != NULL )
  1842. {
  1843. CredFree( pCredentialUserName );
  1844. }
  1845. SafeAllocaFree( SlowUserName );
  1846. return Status;
  1847. }
  1848. //+-------------------------------------------------------------------------
  1849. //
  1850. // Function: KerbMarshallMSVCredential
  1851. //
  1852. // Synopsis: Takes a SECPKG_SUPPLEMENTAL_CRED and bundles it up as a
  1853. // PCREDENTIAL structure.
  1854. //
  1855. // Arguments: NtlmCred - Supplemental cred from PAC
  1856. // MarshalledCred - credential being created.
  1857. //
  1858. // Requires:
  1859. //
  1860. // Returns:
  1861. //
  1862. // Notes:
  1863. //
  1864. //
  1865. //--------------------------------------------------------------------------
  1866. NTSTATUS
  1867. KerbMarshallMSVCredential(
  1868. IN PSECPKG_SUPPLEMENTAL_CRED NtlmCred,
  1869. IN PUNICODE_STRING UserName,
  1870. IN PUNICODE_STRING TargetName,
  1871. IN OUT PKERB_QUERY_SUPPLEMENTAL_CREDS_RESPONSE * MarshalledCred,
  1872. IN OUT PULONG Size
  1873. )
  1874. {
  1875. PKERB_QUERY_SUPPLEMENTAL_CREDS_RESPONSE LocalCred = NULL;
  1876. NTSTATUS Status = STATUS_SUCCESS;
  1877. PBYTE Where;
  1878. ULONG LocalSize;
  1879. LocalSize = (sizeof(KERB_QUERY_SUPPLEMENTAL_CREDS_RESPONSE) +
  1880. ROUND_UP_COUNT( UserName->MaximumLength, ALIGN_LPDWORD ) +
  1881. ROUND_UP_COUNT( TargetName->MaximumLength, ALIGN_LPDWORD ) +
  1882. ROUND_UP_COUNT( NtlmCred->CredentialSize, DESX_BLOCKLEN));
  1883. LocalCred = (PKERB_QUERY_SUPPLEMENTAL_CREDS_RESPONSE) KerbAllocate(LocalSize);
  1884. if (LocalCred == NULL)
  1885. {
  1886. Status = STATUS_NO_MEMORY;
  1887. goto Cleanup;
  1888. }
  1889. Where = (PBYTE) LocalCred + sizeof(KERB_QUERY_SUPPLEMENTAL_CREDS_RESPONSE);
  1890. RtlCopyMemory(
  1891. Where,
  1892. UserName->Buffer,
  1893. UserName->MaximumLength
  1894. );
  1895. LocalCred->ReturnedCreds.Cred.UserName = (LPWSTR) Where;
  1896. Where += ROUND_UP_COUNT(UserName->MaximumLength, ALIGN_LPDWORD);
  1897. RtlCopyMemory(
  1898. Where,
  1899. TargetName->Buffer,
  1900. TargetName->MaximumLength
  1901. );
  1902. LocalCred->ReturnedCreds.Cred.TargetName = (LPWSTR) Where;
  1903. Where += ROUND_UP_COUNT(TargetName->MaximumLength, ALIGN_LPDWORD);
  1904. RtlCopyMemory(
  1905. Where,
  1906. NtlmCred->Credentials,
  1907. NtlmCred->CredentialSize
  1908. );
  1909. LocalCred->ReturnedCreds.ClearCredentialBlobSize = NtlmCred->CredentialSize;
  1910. LocalCred->ReturnedCreds.Cred.CredentialBlob = Where;
  1911. LocalCred->ReturnedCreds.Cred.CredentialBlobSize = NtlmCred->CredentialSize;
  1912. LsaFunctions->LsaProtectMemory(
  1913. LocalCred->ReturnedCreds.Cred.CredentialBlob,
  1914. (ROUND_UP_COUNT(NtlmCred->CredentialSize, DESX_BLOCKLEN))
  1915. );
  1916. LocalCred->ReturnedCreds.Cred.Flags = CRED_FLAGS_OWF_CRED_BLOB;
  1917. LocalCred->ReturnedCreds.Cred.Type = CRED_TYPE_DOMAIN_PASSWORD;
  1918. LocalCred->ReturnedCreds.Cred.Persist = CRED_PERSIST_SESSION;
  1919. *MarshalledCred = LocalCred;
  1920. LocalCred = NULL;
  1921. *Size = LocalSize;
  1922. Cleanup:
  1923. if (LocalCred)
  1924. {
  1925. KerbFree(LocalCred);
  1926. }
  1927. return Status;
  1928. }
  1929. //+-------------------------------------------------------------------------
  1930. //
  1931. // Function: KerbRetrieveOWF
  1932. //
  1933. // Synopsis: Converts a smartcard credential into a credential containing
  1934. // the NT_OWF.
  1935. //
  1936. // Arguments: NtlmCred - Supplemental cred from PAC
  1937. // MarshalledCred - credential being created.
  1938. //
  1939. // Requires:
  1940. //
  1941. // Returns:
  1942. //
  1943. // Notes:
  1944. //
  1945. //
  1946. //--------------------------------------------------------------------------
  1947. NTSTATUS
  1948. KerbRetrieveOWF(
  1949. IN PKERB_LOGON_SESSION LogonSession,
  1950. IN PKERB_CREDENTIAL Credential,
  1951. IN PKERB_CREDMAN_CRED CredmanCred,
  1952. IN PUNICODE_STRING CredTargetName,
  1953. IN OUT PKERB_QUERY_SUPPLEMENTAL_CREDS_RESPONSE * Response,
  1954. IN OUT PULONG ResponseSize
  1955. )
  1956. {
  1957. NTSTATUS Status;
  1958. ULONG i;
  1959. PSECPKG_SUPPLEMENTAL_CRED_ARRAY PacCreds = NULL;
  1960. PSECPKG_SUPPLEMENTAL_CRED NtlmCred = NULL;
  1961. PKERB_TICKET_CACHE_ENTRY NewTicket = NULL;
  1962. PKERB_TICKET_CACHE_ENTRY Tgt = NULL;
  1963. UNICODE_STRING TempName = {0};
  1964. UNICODE_STRING UserName = {0};
  1965. UNICODE_STRING Package = {0};
  1966. BOOLEAN CrossRealm = FALSE;
  1967. LPWSTR tmp = NULL;
  1968. KERB_TGT_REPLY TgtReply = {0};
  1969. PKERB_INTERNAL_NAME TargetName = NULL;
  1970. PNETLOGON_VALIDATION_SAM_INFO3 ValidationInfo = NULL;
  1971. ULONG Size;
  1972. PKERB_QUERY_SUPPLEMENTAL_CREDS_RESPONSE LocalResponse = NULL;
  1973. *Response = NULL;
  1974. *ResponseSize = 0;
  1975. //
  1976. // First get a TGT for U2U
  1977. //
  1978. KerbReadLockLogonSessions( LogonSession );
  1979. Status = KerbGetTgtForService(
  1980. LogonSession,
  1981. Credential,
  1982. CredmanCred,
  1983. NULL,
  1984. &TempName, // no target realm
  1985. KERB_TICKET_CACHE_PRIMARY_TGT,
  1986. &Tgt,
  1987. &CrossRealm
  1988. );
  1989. KerbUnlockLogonSessions( LogonSession );
  1990. if (!NT_SUCCESS(Status))
  1991. {
  1992. DebugLog((DEB_ERROR, "KerbRetrieveOWF failed to get TGT: %#x\n", Status));
  1993. goto Cleanup;
  1994. }
  1995. if (CrossRealm)
  1996. {
  1997. DsysAssert(CrossRealm == FALSE);
  1998. Status = STATUS_INTERNAL_ERROR;
  1999. goto Cleanup;
  2000. }
  2001. TgtReply.version = KERBEROS_VERSION;
  2002. TgtReply.message_type = KRB_TGT_REP;
  2003. TgtReply.ticket = Tgt->Ticket;
  2004. DsysAssert(CredmanCred->CredmanDomainName.Length == 0);
  2005. if (!KERB_SUCCESS(KerbConvertStringToKdcName(
  2006. &TargetName,
  2007. &CredmanCred->CredmanUserName
  2008. )))
  2009. {
  2010. Status = STATUS_NO_MEMORY;
  2011. goto Cleanup;
  2012. }
  2013. //
  2014. // HACK HACK
  2015. // KerbConvertStringToKdcName builds a bogus name type.
  2016. //
  2017. TargetName->NameType = KRB_NT_ENTERPRISE_PRINCIPAL;
  2018. Status = KerbGetServiceTicket(
  2019. LogonSession,
  2020. Credential,
  2021. CredmanCred,
  2022. TargetName, // should be a UPN
  2023. &Tgt->ClientDomainName,
  2024. NULL,
  2025. 0,
  2026. 0,
  2027. 0,
  2028. NULL,
  2029. NULL,
  2030. &TgtReply,
  2031. &NewTicket,
  2032. NULL
  2033. );
  2034. if (!NT_SUCCESS( Status ))
  2035. {
  2036. DebugLog((DEB_ERROR, "KerbGetServiceTicket failed - %x\n", Status ));
  2037. goto Cleanup;
  2038. }
  2039. //
  2040. // Pull the supplemental creds from the ticket.
  2041. //
  2042. Status = KerbGetCredsFromU2UTicket(
  2043. NewTicket,
  2044. Tgt,
  2045. &PacCreds,
  2046. &ValidationInfo
  2047. );
  2048. if (!NT_SUCCESS( Status))
  2049. {
  2050. DebugLog((DEB_ERROR, "KerbGetCredsFromTicket failed %x\n", Status));
  2051. goto Cleanup;
  2052. }
  2053. RtlInitUnicodeString(
  2054. &Package,
  2055. NTLMSP_NAME
  2056. );
  2057. for ( i = 0; i < PacCreds->CredentialCount; i++ )
  2058. {
  2059. if (RtlEqualUnicodeString(
  2060. &PacCreds->Credentials[i].PackageName,
  2061. &Package,
  2062. TRUE
  2063. ))
  2064. {
  2065. NtlmCred = &PacCreds->Credentials[i];
  2066. break;
  2067. }
  2068. }
  2069. if (NtlmCred == NULL || ValidationInfo == NULL)
  2070. {
  2071. DebugLog((DEB_ERROR, "No NTLM creds %p or ValidationInfo %p found in PAC\n", NtlmCred, ValidationInfo));
  2072. Status = STATUS_NOT_FOUND;
  2073. DsysAssert(FALSE);
  2074. goto Cleanup;
  2075. }
  2076. UserName.MaximumLength = ValidationInfo->EffectiveName.Length + ValidationInfo->LogonDomainName.Length + (2 * sizeof(WCHAR));
  2077. UserName.Length = UserName.MaximumLength - sizeof(WCHAR);
  2078. SafeAllocaAllocate(UserName.Buffer, UserName.MaximumLength);
  2079. if (UserName.Buffer == NULL)
  2080. {
  2081. Status = STATUS_NO_MEMORY;
  2082. goto Cleanup;
  2083. }
  2084. RtlZeroMemory(UserName.Buffer, UserName.MaximumLength);
  2085. //
  2086. // ntlm accepts empty domain name, therefore "\username" is valid
  2087. //
  2088. tmp = UserName.Buffer;
  2089. RtlCopyMemory(
  2090. tmp,
  2091. ValidationInfo->LogonDomainName.Buffer,
  2092. ValidationInfo->LogonDomainName.Length
  2093. );
  2094. tmp += (ValidationInfo->LogonDomainName.Length / sizeof(WCHAR));
  2095. *tmp = L'\\';
  2096. tmp++;
  2097. RtlCopyMemory(
  2098. tmp,
  2099. ValidationInfo->EffectiveName.Buffer,
  2100. ValidationInfo->EffectiveName.Length
  2101. );
  2102. //
  2103. // Build the resultant CREDENTIAL for MSV.
  2104. //
  2105. Status = KerbMarshallMSVCredential(
  2106. NtlmCred,
  2107. &UserName,
  2108. CredTargetName,
  2109. &LocalResponse,
  2110. &Size
  2111. );
  2112. if (!NT_SUCCESS( Status ))
  2113. {
  2114. DebugLog((DEB_ERROR, "KerbMarshalMSVCredential failed %x\n", Status));
  2115. goto Cleanup;
  2116. }
  2117. *Response = LocalResponse;
  2118. LocalResponse = NULL;
  2119. *ResponseSize = Size;
  2120. Cleanup:
  2121. if ( PacCreds )
  2122. {
  2123. MIDL_user_free( PacCreds );
  2124. }
  2125. if ( ValidationInfo )
  2126. {
  2127. MIDL_user_free( ValidationInfo );
  2128. }
  2129. SafeAllocaFree( UserName.Buffer );
  2130. KerbFreeString( &TempName );
  2131. if ( TargetName )
  2132. {
  2133. KerbFreeKdcName( &TargetName );
  2134. }
  2135. if ( NewTicket )
  2136. {
  2137. KerbDereferenceTicketCacheEntry( NewTicket );
  2138. }
  2139. if ( LocalResponse )
  2140. {
  2141. KerbFree(LocalResponse);
  2142. }
  2143. return Status;
  2144. }
  2145. //+-------------------------------------------------------------------------
  2146. //
  2147. // Function: KerbTicklePackage
  2148. //
  2149. // Synopsis:
  2150. //
  2151. // Effects:
  2152. //
  2153. // Arguments:
  2154. //
  2155. // Requires: Readlock logon session
  2156. //
  2157. // Returns:
  2158. //
  2159. // Notes: In order to optimize perf, we order this 1. current password,
  2160. // 2. "extra" credentials, 3. old passwords
  2161. //
  2162. //
  2163. //--------------------------------------------------------------------------
  2164. NTSTATUS
  2165. KerbTicklePackage(
  2166. IN PKERB_LOGON_SESSION LogonSession,
  2167. IN PUNICODE_STRING CredentialBlob
  2168. )
  2169. {
  2170. NTSTATUS Status;
  2171. PKERB_PRIMARY_CREDENTIAL CertCred = NULL;
  2172. PKERB_CREDMAN_CRED CredmanCred = NULL;
  2173. UNICODE_STRING TargetName;
  2174. LPWSTR CredBlob = NULL;
  2175. HANDLE OldToken = NULL, ClientToken = NULL;
  2176. //
  2177. // Fester:
  2178. // If we ever extend this, we may need to change the value here.
  2179. // For now, its just the *session
  2180. //
  2181. RtlInitUnicodeString(
  2182. &TargetName,
  2183. CRED_SESSION_WILDCARD_NAME_W
  2184. );
  2185. //
  2186. // Make sure we pass a NULL terminated cred
  2187. //
  2188. SafeAllocaAllocate( CredBlob, ( CredentialBlob->MaximumLength + sizeof(WCHAR)));
  2189. if ( CredBlob == NULL )
  2190. {
  2191. Status = STATUS_NO_MEMORY;
  2192. goto Cleanup;
  2193. }
  2194. RtlZeroMemory( CredBlob, ( CredentialBlob->MaximumLength + sizeof(WCHAR)));
  2195. RtlCopyMemory( CredBlob, CredentialBlob->Buffer, CredentialBlob->Length );
  2196. //
  2197. // Got to be impersonating to make this call...
  2198. //
  2199. Status = NtOpenThreadToken(
  2200. NtCurrentThread(),
  2201. TOKEN_QUERY | TOKEN_IMPERSONATE,
  2202. TRUE,
  2203. &OldToken
  2204. );
  2205. if (!NT_SUCCESS( Status ) && Status != STATUS_NO_TOKEN )
  2206. {
  2207. DebugLog((DEB_ERROR, "NtOpenThreadToken failed %x\n", Status));
  2208. goto Cleanup;
  2209. }
  2210. Status = LsaFunctions->OpenTokenByLogonId(
  2211. &LogonSession->LogonId,
  2212. &ClientToken
  2213. );
  2214. if (!NT_SUCCESS(Status))
  2215. {
  2216. D_DebugLog((DEB_ERROR,"Unable to get the client token handle.\n"));
  2217. goto Cleanup;
  2218. }
  2219. if(!SetThreadToken(NULL, ClientToken))
  2220. {
  2221. D_DebugLog((DEB_ERROR,"Unable to impersonate the client token handle.\n"));
  2222. Status = STATUS_CANNOT_IMPERSONATE;
  2223. goto Cleanup;
  2224. }
  2225. Status = KerbConvertCertCredential(
  2226. LogonSession,
  2227. CredBlob,
  2228. &TargetName,
  2229. &CertCred
  2230. );
  2231. if (!NT_SUCCESS( Status ))
  2232. {
  2233. D_DebugLog((DEB_ERROR, "Failed to convert cert cred %x\n", Status));
  2234. goto Cleanup;
  2235. }
  2236. KerbReadLockLogonSessions(LogonSession);
  2237. Status = KerbAddCredmanCredToLogonSession(
  2238. LogonSession,
  2239. CertCred, // note: freed by this fn, if necessary...
  2240. RAS_CREDENTIAL,
  2241. &CredmanCred
  2242. );
  2243. KerbUnlockLogonSessions(LogonSession);
  2244. if (!NT_SUCCESS( Status ))
  2245. {
  2246. D_DebugLog((DEB_ERROR, "Failed to add credman cred %x\n", Status));
  2247. goto Cleanup;
  2248. }
  2249. Cleanup:
  2250. if ( OldToken )
  2251. {
  2252. if (!SetThreadToken( NULL, OldToken ))
  2253. {
  2254. D_DebugLog((DEB_ERROR,"Unable to impersonate the client token handle.\n"));
  2255. Status = STATUS_CANNOT_IMPERSONATE;
  2256. }
  2257. CloseHandle( OldToken );
  2258. }
  2259. else
  2260. {
  2261. RevertToSelf();
  2262. }
  2263. if ( ClientToken )
  2264. {
  2265. CloseHandle( ClientToken );
  2266. }
  2267. SafeAllocaFree( CredBlob );
  2268. if ( CredmanCred )
  2269. {
  2270. KerbDereferenceCredmanCred(
  2271. CredmanCred,
  2272. &LogonSession->CredmanCredentials
  2273. );
  2274. }
  2275. return Status;
  2276. }