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.

1420 lines
39 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, CRED_MGR_LOCK_ENUM );
  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, FALSE);
  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. // f
  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. //
  543. // get TGTs in order to normalize supplied UPN credentials for non TCB callers
  544. //
  545. if ((RequiredFlags & KERB_CRED_INBOUND)
  546. && LocalCredential->SuppliedCredentials
  547. && (LocalCredential->SuppliedCredentials->DomainName.Length == 0)
  548. && ((CallInfo.Attributes & SECPKG_CALL_IS_TCB) == 0))
  549. {
  550. DebugLog((DEB_TRACE_CRED, "KerbReferenceCredential trying to normalize UPN %wZ, RequiredFlags %#x\n", &LocalCredential->SuppliedCredentials->UserName, RequiredFlags));
  551. RequiredFlags |= KERB_CRED_TGT_AVAIL;
  552. }
  553. //
  554. // In some cases, we need to get a TGT here - but not for the S4U cases.
  555. //
  556. ULONG MissingFlags = RequiredFlags - (LocalCredential->CredentialFlags & RequiredFlags);
  557. if (MissingFlags != 0)
  558. {
  559. D_DebugLog((DEB_TRACE, "Credential %p is missing flags: needs %x\n",
  560. Credential,
  561. MissingFlags));
  562. if ((MissingFlags &= KERB_CRED_TGT_AVAIL) != 0)
  563. {
  564. if (( LocalCredential->CredentialFlags & (KERB_CRED_S4U_REQUIRED | KERB_CRED_LOCAL_ACCOUNT )) == 0)
  565. {
  566. DsysAssert(!RemoveFromList);
  567. KerbUnlockList(&KerbCredentialList);
  568. D_DebugLog((DEB_TRACE_CRED,"Getting missing TGT for credential %x\n", LocalCredential));
  569. Status = KerbGetTicketForCredential(
  570. NULL,
  571. LocalCredential,
  572. NULL,
  573. NULL
  574. );
  575. KerbLockList(&KerbCredentialList);
  576. }
  577. }
  578. else
  579. {
  580. Status = SEC_E_NO_CREDENTIALS;
  581. }
  582. }
  583. if (NT_SUCCESS(Status))
  584. {
  585. //
  586. // Since there may be multiple outstanding handles using this
  587. // structure we don't want to really remove it from the list unless
  588. // the last one releases it.
  589. //
  590. if (RemoveFromList)
  591. {
  592. ASSERT( DereferenceCount != 0 );
  593. ASSERT ( (DereferenceCount <= LocalCredential->HandleCount) );
  594. if( DereferenceCount > LocalCredential->HandleCount ) {
  595. LocalCredential->HandleCount = 0;
  596. } else {
  597. LocalCredential->HandleCount -= DereferenceCount;
  598. }
  599. if (LocalCredential->HandleCount == 0)
  600. {
  601. LocalRemoveFromList = TRUE;
  602. }
  603. }
  604. KerbReferenceListEntry(
  605. &KerbCredentialList,
  606. &LocalCredential->ListEntry,
  607. LocalRemoveFromList
  608. );
  609. KerbDereferenceCredential(LocalCredential);
  610. }
  611. else
  612. {
  613. //
  614. // Remove the earlier reference
  615. //
  616. KerbDereferenceCredential(LocalCredential);
  617. LocalCredential = NULL;
  618. }
  619. *Credential = LocalCredential;
  620. }
  621. KerbUnlockList(&KerbCredentialList);
  622. return(Status);
  623. }
  624. //+-------------------------------------------------------------------------
  625. //
  626. // Function: KerbDereferenceCredential
  627. //
  628. // Synopsis: Dereferences a logon session - if reference count goes
  629. // to zero it frees the logon session
  630. //
  631. // Effects: decrements reference count
  632. //
  633. // Arguments: Credential - Logon session to dereference
  634. //
  635. // Requires:
  636. //
  637. // Returns: none
  638. //
  639. // Notes:
  640. //
  641. //
  642. //--------------------------------------------------------------------------
  643. VOID
  644. KerbDereferenceCredential(
  645. IN PKERB_CREDENTIAL Credential
  646. )
  647. {
  648. if (KerbDereferenceListEntry(
  649. &Credential->ListEntry,
  650. &KerbCredentialList
  651. ) )
  652. {
  653. KerbFreeCredential(Credential);
  654. }
  655. }
  656. //+-------------------------------------------------------------------------
  657. //
  658. // Function: KerbPurgeCredentials
  659. //
  660. // Synopsis: Purges the list of credentials associated with a logon session
  661. // by dereferencing and unlinking them.
  662. //
  663. // Effects: Unlinks all credential on the list
  664. //
  665. // Arguments: CredentialList - List of credentials to purge
  666. //
  667. // Requires:
  668. //
  669. // Returns: none
  670. //
  671. // Notes: No longer used, as some system processes hold cred handles long
  672. // after logons go away. Leads to refcounting disasters.
  673. //
  674. //
  675. //--------------------------------------------------------------------------
  676. /*
  677. VOID
  678. KerbPurgeCredentials(
  679. IN PLIST_ENTRY CredentialList
  680. )
  681. {
  682. PKERB_CREDENTIAL Credential;
  683. KerbLockList(&KerbCredentialList);
  684. while (!IsListEmpty(CredentialList))
  685. {
  686. Credential = CONTAINING_RECORD(
  687. CredentialList->Flink,
  688. KERB_CREDENTIAL,
  689. NextForThisLogonSession
  690. );
  691. //
  692. // Remove it from the credential list
  693. //
  694. //RemoveEntryList(&Credential->NextForThisLogonSession);
  695. Credential->HandleCount = 0;
  696. //
  697. // Reference it to unlink it and then dereference it
  698. //
  699. KerbReferenceListEntry(
  700. &KerbCredentialList,
  701. &Credential->ListEntry,
  702. TRUE
  703. );
  704. KerbDereferenceCredential(Credential);
  705. }
  706. KerbUnlockList(&KerbCredentialList);
  707. } */
  708. //+-------------------------------------------------------------------------
  709. //
  710. // Function: KerbLocateCredential
  711. //
  712. // Synopsis:
  713. //
  714. // Effects:
  715. //
  716. // Arguments:
  717. //
  718. // Requires:
  719. //
  720. // Returns:
  721. //
  722. // Notes:
  723. //
  724. //
  725. //--------------------------------------------------------------------------
  726. PKERB_CREDENTIAL
  727. KerbLocateCredential(
  728. IN PLUID LogonId,
  729. IN ULONG CredentialUseFlags,
  730. IN PKERB_PRIMARY_CREDENTIAL SuppliedCredentials,
  731. IN ULONG CredentialFlags,
  732. IN PUNICODE_STRING CredentialName
  733. )
  734. {
  735. PLIST_ENTRY ListEntry;
  736. PKERB_CREDENTIAL Credential = NULL;
  737. BOOLEAN Found = FALSE;
  738. SECPKG_CALL_INFO CallInfo;
  739. NT_OWF_PASSWORD HashPassword;
  740. if( SuppliedCredentials != NULL )
  741. {
  742. if ( SuppliedCredentials->ClearPassword.Buffer != NULL )
  743. {
  744. RtlCalculateNtOwfPassword(
  745. &SuppliedCredentials->ClearPassword,
  746. &HashPassword
  747. );
  748. }
  749. else
  750. {
  751. ZeroMemory( &HashPassword, sizeof(HashPassword) );
  752. }
  753. }
  754. //
  755. // Match both flags
  756. //
  757. CredentialUseFlags |= CredentialFlags;
  758. if(!LsaFunctions->GetCallInfo(&CallInfo))
  759. {
  760. D_DebugLog((DEB_ERROR,"Failed to get client info:. %ws, line %d\n",
  761. THIS_FILE, __LINE__));
  762. return(NULL);
  763. }
  764. KerbLockList(&KerbCredentialList);
  765. //
  766. // Go through the list of logon sessions looking for the correct
  767. // LUID
  768. //
  769. for (ListEntry = KerbCredentialList.List.Flink ;
  770. ListEntry != &KerbCredentialList.List ;
  771. ListEntry = ListEntry->Flink )
  772. {
  773. Credential = CONTAINING_RECORD(ListEntry, KERB_CREDENTIAL, ListEntry.Next);
  774. if( (Credential->ClientProcess != CallInfo.ProcessId) )
  775. {
  776. continue;
  777. }
  778. if ( (Credential->CredentialFlags & KERB_CRED_MATCH_FLAGS) != CredentialUseFlags)
  779. {
  780. continue;
  781. }
  782. if(!RtlEqualLuid(
  783. &Credential->LogonId,
  784. LogonId
  785. ))
  786. {
  787. continue;
  788. }
  789. if(!RtlEqualUnicodeString(
  790. CredentialName,
  791. &Credential->CredentialName,
  792. FALSE
  793. ))
  794. {
  795. continue;
  796. }
  797. if( SuppliedCredentials != NULL )
  798. {
  799. //
  800. // credentials supplied, but candidate didn't have creds. continue search.
  801. //
  802. if( Credential->SuppliedCredentials == NULL )
  803. {
  804. continue;
  805. }
  806. if(!RtlEqualUnicodeString(
  807. &SuppliedCredentials->UserName,
  808. &Credential->SuppliedCredentials->UserName,
  809. FALSE
  810. ))
  811. {
  812. if(!RtlEqualUnicodeString(
  813. &SuppliedCredentials->UserName,
  814. &Credential->SuppliedCredentials->OldUserName,
  815. FALSE
  816. ))
  817. {
  818. continue;
  819. }
  820. }
  821. //
  822. // note: both candidate and input SuppliedCredentials ClearPassword
  823. // is actually encrypted via KerbHidePassword().
  824. //
  825. if(!RtlEqualMemory(
  826. &HashPassword,
  827. &Credential->SuppliedCredentials->OldHashPassword,
  828. sizeof(HashPassword)
  829. ))
  830. {
  831. continue;
  832. }
  833. //
  834. // optimize for UPN case:
  835. // check as typed versus as stored/updated first,
  836. // then check as typed versus as typed in original cred build.
  837. //
  838. if(!RtlEqualUnicodeString(
  839. &SuppliedCredentials->DomainName,
  840. &Credential->SuppliedCredentials->DomainName,
  841. FALSE
  842. ))
  843. {
  844. if(!RtlEqualUnicodeString(
  845. &SuppliedCredentials->DomainName,
  846. &Credential->SuppliedCredentials->OldDomainName,
  847. FALSE
  848. ))
  849. {
  850. continue;
  851. }
  852. }
  853. if ((SuppliedCredentials->PublicKeyCreds != NULL) &&
  854. (Credential->SuppliedCredentials->PublicKeyCreds != NULL))
  855. {
  856. //
  857. // note: both candidate and input SuppliedCredentials Pin
  858. // is actually encrypted via KerbHidePassword().
  859. //
  860. if(!RtlEqualUnicodeString(
  861. &SuppliedCredentials->PublicKeyCreds->Pin,
  862. &Credential->SuppliedCredentials->PublicKeyCreds->Pin,
  863. FALSE
  864. ))
  865. {
  866. continue;
  867. }
  868. if (!KerbComparePublicKeyCreds(
  869. SuppliedCredentials->PublicKeyCreds,
  870. Credential->SuppliedCredentials->PublicKeyCreds
  871. ))
  872. {
  873. continue;
  874. }
  875. }
  876. } else {
  877. //
  878. // credentials not supplied, but candidate has creds. continue search
  879. //
  880. if( Credential->SuppliedCredentials != NULL )
  881. {
  882. continue;
  883. }
  884. }
  885. KerbReferenceListEntry(
  886. &KerbCredentialList,
  887. &Credential->ListEntry,
  888. FALSE
  889. );
  890. Credential->HandleCount++;
  891. Found = TRUE;
  892. break;
  893. }
  894. KerbUnlockList(&KerbCredentialList);
  895. if (!Found)
  896. {
  897. Credential = NULL;
  898. }
  899. return(Credential);
  900. }
  901. //+-------------------------------------------------------------------------
  902. //
  903. // Function: KerbCreateCredential
  904. //
  905. // Synopsis: Creates a new credential and links it to the credential list
  906. // and the list for this logon session
  907. //
  908. // Effects:
  909. //
  910. // Arguments: LogonId - LogonId for this logon session
  911. // LogonSession - LogonSession for the client
  912. // CredentialUseFlags - Flags indicating if the credential is
  913. // inbound or outbound
  914. // SuppliedCredentials - (Optionally) supplied credentials to store
  915. // in the credentials. If these are present, there need
  916. // not be a password on the logon session. The field is
  917. // zeroed when the primary creds are stuck in the
  918. // credential structure.
  919. // CredentialFlags - Flags about how credentials are to be
  920. // used, such as to not use a PAC or to use a null
  921. // session.
  922. // NewCredential - Receives new credential, referenced and linked
  923. // ExpirationTime - Receives credential expiration time
  924. //
  925. // Requires:
  926. //
  927. // Returns: STATUS_SUCCESS on success,
  928. // STATUS_INSUFFICIENT_RESOURCES on allocation failure
  929. //
  930. // Notes: Readers and writers of this credential must hold the
  931. // credential lock
  932. //
  933. //
  934. //--------------------------------------------------------------------------
  935. NTSTATUS
  936. KerbCreateCredential(
  937. IN PLUID LogonId,
  938. IN PKERB_LOGON_SESSION LogonSession,
  939. IN ULONG CredentialUseFlags,
  940. IN PKERB_PRIMARY_CREDENTIAL * SuppliedCredentials,
  941. IN ULONG CredentialFlags,
  942. IN PUNICODE_STRING CredentialName,
  943. OUT PKERB_CREDENTIAL * NewCredential,
  944. OUT PTimeStamp ExpirationTime
  945. )
  946. {
  947. NTSTATUS Status;
  948. PKERB_CREDENTIAL Credential = NULL;
  949. ULONG LogonSessionFlags = 0;
  950. UNICODE_STRING ServiceRealm = NULL_UNICODE_STRING;
  951. BOOLEAN FoundKdc = FALSE;
  952. //
  953. // Make sure the flags are valid
  954. //
  955. if (( CredentialUseFlags == 0) ||
  956. ((CredentialUseFlags & ~SECPKG_CRED_BOTH) != 0))
  957. {
  958. D_DebugLog((DEB_ERROR,"Invalid credential use flags: 0x%x. %ws, line %d\n",CredentialUseFlags, THIS_FILE, __LINE__));
  959. Status = STATUS_INVALID_PARAMETER;
  960. goto Cleanup;
  961. }
  962. //
  963. // Check to see if we already have a credential for this situation
  964. //
  965. Credential = KerbLocateCredential(
  966. LogonId,
  967. CredentialUseFlags,
  968. *SuppliedCredentials,
  969. CredentialFlags,
  970. CredentialName
  971. );
  972. if (Credential != NULL)
  973. {
  974. KerbReadLockLogonSessions(LogonSession);
  975. *ExpirationTime = LogonSession->Lifetime;
  976. KerbUnlockLogonSessions(LogonSession);
  977. *NewCredential = Credential;
  978. return(STATUS_SUCCESS);
  979. }
  980. Status = KerbAllocateCredential(&Credential);
  981. if (!NT_SUCCESS(Status))
  982. {
  983. goto Cleanup;
  984. }
  985. Credential->LogonId = *LogonId;
  986. //
  987. // Make sure the logon session is valid for acquiring credentials
  988. //
  989. KerbReadLockLogonSessions(LogonSession);
  990. LogonSessionFlags = LogonSession->LogonSessionFlags;
  991. *ExpirationTime = LogonSession->Lifetime;
  992. KerbUnlockLogonSessions(LogonSession);
  993. Credential->SuppliedCredentials = *SuppliedCredentials;
  994. *SuppliedCredentials = NULL;
  995. Credential->CredentialName = *CredentialName;
  996. CredentialName->Buffer = NULL;
  997. Credential->CredentialFlags = CredentialUseFlags | CredentialFlags;
  998. if (( CredentialUseFlags & KERB_CRED_BOTH ) == 0)
  999. {
  1000. D_DebugLog((DEB_ERROR,"Invalid credential use flags: 0x%x. %ws, line %d\n",CredentialUseFlags, THIS_FILE, __LINE__));
  1001. Status = STATUS_INVALID_PARAMETER;
  1002. goto Cleanup;
  1003. }
  1004. //
  1005. // Evaluate the outbound credential cases. We may need to do S4U.
  1006. //
  1007. if (( Credential->SuppliedCredentials == NULL ) &&
  1008. (( CredentialFlags & KERB_CRED_NULL_SESSION) == 0))
  1009. {
  1010. if (( CredentialUseFlags & KERB_CRED_OUTBOUND) != 0)
  1011. {
  1012. if (( LogonSessionFlags & KERB_LOGON_S4U_REQUIRED ) != 0)
  1013. {
  1014. //
  1015. // This logon session was created w/o the capabilities of
  1016. // going "off box". In this case, we need to fail this transaction
  1017. //
  1018. if (( LogonSessionFlags & KERB_LOGON_DELEGATE_OK ) == 0)
  1019. {
  1020. DebugLog((DEB_TRACE_CRED, "Cant go off box w/ non-fwdble logon session & no supp creds\n"));
  1021. Status = SEC_E_NO_CREDENTIALS;
  1022. goto Cleanup;
  1023. }
  1024. else
  1025. {
  1026. //
  1027. // All others are OK for leaving the machine. Mark this cred
  1028. // as an S4U cred, then get the heck out of here.
  1029. //
  1030. DebugLog((DEB_TRACE_CRED, "Acquiring cred, S4U required\n"));
  1031. Credential->CredentialFlags |= KERB_CRED_S4U_REQUIRED;
  1032. }
  1033. }
  1034. else if (( LogonSessionFlags & KERB_LOGON_LOCAL_ONLY ) != 0)
  1035. {
  1036. //
  1037. // Local logon session - we can only use these w/o supplied creds
  1038. // if we use credman.
  1039. //
  1040. Credential->CredentialFlags |= KERB_CRED_LOCAL_ACCOUNT;
  1041. }
  1042. else if (( LogonSessionFlags & KERB_LOGON_DEFERRED ) != 0)
  1043. {
  1044. //
  1045. // I don't believe we'll hit this case anymore, as all "non pwd" logon sessions
  1046. // should be using S4U, unless they're local.
  1047. //
  1048. if (( LogonSessionFlags & KERB_LOGON_NO_PASSWORD ) != 0 )
  1049. {
  1050. DebugLog((DEB_TRACE_CRED,"Trying to acquire cred handle w/ no supplied creds for ls (%p) no pass or TGT\n", LogonSession));
  1051. Status = SEC_E_NO_CREDENTIALS;
  1052. goto Cleanup;
  1053. }
  1054. }
  1055. else if (( LogonSessionFlags & KERB_LOGON_DEFERRED ) == 0)
  1056. {
  1057. //
  1058. // Normal case, where we have a TGT
  1059. //
  1060. Credential->CredentialFlags |= KERB_CRED_TGT_AVAIL;
  1061. }
  1062. else
  1063. {
  1064. //
  1065. // Generic case, where we don't have a TGT for a domain logon,
  1066. // but we have info for a TGT. Assert until we verify we're not missing
  1067. // anything important - Contact Todds
  1068. //
  1069. DebugLog((DEB_ERROR, "Missing case for session FLAGs %x\n", LogonSessionFlags));
  1070. }
  1071. }
  1072. if (( CredentialUseFlags & KERB_CRED_INBOUND) != 0)
  1073. {
  1074. if (( LogonSessionFlags & KERB_LOGON_DEFERRED ) == 0)
  1075. {
  1076. Credential->CredentialFlags |= KERB_CRED_TGT_AVAIL;
  1077. }
  1078. else
  1079. {
  1080. if ((LogonSessionFlags & (KERB_LOGON_NO_PASSWORD | KERB_LOGON_LOCAL_ONLY)) != 0)
  1081. {
  1082. D_DebugLog((DEB_WARN, "Trying to get inbound cred with no supplied creds, no pwd or tgt, or local only\n"));
  1083. Status = SEC_E_NO_CREDENTIALS;
  1084. goto Cleanup;
  1085. }
  1086. KerbReadLockLogonSessions(LogonSession);
  1087. Status = KerbDuplicateString(
  1088. &ServiceRealm,
  1089. &LogonSession->PrimaryCredentials.DomainName
  1090. );
  1091. KerbUnlockLogonSessions(LogonSession);
  1092. if (!NT_SUCCESS(Status))
  1093. {
  1094. goto Cleanup;
  1095. }
  1096. //
  1097. // If there was no domain name, then assume there is a KDC.
  1098. //
  1099. if (ServiceRealm.Length == 0)
  1100. {
  1101. FoundKdc = TRUE;
  1102. }
  1103. if (!FoundKdc)
  1104. {
  1105. //
  1106. // If we are a DC, or f this domain is our worksatation
  1107. // domain, check to see if
  1108. // we have a DNS name. If we do, then at one point we were
  1109. // part of an NT5 domain. Otherwise call DsGetDCName to see
  1110. // if there is a KDC around
  1111. //
  1112. if ((KerbGlobalRole == KerbRoleDomainController) ||
  1113. KerbIsThisOurDomain(
  1114. &ServiceRealm
  1115. ))
  1116. {
  1117. FoundKdc = TRUE;
  1118. }
  1119. }
  1120. //
  1121. // If we haven't found one yet, try looking for a KDC in
  1122. // this domain
  1123. //
  1124. if (!FoundKdc)
  1125. {
  1126. PKERB_BINDING_CACHE_ENTRY BindingHandle = NULL;
  1127. DsysAssert(ServiceRealm.MaximumLength >= ServiceRealm.Length + sizeof(WCHAR));
  1128. DsysAssert(ServiceRealm.Buffer[ServiceRealm.Length/sizeof(WCHAR)] == L'\0');
  1129. Status = KerbGetKdcBinding(
  1130. &ServiceRealm,
  1131. NULL, // no account name
  1132. 0, // no desired flags,
  1133. FALSE, // don't call kadmin
  1134. FALSE,
  1135. &BindingHandle
  1136. );
  1137. if (NT_SUCCESS(Status))
  1138. {
  1139. FoundKdc = TRUE;
  1140. KerbDereferenceBindingCacheEntry(BindingHandle);
  1141. }
  1142. }
  1143. if (!FoundKdc)
  1144. {
  1145. D_DebugLog((DEB_ERROR,"Didn't find KDC for domain %wZ. %ws, line %d\n",
  1146. &ServiceRealm, THIS_FILE, __LINE__ ));
  1147. Status = SEC_E_NO_AUTHENTICATING_AUTHORITY;
  1148. goto Cleanup;
  1149. }
  1150. }
  1151. }
  1152. }
  1153. else
  1154. {
  1155. //
  1156. // In this case, we have supplied credentials.
  1157. //
  1158. D_DebugLog((DEB_TRACE, "Got supplied credentials\n"));
  1159. }
  1160. //
  1161. // Insert the credential into the list of credentials
  1162. //
  1163. KerbInsertCredential(Credential);
  1164. //
  1165. // Notice: the order of acquiring these locks is important.
  1166. //
  1167. *NewCredential = Credential;
  1168. Cleanup:
  1169. if (!NT_SUCCESS(Status))
  1170. {
  1171. if (Credential != NULL)
  1172. {
  1173. //
  1174. // Make sure we haven't linked this one yet.
  1175. //
  1176. DsysAssert(Credential->ListEntry.ReferenceCount == 1);
  1177. KerbFreeCredential(Credential);
  1178. }
  1179. //
  1180. // Map the error if necessary. Normally STATUS_OBJECT_NAME_NOT_FOUND
  1181. // gets mapped to SEC_E_UNKNOWN_TARGET, but this is an invalid
  1182. // status to return from AcquireCredentialsHandle, so instead
  1183. // return SEC_E_NO_CREDENTIALS.
  1184. //
  1185. if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
  1186. {
  1187. Status = SEC_E_NO_CREDENTIALS;
  1188. }
  1189. }
  1190. KerbFreeString(&ServiceRealm);
  1191. return(Status);
  1192. }