Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2062 lines
51 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. //+-------------------------------------------------------------------------
  21. //
  22. // Function: KerbFreeCredmanCred
  23. //
  24. // Synopsis: Frees credman cred
  25. //
  26. // Arguments:
  27. //
  28. // Requires:
  29. //
  30. // Returns: NTSTATUS, typically ignored, as failure to update the credman
  31. // should not be fatal.
  32. //
  33. // Notes:
  34. //
  35. //
  36. //--------------------------------------------------------------------------
  37. VOID
  38. KerbFreeCredmanCred(
  39. IN PKERB_CREDMAN_CRED CredToFree
  40. )
  41. {
  42. DsysAssert(CredToFree);
  43. KerbFreePrimaryCredentials(CredToFree->SuppliedCredentials, TRUE);
  44. KerbFreeString(&CredToFree->CredmanDomainName);
  45. KerbFreeString(&CredToFree->CredmanUserName);
  46. KerbFree(CredToFree);
  47. }
  48. //+-------------------------------------------------------------------------
  49. //
  50. // Function: KerbReferenceCredmanCred
  51. //
  52. // Synopsis: Frees credman cred
  53. //
  54. // Arguments:
  55. //
  56. // Requires:
  57. //
  58. //
  59. // Notes:
  60. //
  61. //
  62. //--------------------------------------------------------------------------
  63. VOID
  64. KerbReferenceCredmanCred(
  65. IN PKERB_CREDMAN_CRED Cred,
  66. IN PKERB_LOGON_SESSION LogonSession,
  67. IN BOOLEAN Unlink
  68. )
  69. {
  70. KerbReferenceListEntry(
  71. &LogonSession->CredmanCredentials,
  72. &Cred->ListEntry,
  73. Unlink
  74. );
  75. }
  76. //+-------------------------------------------------------------------------
  77. //
  78. // Function: KerbDereferenceCredmanCred
  79. //
  80. // Synopsis: Frees credman cred
  81. //
  82. // Arguments:
  83. //
  84. // Requires:
  85. //
  86. // Returns: NTSTATUS, typically ignored, as failure to update the credman
  87. // should not be fatal.
  88. //
  89. // Notes:
  90. //
  91. //
  92. //--------------------------------------------------------------------------
  93. VOID
  94. KerbDereferenceCredmanCred(
  95. IN PKERB_CREDMAN_CRED Cred,
  96. IN PKERBEROS_LIST CredmanList
  97. )
  98. {
  99. if (KerbDereferenceListEntry(
  100. &Cred->ListEntry,
  101. CredmanList
  102. ))
  103. {
  104. KerbFreeCredmanCred(Cred);
  105. }
  106. }
  107. //+-------------------------------------------------------------------------
  108. //
  109. // Function: KerbFreeCredmanList
  110. //
  111. // Synopsis: Free a credman list from a logon session...
  112. //
  113. // Arguments:
  114. //
  115. // Requires:
  116. //
  117. // Returns: NTSTATUS, typically ignored, as failure to update the credman
  118. // should not be fatal.
  119. //
  120. // Notes:
  121. //
  122. //
  123. //--------------------------------------------------------------------------
  124. VOID
  125. KerbFreeCredmanList(
  126. KERBEROS_LIST CredmanList
  127. )
  128. {
  129. PKERB_CREDMAN_CRED Cred;
  130. KerbLockList(&CredmanList);
  131. //
  132. // Go through the list of credman creds and dereferences them all
  133. //
  134. while (!IsListEmpty(&CredmanList.List))
  135. {
  136. Cred = CONTAINING_RECORD(
  137. CredmanList.List.Flink,
  138. KERB_CREDMAN_CRED,
  139. ListEntry.Next
  140. );
  141. // unlink cred from list
  142. KerbReferenceListEntry(
  143. &CredmanList,
  144. &Cred->ListEntry,
  145. TRUE
  146. );
  147. KerbDereferenceCredmanCred(
  148. Cred,
  149. &CredmanList
  150. );
  151. }
  152. RtlDeleteCriticalSection(&CredmanList.Lock);
  153. }
  154. //+-------------------------------------------------------------------------
  155. //
  156. // Function: KerbNotifyCredentialManager
  157. //
  158. // Synopsis: This function is used to notify the credential manager of a
  159. // password change event. Note: This will always be a MIT
  160. // session.
  161. //
  162. // Arguments:
  163. //
  164. // Requires:
  165. //
  166. // Returns: NTSTATUS, typically ignored, as failure to update the credman
  167. // should not be fatal.
  168. //
  169. // Notes:
  170. //
  171. //
  172. //--------------------------------------------------------------------------
  173. VOID
  174. KerbNotifyCredentialManager(
  175. IN PKERB_LOGON_SESSION LogonSession,
  176. IN PKERB_CHANGEPASSWORD_REQUEST ChangeRequest,
  177. IN PKERB_INTERNAL_NAME ClientName,
  178. IN PUNICODE_STRING RealmName
  179. )
  180. {
  181. UNICODE_STRING ClientNameU = {0};
  182. KERBERR KerbErr;
  183. // FESTER:
  184. // We should only expect to get pwd change notification on
  185. // an MIT Realm pwd change, in which case, there isn't a concept of a
  186. // Netbios name ....
  187. KerbErr = KerbConvertKdcNameToString(
  188. &ClientNameU,
  189. ClientName,
  190. NULL
  191. );
  192. if (!KERB_SUCCESS(KerbErr))
  193. {
  194. return;
  195. }
  196. LsaINotifyPasswordChanged(
  197. NULL,
  198. &ClientNameU,
  199. RealmName,
  200. NULL,
  201. &ChangeRequest->OldPassword,
  202. &ChangeRequest->NewPassword,
  203. ChangeRequest->Impersonating
  204. );
  205. KerbFreeString(&ClientNameU);
  206. }
  207. //+-------------------------------------------------------------------------
  208. //
  209. // Function: KerbComparePasswords
  210. //
  211. // Synopsis: Verifies that two stored credentials are identical, simply
  212. // through comparison of KERB_ETYPE_RC4_HMAC_NT keys
  213. //
  214. // Arguments:
  215. //
  216. // Requires:
  217. //
  218. // Returns:
  219. // NULL if the user name is not a marshalled cert, a pointer
  220. // to the
  221. //
  222. // Notes:
  223. //
  224. //
  225. //--------------------------------------------------------------------------
  226. BOOL
  227. KerbComparePasswords(
  228. IN PKERB_STORED_CREDENTIAL PwdList1,
  229. IN PKERB_STORED_CREDENTIAL PwdList2
  230. )
  231. {
  232. PKERB_ENCRYPTION_KEY Key1 = NULL;
  233. PKERB_ENCRYPTION_KEY Key2 = NULL;
  234. Key1 = KerbGetKeyFromList(
  235. PwdList1,
  236. KERB_ETYPE_RC4_HMAC_NT
  237. );
  238. if (NULL == Key1)
  239. {
  240. D_DebugLog((DEB_ERROR, "Cred1 missing RC4 key!\n"));
  241. DsysAssert(FALSE);
  242. return FALSE;
  243. }
  244. Key2 = KerbGetKeyFromList(
  245. PwdList2,
  246. KERB_ETYPE_RC4_HMAC_NT
  247. );
  248. if (NULL == Key2)
  249. {
  250. D_DebugLog((DEB_ERROR, "Cred2 missing RC4 key!\n"));
  251. DsysAssert(FALSE);
  252. return FALSE;
  253. }
  254. return (RtlEqualMemory(
  255. Key1->keyvalue.value,
  256. Key2->keyvalue.value,
  257. Key1->keyvalue.length
  258. ));
  259. }
  260. //+-------------------------------------------------------------------------
  261. //
  262. // Function: KerbCheckUserNameForCert
  263. //
  264. // Synopsis: Looks at the passed in user name and determines if that
  265. // user name is a marshalled cert. If it is the function
  266. // opens the user cert store and then attempts to find the
  267. // cert in the store.
  268. //
  269. // Arguments:
  270. //
  271. // Requires:
  272. //
  273. // Returns:
  274. // NULL if the user name is not a marshalled cert, a pointer
  275. // to the
  276. //
  277. // Notes:
  278. //
  279. //
  280. //--------------------------------------------------------------------------
  281. NTSTATUS
  282. KerbCheckUserNameForCert(
  283. IN PLUID ClientLogonId,
  284. IN BOOLEAN fImpersonateClient,
  285. IN UNICODE_STRING *pUserName,
  286. OUT PCERT_CONTEXT *ppCertContext
  287. )
  288. {
  289. CRED_MARSHAL_TYPE MarshalType;
  290. PCERT_CREDENTIAL_INFO pCertCredInfo = NULL;
  291. HCERTSTORE hCertStore = NULL;
  292. CRYPT_HASH_BLOB HashBlob;
  293. LPWSTR rgwszUserName;
  294. WCHAR FastUserName[(UNLEN + 1) * sizeof(WCHAR)];
  295. LPWSTR SlowUserName = NULL;
  296. BOOLEAN fImpersonating = FALSE;
  297. HANDLE ClientTokenHandle = NULL;
  298. NTSTATUS Status = STATUS_SUCCESS;
  299. *ppCertContext = NULL;
  300. // Switch to stackalloc routine when available.
  301. if( pUserName->Length+sizeof(WCHAR) <= sizeof(FastUserName) )
  302. {
  303. rgwszUserName = FastUserName;
  304. } else {
  305. SlowUserName = (LPWSTR)KerbAllocate( pUserName->Length+sizeof(WCHAR) );
  306. if( SlowUserName == NULL )
  307. {
  308. Status = STATUS_INSUFFICIENT_RESOURCES;
  309. goto Cleanup;
  310. }
  311. rgwszUserName = SlowUserName;
  312. }
  313. RtlCopyMemory(
  314. rgwszUserName,
  315. pUserName->Buffer,
  316. pUserName->Length);
  317. rgwszUserName[pUserName->Length / sizeof(WCHAR)] = L'\0';
  318. //
  319. // unmarshall the cert cred info from the user name field
  320. // of the cred man cred
  321. //
  322. if (!CredUnmarshalCredentialW(
  323. rgwszUserName,
  324. &MarshalType,
  325. (void**)&pCertCredInfo
  326. ))
  327. {
  328. goto Cleanup;
  329. }
  330. if (CertCredential != MarshalType)
  331. {
  332. goto Cleanup;
  333. }
  334. // first need to impersonate the user so that we can call the
  335. // credential manager as that user
  336. // TODO: check if this fails.
  337. // don't do this until new ImpersonateLuid() is available.
  338. //
  339. if (NULL == ClientLogonId)
  340. {
  341. if (fImpersonateClient)
  342. {
  343. Status = LsaFunctions->ImpersonateClient();
  344. if (!NT_SUCCESS (Status))
  345. {
  346. goto Cleanup;
  347. }
  348. }
  349. else
  350. {
  351. goto Cleanup;
  352. }
  353. }
  354. else
  355. {
  356. Status = LsaFunctions->OpenTokenByLogonId(
  357. ClientLogonId,
  358. &ClientTokenHandle
  359. );
  360. if (!NT_SUCCESS(Status))
  361. {
  362. D_DebugLog((DEB_ERROR,"Unable to get the client token handle.\n"));
  363. goto Cleanup;
  364. }
  365. if(!SetThreadToken(NULL, ClientTokenHandle))
  366. {
  367. D_DebugLog((DEB_ERROR,"Unable to impersonate the client token handle.\n"));
  368. Status = STATUS_CANNOT_IMPERSONATE;
  369. goto Cleanup;
  370. }
  371. }
  372. fImpersonating = TRUE;
  373. // open a cert store if necessary
  374. if (NULL == hCertStore)
  375. {
  376. hCertStore = CertOpenStore(
  377. CERT_STORE_PROV_SYSTEM_W,
  378. 0,
  379. 0,
  380. CERT_SYSTEM_STORE_CURRENT_USER,
  381. L"MY");
  382. if (NULL == hCertStore)
  383. {
  384. Status = SEC_E_NO_CREDENTIALS;
  385. D_DebugLog((DEB_ERROR,"Failed to open the user cert store even though a cert cred was found.\n"));
  386. goto Cleanup;
  387. }
  388. }
  389. // find the cert in the store which meets this hash
  390. HashBlob.cbData = sizeof(pCertCredInfo->rgbHashOfCert);
  391. HashBlob.pbData = pCertCredInfo->rgbHashOfCert;
  392. *ppCertContext = (PCERT_CONTEXT)CertFindCertificateInStore(
  393. hCertStore,
  394. X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  395. 0,
  396. CERT_FIND_HASH,
  397. &HashBlob,
  398. NULL);
  399. if (NULL == *ppCertContext)
  400. {
  401. Status = SEC_E_NO_CREDENTIALS;
  402. D_DebugLog((DEB_ERROR,"Failed to find cert in store even though a cert cred was found.\n"));
  403. goto Cleanup;
  404. }
  405. Cleanup:
  406. if (NULL != hCertStore)
  407. {
  408. CertCloseStore(hCertStore, 0);
  409. }
  410. if (fImpersonating)
  411. {
  412. RevertToSelf();
  413. }
  414. if (NULL != pCertCredInfo)
  415. {
  416. CredFree (pCertCredInfo);
  417. }
  418. if(ClientTokenHandle != NULL)
  419. {
  420. CloseHandle( ClientTokenHandle );
  421. }
  422. if( SlowUserName )
  423. {
  424. KerbFree( SlowUserName );
  425. }
  426. return Status;
  427. }
  428. NTSTATUS
  429. KerbInitPrimaryCreds(
  430. IN PKERB_LOGON_SESSION LogonSession,
  431. IN PUNICODE_STRING UserString,
  432. IN PUNICODE_STRING DomainString,
  433. IN PUNICODE_STRING PrincipalName,
  434. IN PUNICODE_STRING PasswordString, // either the password or if pin
  435. IN BOOLEAN PubKeyCreds,
  436. IN OPTIONAL PCERT_CONTEXT pCertContext,
  437. OUT PKERB_PRIMARY_CREDENTIAL * PrimaryCreds
  438. );
  439. // check username for domain/ or @ format
  440. NTSTATUS
  441. CredpParseUserName(
  442. IN OUT LPWSTR ParseName,
  443. OUT PUNICODE_STRING pUserName,
  444. OUT PUNICODE_STRING pDomainName
  445. )
  446. /*++
  447. Routine Description:
  448. This routine separates a passed in user name into domain and username. A user name must have one
  449. of the following two syntaxes:
  450. <DomainName>\<UserName>
  451. <UserName>@<DnsDomainName>
  452. The name is considered to have the first syntax if the string contains an \.
  453. A string containing a @ is ambiguous since <UserName> may contain an @.
  454. For the second syntax, the last @ in the string is used since <UserName> may
  455. contain an @ but <DnsDomainName> cannot.
  456. NOTE - The function does not allocate the UNICODE_STRING buffers
  457. so these should not be freed (RtlInitUnicodeString is used)
  458. Arguments:
  459. ParseName - Name of user to validate - will be modified
  460. pUserName - Returned pointing to canonical name inside of ParseName
  461. pDomainName - Returned pointing to domain name inside of ParseName
  462. Return Values:
  463. The following status codes may be returned:
  464. STATUS_INVALID_ACCOUNT_NAME - The user name is not valid.
  465. --*/
  466. {
  467. NTSTATUS Status;
  468. LPWSTR SlashPointer;
  469. LPWSTR AtPointer;
  470. LPWSTR pTmpUserName = NULL;
  471. LPWSTR pTmpDomainName = NULL;
  472. //
  473. // NULL is invalid
  474. //
  475. if ( ParseName == NULL ) {
  476. Status = STATUS_INVALID_ACCOUNT_NAME;
  477. goto Cleanup;
  478. }
  479. //
  480. // Classify the input account name.
  481. //
  482. // The name is considered to be <DomainName>\<UserName> if the string
  483. // contains an \.
  484. //
  485. SlashPointer = wcsrchr( ParseName, L'\\' );
  486. if ( SlashPointer != NULL )
  487. {
  488. //
  489. // point the output strings
  490. //
  491. pTmpDomainName = ParseName;
  492. //
  493. // Skip the backslash
  494. //
  495. *SlashPointer = L'\0';
  496. SlashPointer ++;
  497. pTmpUserName = SlashPointer;
  498. //
  499. // Otherwise the name must be a UPN
  500. //
  501. }
  502. else
  503. {
  504. //
  505. // A UPN has the syntax <AccountName>@<DnsDomainName>.
  506. // If there are multiple @ signs,
  507. // use the last one since an AccountName can have an @ in it.
  508. //
  509. //
  510. AtPointer = wcsrchr( ParseName, L'@' );
  511. if ( AtPointer == NULL )
  512. {
  513. // must be just <username>
  514. pTmpUserName = ParseName;
  515. }
  516. else
  517. {
  518. pTmpUserName = ParseName;
  519. *AtPointer = L'\0';
  520. AtPointer ++;
  521. pTmpDomainName = AtPointer;
  522. }
  523. }
  524. RtlInitUnicodeString( pUserName, pTmpUserName );
  525. RtlInitUnicodeString( pDomainName, pTmpDomainName );
  526. Status = STATUS_SUCCESS;
  527. //
  528. // Cleanup
  529. //
  530. Cleanup:
  531. return Status;
  532. }
  533. NTSTATUS
  534. CredpExtractMarshalledTargetInfo(
  535. IN PUNICODE_STRING TargetServerName,
  536. OUT CREDENTIAL_TARGET_INFORMATIONW **pTargetInfo
  537. )
  538. {
  539. PWSTR Candidate;
  540. ULONG CandidateSize;
  541. NTSTATUS Status = STATUS_SUCCESS;
  542. //
  543. // LSA will set Length to include only the non-marshalled portion,
  544. // with MaximumLength trailing data to include marshalled portion.
  545. //
  546. if( (TargetServerName == NULL) ||
  547. (TargetServerName->Buffer == NULL) ||
  548. (TargetServerName->Length >= TargetServerName->MaximumLength) ||
  549. ((TargetServerName->MaximumLength - TargetServerName->Length) <
  550. (sizeof( CREDENTIAL_TARGET_INFORMATIONW )/(sizeof(ULONG_PTR)/2)) )
  551. )
  552. {
  553. return STATUS_SUCCESS;
  554. }
  555. RtlCopyMemory(
  556. &CandidateSize,
  557. (PBYTE)TargetServerName->Buffer + TargetServerName->MaximumLength - sizeof(ULONG),
  558. sizeof( CandidateSize )
  559. );
  560. if( CandidateSize >= TargetServerName->MaximumLength )
  561. {
  562. return STATUS_SUCCESS;
  563. }
  564. Candidate = (PWSTR)(
  565. (PBYTE)TargetServerName->Buffer + TargetServerName->MaximumLength - CandidateSize
  566. );
  567. Status = CredUnmarshalTargetInfo (
  568. Candidate,
  569. CandidateSize,
  570. pTargetInfo
  571. );
  572. if( !NT_SUCCESS(Status) )
  573. {
  574. if( Status == STATUS_INVALID_PARAMETER )
  575. {
  576. Status = STATUS_SUCCESS;
  577. }
  578. }
  579. return Status ;
  580. }
  581. //+-------------------------------------------------------------------------
  582. //
  583. // Function: KerbCheckForPKINITEnhKeyUsage
  584. //
  585. // Synopsis: Checks if the passed in cert context contains the
  586. // PKINIT enhanced key usage.
  587. //
  588. // Arguments: pCertContext - cert context to check for enh key usage
  589. //
  590. // Requires:
  591. //
  592. // Returns: TRUE is success, FALSE is failure
  593. //
  594. // Notes:
  595. //
  596. //
  597. //--------------------------------------------------------------------------
  598. BOOL
  599. KerbCheckForPKINITEnhKeyUsage(
  600. IN PCERT_CONTEXT pCertContext
  601. )
  602. {
  603. LPSTR pszClientAuthUsage = KERB_PKINIT_CLIENT_CERT_TYPE;
  604. PCERT_ENHKEY_USAGE pEnhKeyUsage = NULL;
  605. ULONG cbEnhKeyUsage = 0;
  606. ULONG i;
  607. BOOLEAN fRet = FALSE;
  608. if (!CertGetEnhancedKeyUsage(
  609. pCertContext,
  610. CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG,
  611. NULL,
  612. &cbEnhKeyUsage))
  613. {
  614. goto Cleanup;
  615. }
  616. // allocate space for the key usage structure
  617. pEnhKeyUsage = (PCERT_ENHKEY_USAGE)KerbAllocate(cbEnhKeyUsage);
  618. if (NULL == pEnhKeyUsage)
  619. {
  620. goto Cleanup;
  621. }
  622. if (!CertGetEnhancedKeyUsage(
  623. pCertContext,
  624. CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG,
  625. pEnhKeyUsage,
  626. &cbEnhKeyUsage))
  627. {
  628. goto Cleanup;
  629. }
  630. // enumerate through the enh key usages looking for the PKINIT one
  631. for (i=0;i<pEnhKeyUsage->cUsageIdentifier;i++)
  632. {
  633. if (0 == strcmp(pszClientAuthUsage, pEnhKeyUsage->rgpszUsageIdentifier[i]))
  634. {
  635. fRet = TRUE;
  636. goto Cleanup;
  637. }
  638. }
  639. Cleanup:
  640. if (NULL != pEnhKeyUsage)
  641. {
  642. KerbFree(pEnhKeyUsage);
  643. }
  644. return fRet;
  645. }
  646. //+-------------------------------------------------------------------------
  647. //
  648. // Function: KerbAddCertCredToPrimaryCredential
  649. //
  650. // Synopsis: Adds cert context and Pin info to the kerb credential
  651. // structure.
  652. //
  653. // Arguments: pCertContext - logon session
  654. // pCertCredInfo - cert cred manager info
  655. // pKerbCred - credential to be updated
  656. //
  657. // Requires:
  658. //
  659. // Returns:
  660. //
  661. // Notes:
  662. //
  663. //
  664. //--------------------------------------------------------------------------
  665. NTSTATUS
  666. KerbAddCertCredToPrimaryCredential(
  667. IN PKERB_LOGON_SESSION pLogonSession,
  668. IN PUNICODE_STRING pTargetName,
  669. IN PCERT_CONTEXT pCertContext,
  670. IN PUNICODE_STRING pPin,
  671. IN ULONG CredFlags,
  672. IN OUT PKERB_PRIMARY_CREDENTIAL *ppCredMgrCred
  673. )
  674. {
  675. UNICODE_STRING UserName = {0};
  676. UNICODE_STRING DomainName = {0}; // get the domain from the UPN in the cert
  677. PKERB_PRIMARY_CREDENTIAL pOldCred;
  678. PKERB_PRIMARY_CREDENTIAL pNewCred = NULL;
  679. NTSTATUS Status = STATUS_SUCCESS;
  680. //
  681. // Get the client name from the cert.
  682. // Place it in the return location
  683. //
  684. Status = KerbGetPrincipalNameFromCertificate(pCertContext, &UserName);
  685. if (!NT_SUCCESS(Status))
  686. {
  687. goto Cleanup;
  688. }
  689. //
  690. // Initialize the primary credentials structure
  691. //
  692. Status = KerbInitPrimaryCreds(
  693. pLogonSession,
  694. &UserName,
  695. &DomainName,
  696. pTargetName,
  697. pPin,
  698. TRUE,
  699. pCertContext,
  700. &pNewCred
  701. );
  702. if (!NT_SUCCESS(Status))
  703. {
  704. goto Cleanup;
  705. }
  706. pNewCred->PublicKeyCreds->InitializationInfo |= CredFlags;
  707. Status = KerbInitializePkCreds(
  708. pNewCred->PublicKeyCreds
  709. );
  710. if (!NT_SUCCESS(Status))
  711. {
  712. goto Cleanup;
  713. }
  714. pOldCred = *ppCredMgrCred;
  715. *ppCredMgrCred = pNewCred;
  716. pNewCred = NULL;
  717. KerbFreePrimaryCredentials(pOldCred, TRUE);
  718. Cleanup:
  719. KerbFreeString(&UserName);
  720. KerbFreePrimaryCredentials(pNewCred, TRUE);
  721. return Status;
  722. }
  723. //+-------------------------------------------------------------------------
  724. //
  725. // Function: KerbAddPasswordCredToPrimaryCredential
  726. //
  727. // Synopsis: Adds cert context and Pin info to the kerb credential
  728. // structure.
  729. //
  730. // Arguments: pCertContext - logon session
  731. // pCertCredInfo - cert cred manager info
  732. // pKerbCred - credential to be updated
  733. //
  734. // Requires:
  735. //
  736. // Returns:
  737. //
  738. // Notes:
  739. //
  740. //
  741. //--------------------------------------------------------------------------
  742. NTSTATUS
  743. KerbAddPasswordCredToPrimaryCredential(
  744. IN PKERB_LOGON_SESSION pLogonSession,
  745. IN PUNICODE_STRING pUserName,
  746. IN PUNICODE_STRING pTargetDomainName,
  747. IN PUNICODE_STRING pTargetName,
  748. IN PUNICODE_STRING pPassword,
  749. IN OUT PKERB_PRIMARY_CREDENTIAL *ppCredMgrCred
  750. )
  751. {
  752. PKERB_PRIMARY_CREDENTIAL pOldCred;
  753. PKERB_PRIMARY_CREDENTIAL pNewCred = NULL;
  754. UNICODE_STRING RevealedPassword;
  755. NTSTATUS Status = STATUS_SUCCESS;
  756. RtlZeroMemory(&RevealedPassword, sizeof(RevealedPassword));
  757. Status = KerbDuplicatePassword(
  758. &RevealedPassword,
  759. pPassword
  760. );
  761. if (!NT_SUCCESS(Status))
  762. {
  763. goto Cleanup;
  764. }
  765. KerbRevealPassword( &RevealedPassword );
  766. //
  767. // Initialize the primary credentials structure
  768. //
  769. Status = KerbInitPrimaryCreds(
  770. pLogonSession,
  771. pUserName,
  772. pTargetDomainName,
  773. pTargetName,
  774. &RevealedPassword,
  775. FALSE,
  776. NULL,
  777. &pNewCred
  778. );
  779. if (!NT_SUCCESS(Status))
  780. {
  781. goto Cleanup;
  782. }
  783. pOldCred = *ppCredMgrCred;
  784. *ppCredMgrCred = pNewCred;
  785. pNewCred = NULL;
  786. KerbFreePrimaryCredentials(pOldCred, TRUE);
  787. Cleanup:
  788. if ((0 != RevealedPassword.Length) && (NULL != RevealedPassword.Buffer))
  789. {
  790. RtlZeroMemory(RevealedPassword.Buffer, RevealedPassword.Length);
  791. KerbFreeString(&RevealedPassword);
  792. }
  793. RtlZeroMemory(&RevealedPassword, sizeof(RevealedPassword));
  794. KerbFreePrimaryCredentials(pNewCred, TRUE);
  795. return Status;
  796. }
  797. //+-------------------------------------------------------------------------
  798. //
  799. // Function: KerbCreateCredmanCred
  800. //
  801. // Synopsis: Goes to the credential manager to try and find
  802. // credentials for the specific target
  803. //
  804. // Arguments:
  805. // CredToAdd - PrimaryCredential to add to credman cred
  806. // ppNewCred - IN OUT built cred, free w/ KerbFreeCredmanCred
  807. //
  808. // Requires:
  809. //
  810. // Returns:
  811. //
  812. // Notes:
  813. //
  814. //
  815. //--------------------------------------------------------------------------
  816. NTSTATUS
  817. KerbCreateCredmanCred(
  818. IN PKERB_PRIMARY_CREDENTIAL CredToAdd,
  819. IN OUT PKERB_CREDMAN_CRED * ppNewCred
  820. )
  821. {
  822. NTSTATUS Status = STATUS_SUCCESS;
  823. *ppNewCred = NULL;
  824. *ppNewCred = (PKERB_CREDMAN_CRED) KerbAllocate(sizeof(KERB_CREDMAN_CRED));
  825. if (NULL == *ppNewCred)
  826. {
  827. return STATUS_INSUFFICIENT_RESOURCES;
  828. }
  829. Status = KerbDuplicateStringEx(
  830. &(*ppNewCred)->CredmanUserName,
  831. &CredToAdd->UserName,
  832. FALSE
  833. );
  834. if (!NT_SUCCESS(Status))
  835. {
  836. goto Cleanup;
  837. }
  838. Status = KerbDuplicateStringEx(
  839. &(*ppNewCred)->CredmanDomainName,
  840. &CredToAdd->DomainName,
  841. FALSE
  842. );
  843. if (!NT_SUCCESS(Status))
  844. {
  845. goto Cleanup;
  846. }
  847. (*ppNewCred)->SuppliedCredentials = CredToAdd;
  848. Cleanup:
  849. if (!NT_SUCCESS(Status))
  850. {
  851. KerbFreeCredmanCred(*ppNewCred);
  852. *ppNewCred = NULL;
  853. }
  854. return (Status);
  855. }
  856. //+-------------------------------------------------------------------------
  857. //
  858. // Function: KerbAddCredmanCredToLogonSession
  859. //
  860. // Synopsis: Goes to the credential manager to try and find
  861. // credentials for the specific target
  862. //
  863. // Arguments: pLogonSession - logon session
  864. // CredToMatch - PrimaryCredential to look for in logon session
  865. //
  866. // Requires:
  867. //
  868. // Returns:
  869. //
  870. // Notes: CredToMatch freed in this function...
  871. //
  872. //
  873. //--------------------------------------------------------------------------
  874. NTSTATUS
  875. KerbAddCredmanCredToLogonSession(
  876. IN PKERB_LOGON_SESSION pLogonSession,
  877. IN PKERB_PRIMARY_CREDENTIAL CredToMatch,
  878. IN OUT PKERB_CREDMAN_CRED *NewCred
  879. )
  880. {
  881. NTSTATUS Status = STATUS_SUCCESS;
  882. PKERB_CREDMAN_CRED CredmanCred = NULL;
  883. PLIST_ENTRY ListEntry;
  884. BOOLEAN PublicKeyCred = FALSE;
  885. BOOLEAN Found = FALSE;
  886. *NewCred = NULL;
  887. //
  888. // First, make a determination if the cred's already listed
  889. // Replace w/ new one if password has changed.
  890. //
  891. KerbLockList(&pLogonSession->CredmanCredentials);
  892. //
  893. // Go through the list of logon sessions looking for the correct
  894. // credentials, if they exist...
  895. //
  896. for (ListEntry = pLogonSession->CredmanCredentials.List.Flink ;
  897. (ListEntry != &pLogonSession->CredmanCredentials.List && !Found);
  898. ListEntry = ListEntry->Flink )
  899. {
  900. CredmanCred = CONTAINING_RECORD(ListEntry, KERB_CREDMAN_CRED, ListEntry.Next);
  901. // We only match on UserName / DomainName for credman creds
  902. if(!RtlEqualUnicodeString(
  903. &CredToMatch->UserName,
  904. &CredmanCred->CredmanUserName,
  905. TRUE
  906. ))
  907. {
  908. continue;
  909. }
  910. if(!RtlEqualUnicodeString(
  911. &CredToMatch->DomainName,
  912. &CredmanCred->CredmanDomainName,
  913. TRUE
  914. ))
  915. {
  916. continue;
  917. }
  918. //
  919. // Differentiate between pkiint & password based structures
  920. //
  921. if ((CredmanCred->SuppliedCredentials->PublicKeyCreds != NULL) &&
  922. (CredToMatch->PublicKeyCreds != NULL))
  923. {
  924. if (!KerbComparePublicKeyCreds(
  925. CredToMatch->PublicKeyCreds,
  926. CredmanCred->SuppliedCredentials->PublicKeyCreds
  927. ))
  928. {
  929. continue;
  930. }
  931. PublicKeyCred = TRUE;
  932. }
  933. Found = TRUE;
  934. *NewCred = CredmanCred;
  935. } // FOR
  936. if (Found)
  937. {
  938. KerbReferenceCredmanCred(
  939. *NewCred,
  940. pLogonSession,
  941. FALSE
  942. );
  943. //
  944. // Found one. Now we've got to compare the pwd information, and
  945. // change it, if needed...
  946. //
  947. if (!PublicKeyCred)
  948. {
  949. //
  950. // Compare the password list, as the pwd may have changed...
  951. // Note: This has the by-product of tossing old tickets, but
  952. // that's desirable if the pwd's changed, so user knows the creds
  953. // are bogus.
  954. //
  955. if (!KerbComparePasswords(
  956. (*NewCred)->SuppliedCredentials->Passwords,
  957. CredToMatch->Passwords
  958. ))
  959. {
  960. D_DebugLog((DEB_ERROR, "Changing credman cred password\n"));
  961. PKERB_PRIMARY_CREDENTIAL OldPwds = (*NewCred)->SuppliedCredentials;
  962. (*NewCred)->SuppliedCredentials = CredToMatch;
  963. KerbFreePrimaryCredentials(OldPwds, TRUE);
  964. (*NewCred)->CredentialFlags &= ~KERB_CRED_TGT_AVAIL;
  965. }
  966. else
  967. {
  968. KerbFreePrimaryCredentials(CredToMatch, TRUE);
  969. }
  970. }
  971. //
  972. // Free up the cred to match, since we already have a copy stored w/ our credential
  973. //
  974. }
  975. else // new cred, so prepare CredmanCred to add to list...
  976. {
  977. Status = KerbCreateCredmanCred(
  978. CredToMatch,
  979. NewCred
  980. );
  981. if (!NT_SUCCESS(Status))
  982. {
  983. goto Cleanup;
  984. }
  985. KerbInsertListEntryTail(
  986. &((*NewCred)->ListEntry),
  987. &pLogonSession->CredmanCredentials
  988. );
  989. // add a ref for caller of this function.
  990. KerbReferenceCredmanCred(
  991. (*NewCred),
  992. pLogonSession,
  993. FALSE
  994. );
  995. }
  996. //
  997. // We need an initial TGT for this cred
  998. //
  999. if (((*NewCred)->CredentialFlags & KERB_CRED_TGT_AVAIL) == 0)
  1000. {
  1001. //
  1002. // Get an initial TGT for this cred.
  1003. //
  1004. Status = KerbGetTicketGrantingTicket(
  1005. pLogonSession,
  1006. NULL,
  1007. (*NewCred),
  1008. NULL,
  1009. NULL,
  1010. NULL
  1011. );
  1012. if (!NT_SUCCESS(Status))
  1013. {
  1014. DebugLog((DEB_ERROR, "Failed to get TGT for credman cred - %x\n",Status));
  1015. if( Status == STATUS_NO_LOGON_SERVERS )
  1016. {
  1017. //
  1018. // negotiate treats NO_LOGON_SERVERS as a downgrade.
  1019. // Nego allows downgrade for explicit creds, but not default creds.
  1020. // Credman is basically explicit creds. So over-ride the error code.
  1021. //
  1022. Status = SEC_E_TARGET_UNKNOWN;
  1023. }
  1024. goto Cleanup;
  1025. }
  1026. (*NewCred)->CredentialFlags |= KERB_CRED_TGT_AVAIL;
  1027. }
  1028. Cleanup:
  1029. KerbUnlockList(&pLogonSession->CredmanCredentials);
  1030. return (Status);
  1031. }
  1032. //+-------------------------------------------------------------------------
  1033. //
  1034. // Function: KerbCheckCredMgrForGivenTarget
  1035. //
  1036. // Synopsis: Goes to the credential manager to try and find
  1037. // credentials for the specific target
  1038. //
  1039. // Arguments: pLogonSession - logon session
  1040. // pTargetName - service name
  1041. // pTargetDomainName - domain name
  1042. // pTargetForestName - forest name
  1043. // pKerbCred - credential to be allocated
  1044. //
  1045. // Requires:
  1046. //
  1047. // Returns:
  1048. //
  1049. // Notes:
  1050. //
  1051. //
  1052. //--------------------------------------------------------------------------
  1053. NTSTATUS
  1054. KerbCheckCredMgrForGivenTarget(
  1055. IN PKERB_LOGON_SESSION pLogonSession,
  1056. IN PKERB_CREDENTIAL Credential,
  1057. IN PUNICODE_STRING SuppliedTargetName,
  1058. IN PKERB_INTERNAL_NAME pTargetName,
  1059. IN ULONG TargetInfoFlags,
  1060. IN PUNICODE_STRING pTargetDomainName,
  1061. IN PUNICODE_STRING pTargetForestName,
  1062. IN OUT PKERB_CREDMAN_CRED *CredmanCred,
  1063. IN OUT PBYTE *pbMarshalledTargetInfo,
  1064. IN OUT ULONG *cbMarshalledTargetInfo
  1065. )
  1066. {
  1067. CREDENTIAL_TARGET_INFORMATIONW CredTargetInfo;
  1068. ULONG cCreds = 0;
  1069. PCREDENTIALW *rgpCreds = NULL;
  1070. PENCRYPTED_CREDENTIALW *rgpEncryptedCreds = NULL;
  1071. LPWSTR pwszTargetName = NULL;
  1072. LPWSTR pwszDomainName = NULL;
  1073. LPWSTR pwszForestName = NULL;
  1074. BOOLEAN fImpersonating = FALSE;
  1075. ULONG i;
  1076. BOOLEAN fFoundCredManCred = FALSE;
  1077. PCERT_CREDENTIAL_INFO pCertCredInfo = NULL;
  1078. HCERTSTORE hCertStore = NULL;
  1079. PCERT_CONTEXT pCertContext = NULL;
  1080. CRYPT_HASH_BLOB HashBlob;
  1081. UNICODE_STRING CredManUserName = {0};
  1082. UNICODE_STRING CredManDomainName = {0};
  1083. UNICODE_STRING CredManTargetName = {0};
  1084. UNICODE_STRING Password = {0};
  1085. CRED_MARSHAL_TYPE MarshalType;
  1086. UNICODE_STRING RevealedPassword;
  1087. HANDLE ClientTokenHandle = NULL;
  1088. CREDENTIAL_TARGET_INFORMATIONW *pTargetInfo = NULL;
  1089. NTSTATUS Status = STATUS_SUCCESS;
  1090. PKERB_PRIMARY_CREDENTIAL pCredMgrCred = NULL;
  1091. RtlZeroMemory(&CredTargetInfo, sizeof(CredTargetInfo));
  1092. RtlZeroMemory(&RevealedPassword, sizeof(RevealedPassword));
  1093. *CredmanCred = NULL;
  1094. Status = CredpExtractMarshalledTargetInfo(
  1095. SuppliedTargetName,
  1096. &pTargetInfo
  1097. );
  1098. if(!NT_SUCCESS(Status))
  1099. {
  1100. goto Cleanup;
  1101. }
  1102. //
  1103. // allocate space for the names
  1104. //
  1105. if (NULL != pTargetName)
  1106. {
  1107. //
  1108. // want to use the second part of the SPN
  1109. //
  1110. if (pTargetName->NameCount > 1)
  1111. {
  1112. pwszTargetName = (LPWSTR)KerbAllocate(pTargetName->Names[1].Length + sizeof(WCHAR));
  1113. if (NULL == pwszTargetName)
  1114. {
  1115. Status = STATUS_INSUFFICIENT_RESOURCES;
  1116. goto Cleanup;
  1117. }
  1118. RtlCopyMemory(
  1119. (PUCHAR)pwszTargetName,
  1120. pTargetName->Names[1].Buffer,
  1121. pTargetName->Names[1].Length);
  1122. pwszTargetName[pTargetName->Names[1].Length / sizeof(WCHAR)] = L'\0';
  1123. CredTargetInfo.DnsServerName = pwszTargetName;
  1124. RtlInitUnicodeString(&CredManTargetName, pwszTargetName);
  1125. }
  1126. }
  1127. if ((NULL != pTargetDomainName) && (0 != pTargetDomainName->Length) &&
  1128. (NULL != pTargetDomainName->Buffer))
  1129. {
  1130. pwszDomainName = (LPWSTR)KerbAllocate(pTargetDomainName->Length + sizeof(WCHAR));
  1131. if (NULL == pwszDomainName)
  1132. {
  1133. Status = STATUS_INSUFFICIENT_RESOURCES;
  1134. goto Cleanup;
  1135. }
  1136. RtlCopyMemory(
  1137. (PUCHAR)pwszDomainName,
  1138. pTargetDomainName->Buffer,
  1139. pTargetDomainName->Length);
  1140. pwszDomainName[pTargetDomainName->Length / sizeof(WCHAR)] = L'\0';
  1141. CredTargetInfo.DnsDomainName = pwszDomainName;
  1142. }
  1143. if ((NULL != pTargetForestName) && (0 != pTargetForestName->Length) &&
  1144. (NULL != pTargetForestName->Buffer))
  1145. {
  1146. pwszForestName = (LPWSTR)KerbAllocate(pTargetForestName->Length + sizeof(WCHAR));
  1147. if (NULL == pwszForestName)
  1148. {
  1149. Status = STATUS_INSUFFICIENT_RESOURCES;
  1150. goto Cleanup;
  1151. }
  1152. RtlCopyMemory(
  1153. (PUCHAR)pwszForestName,
  1154. pTargetForestName->Buffer,
  1155. pTargetForestName->Length);
  1156. pwszForestName[pTargetForestName->Length / sizeof(WCHAR)] = L'\0';
  1157. CredTargetInfo.DnsTreeName = pwszForestName;
  1158. }
  1159. CredTargetInfo.PackageName = KERBEROS_PACKAGE_NAME;
  1160. //
  1161. // if marshalled targetinfo supplied, use it instead.
  1162. //
  1163. if( pTargetInfo )
  1164. {
  1165. CredTargetInfo.TargetName = pTargetInfo->TargetName;
  1166. CredTargetInfo.NetbiosServerName = pTargetInfo->NetbiosServerName;
  1167. CredTargetInfo.DnsServerName = pTargetInfo->DnsServerName;
  1168. CredTargetInfo.NetbiosDomainName = pTargetInfo->NetbiosDomainName;
  1169. CredTargetInfo.DnsDomainName = pTargetInfo->DnsDomainName;
  1170. CredTargetInfo.DnsTreeName = pTargetInfo->DnsTreeName;
  1171. CredTargetInfo.Flags |= pTargetInfo->Flags;
  1172. } else {
  1173. //
  1174. // copy the names in to the memory and set the names
  1175. // in the PCREDENTIAL_TARGET_INFORMATIONW struct
  1176. //
  1177. if (pwszTargetName)
  1178. {
  1179. CredTargetInfo.Flags |= CRED_TI_SERVER_FORMAT_UNKNOWN;
  1180. }
  1181. if (pwszDomainName)
  1182. {
  1183. CredTargetInfo.Flags |= CRED_TI_DOMAIN_FORMAT_UNKNOWN;
  1184. }
  1185. CredTargetInfo.Flags |= TargetInfoFlags;
  1186. }
  1187. // need to specify a flag to indicate that we don't know what we are
  1188. // doing and both types of names should be checked.
  1189. Status = LsaFunctions->CrediReadDomainCredentials(
  1190. &pLogonSession->LogonId,
  1191. CREDP_FLAGS_IN_PROCESS, // Allow password to be returned
  1192. &CredTargetInfo,
  1193. 0,
  1194. &cCreds,
  1195. &rgpEncryptedCreds );
  1196. rgpCreds = (PCREDENTIALW *) rgpEncryptedCreds;
  1197. if (!NT_SUCCESS(Status))
  1198. {
  1199. // quiet these.
  1200. if ((Status == STATUS_NOT_FOUND) ||(Status == STATUS_NO_SUCH_LOGON_SESSION) )
  1201. {
  1202. D_DebugLog((DEB_TRACE,"No credentials from the cred mgr!\n", Status));
  1203. }
  1204. else
  1205. {
  1206. DebugLog((DEB_WARN,"Failed to read credentials from the cred mgr 0x%x.\n", Status));
  1207. }
  1208. // indicate success so we proceed with default creds
  1209. Status = STATUS_SUCCESS;
  1210. goto Cleanup;
  1211. }
  1212. //
  1213. // now evaluate the creds which were returned to determine
  1214. // which one we should use.
  1215. //
  1216. // First choice is a certificate which may be
  1217. // used for PKINIT.
  1218. //
  1219. for(i=0;i<cCreds;i++)
  1220. {
  1221. // check if this is a cert type
  1222. if (CRED_TYPE_DOMAIN_CERTIFICATE != (rgpCreds[i])->Type)
  1223. {
  1224. continue;
  1225. }
  1226. if( !fImpersonating )
  1227. {
  1228. //
  1229. // no longer need to be impersonating prior to calling credmanager
  1230. // only impersonate if doing Certificate operations.
  1231. //
  1232. Status = LsaFunctions->OpenTokenByLogonId(
  1233. &pLogonSession->LogonId,
  1234. &ClientTokenHandle
  1235. );
  1236. if (!NT_SUCCESS(Status))
  1237. {
  1238. D_DebugLog((DEB_ERROR,"Unable to get the client token handle.\n"));
  1239. goto Cleanup;
  1240. }
  1241. if(!SetThreadToken(NULL, ClientTokenHandle))
  1242. {
  1243. D_DebugLog((DEB_ERROR,"Unable to impersonate the client token handle.\n"));
  1244. Status = STATUS_CANNOT_IMPERSONATE;
  1245. goto Cleanup;
  1246. }
  1247. fImpersonating = TRUE;
  1248. }
  1249. // check for the prompt now flag
  1250. if ((rgpCreds[i])->Flags & CRED_FLAGS_PROMPT_NOW)
  1251. {
  1252. Status = SEC_E_LOGON_DENIED;
  1253. goto Cleanup;
  1254. }
  1255. //
  1256. // unmarshal the cert cred info from the user name field
  1257. // of the cred man cred
  1258. //
  1259. if (!CredUnmarshalCredentialW(
  1260. (rgpCreds[i])->UserName,
  1261. &MarshalType,
  1262. (void**)&pCertCredInfo
  1263. ))
  1264. {
  1265. Status = STATUS_LOGON_FAILURE;
  1266. goto Cleanup;
  1267. }
  1268. if (CertCredential != MarshalType)
  1269. {
  1270. Status = STATUS_LOGON_FAILURE;
  1271. goto Cleanup;
  1272. }
  1273. // open a cert store if necessary
  1274. if (NULL == hCertStore)
  1275. {
  1276. hCertStore = CertOpenStore(
  1277. CERT_STORE_PROV_SYSTEM_W,
  1278. 0,
  1279. 0,
  1280. CERT_SYSTEM_STORE_CURRENT_USER,
  1281. L"MY");
  1282. if (NULL == hCertStore)
  1283. {
  1284. D_DebugLog((DEB_ERROR,"Failed to open the user cert store even though a cert cred was found.\n"));
  1285. break;
  1286. }
  1287. }
  1288. // find the cert in the store which meets this hash
  1289. HashBlob.cbData = sizeof(pCertCredInfo->rgbHashOfCert);
  1290. HashBlob.pbData = pCertCredInfo->rgbHashOfCert;
  1291. pCertContext = (PCERT_CONTEXT)CertFindCertificateInStore(
  1292. hCertStore,
  1293. X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
  1294. 0,
  1295. CERT_FIND_HASH,
  1296. &HashBlob,
  1297. pCertContext);
  1298. if (NULL != pCertContext)
  1299. {
  1300. // check if the PKINIT (SC Logon) OID is in the cert
  1301. if (KerbCheckForPKINITEnhKeyUsage(pCertContext))
  1302. {
  1303. //
  1304. // add the cert credential to the Kerb credential
  1305. //
  1306. // Cred man will no longer give us a pin..
  1307. Status = KerbAddCertCredToPrimaryCredential(
  1308. pLogonSession,
  1309. &CredManTargetName,
  1310. pCertContext,
  1311. &Password, // essentially, a NULL string
  1312. CONTEXT_INITIALIZED_WITH_CRED_MAN_CREDS,
  1313. &pCredMgrCred
  1314. );
  1315. if (NT_SUCCESS(Status))
  1316. {
  1317. fFoundCredManCred = TRUE;
  1318. }
  1319. else
  1320. {
  1321. D_DebugLog((DEB_WARN,"Failed to add the cert cred to the credential.\n"));
  1322. }
  1323. break;
  1324. }
  1325. else
  1326. {
  1327. D_DebugLog((DEB_WARN,"Failed to find the PKINIT EKU in the cred mgr cert.\n"));
  1328. }
  1329. }
  1330. else
  1331. {
  1332. D_DebugLog((DEB_WARN,"Failed to find a cert that the cred mgr iniddciated existed\n"));
  1333. }
  1334. }
  1335. //
  1336. // If we didn't find a cert we can use then try for a password cred.
  1337. //
  1338. if (!fFoundCredManCred)
  1339. {
  1340. for(i=0;i<cCreds;i++)
  1341. {
  1342. // check if this is a password cred type and pick the first one
  1343. // that we find
  1344. if (CRED_TYPE_DOMAIN_PASSWORD == (rgpCreds[i])->Type)
  1345. {
  1346. // check for the prompt now flag
  1347. if ((rgpCreds[i])->Flags & CRED_FLAGS_PROMPT_NOW)
  1348. {
  1349. Status = SEC_E_LOGON_DENIED;
  1350. goto Cleanup;
  1351. }
  1352. //
  1353. // get the user name and domain name from the credential manager info
  1354. //
  1355. // NOTE - CredpParseUserName does not allocate the UNICODE_STRING
  1356. // buffers so these should not be freed (RtlInitUnicodeString is used)
  1357. //
  1358. Status = CredpParseUserName(
  1359. (rgpCreds[i])->UserName,
  1360. &CredManUserName,
  1361. &CredManDomainName);
  1362. if (!NT_SUCCESS(Status))
  1363. {
  1364. D_DebugLog((DEB_WARN,"Failed to parse the add the cert cred to the credential.\n"));
  1365. fFoundCredManCred = TRUE;
  1366. break;
  1367. }
  1368. Password.Buffer = (LPWSTR)((rgpCreds[i])->CredentialBlob);
  1369. Password.MaximumLength = (USHORT)(rgpCreds[i])->CredentialBlobSize;
  1370. Password.Length = (USHORT)(rgpEncryptedCreds[i])->ClearCredentialBlobSize;
  1371. // add the cert credential to the Kerb credential
  1372. Status = KerbAddPasswordCredToPrimaryCredential(
  1373. pLogonSession,
  1374. &CredManUserName,
  1375. &CredManDomainName,
  1376. &CredManTargetName,
  1377. &Password,
  1378. &pCredMgrCred
  1379. );
  1380. if (!NT_SUCCESS(Status))
  1381. {
  1382. DebugLog((DEB_WARN,"Failed to add the cred mgr password to the credential.\n"));
  1383. }
  1384. }
  1385. }
  1386. }
  1387. //
  1388. // We've built the credman cred, now go ahead and add it to the logon.
  1389. //
  1390. if (NT_SUCCESS(Status) && (NULL != pCredMgrCred))
  1391. {
  1392. Status = KerbAddCredmanCredToLogonSession(
  1393. pLogonSession,
  1394. pCredMgrCred, // note: freed by this fn
  1395. CredmanCred
  1396. );
  1397. if (!NT_SUCCESS(Status))
  1398. {
  1399. DebugLog((DEB_ERROR, "Failed to add credman cred to logon session\n"));
  1400. goto Cleanup;
  1401. }
  1402. }
  1403. Cleanup:
  1404. if( NT_SUCCESS(Status) )
  1405. {
  1406. SECPKG_CALL_INFO CallInfo;
  1407. //
  1408. // return a copy of the credential target info for kernel callers (MUP/DFS/RDR).
  1409. //
  1410. if( LsaFunctions->GetCallInfo(&CallInfo) &&
  1411. (CallInfo.Attributes & SECPKG_CALL_KERNEL_MODE)
  1412. )
  1413. {
  1414. CredMarshalTargetInfo(
  1415. &CredTargetInfo,
  1416. (PUSHORT*)pbMarshalledTargetInfo,
  1417. cbMarshalledTargetInfo
  1418. );
  1419. }
  1420. }
  1421. if (fImpersonating)
  1422. {
  1423. RevertToSelf();
  1424. }
  1425. if( ClientTokenHandle != NULL )
  1426. {
  1427. CloseHandle( ClientTokenHandle );
  1428. }
  1429. if (NULL != pCertContext)
  1430. {
  1431. CertFreeCertificateContext(pCertContext);
  1432. }
  1433. if (NULL != hCertStore)
  1434. {
  1435. CertCloseStore(hCertStore, 0);
  1436. }
  1437. if( pTargetInfo != NULL )
  1438. {
  1439. LocalFree( pTargetInfo );
  1440. }
  1441. if (NULL != pwszTargetName)
  1442. {
  1443. KerbFree(pwszTargetName);
  1444. }
  1445. if (NULL != pwszDomainName)
  1446. {
  1447. KerbFree(pwszDomainName);
  1448. }
  1449. if (NULL != pwszForestName)
  1450. {
  1451. KerbFree(pwszForestName);
  1452. }
  1453. if (NULL != rgpCreds)
  1454. {
  1455. //
  1456. // Free the returned credentials
  1457. //
  1458. LsaFunctions->CrediFreeCredentials(
  1459. cCreds,
  1460. rgpEncryptedCreds );
  1461. }
  1462. if (NULL != pCertCredInfo)
  1463. {
  1464. CredFree(pCertCredInfo);
  1465. }
  1466. return Status;
  1467. }
  1468. NTSTATUS
  1469. CopyCredManCredentials(
  1470. IN PLUID LogonId,
  1471. CREDENTIAL_TARGET_INFORMATIONW* pTargetInfo,
  1472. IN OUT PUNICODE_STRING pUserName,
  1473. IN OUT PUNICODE_STRING pDomainName,
  1474. IN OUT PUNICODE_STRING pPassword
  1475. )
  1476. /*++
  1477. Routine Description:
  1478. Look for a keyring credential entry for the specified domain, and copy to Context handle if found
  1479. Arguments:
  1480. LogonId -- LogonId of the calling process.
  1481. pTargetInfo -- Information on target to search for creds.
  1482. Context - Points to the ContextHandle of the Context
  1483. to be referenced.
  1484. Return Value:
  1485. STATUS_SUCCESS -- All OK
  1486. STATUS_NOT_FOUND - Credential couldn't be found.
  1487. All others are real failures and should be returned to the caller.
  1488. --*/
  1489. {
  1490. NTSTATUS Status;
  1491. PCREDENTIALW *Credentials = NULL;
  1492. PENCRYPTED_CREDENTIALW *EncryptedCredentials = NULL;
  1493. ULONG CredentialCount;
  1494. ULONG CredIndex;
  1495. RtlInitUnicodeString(pUserName, NULL);
  1496. RtlInitUnicodeString(pDomainName, NULL);
  1497. RtlInitUnicodeString(pPassword, NULL);
  1498. Status = LsaFunctions->CrediReadDomainCredentials(
  1499. LogonId,
  1500. CREDP_FLAGS_IN_PROCESS, // Allow password to be returned
  1501. pTargetInfo,
  1502. 0, // no flags
  1503. &CredentialCount,
  1504. &EncryptedCredentials );
  1505. Credentials = (PCREDENTIALW *) EncryptedCredentials;
  1506. if(!NT_SUCCESS(Status))
  1507. {
  1508. //
  1509. // Ideally, only STATUS_NO_SUCH_LOGON_SESSION should be converted to
  1510. // STATUS_NOT_FOUND. However, swallowing all failures and asserting
  1511. // these specific two works around a bug in CrediReadDomainCredentials
  1512. // which returns invalid parameter if the target is a user account name.
  1513. // Eventually, CrediReadDomainCredentials should return a more appropriate
  1514. // error in this case.
  1515. //
  1516. return STATUS_NOT_FOUND;
  1517. }
  1518. //
  1519. // Loop through the list of credentials
  1520. //
  1521. for ( CredIndex=0; CredIndex<CredentialCount; CredIndex++ ) {
  1522. UNICODE_STRING UserName;
  1523. UNICODE_STRING DomainName;
  1524. UNICODE_STRING TempString;
  1525. //
  1526. // only supports password credentials
  1527. //
  1528. if ( Credentials[CredIndex]->Type != CRED_TYPE_DOMAIN_PASSWORD ) {
  1529. continue;
  1530. }
  1531. if ( Credentials[CredIndex]->Flags & CRED_FLAGS_PROMPT_NOW ) {
  1532. Status = SEC_E_LOGON_DENIED;
  1533. goto Cleanup;
  1534. }
  1535. //
  1536. // Sanity check the credential
  1537. //
  1538. if ( Credentials[CredIndex]->UserName == NULL ) {
  1539. Status = STATUS_NOT_FOUND;
  1540. goto Cleanup;
  1541. }
  1542. //
  1543. // Convert the UserName to domain name and user name
  1544. //
  1545. Status = CredpParseUserName(
  1546. Credentials[CredIndex]->UserName,
  1547. &UserName,
  1548. &DomainName
  1549. );
  1550. if(!NT_SUCCESS(Status))
  1551. {
  1552. goto Cleanup;
  1553. }
  1554. if( DomainName.Buffer )
  1555. {
  1556. Status = KerbDuplicateString(pDomainName, &DomainName);
  1557. if ( !NT_SUCCESS( Status ) )
  1558. {
  1559. goto Cleanup;
  1560. }
  1561. }
  1562. if( UserName.Buffer )
  1563. {
  1564. Status = KerbDuplicateString(pUserName, &UserName);
  1565. if ( !NT_SUCCESS( Status ) )
  1566. {
  1567. goto Cleanup;
  1568. }
  1569. }
  1570. //
  1571. // Free the existing password and add the new one
  1572. //
  1573. TempString.Buffer = (LPWSTR)Credentials[CredIndex]->CredentialBlob;
  1574. TempString.MaximumLength = (USHORT) Credentials[CredIndex]->CredentialBlobSize;
  1575. TempString.Length = (USHORT) EncryptedCredentials[CredIndex]->ClearCredentialBlobSize;
  1576. // zero length password must be treated as blank or will assume it should use the
  1577. // password of the currently logged in user.
  1578. if ( TempString.Length == 0 )
  1579. {
  1580. TempString.Buffer = L"";
  1581. }
  1582. Status = KerbDuplicatePassword(pPassword, &TempString);
  1583. if ( !NT_SUCCESS( Status ) )
  1584. {
  1585. goto Cleanup;
  1586. }
  1587. goto Cleanup;
  1588. }
  1589. Status = STATUS_NOT_FOUND;
  1590. Cleanup:
  1591. if(!NT_SUCCESS(Status))
  1592. {
  1593. KerbFreeString( pUserName );
  1594. KerbFreeString( pDomainName );
  1595. KerbFreeString( pPassword );
  1596. pUserName->Buffer = NULL;
  1597. pDomainName->Buffer = NULL;
  1598. pPassword->Buffer = NULL;
  1599. }
  1600. //
  1601. // Free the returned credentials
  1602. //
  1603. LsaFunctions->CrediFreeCredentials(
  1604. CredentialCount,
  1605. EncryptedCredentials );
  1606. return Status;
  1607. }
  1608. NTSTATUS
  1609. KerbProcessUserNameCredential(
  1610. IN PUNICODE_STRING MarshalledUserName,
  1611. OUT PUNICODE_STRING UserName,
  1612. OUT PUNICODE_STRING DomainName,
  1613. OUT PUNICODE_STRING Password
  1614. )
  1615. {
  1616. WCHAR FastUserName[ UNLEN+1 ];
  1617. LPWSTR SlowUserName = NULL;
  1618. LPWSTR TempUserName;
  1619. CRED_MARSHAL_TYPE CredMarshalType;
  1620. PUSERNAME_TARGET_CREDENTIAL_INFO pCredentialUserName = NULL;
  1621. CREDENTIAL_TARGET_INFORMATIONW TargetInfo;
  1622. ULONG CredTypes;
  1623. SECPKG_CLIENT_INFO ClientInfo;
  1624. NTSTATUS Status = STATUS_NOT_FOUND;
  1625. if( (MarshalledUserName->Length+sizeof(WCHAR)) <= sizeof(FastUserName) )
  1626. {
  1627. TempUserName = FastUserName;
  1628. } else {
  1629. SlowUserName = (LPWSTR)KerbAllocate( MarshalledUserName->Length + sizeof(WCHAR) );
  1630. if( SlowUserName == NULL )
  1631. {
  1632. return STATUS_INSUFFICIENT_RESOURCES;
  1633. }
  1634. TempUserName = SlowUserName;
  1635. }
  1636. //
  1637. // copy the input to a NULL terminated string, then attempt to unmarshal it.
  1638. //
  1639. RtlCopyMemory( TempUserName,
  1640. MarshalledUserName->Buffer,
  1641. MarshalledUserName->Length
  1642. );
  1643. TempUserName[ MarshalledUserName->Length / sizeof(WCHAR) ] = L'\0';
  1644. if(!CredUnmarshalCredentialW(
  1645. TempUserName,
  1646. &CredMarshalType,
  1647. (VOID**)&pCredentialUserName
  1648. ))
  1649. {
  1650. goto Cleanup;
  1651. }
  1652. if( (CredMarshalType != UsernameTargetCredential) )
  1653. {
  1654. goto Cleanup;
  1655. }
  1656. //
  1657. // now query credential manager for a match.
  1658. //
  1659. Status = LsaFunctions->GetClientInfo(&ClientInfo);
  1660. if(!NT_SUCCESS(Status))
  1661. {
  1662. goto Cleanup;
  1663. }
  1664. ZeroMemory( &TargetInfo, sizeof(TargetInfo) );
  1665. CredTypes = CRED_TYPE_DOMAIN_PASSWORD;
  1666. TargetInfo.Flags = CRED_TI_USERNAME_TARGET;
  1667. TargetInfo.TargetName = pCredentialUserName->UserName;
  1668. TargetInfo.PackageName = KERBEROS_PACKAGE_NAME;
  1669. TargetInfo.CredTypeCount = 1;
  1670. TargetInfo.CredTypes = &CredTypes;
  1671. Status = CopyCredManCredentials(
  1672. &ClientInfo.LogonId,
  1673. &TargetInfo,
  1674. UserName,
  1675. DomainName,
  1676. Password
  1677. );
  1678. if(!NT_SUCCESS(Status))
  1679. {
  1680. goto Cleanup;
  1681. }
  1682. KerbRevealPassword( Password );
  1683. Cleanup:
  1684. if( pCredentialUserName != NULL )
  1685. {
  1686. CredFree( pCredentialUserName );
  1687. }
  1688. if( SlowUserName )
  1689. {
  1690. KerbFree( SlowUserName );
  1691. }
  1692. return Status;
  1693. }