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.

1400 lines
36 KiB

  1. //+-----------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (c) Microsoft Corporation 1992 - 1996
  6. //
  7. // File: credmgr.cxx
  8. //
  9. // Contents: Code for managing credentials list for the Kerberos package
  10. //
  11. //
  12. // History: 17-April-1996 Created MikeSw
  13. // 26-Sep-1998 ChandanS
  14. // Added more debugging support etc.
  15. //
  16. //------------------------------------------------------------------------
  17. #include <kerb.hxx>
  18. #define CREDMGR_ALLOCATE
  19. #include <kerbp.h>
  20. #ifdef RETAIL_LOG_SUPPORT
  21. static TCHAR THIS_FILE[]=TEXT(__FILE__);
  22. #endif
  23. //+-------------------------------------------------------------------------
  24. //
  25. // Function: KerbInitCredentialList
  26. //
  27. // Synopsis: Initializes the Credentials list
  28. //
  29. // Effects: allocates a resources
  30. //
  31. // Arguments: none
  32. //
  33. // Requires:
  34. //
  35. // Returns: STATUS_SUCCESS on success, other error codes
  36. // on failure
  37. //
  38. // Notes:
  39. //
  40. //
  41. //--------------------------------------------------------------------------
  42. NTSTATUS
  43. KerbInitCredentialList(
  44. VOID
  45. )
  46. {
  47. NTSTATUS Status;
  48. Status = KerbInitializeList( &KerbCredentialList );
  49. if (!NT_SUCCESS(Status))
  50. {
  51. goto Cleanup;
  52. }
  53. KerberosCredentialsInitialized = TRUE;
  54. Cleanup:
  55. if (!NT_SUCCESS(Status))
  56. {
  57. KerbFreeList( &KerbCredentialList);
  58. }
  59. return(Status);
  60. }
  61. //+-------------------------------------------------------------------------
  62. //
  63. // Function: KerbFreeCredentialList
  64. //
  65. // Synopsis: Frees the credentials list
  66. //
  67. // Effects:
  68. //
  69. // Arguments: none
  70. //
  71. // Requires:
  72. //
  73. // Returns: none
  74. //
  75. // Notes:
  76. //
  77. //
  78. //--------------------------------------------------------------------------
  79. VOID
  80. KerbFreeCredentialList(
  81. VOID
  82. )
  83. {
  84. PKERB_CREDENTIAL Credential;
  85. if (KerberosCredentialsInitialized)
  86. {
  87. KerbLockList(&KerbCredentialList);
  88. //
  89. // Go through the list of logon sessions and dereferences them all
  90. //
  91. while (!IsListEmpty(&KerbCredentialList.List))
  92. {
  93. Credential = CONTAINING_RECORD(
  94. KerbCredentialList.List.Flink,
  95. KERB_CREDENTIAL,
  96. ListEntry.Next
  97. );
  98. KerbReferenceListEntry(
  99. &KerbCredentialList,
  100. &Credential->ListEntry,
  101. TRUE
  102. );
  103. KerbDereferenceCredential(Credential);
  104. }
  105. KerbFreeList(&KerbCredentialList);
  106. }
  107. }
  108. //+-------------------------------------------------------------------------
  109. //
  110. // Function: KerbAllocateCredential
  111. //
  112. // Synopsis: Allocates a credential structure
  113. //
  114. // Effects: Allocates a credential, but does not add it to the
  115. // list of credentials
  116. //
  117. // Arguments: NewCredential - receives a new credential allocated
  118. // with KerbAllocate
  119. //
  120. // Requires:
  121. //
  122. // Returns: STATUS_SUCCESS on success
  123. // STATUS_INSUFFICIENT_RESOURCES if the allocation fails
  124. //
  125. // Notes:
  126. //
  127. //
  128. //--------------------------------------------------------------------------
  129. NTSTATUS
  130. KerbAllocateCredential(
  131. PKERB_CREDENTIAL * NewCredential
  132. )
  133. {
  134. PKERB_CREDENTIAL Credential;
  135. SECPKG_CALL_INFO CallInfo;
  136. NTSTATUS Status = STATUS_SUCCESS;
  137. *NewCredential = NULL;
  138. if (!LsaFunctions->GetCallInfo(&CallInfo))
  139. {
  140. D_DebugLog((DEB_ERROR,"Failed to get call info. %ws, line %d\n",
  141. THIS_FILE, __LINE__));
  142. Status = STATUS_INSUFFICIENT_RESOURCES;
  143. goto Cleanup;
  144. }
  145. Credential = (PKERB_CREDENTIAL) KerbAllocate(
  146. sizeof(KERB_CREDENTIAL) );
  147. if (Credential == NULL)
  148. {
  149. Status = STATUS_INSUFFICIENT_RESOURCES;
  150. goto Cleanup;
  151. }
  152. Credential->ClientProcess = CallInfo.ProcessId;
  153. KerbInitializeListEntry(
  154. &Credential->ListEntry
  155. );
  156. //
  157. // Set the references to 1 since we are returning a pointer to the
  158. // logon session
  159. //
  160. Credential->HandleCount = 1;
  161. *NewCredential = Credential;
  162. Cleanup:
  163. return(Status);
  164. }
  165. //+-------------------------------------------------------------------------
  166. //
  167. // Function: KerbInsertCredential
  168. //
  169. // Synopsis: Inserts a logon session into the list of logon sessions
  170. //
  171. // Effects: bumps reference count on logon session
  172. //
  173. // Arguments: Credential - Credential to insert
  174. //
  175. // Requires:
  176. //
  177. // Returns: STATUS_SUCCESS always
  178. //
  179. // Notes:
  180. //
  181. //
  182. //--------------------------------------------------------------------------
  183. NTSTATUS
  184. KerbInsertCredential(
  185. IN PKERB_CREDENTIAL Credential
  186. )
  187. {
  188. //
  189. // insert entry at tail of list.
  190. // reason: entries at the head are more likely to have _TGT flag set
  191. // and, those are the ones we want to satisfy from cache for repeat
  192. // high stress offenders...
  193. //
  194. Credential->CredentialTag = KERB_CREDENTIAL_TAG_ACTIVE;
  195. KerbInsertListEntryTail(
  196. &Credential->ListEntry,
  197. &KerbCredentialList
  198. );
  199. return(STATUS_SUCCESS);
  200. }
  201. //+-------------------------------------------------------------------------
  202. //
  203. // Function: KerbFreePrimaryCredentials
  204. //
  205. // Synopsis: frees a primary credentials structure
  206. //
  207. // Effects:
  208. //
  209. // Arguments:
  210. //
  211. // Requires:
  212. //
  213. // Returns:
  214. //
  215. // Notes:
  216. //
  217. //
  218. //--------------------------------------------------------------------------
  219. VOID
  220. KerbFreePrimaryCredentials(
  221. IN PKERB_PRIMARY_CREDENTIAL Credentials,
  222. IN BOOLEAN FreeBaseStructure
  223. )
  224. {
  225. if (Credentials != NULL)
  226. {
  227. KerbFreeString(&Credentials->DomainName);
  228. KerbFreeString(&Credentials->OldDomainName);
  229. KerbFreeString(&Credentials->UserName);
  230. KerbFreeString(&Credentials->OldUserName);
  231. RtlZeroMemory( &Credentials->OldHashPassword, sizeof(Credentials->OldHashPassword) );
  232. if (Credentials->ClearPassword.Buffer != NULL)
  233. {
  234. RtlZeroMemory(
  235. Credentials->ClearPassword.Buffer,
  236. Credentials->ClearPassword.Length
  237. );
  238. KerbFreeString(&Credentials->ClearPassword);
  239. RtlZeroMemory(&Credentials->ClearPassword, sizeof(Credentials->ClearPassword));
  240. }
  241. if (Credentials->Passwords != NULL)
  242. {
  243. KerbFreeStoredCred(Credentials->Passwords);
  244. }
  245. if (Credentials->OldPasswords != NULL)
  246. {
  247. KerbFreeStoredCred(Credentials->OldPasswords);
  248. }
  249. KerbPurgeTicketCache(&Credentials->ServerTicketCache);
  250. KerbPurgeTicketCache(&Credentials->AuthenticationTicketCache);
  251. KerbFreePKCreds(Credentials->PublicKeyCreds);
  252. Credentials->PublicKeyCreds = NULL;
  253. if (FreeBaseStructure)
  254. {
  255. KerbFree(Credentials);
  256. }
  257. }
  258. }
  259. //+-------------------------------------------------------------------------
  260. //
  261. // Function: KerbFreeCredential
  262. //
  263. // Synopsis: Frees a credential structure and all contained data
  264. //
  265. // Effects:
  266. //
  267. // Arguments: Credential - The credential to free.
  268. //
  269. // Requires: This credential must be unlinked
  270. //
  271. // Returns:
  272. //
  273. // Notes:
  274. //
  275. //
  276. //--------------------------------------------------------------------------
  277. VOID
  278. KerbFreeCredential(
  279. IN PKERB_CREDENTIAL Credential
  280. )
  281. {
  282. Credential->CredentialTag = KERB_CREDENTIAL_TAG_DELETE;
  283. if (Credential->SuppliedCredentials != NULL)
  284. {
  285. KerbFreePrimaryCredentials(Credential->SuppliedCredentials, TRUE);
  286. }
  287. DsysAssert(Credential->ListEntry.Next.Flink == NULL);
  288. KerbFreeString(&Credential->CredentialName);
  289. KerbFree(Credential);
  290. }
  291. //+-------------------------------------------------------------------------
  292. //
  293. // Function: KerbGetTicketForCredential
  294. //
  295. // Synopsis: Obtains a TGT for a credential if it doesn't already
  296. // have one.
  297. //
  298. // Effects:
  299. //
  300. // Arguments:
  301. //
  302. // Requires:
  303. //
  304. // Returns:
  305. //
  306. // Notes:
  307. //
  308. //
  309. //--------------------------------------------------------------------------
  310. NTSTATUS
  311. KerbGetTicketForCredential(
  312. IN OPTIONAL PKERB_LOGON_SESSION LogonSession,
  313. IN PKERB_CREDENTIAL Credential,
  314. IN OPTIONAL PKERB_CREDMAN_CRED CredManCredentials,
  315. IN OPTIONAL PUNICODE_STRING SuppRealm
  316. )
  317. {
  318. NTSTATUS Status = STATUS_SUCCESS;
  319. PKERB_LOGON_SESSION LocalLogonSession = NULL;
  320. BOOLEAN GetRestrictedTgt = FALSE;
  321. PKERB_TICKET_CACHE_ENTRY Tgt = NULL;
  322. BOOLEAN PrimaryLogonSessionCredential = FALSE;
  323. //
  324. // We've got to make a determination w.r.t. whether this
  325. // is an attempt to renew our primary credential.
  326. // This will affect our logon session flags.
  327. //
  328. if (!ARGUMENT_PRESENT(LogonSession))
  329. {
  330. LocalLogonSession = KerbReferenceLogonSession(
  331. &Credential->LogonId,
  332. FALSE // don't unlink
  333. );
  334. if (LocalLogonSession == NULL)
  335. {
  336. Status = STATUS_NO_SUCH_LOGON_SESSION;
  337. goto Cleanup;
  338. }
  339. }
  340. else
  341. {
  342. LocalLogonSession = LogonSession;
  343. }
  344. //
  345. // Here we make the assumption that if we didn't get a credential
  346. // and we also got a logon session, then we're dealing w/ the
  347. // logon session's primary credential
  348. //
  349. if ((ARGUMENT_PRESENT(Credential)) &&
  350. (Credential->SuppliedCredentials == NULL) &&
  351. (!ARGUMENT_PRESENT(CredManCredentials)))
  352. {
  353. PrimaryLogonSessionCredential = TRUE;
  354. D_DebugLog((DEB_TRACE_CRED, "Getting Credentials for primary logon session - %x\n", LogonSession));
  355. }
  356. else
  357. {
  358. D_DebugLog((DEB_TRACE_CRED, "Got a credential && a logon session\n"));
  359. }
  360. Status = KerbGetTicketGrantingTicket(
  361. LocalLogonSession,
  362. Credential,
  363. CredManCredentials,
  364. SuppRealm,
  365. &Tgt,
  366. NULL // don't return credential key
  367. );
  368. if (!NT_SUCCESS(Status))
  369. {
  370. goto Cleanup;
  371. }
  372. KerbWriteLockLogonSessions(LocalLogonSession);
  373. //
  374. // Clear the logon deferred bit for the logon session, if set
  375. // Note: This will only be cleared as a result of refreshing
  376. // logon session's primary cred tgt
  377. //
  378. if (PrimaryLogonSessionCredential &&
  379. ((LocalLogonSession->LogonSessionFlags & KERB_LOGON_DEFERRED) != 0))
  380. {
  381. LocalLogonSession->LogonSessionFlags &= ~KERB_LOGON_DEFERRED;
  382. }
  383. //
  384. // If we have a credential, be sure to set the TGT_avail bit for
  385. // those credentials.
  386. //
  387. if (ARGUMENT_PRESENT(Credential))
  388. {
  389. Credential->CredentialFlags |= KERB_CRED_TGT_AVAIL;
  390. if ((Credential->CredentialFlags & KERB_CRED_RESTRICTED) != 0)
  391. {
  392. GetRestrictedTgt = TRUE;
  393. }
  394. }
  395. if (ARGUMENT_PRESENT(CredManCredentials))
  396. {
  397. CredManCredentials->CredentialFlags |= KERB_CRED_TGT_AVAIL;
  398. }
  399. KerbUnlockLogonSessions(LocalLogonSession);
  400. #ifdef RESTRICTED_TOKEN
  401. if (GetRestrictedTgt)
  402. {
  403. Status = KerbGetRestrictedTgtForCredential(
  404. LocalLogonSession,
  405. Credential
  406. );
  407. if (!NT_SUCCESS(Status))
  408. {
  409. DebugLog((DEB_ERROR,"Failed to get restricted TGT for credential: 0x%x\n",Status));
  410. KerbRemoveTicketCacheEntry(Tgt);
  411. goto Cleanup;
  412. }
  413. }
  414. #endif
  415. Cleanup:
  416. //
  417. // Reset the bits if we failed
  418. //
  419. if (LocalLogonSession && !NT_SUCCESS(Status))
  420. {
  421. KerbWriteLockLogonSessions(LocalLogonSession);
  422. //
  423. // Don't touch logon session flag, unless we're
  424. // dealing w/ our own logon session. This means
  425. // we don't have a TGT for our initial logon session,
  426. // See RefreshTgt() -- only place we supply logon session
  427. //
  428. if (PrimaryLogonSessionCredential)
  429. {
  430. LocalLogonSession->LogonSessionFlags |= KERB_LOGON_DEFERRED;
  431. D_DebugLog((DEB_TRACE_CRED, "Toggling ON logon deferred bit for logon session %x\n", LogonSession));
  432. }
  433. //
  434. // Or, we expected it to be there with our (supplied) credential
  435. //
  436. if (ARGUMENT_PRESENT(Credential))
  437. {
  438. Credential->CredentialFlags &= ~KERB_CRED_TGT_AVAIL;
  439. }
  440. KerbUnlockLogonSessions(LocalLogonSession);
  441. }
  442. if (!ARGUMENT_PRESENT(LogonSession) && (LocalLogonSession != NULL))
  443. {
  444. KerbDereferenceLogonSession(LocalLogonSession);
  445. }
  446. if (Tgt != NULL)
  447. {
  448. KerbDereferenceTicketCacheEntry(
  449. Tgt
  450. );
  451. }
  452. return(Status);
  453. }
  454. //+-------------------------------------------------------------------------
  455. //
  456. // Function: KerbReferenceCredential
  457. //
  458. // Synopsis: Locates a logon session from the logon ID and references it
  459. //
  460. // Effects: Increments reference count and possible unlinks it from list
  461. //
  462. // Arguments: LogonId - LogonId of logon session to locate
  463. // RequiredFlags - Flags required
  464. // RemoveFromList - If TRUE, logon session will be delinked
  465. // Credential - Receives the referenced credential
  466. //
  467. // Requires:
  468. //
  469. // Returns: NT status codes
  470. //
  471. // Notes:
  472. //
  473. //
  474. //--------------------------------------------------------------------------
  475. NTSTATUS
  476. KerbReferenceCredential(
  477. IN LSA_SEC_HANDLE CredentialHandle,
  478. IN ULONG RequiredFlags,
  479. IN BOOLEAN RemoveFromList,
  480. OUT PKERB_CREDENTIAL * Credential
  481. )
  482. {
  483. PKERB_CREDENTIAL LocalCredential = NULL;
  484. BOOLEAN Found = FALSE;
  485. SECPKG_CALL_INFO CallInfo;
  486. BOOLEAN LocalRemoveFromList = FALSE;
  487. NTSTATUS Status = STATUS_SUCCESS;
  488. ULONG DereferenceCount;
  489. *Credential = NULL;
  490. if(LsaFunctions->GetCallInfo(&CallInfo))
  491. {
  492. DereferenceCount = CallInfo.CallCount;
  493. } else {
  494. ASSERT((STATUS_INTERNAL_ERROR == STATUS_SUCCESS));
  495. return STATUS_INTERNAL_ERROR;
  496. }
  497. if( CallInfo.Attributes & SECPKG_CALL_CLEANUP )
  498. {
  499. CallInfo.Attributes |= SECPKG_CALL_IS_TCB;
  500. DebugLog((DEB_TRACE, "CredHandle %p leaked by ProcessId %x Deref count: %x\n",
  501. CredentialHandle, CallInfo.ProcessId, DereferenceCount));
  502. }
  503. KerbLockList(&KerbCredentialList);
  504. //
  505. // Go through the list of logon sessions looking for the correct
  506. // LUID
  507. //
  508. __try {
  509. LocalCredential = (PKERB_CREDENTIAL)CredentialHandle;
  510. while( LocalCredential->CredentialTag == KERB_CREDENTIAL_TAG_ACTIVE )
  511. {
  512. if (((CallInfo.Attributes & SECPKG_CALL_IS_TCB) == 0) &&
  513. (LocalCredential->ClientProcess != CallInfo.ProcessId) )
  514. {
  515. D_DebugLog((DEB_ERROR,"Trying to reference a credential from another process! %ws, line %d\n", THIS_FILE, __LINE__));
  516. // FESTER
  517. D_DebugLog((DEB_ERROR, "Cred - %x \nClient process - %d Call info Pid - %d\n", LocalCredential, LocalCredential->ClientProcess, CallInfo.ProcessId));
  518. Found = FALSE;
  519. Status = STATUS_PRIVILEGE_NOT_HELD;
  520. break;
  521. }
  522. KerbReferenceListEntry(
  523. &KerbCredentialList,
  524. &LocalCredential->ListEntry,
  525. FALSE // don't remove
  526. );
  527. Found = TRUE;
  528. break;
  529. }
  530. } __except (EXCEPTION_EXECUTE_HANDLER)
  531. {
  532. D_DebugLog((DEB_ERROR,"Trying to reference invalid credential %ws, line %d\n", THIS_FILE, __LINE__));
  533. Found = FALSE;
  534. }
  535. if (!Found)
  536. {
  537. LocalCredential = NULL;
  538. Status = STATUS_INVALID_HANDLE;
  539. }
  540. else
  541. {
  542. ULONG MissingFlags = RequiredFlags - (LocalCredential->CredentialFlags & RequiredFlags);
  543. if (MissingFlags != 0)
  544. {
  545. D_DebugLog((DEB_TRACE,"Credential %p is missing flags: needs %x\n",
  546. Credential,
  547. MissingFlags));
  548. if ((MissingFlags &= KERB_CRED_TGT_AVAIL) != 0)
  549. {
  550. //
  551. // if this is a cred for a local account then no point in attempting to
  552. // get a TGT
  553. //
  554. if ((LocalCredential->CredentialFlags & KERB_CRED_LOCAL_ACCOUNT) == 0)
  555. {
  556. DsysAssert(!RemoveFromList);
  557. KerbUnlockList(&KerbCredentialList);
  558. D_DebugLog((DEB_TRACE_CRED,"Getting missing TGT for credential %x\n", LocalCredential));
  559. Status = KerbGetTicketForCredential(
  560. NULL,
  561. LocalCredential,
  562. NULL,
  563. NULL
  564. );
  565. KerbLockList(&KerbCredentialList);
  566. }
  567. }
  568. else
  569. {
  570. Status = SEC_E_NO_CREDENTIALS;
  571. }
  572. }
  573. if (NT_SUCCESS(Status))
  574. {
  575. //
  576. // Since there may be multiple outstanding handles using this
  577. // structure we don't want to really remove it from the list unless
  578. // the last one releases it.
  579. //
  580. if (RemoveFromList)
  581. {
  582. ASSERT( DereferenceCount != 0 );
  583. ASSERT ( (DereferenceCount <= LocalCredential->HandleCount) );
  584. if( DereferenceCount > LocalCredential->HandleCount ) {
  585. LocalCredential->HandleCount = 0;
  586. } else {
  587. LocalCredential->HandleCount -= DereferenceCount;
  588. }
  589. if (LocalCredential->HandleCount == 0)
  590. {
  591. LocalRemoveFromList = TRUE;
  592. }
  593. }
  594. KerbReferenceListEntry(
  595. &KerbCredentialList,
  596. &LocalCredential->ListEntry,
  597. LocalRemoveFromList
  598. );
  599. KerbDereferenceCredential(LocalCredential);
  600. }
  601. else
  602. {
  603. //
  604. // Remove the earlier reference
  605. //
  606. KerbDereferenceCredential(LocalCredential);
  607. LocalCredential = NULL;
  608. }
  609. *Credential = LocalCredential;
  610. }
  611. KerbUnlockList(&KerbCredentialList);
  612. return(Status);
  613. }
  614. //+-------------------------------------------------------------------------
  615. //
  616. // Function: KerbDereferenceCredential
  617. //
  618. // Synopsis: Dereferences a logon session - if reference count goes
  619. // to zero it frees the logon session
  620. //
  621. // Effects: decrements reference count
  622. //
  623. // Arguments: Credential - Logon session to dereference
  624. //
  625. // Requires:
  626. //
  627. // Returns: none
  628. //
  629. // Notes:
  630. //
  631. //
  632. //--------------------------------------------------------------------------
  633. VOID
  634. KerbDereferenceCredential(
  635. IN PKERB_CREDENTIAL Credential
  636. )
  637. {
  638. if (KerbDereferenceListEntry(
  639. &Credential->ListEntry,
  640. &KerbCredentialList
  641. ) )
  642. {
  643. KerbFreeCredential(Credential);
  644. }
  645. }
  646. //+-------------------------------------------------------------------------
  647. //
  648. // Function: KerbPurgeCredentials
  649. //
  650. // Synopsis: Purges the list of credentials associated with a logon session
  651. // by dereferencing and unlinking them.
  652. //
  653. // Effects: Unlinks all credential on the list
  654. //
  655. // Arguments: CredentialList - List of credentials to purge
  656. //
  657. // Requires:
  658. //
  659. // Returns: none
  660. //
  661. // Notes: No longer used, as some system processes hold cred handles long
  662. // after logons go away. Leads to refcounting disasters.
  663. //
  664. //
  665. //--------------------------------------------------------------------------
  666. /*
  667. VOID
  668. KerbPurgeCredentials(
  669. IN PLIST_ENTRY CredentialList
  670. )
  671. {
  672. PKERB_CREDENTIAL Credential;
  673. KerbLockList(&KerbCredentialList);
  674. while (!IsListEmpty(CredentialList))
  675. {
  676. Credential = CONTAINING_RECORD(
  677. CredentialList->Flink,
  678. KERB_CREDENTIAL,
  679. NextForThisLogonSession
  680. );
  681. //
  682. // Remove it from the credential list
  683. //
  684. //RemoveEntryList(&Credential->NextForThisLogonSession);
  685. Credential->HandleCount = 0;
  686. //
  687. // Reference it to unlink it and then dereference it
  688. //
  689. KerbReferenceListEntry(
  690. &KerbCredentialList,
  691. &Credential->ListEntry,
  692. TRUE
  693. );
  694. KerbDereferenceCredential(Credential);
  695. }
  696. KerbUnlockList(&KerbCredentialList);
  697. } */
  698. //+-------------------------------------------------------------------------
  699. //
  700. // Function: KerbLocateCredential
  701. //
  702. // Synopsis:
  703. //
  704. // Effects:
  705. //
  706. // Arguments:
  707. //
  708. // Requires:
  709. //
  710. // Returns:
  711. //
  712. // Notes:
  713. //
  714. //
  715. //--------------------------------------------------------------------------
  716. PKERB_CREDENTIAL
  717. KerbLocateCredential(
  718. IN PLUID LogonId,
  719. IN ULONG CredentialUseFlags,
  720. IN PKERB_PRIMARY_CREDENTIAL SuppliedCredentials,
  721. IN ULONG CredentialFlags,
  722. IN PUNICODE_STRING CredentialName
  723. )
  724. {
  725. PLIST_ENTRY ListEntry;
  726. PKERB_CREDENTIAL Credential = NULL;
  727. BOOLEAN Found = FALSE;
  728. SECPKG_CALL_INFO CallInfo;
  729. NT_OWF_PASSWORD HashPassword;
  730. if( SuppliedCredentials != NULL )
  731. {
  732. if( SuppliedCredentials->ClearPassword.Buffer != NULL )
  733. {
  734. RtlCalculateNtOwfPassword(
  735. &SuppliedCredentials->ClearPassword,
  736. &HashPassword
  737. );
  738. } else {
  739. ZeroMemory( &HashPassword, sizeof(HashPassword) );
  740. }
  741. }
  742. //
  743. // Match both flags
  744. //
  745. CredentialUseFlags |= CredentialFlags;
  746. if(!LsaFunctions->GetCallInfo(&CallInfo))
  747. {
  748. D_DebugLog((DEB_ERROR,"Failed to get client info:. %ws, line %d\n",
  749. THIS_FILE, __LINE__));
  750. return(NULL);
  751. }
  752. KerbLockList(&KerbCredentialList);
  753. //
  754. // Go through the list of logon sessions looking for the correct
  755. // LUID
  756. //
  757. for (ListEntry = KerbCredentialList.List.Flink ;
  758. ListEntry != &KerbCredentialList.List ;
  759. ListEntry = ListEntry->Flink )
  760. {
  761. Credential = CONTAINING_RECORD(ListEntry, KERB_CREDENTIAL, ListEntry.Next);
  762. if( (Credential->ClientProcess != CallInfo.ProcessId) )
  763. {
  764. continue;
  765. }
  766. if ( (Credential->CredentialFlags & KERB_CRED_MATCH_FLAGS) != CredentialUseFlags)
  767. {
  768. continue;
  769. }
  770. if(!RtlEqualLuid(
  771. &Credential->LogonId,
  772. LogonId
  773. ))
  774. {
  775. continue;
  776. }
  777. if(!RtlEqualUnicodeString(
  778. CredentialName,
  779. &Credential->CredentialName,
  780. FALSE
  781. ))
  782. {
  783. continue;
  784. }
  785. if( SuppliedCredentials != NULL )
  786. {
  787. //
  788. // credentials supplied, but candidate didn't have creds. continue search.
  789. //
  790. if( Credential->SuppliedCredentials == NULL )
  791. {
  792. continue;
  793. }
  794. if(!RtlEqualUnicodeString(
  795. &SuppliedCredentials->UserName,
  796. &Credential->SuppliedCredentials->UserName,
  797. FALSE
  798. ))
  799. {
  800. if(!RtlEqualUnicodeString(
  801. &SuppliedCredentials->UserName,
  802. &Credential->SuppliedCredentials->OldUserName,
  803. FALSE
  804. ))
  805. {
  806. continue;
  807. }
  808. }
  809. //
  810. // note: both candidate and input SuppliedCredentials ClearPassword
  811. // is actually encrypted via KerbHidePassword().
  812. //
  813. if(!RtlEqualMemory(
  814. &HashPassword,
  815. &Credential->SuppliedCredentials->OldHashPassword,
  816. sizeof(HashPassword)
  817. ))
  818. {
  819. continue;
  820. }
  821. //
  822. // optimize for UPN case:
  823. // check as typed versus as stored/updated first,
  824. // then check as typed versus as typed in original cred build.
  825. //
  826. if(!RtlEqualUnicodeString(
  827. &SuppliedCredentials->DomainName,
  828. &Credential->SuppliedCredentials->DomainName,
  829. FALSE
  830. ))
  831. {
  832. if(!RtlEqualUnicodeString(
  833. &SuppliedCredentials->DomainName,
  834. &Credential->SuppliedCredentials->OldDomainName,
  835. FALSE
  836. ))
  837. {
  838. continue;
  839. }
  840. }
  841. if ((SuppliedCredentials->PublicKeyCreds != NULL) &&
  842. (Credential->SuppliedCredentials->PublicKeyCreds != NULL))
  843. {
  844. if(!RtlEqualUnicodeString(
  845. &SuppliedCredentials->PublicKeyCreds->Pin,
  846. &Credential->SuppliedCredentials->PublicKeyCreds->Pin,
  847. FALSE
  848. ))
  849. {
  850. continue;
  851. }
  852. if (!KerbComparePublicKeyCreds(
  853. SuppliedCredentials->PublicKeyCreds,
  854. Credential->SuppliedCredentials->PublicKeyCreds
  855. ))
  856. {
  857. continue;
  858. }
  859. }
  860. } else {
  861. //
  862. // credentials not supplied, but candidate has creds. continue search
  863. //
  864. if( Credential->SuppliedCredentials != NULL )
  865. {
  866. continue;
  867. }
  868. }
  869. KerbReferenceListEntry(
  870. &KerbCredentialList,
  871. &Credential->ListEntry,
  872. FALSE
  873. );
  874. Credential->HandleCount++;
  875. Found = TRUE;
  876. break;
  877. }
  878. KerbUnlockList(&KerbCredentialList);
  879. if (!Found)
  880. {
  881. Credential = NULL;
  882. }
  883. return(Credential);
  884. }
  885. //+-------------------------------------------------------------------------
  886. //
  887. // Function: KerbCreateCredential
  888. //
  889. // Synopsis: Creates a new credential and links it to the credential list
  890. // and the list for this logon session
  891. //
  892. // Effects:
  893. //
  894. // Arguments: LogonId - LogonId for this logon session
  895. // LogonSession - LogonSession for the client
  896. // CredentialUseFlags - Flags indicating if the credential is
  897. // inbound or outbound
  898. // SuppliedCredentials - (Optionally) supplied credentials to store
  899. // in the credentials. If these are present, there need
  900. // not be a password on the logon session. The field is
  901. // zeroed when the primary creds are stuck in the
  902. // credential structure.
  903. // CredentialFlags - Flags about how credentials are to be
  904. // used, such as to not use a PAC or to use a null
  905. // session.
  906. // NewCredential - Receives new credential, referenced and linked
  907. // ExpirationTime - Receives credential expiration time
  908. //
  909. // Requires:
  910. //
  911. // Returns: STATUS_SUCCESS on success,
  912. // STATUS_INSUFFICIENT_RESOURCES on allocation failure
  913. //
  914. // Notes: Readers and writers of this credential must hold the
  915. // credential lock
  916. //
  917. //
  918. //--------------------------------------------------------------------------
  919. NTSTATUS
  920. KerbCreateCredential(
  921. IN PLUID LogonId,
  922. IN PKERB_LOGON_SESSION LogonSession,
  923. IN ULONG CredentialUseFlags,
  924. IN PKERB_PRIMARY_CREDENTIAL * SuppliedCredentials,
  925. IN ULONG CredentialFlags,
  926. IN PUNICODE_STRING CredentialName,
  927. OUT PKERB_CREDENTIAL * NewCredential,
  928. OUT PTimeStamp ExpirationTime
  929. )
  930. {
  931. NTSTATUS Status;
  932. PKERB_CREDENTIAL Credential = NULL;
  933. ULONG LogonSessionFlags = 0;
  934. UNICODE_STRING ServiceRealm = NULL_UNICODE_STRING;
  935. BOOLEAN FoundKdc = FALSE;
  936. BOOLEAN fLocalCredential = FALSE;
  937. //
  938. // Check to see if we already have a credential for this situation
  939. //
  940. Credential = KerbLocateCredential(
  941. LogonId,
  942. CredentialUseFlags,
  943. *SuppliedCredentials,
  944. CredentialFlags,
  945. CredentialName
  946. );
  947. if (Credential != NULL)
  948. {
  949. KerbReadLockLogonSessions(LogonSession);
  950. *ExpirationTime = LogonSession->Lifetime;
  951. KerbUnlockLogonSessions(LogonSession);
  952. *NewCredential = Credential;
  953. return(STATUS_SUCCESS);
  954. }
  955. Status = KerbAllocateCredential(&Credential);
  956. if (!NT_SUCCESS(Status))
  957. {
  958. goto Cleanup;
  959. }
  960. Credential->LogonId = *LogonId;
  961. //
  962. // Make sure the flags are valid
  963. //
  964. if ((CredentialUseFlags & ~SECPKG_CRED_BOTH) != 0)
  965. {
  966. D_DebugLog((DEB_ERROR,"Invalid credential use flags: 0x%x. %ws, line %d\n",CredentialUseFlags, THIS_FILE, __LINE__));
  967. Status = STATUS_INVALID_PARAMETER;
  968. goto Cleanup;
  969. }
  970. //
  971. // Make sure the logon session is valid for acquiring credentials
  972. //
  973. KerbReadLockLogonSessions(LogonSession);
  974. LogonSessionFlags = LogonSession->LogonSessionFlags;
  975. *ExpirationTime = LogonSession->Lifetime;
  976. KerbUnlockLogonSessions(LogonSession);
  977. //
  978. // Make sure the logon session is not local only.
  979. // We do not handle the local only case.
  980. //
  981. if (((LogonSessionFlags & KERB_LOGON_LOCAL_ONLY) != 0) &&
  982. (*SuppliedCredentials == NULL ))
  983. {
  984. D_DebugLog((DEB_WARN, "Trying to acquire cred handle for local logon session\n"));
  985. fLocalCredential = TRUE;
  986. /*
  987. Status = SEC_E_NO_CREDENTIALS;
  988. goto Cleanup;
  989. */
  990. }
  991. Credential->SuppliedCredentials = *SuppliedCredentials;
  992. *SuppliedCredentials = NULL;
  993. Credential->CredentialName = *CredentialName;
  994. CredentialName->Buffer = NULL;
  995. Credential->CredentialFlags = CredentialUseFlags | CredentialFlags;
  996. if (fLocalCredential)
  997. {
  998. Credential->CredentialFlags |= KERB_CRED_LOCAL_ACCOUNT;
  999. }
  1000. //
  1001. // If we have no password, we must either not have deferred the logon
  1002. // and we can't do inbound with no TGT.
  1003. //
  1004. if (((LogonSessionFlags & KERB_LOGON_NO_PASSWORD) != 0) &&
  1005. ((LogonSessionFlags & KERB_LOGON_SMARTCARD) == 0) &&
  1006. (Credential->SuppliedCredentials == NULL))
  1007. {
  1008. if (((CredentialUseFlags & SECPKG_CRED_OUTBOUND) != 0) &&
  1009. ((LogonSessionFlags & KERB_LOGON_DEFERRED) != 0))
  1010. {
  1011. DebugLog((DEB_WARN,"Trying to acquire cred handle w/ no supplied creds for logon session with no pass or TGT\n"));
  1012. Status = SEC_E_NO_CREDENTIALS;
  1013. goto Cleanup;
  1014. }
  1015. }
  1016. //
  1017. // Check to see if this was a deferred logon - if so, try to
  1018. // finish it now.
  1019. //
  1020. if (((CredentialFlags & KERB_CRED_NULL_SESSION) == 0) &&
  1021. (((LogonSessionFlags & KERB_LOGON_DEFERRED) != 0) ||
  1022. (Credential->SuppliedCredentials != NULL)))
  1023. {
  1024. //
  1025. // If this is outbound, we need to get a TGT or supplied credentials
  1026. //
  1027. if ((CredentialUseFlags & SECPKG_CRED_OUTBOUND) != 0)
  1028. {
  1029. //
  1030. // Get an authentication ticket
  1031. //
  1032. #ifdef notdef
  1033. Status = KerbGetTicketForCredential(
  1034. LogonSession,
  1035. Credential
  1036. );
  1037. if (!NT_SUCCESS(Status))
  1038. {
  1039. DebugLog((DEB_ERROR,"Failed to get TGT for credential: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__ ));
  1040. goto Cleanup;
  1041. }
  1042. #endif
  1043. }
  1044. else
  1045. {
  1046. //
  1047. // Verify that a KDC actually exists for this users's realm -
  1048. // otherwise we don't want to bother with Kerberos. If someone
  1049. // is explicitly supplying credentials we probably want to
  1050. // allow them to do it.
  1051. //
  1052. if (((CredentialUseFlags & SECPKG_CRED_INBOUND) != 0) &&
  1053. ((LogonSessionFlags & KERB_LOGON_DEFERRED) != 0) &&
  1054. ((CredentialFlags & KERB_CRED_NULL_SESSION) == 0) &&
  1055. (Credential->SuppliedCredentials == NULL))
  1056. {
  1057. if ((LogonSessionFlags & KERB_LOGON_NO_PASSWORD) != 0)
  1058. {
  1059. D_DebugLog((DEB_WARN,"Trying to get inbound cred with no pwd or tgt\n"));
  1060. Status = SEC_E_NO_CREDENTIALS;
  1061. goto Cleanup;
  1062. }
  1063. KerbReadLockLogonSessions(LogonSession);
  1064. Status = KerbDuplicateString(
  1065. &ServiceRealm,
  1066. &LogonSession->PrimaryCredentials.DomainName
  1067. );
  1068. KerbUnlockLogonSessions(LogonSession);
  1069. if (!NT_SUCCESS(Status))
  1070. {
  1071. goto Cleanup;
  1072. }
  1073. //
  1074. // If there was no domain name, then assume there is a KDC.
  1075. //
  1076. if (ServiceRealm.Length == 0)
  1077. {
  1078. FoundKdc = TRUE;
  1079. }
  1080. if (!FoundKdc)
  1081. {
  1082. //
  1083. // If we are a DC, or f this domain is our worksatation
  1084. // domain, check to see if
  1085. // we have a DNS name. If we do, then at one point we were
  1086. // part of an NT5 domain. Otherwise call DsGetDCName to see
  1087. // if there is a KDC around
  1088. //
  1089. if ((KerbGlobalRole == KerbRoleDomainController) ||
  1090. KerbIsThisOurDomain(
  1091. &ServiceRealm
  1092. ))
  1093. {
  1094. FoundKdc = TRUE;
  1095. }
  1096. }
  1097. //
  1098. // If we haven't found one yet, try looking for a KDC in
  1099. // this domain
  1100. //
  1101. if (!FoundKdc)
  1102. {
  1103. PKERB_BINDING_CACHE_ENTRY BindingHandle = NULL;
  1104. DsysAssert(ServiceRealm.MaximumLength >= ServiceRealm.Length + sizeof(WCHAR));
  1105. DsysAssert(ServiceRealm.Buffer[ServiceRealm.Length/sizeof(WCHAR)] == L'\0');
  1106. Status = KerbGetKdcBinding(
  1107. &ServiceRealm,
  1108. NULL, // no account name
  1109. 0, // no desired flags,
  1110. FALSE, // don't call kadmin
  1111. FALSE,
  1112. &BindingHandle
  1113. );
  1114. if (NT_SUCCESS(Status))
  1115. {
  1116. FoundKdc = TRUE;
  1117. KerbDereferenceBindingCacheEntry(BindingHandle);
  1118. }
  1119. }
  1120. if (!FoundKdc)
  1121. {
  1122. D_DebugLog((DEB_ERROR,"Didn't find KDC for domain %wZ. %ws, line %d\n",
  1123. &ServiceRealm, THIS_FILE, __LINE__ ));
  1124. Status = SEC_E_NO_AUTHENTICATING_AUTHORITY;
  1125. goto Cleanup;
  1126. }
  1127. }
  1128. }
  1129. }
  1130. else
  1131. {
  1132. Credential->CredentialFlags |= KERB_CRED_TGT_AVAIL;
  1133. }
  1134. //
  1135. // Insert the credential into the list of credentials
  1136. //
  1137. KerbInsertCredential(Credential);
  1138. //
  1139. // Notice: the order of acquiring these locks is important.
  1140. //
  1141. *NewCredential = Credential;
  1142. Cleanup:
  1143. if (!NT_SUCCESS(Status))
  1144. {
  1145. if (Credential != NULL)
  1146. {
  1147. //
  1148. // Make sure we haven't linked this one yet.
  1149. //
  1150. DsysAssert(Credential->ListEntry.ReferenceCount == 1);
  1151. KerbFreeCredential(Credential);
  1152. }
  1153. //
  1154. // Map the error if necessary. Normally STATUS_OBJECT_NAME_NOT_FOUND
  1155. // gets mapped to SEC_E_UNKNOWN_TARGET, but this is an invalid
  1156. // status to return from AcquireCredentialsHandle, so instead
  1157. // return SEC_E_NO_CREDENTIALS.
  1158. //
  1159. if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
  1160. {
  1161. Status = SEC_E_NO_CREDENTIALS;
  1162. }
  1163. }
  1164. KerbFreeString(&ServiceRealm);
  1165. return(Status);
  1166. }