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.

2990 lines
79 KiB

  1. //+-----------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (c) Microsoft Corporation 1992 - 1996
  6. //
  7. // File: logonses.cxx
  8. //
  9. // Contents: Code for managing the global list of logon sessions
  10. //
  11. //
  12. // History: 16-April-1996 Created MikeSw
  13. //
  14. //------------------------------------------------------------------------
  15. #include <kerb.hxx>
  16. #define LOGONSES_ALLOCATE
  17. #include <kerbp.h>
  18. #ifdef DEBUG_SUPPORT
  19. static TCHAR THIS_FILE[]=TEXT(__FILE__);
  20. #endif
  21. LONG LogonSessionCount;
  22. //+-------------------------------------------------------------------------
  23. //
  24. // Function: KerbInitLogonSessionList
  25. //
  26. // Synopsis: Initializes logon session list
  27. //
  28. // Effects: allocates a resources
  29. //
  30. // Arguments: none
  31. //
  32. // Requires:
  33. //
  34. // Returns: STATUS_SUCCESS on success, other error codes
  35. // on failure
  36. //
  37. // Notes:
  38. //
  39. //
  40. //--------------------------------------------------------------------------
  41. NTSTATUS
  42. KerbInitLogonSessionList(
  43. VOID
  44. )
  45. {
  46. NTSTATUS Status;
  47. Status = KerbInitializeList( &KerbLogonSessionList, LS_LIST_LOCK_ENUM );
  48. if (!NT_SUCCESS(Status))
  49. {
  50. goto Cleanup;
  51. }
  52. KerberosLogonSessionsInitialized = TRUE;
  53. Cleanup:
  54. if (!NT_SUCCESS(Status))
  55. {
  56. KerbFreeList( &KerbLogonSessionList);
  57. }
  58. return(Status);
  59. }
  60. //+-------------------------------------------------------------------------
  61. //
  62. // Function: KerbInitLoopbackDetection
  63. //
  64. // Synopsis: Initialize the network service session loopback detection
  65. //
  66. // Effects: Allocates a resources
  67. //
  68. // Arguments: none
  69. //
  70. // Requires:
  71. //
  72. // Returns: STATUS_SUCCESS on success, other error codes on failure
  73. //
  74. // Notes:
  75. //
  76. //--------------------------------------------------------------------------
  77. NTSTATUS
  78. KerbInitLoopbackDetection(
  79. VOID
  80. )
  81. {
  82. NTSTATUS Status = STATUS_SUCCESS;
  83. InitializeListHead(&KerbSKeyList);
  84. #if DBG
  85. KerbcSKeyEntries = 0;
  86. #endif
  87. KerbhSKeyTimerQueue = NULL;
  88. __try
  89. {
  90. SafeInitializeResource(&KerbSKeyLock, LOCAL_LOOPBACK_SKEY_LOCK);
  91. }
  92. __except(EXCEPTION_EXECUTE_HANDLER)
  93. {
  94. Status = STATUS_INSUFFICIENT_RESOURCES;
  95. }
  96. return Status;
  97. }
  98. //+-------------------------------------------------------------------------
  99. //
  100. // Function: KerbFreeSKeyListAndLock
  101. //
  102. // Synopsis: Free the network service session key list and its lock
  103. //
  104. // Effects: Frees a resources
  105. //
  106. // Arguments: none
  107. //
  108. // Requires:
  109. //
  110. // Returns:
  111. //
  112. // Notes:
  113. //
  114. //--------------------------------------------------------------------------
  115. VOID
  116. KerbFreeSKeyListAndLock(
  117. VOID
  118. )
  119. {
  120. if (SafeAcquireResourceExclusive(&KerbSKeyLock, TRUE))
  121. {
  122. for (LIST_ENTRY* pListEntry = KerbSKeyList.Flink;
  123. pListEntry != &KerbSKeyList;
  124. pListEntry = pListEntry->Flink)
  125. {
  126. KERB_SESSION_KEY_ENTRY* pSKeyEntry = CONTAINING_RECORD(pListEntry, KERB_SESSION_KEY_ENTRY, ListEntry);
  127. KerbFreeSKeyEntry(pSKeyEntry);
  128. }
  129. SafeReleaseResource(&KerbSKeyLock);
  130. }
  131. SafeDeleteResource(&KerbSKeyLock);
  132. }
  133. //+-------------------------------------------------------------------------
  134. //
  135. // Function: KerbFreeLogonSessionList
  136. //
  137. // Synopsis: Frees the logon session list
  138. //
  139. // Effects:
  140. //
  141. // Arguments: none
  142. //
  143. // Requires:
  144. //
  145. // Returns: none
  146. //
  147. // Notes:
  148. //
  149. //
  150. //--------------------------------------------------------------------------
  151. VOID
  152. KerbFreeLogonSessionList(
  153. VOID
  154. )
  155. {
  156. PKERB_LOGON_SESSION LogonSession;
  157. if (KerberosLogonSessionsInitialized)
  158. {
  159. KerbLockList(&KerbLogonSessionList);
  160. //
  161. // Go through the list of logon sessions and dereferences them all
  162. //
  163. while (!IsListEmpty(&KerbLogonSessionList.List))
  164. {
  165. LogonSession = CONTAINING_RECORD(
  166. KerbLogonSessionList.List.Flink,
  167. KERB_LOGON_SESSION,
  168. ListEntry.Next
  169. );
  170. KerbReferenceListEntry(
  171. &KerbLogonSessionList,
  172. &LogonSession->ListEntry,
  173. TRUE
  174. );
  175. KerbDereferenceLogonSession(LogonSession);
  176. }
  177. KerbFreeList(&KerbLogonSessionList);
  178. }
  179. }
  180. //+-------------------------------------------------------------------------
  181. //
  182. // Function: KerbAllocateLogonSession
  183. //
  184. // Synopsis: Allocates a logon session structure
  185. //
  186. // Effects: Allocates a logon session, but does not add it to the
  187. // list of logon sessions
  188. //
  189. // Arguments: NewLogonSession - receives a new logon session allocated
  190. // with KerbAllocate
  191. //
  192. // Requires:
  193. //
  194. // Returns: STATUS_SUCCESS on success
  195. // STATUS_INSUFFICIENT_RESOURCES if the allocation fails
  196. //
  197. // Notes:
  198. //
  199. //
  200. //--------------------------------------------------------------------------
  201. // Init flags
  202. #define LSI_EXTRA_CREDS 0x1
  203. #define LSI_SESSION_LOCK 0x2
  204. #define LSI_CREDMAN 0x4
  205. NTSTATUS
  206. KerbAllocateLogonSession(
  207. PKERB_LOGON_SESSION * NewLogonSession
  208. )
  209. {
  210. NTSTATUS Status = STATUS_SUCCESS;
  211. PKERB_LOGON_SESSION LogonSession;
  212. ULONG InitStatus = 0;
  213. LogonSession = (PKERB_LOGON_SESSION) KerbAllocate(
  214. sizeof(KERB_LOGON_SESSION)
  215. );
  216. if (LogonSession == NULL)
  217. {
  218. return(STATUS_INSUFFICIENT_RESOURCES);
  219. }
  220. //
  221. // Set the references to 1 since we are returning a pointer to the
  222. // logon session
  223. //
  224. KerbInitializeListEntry(
  225. &LogonSession->ListEntry
  226. );
  227. //
  228. // Initialize the ticket caches
  229. //
  230. KerbInitTicketCache(&LogonSession->PrimaryCredentials.ServerTicketCache);
  231. KerbInitTicketCache(&LogonSession->PrimaryCredentials.AuthenticationTicketCache);
  232. KerbInitTicketCache(&LogonSession->PrimaryCredentials.S4UTicketCache);
  233. Status = KerbInitializeList(
  234. &LogonSession->ExtraCredentials.CredList,
  235. LS_EXTRACRED_LOCK_ENUM
  236. );
  237. if (!NT_SUCCESS(Status))
  238. {
  239. goto Cleanup;
  240. }
  241. InitStatus |= LSI_EXTRA_CREDS;
  242. Status = SafeInitializeCriticalSection(
  243. &LogonSession->Lock,
  244. LOGON_SESSION_LOCK_ENUM
  245. );
  246. if (!NT_SUCCESS(Status))
  247. {
  248. goto Cleanup;
  249. }
  250. InitStatus |= LSI_SESSION_LOCK;
  251. Status = KerbInitializeList(
  252. &LogonSession->CredmanCredentials,
  253. LS_CREDMAN_LOCK_ENUM
  254. );
  255. if (!NT_SUCCESS(Status))
  256. {
  257. goto Cleanup;
  258. }
  259. InitStatus |= LSI_CREDMAN;
  260. *NewLogonSession = LogonSession;
  261. Cleanup:
  262. if (!NT_SUCCESS(Status))
  263. {
  264. if (InitStatus & LSI_EXTRA_CREDS)
  265. {
  266. SafeDeleteCriticalSection(&LogonSession->ExtraCredentials.CredList.Lock);
  267. }
  268. if (InitStatus & LSI_SESSION_LOCK)
  269. {
  270. SafeDeleteCriticalSection(&LogonSession->Lock);
  271. }
  272. if (InitStatus & LSI_CREDMAN)
  273. {
  274. SafeDeleteCriticalSection(&LogonSession->CredmanCredentials.Lock);
  275. }
  276. KerbFree(LogonSession);
  277. }
  278. return Status;
  279. }
  280. //+-------------------------------------------------------------------------
  281. //
  282. // Function: KerbInsertLogonSession
  283. //
  284. // Synopsis: Inserts a logon session into the list of logon sessions
  285. //
  286. // Effects: bumps reference count on logon session
  287. //
  288. // Arguments: LogonSession - LogonSession to insert
  289. //
  290. // Requires:
  291. //
  292. // Returns: STATUS_SUCCESS always
  293. //
  294. // Notes:
  295. //
  296. //
  297. //--------------------------------------------------------------------------
  298. NTSTATUS
  299. KerbInsertLogonSession(
  300. IN PKERB_LOGON_SESSION LogonSession
  301. )
  302. {
  303. KerbInsertListEntry(
  304. &LogonSession->ListEntry,
  305. &KerbLogonSessionList
  306. );
  307. #if DBG
  308. InterlockedIncrement(&LogonSessionCount);
  309. #endif
  310. return(STATUS_SUCCESS);
  311. }
  312. //+-------------------------------------------------------------------------
  313. //
  314. // Function: KerbReferenceLogonSession
  315. //
  316. // Synopsis: Locates a logon session from the logon ID and references it
  317. //
  318. // Effects: Increments reference count and possible unlinks it from list
  319. //
  320. // Arguments: LogonId - LogonId of logon session to locate
  321. // RemoveFromList - If TRUE, logon session will be delinked
  322. //
  323. // Requires:
  324. //
  325. // Returns:
  326. //
  327. // Notes:
  328. //
  329. //
  330. //--------------------------------------------------------------------------
  331. PKERB_LOGON_SESSION
  332. KerbReferenceLogonSession(
  333. IN PLUID LogonId,
  334. IN BOOLEAN RemoveFromList
  335. )
  336. {
  337. PLIST_ENTRY ListEntry;
  338. PKERB_LOGON_SESSION LogonSession = NULL;
  339. BOOLEAN Found = FALSE;
  340. KerbLockList(&KerbLogonSessionList);
  341. //
  342. // Go through the list of logon sessions looking for the correct
  343. // LUID
  344. //
  345. for (ListEntry = KerbLogonSessionList.List.Flink ;
  346. ListEntry != &KerbLogonSessionList.List ;
  347. ListEntry = ListEntry->Flink )
  348. {
  349. LogonSession = CONTAINING_RECORD(ListEntry, KERB_LOGON_SESSION, ListEntry.Next);
  350. if (RtlEqualLuid(
  351. &LogonSession->LogonId,
  352. LogonId
  353. ) )
  354. {
  355. D_DebugLog((DEB_TRACE_LSESS, "Referencing session 0x%x:0x%x, Remove=%d\n",
  356. LogonSession->LogonId.HighPart,
  357. LogonSession->LogonId.LowPart,
  358. RemoveFromList
  359. ));
  360. KerbReferenceListEntry(
  361. &KerbLogonSessionList,
  362. &LogonSession->ListEntry,
  363. RemoveFromList
  364. );
  365. Found = TRUE;
  366. break;
  367. }
  368. }
  369. if (!Found)
  370. {
  371. LogonSession = NULL;
  372. }
  373. KerbUnlockList(&KerbLogonSessionList);
  374. return(LogonSession);
  375. }
  376. //+-------------------------------------------------------------------------
  377. //
  378. // Function: KerbFreeLogonSession
  379. //
  380. // Synopsis: Frees a logon session and all associated data
  381. //
  382. // Effects:
  383. //
  384. // Arguments: LogonSession
  385. //
  386. // Requires: the logon session must already be unlinked
  387. //
  388. // Returns: none
  389. //
  390. // Notes:
  391. //
  392. //
  393. //--------------------------------------------------------------------------
  394. VOID
  395. KerbFreeLogonSession(
  396. IN PKERB_LOGON_SESSION LogonSession
  397. )
  398. {
  399. DsysAssert((LogonSession->ListEntry.Next.Flink == NULL) &&
  400. (LogonSession->ListEntry.Next.Blink == NULL));
  401. // Don't purge creds, as there isn't a ref-count for the credential in
  402. // the logon session list,and there might be outstanding handles to your
  403. // credentials in a local system process.
  404. //KerbPurgeCredentials(&LogonSession->SspCredentials);
  405. KerbPurgeTicketCache(&LogonSession->PrimaryCredentials.ServerTicketCache);
  406. KerbPurgeTicketCache(&LogonSession->PrimaryCredentials.AuthenticationTicketCache);
  407. KerbPurgeTicketCache(&LogonSession->PrimaryCredentials.S4UTicketCache);
  408. if (LogonSession->PrimaryCredentials.Passwords != NULL)
  409. {
  410. KerbFreeStoredCred(LogonSession->PrimaryCredentials.Passwords);
  411. }
  412. if (LogonSession->PrimaryCredentials.OldPasswords != NULL)
  413. {
  414. KerbFreeStoredCred(LogonSession->PrimaryCredentials.OldPasswords);
  415. }
  416. KerbFreeString(&LogonSession->PrimaryCredentials.UserName);
  417. KerbFreeString(&LogonSession->PrimaryCredentials.DomainName);
  418. if (LogonSession->PrimaryCredentials.ClearPassword.Buffer != NULL)
  419. {
  420. RtlZeroMemory(
  421. LogonSession->PrimaryCredentials.ClearPassword.Buffer,
  422. LogonSession->PrimaryCredentials.ClearPassword.Length
  423. );
  424. KerbFreeString(&LogonSession->PrimaryCredentials.ClearPassword);
  425. }
  426. KerbFreeExtraCredList(&LogonSession->ExtraCredentials);
  427. if (LogonSession->PrimaryCredentials.PublicKeyCreds != NULL)
  428. {
  429. #ifndef WIN32_CHICAGO
  430. KerbReleasePkCreds(
  431. LogonSession,
  432. NULL,
  433. FALSE
  434. );
  435. #endif // WIN32_CHICAGO
  436. }
  437. SafeDeleteCriticalSection(&LogonSession->CredmanCredentials.Lock);
  438. SafeDeleteCriticalSection(&LogonSession->ExtraCredentials.CredList.Lock);
  439. SafeDeleteCriticalSection(&LogonSession->Lock);
  440. KerbFree(LogonSession);
  441. }
  442. //+-------------------------------------------------------------------------
  443. //
  444. // Function: KerbDereferenceLogonSession
  445. //
  446. // Synopsis: Dereferences a logon session - if reference count goes
  447. // to zero it frees the logon session
  448. //
  449. // Effects: decrements reference count
  450. //
  451. // Arguments: LogonSession - Logon session to dereference
  452. //
  453. // Requires:
  454. //
  455. // Returns: none
  456. //
  457. // Notes:
  458. //
  459. //
  460. //--------------------------------------------------------------------------
  461. VOID
  462. KerbDereferenceLogonSession(
  463. IN PKERB_LOGON_SESSION LogonSession
  464. )
  465. {
  466. if (KerbDereferenceListEntry(
  467. &LogonSession->ListEntry,
  468. &KerbLogonSessionList
  469. ) )
  470. {
  471. D_DebugLog((DEB_TRACE_LSESS, "Dereferencing and freeing logon session 0x%x:0x%x\n",
  472. LogonSession->LogonId.HighPart,
  473. LogonSession->LogonId.HighPart
  474. ));
  475. KerbFreeLogonSession( LogonSession );
  476. #if DBG
  477. InterlockedDecrement(&LogonSessionCount);
  478. #endif
  479. }
  480. }
  481. //+-------------------------------------------------------------------------
  482. //
  483. // Function: KerbReferenceLogonSessionByPointer
  484. //
  485. // Synopsis: References a LogonSession by the LogonSession pointer itself.
  486. //
  487. // Effects: Increments reference count and possible unlinks it from list
  488. //
  489. // Arguments: LogonSession - The LogonSession to reference.
  490. // RemoveFromList - If TRUE, LogonSession will be delinked
  491. //
  492. // Requires:
  493. //
  494. // Returns:
  495. //
  496. // Notes:
  497. //
  498. //
  499. //--------------------------------------------------------------------------
  500. VOID
  501. KerbReferenceLogonSessionByPointer(
  502. IN PKERB_LOGON_SESSION LogonSession,
  503. IN BOOLEAN RemoveFromList
  504. )
  505. {
  506. KerbLockList(&KerbLogonSessionList);
  507. KerbReferenceListEntry(
  508. &KerbLogonSessionList,
  509. &LogonSession->ListEntry,
  510. RemoveFromList
  511. );
  512. KerbUnlockList(&KerbLogonSessionList);
  513. }
  514. //+-------------------------------------------------------------------------
  515. //
  516. // Function: KerbGetSaltForEtype
  517. //
  518. // Synopsis: Looks in the list of salt for an etype & returns it if found
  519. //
  520. // Effects: Allocate the output string
  521. //
  522. // Arguments: EncryptionType - etype searched for
  523. // EtypeInfo - List of etypes
  524. // DefaultSalt - salt to use if none provided
  525. // Salt - receives the salt to use. On error, no key should be
  526. // generated.
  527. //
  528. // Requires:
  529. //
  530. // Returns:
  531. //
  532. // Notes:
  533. //
  534. //
  535. //--------------------------------------------------------------------------
  536. NTSTATUS
  537. KerbGetSaltForEtype(
  538. IN ULONG EncryptionType,
  539. IN OPTIONAL PKERB_ETYPE_INFO EtypeInfo,
  540. IN OPTIONAL PKERB_STORED_CREDENTIAL PasswordList,
  541. IN PUNICODE_STRING DefaultSalt,
  542. OUT PUNICODE_STRING SaltToUse
  543. )
  544. {
  545. PKERB_ETYPE_INFO ListEntry;
  546. STRING TempString;
  547. //
  548. // If there is no etype, just use the default
  549. //
  550. if (EtypeInfo == NULL)
  551. {
  552. //
  553. // If we have a password list, get the salt from that.
  554. //
  555. if (ARGUMENT_PRESENT(PasswordList))
  556. {
  557. ULONG Index;
  558. for (Index = 0; Index < PasswordList->CredentialCount ; Index++ )
  559. {
  560. if (PasswordList->Credentials[Index].Key.keytype == (int) EncryptionType)
  561. {
  562. if (PasswordList->Credentials[Index].Salt.Buffer != NULL)
  563. {
  564. return(KerbDuplicateString(
  565. SaltToUse,
  566. &PasswordList->Credentials[Index].Salt
  567. ));
  568. }
  569. else if (PasswordList->DefaultSalt.Buffer != NULL)
  570. {
  571. return(KerbDuplicateString(
  572. SaltToUse,
  573. &PasswordList->DefaultSalt
  574. ));
  575. }
  576. break;
  577. }
  578. }
  579. }
  580. //
  581. // otherise return the default
  582. //
  583. return(KerbDuplicateString(
  584. SaltToUse,
  585. DefaultSalt
  586. ));
  587. }
  588. //
  589. // Otherwise, only return salt if the etype is in the list.
  590. //
  591. for (ListEntry = EtypeInfo; ListEntry != NULL ; ListEntry = ListEntry->next )
  592. {
  593. //
  594. // First check for the encryption type we want.
  595. //
  596. if (ListEntry->value.encryption_type == (int) EncryptionType)
  597. {
  598. //
  599. // if it has salt, return that.
  600. //
  601. if ((ListEntry->value.bit_mask & salt_present) != 0)
  602. {
  603. KERBERR KerbErr;
  604. TempString.Buffer = (PCHAR) ListEntry->value.salt.value;
  605. TempString.Length = (USHORT) ListEntry->value.salt.length;
  606. TempString.MaximumLength = (USHORT) ListEntry->value.salt.length;
  607. //
  608. // validate inputs don't overflow USHORT for MaximumLength.
  609. //
  610. if ( TempString.Length > KERB_MAX_STRING )
  611. {
  612. return STATUS_NAME_TOO_LONG;
  613. }
  614. KerbErr = KerbStringToUnicodeString(
  615. SaltToUse,
  616. &TempString
  617. );
  618. return(KerbMapKerbError(KerbErr));
  619. }
  620. else
  621. {
  622. //
  623. // Otherwise return the default
  624. //
  625. return(KerbDuplicateString(
  626. SaltToUse,
  627. DefaultSalt
  628. ));
  629. }
  630. }
  631. }
  632. return(STATUS_OBJECT_NAME_NOT_FOUND);
  633. }
  634. //+-------------------------------------------------------------------------
  635. //
  636. // Function: KerbFreeStoredCred
  637. //
  638. // Synopsis: Frees a KERB_STORED_CREDENTIAL
  639. //
  640. // Effects:
  641. //
  642. // Arguments:
  643. //
  644. // Requires:
  645. //
  646. // Returns:
  647. //
  648. // Notes:
  649. //
  650. //
  651. //--------------------------------------------------------------------------
  652. VOID
  653. KerbFreeStoredCred(
  654. IN PKERB_STORED_CREDENTIAL StoredCred
  655. )
  656. {
  657. USHORT Index;
  658. for (Index = 0; Index < StoredCred->CredentialCount + StoredCred->OldCredentialCount ; Index++ )
  659. {
  660. if (StoredCred->Credentials[Index].Salt.Buffer != NULL)
  661. {
  662. KerbFreeString(&StoredCred->Credentials[Index].Salt);
  663. }
  664. }
  665. KerbFree(StoredCred);
  666. }
  667. //+-------------------------------------------------------------------------
  668. //
  669. // Function: KerbBuildPasswordList
  670. //
  671. // Synopsis: Builds a list of passwords for a logged on user
  672. //
  673. // Effects: allocates memory
  674. //
  675. // Arguments: Password - clear or OWF password
  676. // PasswordFlags - Indicates whether the password is clear or OWF
  677. // PasswordList - Receives new password list
  678. //
  679. // Requires:
  680. //
  681. // Returns:
  682. //
  683. // Notes:
  684. //
  685. //
  686. //--------------------------------------------------------------------------
  687. NTSTATUS
  688. KerbBuildPasswordList(
  689. IN PUNICODE_STRING Password,
  690. IN PUNICODE_STRING UserName,
  691. IN PUNICODE_STRING DomainName,
  692. IN PKERB_ETYPE_INFO SuppliedSalt,
  693. IN PKERB_STORED_CREDENTIAL OldPasswords,
  694. IN OPTIONAL PUNICODE_STRING PrincipalName,
  695. IN KERB_ACCOUNT_TYPE AccountType,
  696. IN ULONG PasswordFlags,
  697. OUT PKERB_STORED_CREDENTIAL * PasswordList
  698. )
  699. {
  700. NTSTATUS Status = STATUS_SUCCESS;
  701. ULONG CryptTypes[KERB_MAX_CRYPTO_SYSTEMS];
  702. ULONG CryptCount = 0 ;
  703. PKERB_STORED_CREDENTIAL Credentials = NULL;
  704. UNICODE_STRING KeySalt = {0};
  705. UNICODE_STRING DefaultSalt = {0};
  706. ULONG CredentialSize = 0;
  707. ULONG CredentialCount = 0;
  708. PCRYPTO_SYSTEM CryptoSystem;
  709. ULONG Index, CredentialIndex = 0;
  710. PUCHAR Base;
  711. ULONG Offset;
  712. KERBERR KerbErr;
  713. *PasswordList = NULL;
  714. //
  715. // If we were passed an OWF, then there is just one password
  716. //
  717. if ((PasswordFlags & PRIMARY_CRED_OWF_PASSWORD) != 0)
  718. {
  719. CredentialSize += Password->Length + sizeof(KERB_KEY_DATA);
  720. CredentialCount++;
  721. #ifndef DONT_SUPPORT_OLD_TYPES
  722. CredentialSize += Password->Length + sizeof(KERB_KEY_DATA);
  723. CredentialCount++;
  724. #endif
  725. }
  726. else if ((PasswordFlags & PRIMARY_CRED_CLEAR_PASSWORD) != 0)
  727. {
  728. //
  729. // Build the key salt.
  730. //
  731. KerbErr = KerbBuildKeySalt(
  732. DomainName,
  733. (ARGUMENT_PRESENT(PrincipalName) && PrincipalName->Length != 0) ? PrincipalName : UserName,
  734. (ARGUMENT_PRESENT(PrincipalName) && PrincipalName->Length != 0) ? UnknownAccount : AccountType,
  735. &DefaultSalt
  736. );
  737. if (!KERB_SUCCESS(KerbErr))
  738. {
  739. D_DebugLog((DEB_ERROR, "Can't build salt. Might as well fail here\n"));
  740. Status = KerbMapKerbError(KerbErr);
  741. goto Cleanup;
  742. }
  743. //
  744. // For a cleartext password, build a list of encryption types and
  745. // create a key for each one
  746. //
  747. Status = CDBuildIntegrityVect(
  748. &CryptCount,
  749. CryptTypes
  750. );
  751. if (!NT_SUCCESS(Status))
  752. {
  753. D_DebugLog((DEB_ERROR,"Can't build a list of encryption types: 0x%x.\n",Status));
  754. goto Cleanup;
  755. }
  756. DsysAssert(CryptCount <= KERB_MAX_CRYPTO_SYSTEMS);
  757. //
  758. // Now find the size of the key for each crypto system
  759. //
  760. for (Index = 0; Index < CryptCount; Index++ )
  761. {
  762. Status = CDLocateCSystem(
  763. CryptTypes[Index],
  764. &CryptoSystem
  765. );
  766. if (!NT_SUCCESS(Status))
  767. {
  768. D_DebugLog((DEB_ERROR,"Failed to load %d crypt system: 0x%x.\n",CryptTypes[Index],Status));
  769. goto Cleanup;
  770. }
  771. if (((CryptoSystem->Attributes & CSYSTEM_USE_PRINCIPAL_NAME) == 0) ||
  772. (DefaultSalt.Buffer != NULL ))
  773. {
  774. CredentialSize += sizeof(KERB_KEY_DATA) + CryptoSystem->KeySize;
  775. CredentialCount++;
  776. }
  777. }
  778. }
  779. else
  780. {
  781. //
  782. // No flags set, so nothing we can do.
  783. //
  784. D_DebugLog((DEB_TRACE, "KerbBuildPasswordList assword passed but no flags set, do nothing\n"));
  785. return(STATUS_SUCCESS);
  786. }
  787. #ifdef notdef
  788. //
  789. // Add the space for the salt
  790. //
  791. CredentialSize += DefaultSalt.Length;
  792. #endif
  793. //
  794. // Add in the size of the base structure
  795. //
  796. CredentialSize += FIELD_OFFSET(KERB_STORED_CREDENTIAL,Credentials);
  797. Credentials = (PKERB_STORED_CREDENTIAL) KerbAllocate(CredentialSize);
  798. if (Credentials == NULL)
  799. {
  800. Status = STATUS_INSUFFICIENT_RESOURCES;
  801. goto Cleanup;
  802. }
  803. //
  804. // Fill in the base structure
  805. //
  806. Credentials->Revision = KERB_PRIMARY_CRED_REVISION;
  807. Credentials->Flags = 0;
  808. Credentials->OldCredentialCount = 0;
  809. //
  810. // Now fill in the individual keys
  811. //
  812. Base = (PUCHAR) Credentials;
  813. Offset = FIELD_OFFSET(KERB_STORED_CREDENTIAL,Credentials) +
  814. CredentialCount * sizeof(KERB_KEY_DATA);
  815. #ifdef notdef
  816. //
  817. // Add the default salt
  818. //
  819. Credentials->DefaultSalt = DefaultSalt;
  820. Credentials->DefaultSalt.Buffer = (LPWSTR) Base+Offset;
  821. RtlCopyMemory(
  822. Base + Offset,
  823. DefaultSalt.Buffer,
  824. DefaultSalt.Length
  825. );
  826. Offset += Credentials->DefaultSalt.Length;
  827. #endif
  828. if ((PasswordFlags & PRIMARY_CRED_OWF_PASSWORD) != 0)
  829. {
  830. RtlCopyMemory(
  831. Base + Offset,
  832. Password->Buffer,
  833. Password->Length
  834. );
  835. if (!KERB_SUCCESS(KerbCreateKeyFromBuffer(
  836. &Credentials->Credentials[CredentialIndex].Key,
  837. Base + Offset,
  838. Password->Length,
  839. KERB_ETYPE_RC4_HMAC_NT
  840. )))
  841. {
  842. Status = STATUS_INSUFFICIENT_RESOURCES;
  843. goto Cleanup;
  844. }
  845. Base += Password->Length;
  846. CredentialIndex++;
  847. #ifndef DONT_SUPPORT_OLD_TYPES
  848. RtlCopyMemory(
  849. Base + Offset,
  850. Password->Buffer,
  851. Password->Length
  852. );
  853. if (!KERB_SUCCESS(KerbCreateKeyFromBuffer(
  854. &Credentials->Credentials[CredentialIndex].Key,
  855. Base + Offset,
  856. Password->Length,
  857. (ULONG) KERB_ETYPE_RC4_HMAC_OLD
  858. )))
  859. {
  860. Status = STATUS_INSUFFICIENT_RESOURCES;
  861. goto Cleanup;
  862. }
  863. Base += Password->Length;
  864. CredentialIndex++;
  865. #endif
  866. }
  867. else if ((PasswordFlags & PRIMARY_CRED_CLEAR_PASSWORD) != 0)
  868. {
  869. KERB_ENCRYPTION_KEY TempKey = {0};
  870. //
  871. // Now find the size of the key for each crypto system
  872. //
  873. for (Index = 0; Index < CryptCount; Index++ )
  874. {
  875. CryptoSystem = NULL;
  876. Status = CDLocateCSystem(
  877. CryptTypes[Index],
  878. &CryptoSystem
  879. );
  880. if (!NT_SUCCESS(Status))
  881. {
  882. Status = STATUS_SUCCESS;
  883. continue;
  884. }
  885. KerbFreeString(&KeySalt);
  886. Status = KerbGetSaltForEtype(
  887. CryptTypes[Index],
  888. SuppliedSalt,
  889. OldPasswords,
  890. &DefaultSalt,
  891. &KeySalt
  892. );
  893. if (!NT_SUCCESS(Status))
  894. {
  895. if ( Status == STATUS_OBJECT_NAME_NOT_FOUND)
  896. {
  897. Status = STATUS_SUCCESS;
  898. continue;
  899. }
  900. goto Cleanup;
  901. }
  902. //
  903. // If we don't have salt, skip this crypt system
  904. //
  905. if (((CryptoSystem->Attributes & CSYSTEM_USE_PRINCIPAL_NAME) != 0) &&
  906. (KeySalt.Buffer == NULL ))
  907. {
  908. continue;
  909. }
  910. KerbErr = KerbHashPasswordEx(
  911. Password,
  912. &KeySalt,
  913. CryptTypes[Index],
  914. &TempKey
  915. );
  916. if (!KERB_SUCCESS(KerbErr))
  917. {
  918. //
  919. // It is possible that the password can't be used for every
  920. // encryption scheme, so skip failures
  921. //
  922. D_DebugLog((DEB_WARN, "Failed to hash pasword %wZ with type 0x%x\n",
  923. Password,CryptTypes[Index] ));
  924. if ( CryptTypes[Index] != KERB_ETYPE_RC4_HMAC_NT ) // RC4_HMAC_NT should not fail
  925. {
  926. Status = STATUS_SUCCESS;
  927. continue;
  928. }
  929. Status = STATUS_INSUFFICIENT_RESOURCES;
  930. goto Cleanup;
  931. }
  932. DsysAssert(CryptoSystem->KeySize >= TempKey.keyvalue.length);
  933. //
  934. // Copy the salt and key data into the credentials
  935. //
  936. Credentials->Credentials[CredentialIndex].Salt = KeySalt;
  937. RtlInitUnicodeString(
  938. &KeySalt,
  939. 0
  940. );
  941. Credentials->Credentials[CredentialIndex].Key = TempKey;
  942. Credentials->Credentials[CredentialIndex].Key.keyvalue.value = Base + Offset;
  943. RtlCopyMemory(
  944. Base + Offset,
  945. TempKey.keyvalue.value,
  946. TempKey.keyvalue.length
  947. );
  948. Offset += TempKey.keyvalue.length;
  949. KerbFreeKey(
  950. &TempKey
  951. );
  952. CredentialIndex++;
  953. }
  954. }
  955. Credentials->CredentialCount = (USHORT) CredentialIndex;
  956. *PasswordList = Credentials;
  957. Credentials = NULL;
  958. Cleanup:
  959. if (Credentials != NULL)
  960. {
  961. KerbFreeStoredCred(Credentials);
  962. }
  963. KerbFreeString(&KeySalt);
  964. KerbFreeString(&DefaultSalt);
  965. return(Status);
  966. }
  967. //+-------------------------------------------------------------------------
  968. //
  969. // Function: KerbReplacePasswords
  970. //
  971. // Synopsis: Validate supplied credentials == default credentials in special
  972. // case.
  973. //
  974. // Effects: Replaces passwords if they're different than defaulted ones.
  975. //
  976. // Arguments: Current - KERB_CREDENTIAL passwords
  977. // New - Default logon session passwords
  978. //
  979. // Requires:
  980. //
  981. // Returns: STATUS_SUCCESS on success
  982. //
  983. //
  984. //
  985. //--------------------------------------------------------------------------
  986. NTSTATUS
  987. KerbReplacePasswords(
  988. IN PKERB_PRIMARY_CREDENTIAL Current,
  989. IN PKERB_PRIMARY_CREDENTIAL New
  990. )
  991. {
  992. PKERB_ENCRYPTION_KEY CurrentKey = NULL;
  993. PKERB_ENCRYPTION_KEY NewKey = NULL;
  994. PKERB_STORED_CREDENTIAL NewPasswords = NULL;
  995. ULONG PasswordSize = 0;
  996. ULONG Index;
  997. PBYTE Where;
  998. DsysAssert(Current->Passwords);
  999. DsysAssert(New->Passwords);
  1000. if ( Current->Passwords != NULL )
  1001. {
  1002. //
  1003. // First off, compare the RC4 HMAC OWF - if its equal, then the other keys are equal.
  1004. // Note - for DES / AES, this might not be the case, but assume that will be fixed up
  1005. // when we re-derive the pwd based on etype info.
  1006. //
  1007. CurrentKey = KerbGetKeyFromList( Current->Passwords, KERB_ETYPE_RC4_HMAC_NT );
  1008. if (CurrentKey == NULL )
  1009. {
  1010. return STATUS_SUCCESS;
  1011. }
  1012. NewKey = KerbGetKeyFromList( New->Passwords, KERB_ETYPE_RC4_HMAC_NT );
  1013. if ( NewKey == NULL )
  1014. {
  1015. return STATUS_SUCCESS;
  1016. }
  1017. DsysAssert( CurrentKey->keyvalue.length == NewKey->keyvalue.length );
  1018. if (RtlEqualMemory( CurrentKey->keyvalue.value, NewKey->keyvalue.value, NewKey->keyvalue.length ))
  1019. {
  1020. return STATUS_SUCCESS;
  1021. }
  1022. }
  1023. DebugLog((DEB_ERROR, "Replacing keys\n"));
  1024. //
  1025. // Walk the credentials, and whackem.
  1026. //
  1027. PasswordSize = sizeof(KERB_STORED_CREDENTIAL) - (sizeof(KERB_KEY_DATA) * ANYSIZE_ARRAY) + (New->Passwords->CredentialCount * sizeof(KERB_KEY_DATA));
  1028. for (Index = 0; Index < New->Passwords->CredentialCount ; Index++ )
  1029. {
  1030. PasswordSize += New->Passwords->Credentials[Index].Key.keyvalue.length;
  1031. }
  1032. NewPasswords = (PKERB_STORED_CREDENTIAL) KerbAllocate(PasswordSize);
  1033. if (NewPasswords == NULL)
  1034. {
  1035. return STATUS_INSUFFICIENT_RESOURCES;
  1036. }
  1037. NewPasswords->Revision = KERB_PRIMARY_CRED_REVISION;
  1038. NewPasswords->Flags = 0;
  1039. NewPasswords->OldCredentialCount = 0;
  1040. //
  1041. // Zero the salt so we don't accidentally re-use it.
  1042. //
  1043. RtlInitUnicodeString(
  1044. &NewPasswords->DefaultSalt,
  1045. NULL
  1046. );
  1047. NewPasswords->CredentialCount = New->Passwords->CredentialCount;
  1048. Where = (PUCHAR) &NewPasswords->Credentials[NewPasswords->CredentialCount];
  1049. //
  1050. // Copy all the old passwords.
  1051. //
  1052. for (Index = 0;
  1053. Index < (USHORT) (NewPasswords->CredentialCount) ;
  1054. Index++ )
  1055. {
  1056. RtlInitUnicodeString(
  1057. &NewPasswords->Credentials[Index].Salt,
  1058. NULL
  1059. );
  1060. NewPasswords->Credentials[Index].Key = New->Passwords->Credentials[Index].Key;
  1061. NewPasswords->Credentials[Index].Key.keyvalue.value = Where;
  1062. RtlCopyMemory(
  1063. Where,
  1064. New->Passwords->Credentials[Index].Key.keyvalue.value,
  1065. New->Passwords->Credentials[Index].Key.keyvalue.length
  1066. );
  1067. Where += NewPasswords->Credentials[Index].Key.keyvalue.length;
  1068. }
  1069. if ( Current->Passwords != NULL )
  1070. {
  1071. KerbFreeStoredCred( Current->Passwords );
  1072. }
  1073. Current->Passwords = NewPasswords;
  1074. return STATUS_SUCCESS;
  1075. }
  1076. //+-------------------------------------------------------------------------
  1077. //
  1078. // Function: KerbCreatePrimaryCredentials
  1079. //
  1080. // Synopsis: Fills in a new primary credentials structure
  1081. //
  1082. // Effects: allocates space for the user name and domain name
  1083. //
  1084. // Arguments: AccountName - Account name of this user
  1085. // DomainName - domain name of this user
  1086. // Password - Optionally contains a kereros hash of the password
  1087. // Note: if present, it is used and zeroed out.
  1088. // PrimaryCredentials - contains structure to fill in.
  1089. //
  1090. // Requires:
  1091. //
  1092. // Returns:
  1093. //
  1094. // Notes:
  1095. //
  1096. //
  1097. //--------------------------------------------------------------------------
  1098. NTSTATUS
  1099. KerbCreatePrimaryCredentials(
  1100. IN PUNICODE_STRING AccountName,
  1101. IN PUNICODE_STRING DomainName,
  1102. IN OPTIONAL PUNICODE_STRING Password,
  1103. IN OPTIONAL PUNICODE_STRING OldPassword,
  1104. IN ULONG PasswordFlags,
  1105. IN PLUID LogonId,
  1106. OUT PKERB_PRIMARY_CREDENTIAL PrimaryCredentials
  1107. )
  1108. {
  1109. NTSTATUS Status;
  1110. LUID SystemLogonId = SYSTEM_LUID;
  1111. LUID NetworkServiceLogonId = NETWORKSERVICE_LUID;
  1112. BOOLEAN IsMachineAccountLogon = FALSE;
  1113. BOOLEAN IsPersonal = KerbRunningPersonal();
  1114. //
  1115. // We can only accept account name / service name / pwds of max_unicode_length
  1116. // -1, because we NULL terminate these for later DES derivation, and the
  1117. // input buffers from LogonUser may not be NULL terminated.
  1118. //
  1119. if ((AccountName->Length > KERB_MAX_UNICODE_STRING) ||
  1120. (DomainName->Length > KERB_MAX_UNICODE_STRING))
  1121. {
  1122. return (STATUS_NAME_TOO_LONG);
  1123. }
  1124. if ((ARGUMENT_PRESENT(Password) && (Password->Length > KERB_MAX_UNICODE_STRING)) ||
  1125. (ARGUMENT_PRESENT(OldPassword) && (OldPassword->Length > KERB_MAX_UNICODE_STRING)))
  1126. {
  1127. return (STATUS_NAME_TOO_LONG);
  1128. }
  1129. if (RtlEqualLuid(
  1130. &SystemLogonId,
  1131. LogonId)
  1132. || RtlEqualLuid(
  1133. &NetworkServiceLogonId,
  1134. LogonId))
  1135. {
  1136. IsMachineAccountLogon = TRUE;
  1137. }
  1138. Status = KerbDuplicateString(
  1139. &PrimaryCredentials->UserName,
  1140. AccountName
  1141. );
  1142. if (!NT_SUCCESS(Status))
  1143. {
  1144. goto Cleanup;
  1145. }
  1146. //
  1147. // The system logon always comes in uppercase, so lowercase it.
  1148. //
  1149. if (IsMachineAccountLogon)
  1150. {
  1151. RtlDowncaseUnicodeString(
  1152. &PrimaryCredentials->UserName,
  1153. &PrimaryCredentials->UserName,
  1154. FALSE
  1155. );
  1156. }
  1157. Status = KerbDuplicateString(
  1158. &PrimaryCredentials->DomainName,
  1159. DomainName
  1160. );
  1161. if (!NT_SUCCESS(Status))
  1162. {
  1163. goto Cleanup;
  1164. }
  1165. //
  1166. // Neuter personal so it can't act as a server,
  1167. // even if someone has hacked in a machine pwd.
  1168. //
  1169. if (IsMachineAccountLogon && IsPersonal)
  1170. {
  1171. PrimaryCredentials->Passwords = NULL;
  1172. PrimaryCredentials->OldPasswords = NULL;
  1173. D_DebugLog((DEB_WARN, "Running personal - No kerberos for SYSTEM LUID\n"));
  1174. }
  1175. else
  1176. {
  1177. Status = KerbBuildPasswordList(
  1178. Password,
  1179. &PrimaryCredentials->UserName,
  1180. &PrimaryCredentials->DomainName,
  1181. NULL, // no supplied salt
  1182. NULL, // no old paswords
  1183. NULL, // no principal name
  1184. IsMachineAccountLogon ? MachineAccount : UserAccount,
  1185. PasswordFlags,
  1186. &PrimaryCredentials->Passwords
  1187. );
  1188. if (!NT_SUCCESS(Status))
  1189. {
  1190. goto Cleanup;
  1191. }
  1192. if (ARGUMENT_PRESENT(OldPassword) && (OldPassword->Buffer != NULL))
  1193. {
  1194. Status = KerbBuildPasswordList(
  1195. OldPassword,
  1196. &PrimaryCredentials->UserName,
  1197. &PrimaryCredentials->DomainName,
  1198. NULL, // no supplied salt
  1199. PrimaryCredentials->Passwords,
  1200. NULL, // no principal name
  1201. IsMachineAccountLogon ? MachineAccount : UserAccount,
  1202. PasswordFlags,
  1203. &PrimaryCredentials->OldPasswords
  1204. );
  1205. if (!NT_SUCCESS(Status))
  1206. {
  1207. goto Cleanup;
  1208. }
  1209. }
  1210. //
  1211. // Store the clear password if necessary
  1212. //
  1213. if ((PasswordFlags & PRIMARY_CRED_CLEAR_PASSWORD) != 0)
  1214. {
  1215. Status = KerbDuplicatePassword(
  1216. &PrimaryCredentials->ClearPassword,
  1217. Password
  1218. );
  1219. if (NT_SUCCESS(Status))
  1220. {
  1221. KerbHidePassword(
  1222. &PrimaryCredentials->ClearPassword
  1223. );
  1224. }
  1225. }
  1226. }
  1227. Cleanup:
  1228. if (!NT_SUCCESS(Status))
  1229. {
  1230. KerbFreeString(&PrimaryCredentials->UserName);
  1231. KerbFreeString(&PrimaryCredentials->DomainName);
  1232. if (PrimaryCredentials->ClearPassword.Buffer != NULL)
  1233. {
  1234. RtlZeroMemory(
  1235. PrimaryCredentials->ClearPassword.Buffer,
  1236. PrimaryCredentials->ClearPassword.Length
  1237. );
  1238. KerbFreeString(&PrimaryCredentials->ClearPassword);
  1239. }
  1240. }
  1241. return(Status);
  1242. }
  1243. //+-------------------------------------------------------------------------
  1244. //
  1245. // Function: KerbChangeCredentialsPassword
  1246. //
  1247. // Synopsis: Changes the password for a KERB_PRIMARY_CREDENTIALS -
  1248. // copies the current password into the old password field
  1249. // and then sets the new pasword as the primary password.
  1250. // If no new password is provided, it just fixes up the salt.
  1251. //
  1252. // Effects:
  1253. //
  1254. // Arguments:
  1255. //
  1256. // Requires:
  1257. //
  1258. // Returns:
  1259. //
  1260. // Notes:
  1261. //
  1262. //
  1263. //--------------------------------------------------------------------------
  1264. NTSTATUS
  1265. KerbChangeCredentialsPassword(
  1266. IN PKERB_PRIMARY_CREDENTIAL PrimaryCredentials,
  1267. IN OPTIONAL PUNICODE_STRING NewPassword,
  1268. IN OPTIONAL PKERB_ETYPE_INFO EtypeInfo,
  1269. IN KERB_ACCOUNT_TYPE AccountType,
  1270. IN ULONG PasswordFlags
  1271. )
  1272. {
  1273. NTSTATUS Status = STATUS_SUCCESS;
  1274. PKERB_STORED_CREDENTIAL Passwords = NULL;
  1275. //
  1276. // LogonSession no password was supplied, use the cleartext password
  1277. //
  1278. if (!ARGUMENT_PRESENT(NewPassword) && (PrimaryCredentials->ClearPassword.Buffer == NULL))
  1279. {
  1280. D_DebugLog((DEB_ERROR,"Can't change password without new password\n"));
  1281. Status = STATUS_INVALID_PARAMETER;
  1282. goto Cleanup;
  1283. }
  1284. if (PrimaryCredentials->ClearPassword.Buffer != NULL)
  1285. {
  1286. KerbRevealPassword(&PrimaryCredentials->ClearPassword);
  1287. }
  1288. Status = KerbBuildPasswordList(
  1289. (ARGUMENT_PRESENT(NewPassword) ? NewPassword : &PrimaryCredentials->ClearPassword),
  1290. &PrimaryCredentials->UserName,
  1291. &PrimaryCredentials->DomainName,
  1292. EtypeInfo,
  1293. PrimaryCredentials->Passwords,
  1294. NULL, // no principal name
  1295. AccountType,
  1296. PasswordFlags,
  1297. &Passwords
  1298. );
  1299. if (!NT_SUCCESS(Status))
  1300. {
  1301. if (PrimaryCredentials->ClearPassword.Buffer != NULL)
  1302. {
  1303. KerbHidePassword(&PrimaryCredentials->ClearPassword);
  1304. }
  1305. }
  1306. else if ((PasswordFlags & PRIMARY_CRED_CLEAR_PASSWORD) != 0)
  1307. {
  1308. KerbFreeString(&PrimaryCredentials->ClearPassword);
  1309. Status = KerbDuplicatePassword(
  1310. &PrimaryCredentials->ClearPassword,
  1311. NewPassword
  1312. );
  1313. if (NT_SUCCESS(Status))
  1314. {
  1315. KerbHidePassword(
  1316. &PrimaryCredentials->ClearPassword
  1317. );
  1318. }
  1319. }
  1320. else
  1321. {
  1322. KerbHidePassword(
  1323. &PrimaryCredentials->ClearPassword
  1324. );
  1325. }
  1326. if (!NT_SUCCESS(Status))
  1327. {
  1328. goto Cleanup;
  1329. }
  1330. //
  1331. // Move the current password to the old password
  1332. //
  1333. if (ARGUMENT_PRESENT(NewPassword))
  1334. {
  1335. if (PrimaryCredentials->OldPasswords != NULL)
  1336. {
  1337. KerbFreeStoredCred(PrimaryCredentials->OldPasswords);
  1338. }
  1339. PrimaryCredentials->OldPasswords = PrimaryCredentials->Passwords;
  1340. }
  1341. else
  1342. {
  1343. KerbFreeStoredCred(PrimaryCredentials->Passwords);
  1344. }
  1345. PrimaryCredentials->Passwords = Passwords;
  1346. Passwords = NULL;
  1347. Cleanup:
  1348. if (Passwords != NULL)
  1349. {
  1350. MIDL_user_free(Passwords);
  1351. }
  1352. return(Status);
  1353. }
  1354. //+-------------------------------------------------------------------------
  1355. //
  1356. // Function: KerbCreateLogonSession
  1357. //
  1358. // Synopsis: Allocates a logon session, fills in the various fields,
  1359. // and inserts it on the logon session list
  1360. //
  1361. // Effects:
  1362. //
  1363. // Arguments: LogonId - LogonId for new logon session
  1364. // AccountName - Account name of user
  1365. // Domain Name - Domain name of user
  1366. // Password - password for user
  1367. // OldPassword - Old password for user, if present
  1368. // LogonType - Type of logon
  1369. // NewLogonSession - Receives new logon session (referenced)
  1370. //
  1371. // Requires:
  1372. //
  1373. // Returns:
  1374. //
  1375. // Notes:
  1376. //
  1377. //
  1378. //--------------------------------------------------------------------------
  1379. NTSTATUS
  1380. KerbCreateLogonSession(
  1381. IN PLUID LogonId,
  1382. IN PUNICODE_STRING AccountName,
  1383. IN PUNICODE_STRING DomainName,
  1384. IN OPTIONAL PUNICODE_STRING Password,
  1385. IN OPTIONAL PUNICODE_STRING OldPassword,
  1386. IN ULONG PasswordFlags,
  1387. IN ULONG LogonSessionFlags,
  1388. IN BOOLEAN AllowDuplicate,
  1389. OUT PKERB_LOGON_SESSION * NewLogonSession
  1390. )
  1391. {
  1392. PKERB_LOGON_SESSION LogonSession = NULL;
  1393. NTSTATUS Status;
  1394. D_DebugLog((DEB_TRACE_LSESS, "Creating logon session for 0x%x:0x%x\n",
  1395. LogonId->HighPart, LogonId->LowPart));
  1396. *NewLogonSession = NULL;
  1397. //
  1398. // Check for a logon session with the same id
  1399. //
  1400. LogonSession = KerbReferenceLogonSession(
  1401. LogonId,
  1402. FALSE // don't unlink
  1403. );
  1404. if (LogonSession != NULL)
  1405. {
  1406. //
  1407. // We already have this logon session, so don't create another one.
  1408. // Up the refcount on the logon session, or simply fail if duplicates
  1409. // aren't allowed...
  1410. //
  1411. if ( AllowDuplicate )
  1412. {
  1413. *NewLogonSession = LogonSession;
  1414. return (STATUS_OBJECT_NAME_EXISTS);
  1415. }
  1416. else
  1417. {
  1418. KerbDereferenceLogonSession(LogonSession);
  1419. return(STATUS_OBJECT_NAME_EXISTS);
  1420. }
  1421. }
  1422. //
  1423. // Allocate the new logon session
  1424. //
  1425. Status = KerbAllocateLogonSession( &LogonSession );
  1426. if (!NT_SUCCESS(Status))
  1427. {
  1428. goto Cleanup;
  1429. }
  1430. //
  1431. // Fill in the logon session components
  1432. //
  1433. LogonSession->LogonId = *LogonId;
  1434. LogonSession->Lifetime = KerbGlobalWillNeverTime;
  1435. //
  1436. // If the domain name is equal to the computer name then the logon was
  1437. // local.
  1438. //
  1439. #ifndef WIN32_CHICAGO
  1440. KerbGlobalReadLock();
  1441. if (RtlEqualDomainName(
  1442. DomainName,
  1443. &KerbGlobalMachineName
  1444. ))
  1445. {
  1446. LogonSession->LogonSessionFlags |= KERB_LOGON_LOCAL_ONLY;
  1447. }
  1448. KerbGlobalReleaseLock();
  1449. #endif // WIN32_CHICAGO
  1450. Status = KerbCreatePrimaryCredentials(
  1451. AccountName,
  1452. DomainName,
  1453. Password,
  1454. OldPassword,
  1455. PasswordFlags,
  1456. LogonId,
  1457. &LogonSession->PrimaryCredentials
  1458. );
  1459. if (!NT_SUCCESS(Status))
  1460. {
  1461. goto Cleanup;
  1462. }
  1463. LogonSession->LogonSessionFlags |= ( KERB_LOGON_DEFERRED | LogonSessionFlags);
  1464. if ((( LogonSessionFlags & KERB_LOGON_SMARTCARD) == 0) &&
  1465. (( LogonSession->PrimaryCredentials.Passwords == NULL) ||
  1466. ( LogonSession->PrimaryCredentials.Passwords->CredentialCount == 0)))
  1467. {
  1468. LogonSession->LogonSessionFlags |= KERB_LOGON_NO_PASSWORD;
  1469. }
  1470. //
  1471. // Now that the logon session structure is filled out insert it
  1472. // into the list. After this you need to hold the logon session lock
  1473. // to read or write this logon session.
  1474. //
  1475. Status = KerbInsertLogonSession(LogonSession);
  1476. if (!NT_SUCCESS(Status))
  1477. {
  1478. goto Cleanup;
  1479. }
  1480. *NewLogonSession = LogonSession;
  1481. Cleanup:
  1482. if (!NT_SUCCESS(Status))
  1483. {
  1484. if (NULL != LogonSession)
  1485. {
  1486. KerbFreeLogonSession(LogonSession); // not yet linked...
  1487. }
  1488. }
  1489. return(Status);
  1490. }
  1491. #ifndef WIN32_CHICAGO
  1492. //+-------------------------------------------------------------------------
  1493. //
  1494. // Function: KerbCreateDummyLogonSession
  1495. //
  1496. // Synopsis: Creates a logon session from scratch - likely created
  1497. // from another package so call the lsa.
  1498. //
  1499. // Effects:
  1500. //
  1501. // Arguments: LogonId - Logon id for the logon session
  1502. //
  1503. // Requires:
  1504. //
  1505. // Returns:
  1506. //
  1507. // Notes:
  1508. //
  1509. //
  1510. //--------------------------------------------------------------------------
  1511. NTSTATUS
  1512. KerbCreateDummyLogonSession(
  1513. IN PLUID LogonId,
  1514. IN OUT PKERB_LOGON_SESSION * NewLogonSession,
  1515. IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
  1516. IN BOOLEAN Impersonating,
  1517. IN OPTIONAL HANDLE hProcess
  1518. )
  1519. {
  1520. NTSTATUS Status;
  1521. PKERB_LOGON_SESSION LogonSession = NULL;
  1522. UNICODE_STRING TmpRealmName = {0};
  1523. UNICODE_STRING RealmName = {0};
  1524. UNICODE_STRING TmpUserName = {0};
  1525. UNICODE_STRING UserName = {0};
  1526. BOOLEAN OkForProxy, UseSamName = FALSE;
  1527. LUID BaseLogonId;
  1528. LUID AnonymousLogonId = ANONYMOUS_LOGON_LUID;
  1529. ULONG LogonSessionFlags = KERB_LOGON_DUMMY_SESSION;
  1530. *NewLogonSession = NULL;
  1531. if (!RtlEqualLuid(LogonId, &AnonymousLogonId))
  1532. {
  1533. //
  1534. // Get the user name / dns domain name from the lsa. If
  1535. // we fail this call, the logon session is of unknown origin,
  1536. // and we'll need to assert (where did it come from?)
  1537. //
  1538. Status = I_LsaIGetNameFromLuid(
  1539. LogonId,
  1540. NameSamCompatible,
  1541. TRUE, // local only
  1542. &TmpUserName
  1543. );
  1544. if (!NT_SUCCESS(Status))
  1545. {
  1546. goto cleanup;
  1547. }
  1548. Status = I_LsaIGetNameFromLuid(
  1549. LogonId,
  1550. NameDnsDomain,
  1551. TRUE, // local only
  1552. &TmpRealmName
  1553. );
  1554. if (!NT_SUCCESS(Status))
  1555. {
  1556. //
  1557. // We may not have a dns domain. In that case,
  1558. // use the first part of the sam name.
  1559. //
  1560. UseSamName = TRUE;
  1561. }
  1562. else
  1563. {
  1564. RealmName = TmpRealmName;
  1565. }
  1566. //
  1567. // No method for "sam" name, so lets split it up.
  1568. //
  1569. for (USHORT i = 0; i <= TmpUserName.Length; i++)
  1570. {
  1571. if (TmpUserName.Buffer[i] == L'\\')
  1572. {
  1573. if ( UseSamName )
  1574. {
  1575. TmpUserName.Buffer[i] = L'\0';
  1576. RtlInitUnicodeString(
  1577. &RealmName,
  1578. TmpUserName.Buffer
  1579. );
  1580. }
  1581. RtlInitUnicodeString(
  1582. &UserName,
  1583. (PWSTR) &TmpUserName.Buffer[++i]
  1584. );
  1585. break;
  1586. }
  1587. }
  1588. //
  1589. // If we haven't gotten a realm yet, we're hosed. This should *never* happen.
  1590. //
  1591. if (RealmName.Length == 0)
  1592. {
  1593. Status = STATUS_NO_SUCH_USER;
  1594. DsysAssert(FALSE);
  1595. goto cleanup;
  1596. }
  1597. }
  1598. else // anonymous does not have a lsap logon session
  1599. {
  1600. //
  1601. // no need to look up anonymous logon, since anonymous logon with
  1602. // no explicit cred supplied will fail with SEC_E_UNKNOWN_CREDENTIALS
  1603. //
  1604. RtlInitUnicodeString(&UserName, L"ANONYMOUS LOGON");
  1605. RtlInitUnicodeString(&RealmName, L"NT AUTHORITY");
  1606. LogonSessionFlags |= KERB_LOGON_LOCAL_ONLY; // anonymous is local well-known principal
  1607. }
  1608. //
  1609. // Create a logon session.
  1610. // If it was created on us during above processing, use that one.
  1611. //
  1612. Status = KerbCreateLogonSession(
  1613. LogonId,
  1614. &UserName,
  1615. &RealmName,
  1616. NULL,
  1617. NULL,
  1618. 0,
  1619. LogonSessionFlags,
  1620. TRUE, // Allow duplicate.
  1621. &LogonSession
  1622. );
  1623. if (!NT_SUCCESS(Status))
  1624. {
  1625. //
  1626. // Already created - we're ok. Move on..
  1627. //
  1628. if ( Status == STATUS_OBJECT_NAME_EXISTS )
  1629. {
  1630. *NewLogonSession = LogonSession;
  1631. LogonSession = NULL;
  1632. Status = STATUS_SUCCESS;
  1633. }
  1634. goto cleanup;
  1635. }
  1636. if ( Impersonating )
  1637. {
  1638. Status = KerbGetCallingLuid(
  1639. &BaseLogonId,
  1640. hProcess
  1641. );
  1642. if (!NT_SUCCESS( Status ))
  1643. {
  1644. goto cleanup;
  1645. }
  1646. OkForProxy = KerbAllowedForS4UProxy(&BaseLogonId);
  1647. DsysAssert(ImpersonationLevel != SecurityAnonymous);
  1648. KerbWriteLockLogonSessions( LogonSession );
  1649. //
  1650. // If we're being called from an impersonation level context &&
  1651. // the process is marked as OK for proxy, allow this to be used
  1652. // for S4u
  1653. //
  1654. // Local accounts *can't* do S4U either...
  1655. //
  1656. if (( ImpersonationLevel >= SecurityImpersonation ) &&
  1657. ( OkForProxy ) &&
  1658. (( LogonSession->LogonSessionFlags & KERB_LOGON_LOCAL_ONLY ) == 0 ))
  1659. {
  1660. LogonSession->LogonSessionFlags |= KERB_LOGON_DELEGATE_OK;
  1661. }
  1662. KerbUnlockLogonSessions( LogonSession );
  1663. }
  1664. DebugLog((DEB_TRACE_S4U, "KerbCreateDummyLogonSession created logon session for 0x%x:0x%x - %p\n",
  1665. LogonId->HighPart, LogonId->LowPart, LogonSession));
  1666. *NewLogonSession = LogonSession;
  1667. LogonSession = NULL;
  1668. cleanup:
  1669. if ( LogonSession != NULL )
  1670. {
  1671. KerbFreeLogonSession( LogonSession );
  1672. }
  1673. if (TmpUserName.Buffer)
  1674. {
  1675. LsaFunctions->FreePrivateHeap(TmpUserName.Buffer);
  1676. }
  1677. if (TmpRealmName.Buffer)
  1678. {
  1679. LsaFunctions->FreePrivateHeap(TmpRealmName.Buffer);
  1680. }
  1681. return Status;
  1682. }
  1683. //+-------------------------------------------------------------------------
  1684. //
  1685. // Function: KerbCreateLogonSessionFromKerbCred
  1686. //
  1687. // Synopsis: Creates a logon session from the delegation information
  1688. // in a KERB_CRED structure. If a logon session is supplied,
  1689. // it is updated with the supplied information.
  1690. //
  1691. // Effects:
  1692. //
  1693. // Arguments: LogonId - Logon id for the logon session
  1694. // Ticket - Ticket from the AP request containing the client's
  1695. // name and realm.
  1696. // KerbCred - KERB_CRED containing the delegation tickets
  1697. // EncryptedCred - Structure containing information about the
  1698. // tickets, such as session keys, flags, etc.
  1699. //
  1700. // Requires:
  1701. //
  1702. // Returns:
  1703. //
  1704. // Notes:
  1705. //
  1706. //
  1707. //--------------------------------------------------------------------------
  1708. NTSTATUS
  1709. KerbCreateLogonSessionFromKerbCred(
  1710. IN OPTIONAL PLUID LogonId,
  1711. IN PKERB_ENCRYPTED_TICKET Ticket,
  1712. IN PKERB_CRED KerbCred,
  1713. IN PKERB_ENCRYPTED_CRED EncryptedCred,
  1714. IN OUT PKERB_LOGON_SESSION *OldLogonSession
  1715. )
  1716. {
  1717. PKERB_LOGON_SESSION LogonSession = NULL;
  1718. NTSTATUS Status = STATUS_SUCCESS;
  1719. UNICODE_STRING AccountName;
  1720. UNICODE_STRING ShortAccountName;
  1721. UNICODE_STRING DomainName;
  1722. KERB_ENCRYPTED_KDC_REPLY FakeReplyBody;
  1723. KERB_KDC_REPLY FakeReply;
  1724. PKERB_CRED_INFO CredInfo;
  1725. PKERB_CRED_TICKET_LIST TicketList;
  1726. PKERB_CRED_INFO_LIST CredInfoList;
  1727. PKERB_TICKET_CACHE_ENTRY TicketCacheEntry;
  1728. ULONG NameType;
  1729. LPWSTR LastSlash;
  1730. BOOLEAN LogonSessionLocked = FALSE;
  1731. BOOLEAN CreatedLogonSession = TRUE;
  1732. PKERB_TICKET_CACHE TicketCache = NULL;
  1733. ULONG TgtFlags = KERB_TICKET_CACHE_PRIMARY_TGT;
  1734. ULONG CacheFlags = 0;
  1735. AccountName.Buffer = NULL;
  1736. DomainName.Buffer = NULL;
  1737. if (ARGUMENT_PRESENT(LogonId))
  1738. {
  1739. D_DebugLog((DEB_TRACE_LSESS, "Creating logon session for 0x%x:0x%x\n",
  1740. LogonId->HighPart,LogonId->LowPart));
  1741. }
  1742. if (!KERB_SUCCESS(KerbConvertPrincipalNameToString(
  1743. &AccountName,
  1744. &NameType,
  1745. &Ticket->client_name
  1746. )))
  1747. {
  1748. Status = STATUS_INSUFFICIENT_RESOURCES;
  1749. goto Cleanup;
  1750. }
  1751. //
  1752. // We need to strip off everything before the last '\' in case there
  1753. // was a domain name.
  1754. //
  1755. LastSlash = wcsrchr(AccountName.Buffer, L'\\');
  1756. if (LastSlash != NULL)
  1757. {
  1758. ShortAccountName.Buffer = LastSlash+1;
  1759. RtlInitUnicodeString(
  1760. &ShortAccountName,
  1761. ShortAccountName.Buffer
  1762. );
  1763. }
  1764. else
  1765. {
  1766. ShortAccountName = AccountName;
  1767. }
  1768. if (!KERB_SUCCESS(KerbConvertRealmToUnicodeString(
  1769. &DomainName,
  1770. &Ticket->client_realm
  1771. )))
  1772. {
  1773. Status = STATUS_INSUFFICIENT_RESOURCES;
  1774. goto Cleanup;
  1775. }
  1776. D_DebugLog((DEB_TRACE, "Creating delegation logon session for %wZ \\ %wZ\n",
  1777. &DomainName, &ShortAccountName ));
  1778. if ((*OldLogonSession) == NULL)
  1779. {
  1780. //
  1781. // Allocate the new logon session
  1782. //
  1783. Status = KerbAllocateLogonSession( &LogonSession );
  1784. if (!NT_SUCCESS(Status))
  1785. {
  1786. goto Cleanup;
  1787. }
  1788. //
  1789. // Fill in the logon session components
  1790. //
  1791. LogonSession->LogonId = *LogonId;
  1792. LogonSession->Lifetime = KerbGlobalWillNeverTime;
  1793. Status = KerbCreatePrimaryCredentials(
  1794. &ShortAccountName,
  1795. &DomainName,
  1796. NULL, // no password
  1797. NULL, // no old password
  1798. 0, // no flags
  1799. LogonId,
  1800. &LogonSession->PrimaryCredentials
  1801. );
  1802. if (!NT_SUCCESS(Status))
  1803. {
  1804. goto Cleanup;
  1805. }
  1806. LogonSession->LogonSessionFlags |= KERB_LOGON_NO_PASSWORD;
  1807. }
  1808. else
  1809. {
  1810. CreatedLogonSession = FALSE;
  1811. DsysAssert( !LogonSessionLocked );
  1812. KerbWriteLockLogonSessions(*OldLogonSession);
  1813. LogonSessionLocked = TRUE;
  1814. LogonSession = *OldLogonSession;
  1815. //
  1816. // If the user name & domain name are blank, update them from the
  1817. // ticket.
  1818. //
  1819. if (LogonSession->PrimaryCredentials.UserName.Length == 0)
  1820. {
  1821. KerbFreeString(&LogonSession->PrimaryCredentials.UserName);
  1822. LogonSession->PrimaryCredentials.UserName = AccountName;
  1823. AccountName.Buffer = NULL;
  1824. }
  1825. if (LogonSession->PrimaryCredentials.DomainName.Length == 0)
  1826. {
  1827. KerbFreeString(&LogonSession->PrimaryCredentials.DomainName);
  1828. LogonSession->PrimaryCredentials.DomainName = DomainName;
  1829. DomainName.Buffer = NULL;
  1830. }
  1831. }
  1832. //
  1833. // Now stick the ticket into the ticket cache. First build up a fake
  1834. // KDC reply message from the encryped cred info.
  1835. //
  1836. TicketList = KerbCred->tickets;
  1837. CredInfoList = EncryptedCred->ticket_info;
  1838. while (TicketList != NULL)
  1839. {
  1840. TimeStamp Endtime = {0};
  1841. if (CredInfoList == NULL)
  1842. {
  1843. D_DebugLog((DEB_ERROR, "No ticket info in encrypted cred. %ws, line %d\n", THIS_FILE, __LINE__));
  1844. Status = STATUS_INVALID_PARAMETER;
  1845. goto Cleanup;
  1846. }
  1847. CredInfo = &CredInfoList->value;
  1848. //
  1849. // Set the lifetime to the end or renew_until of the longest lived ticket
  1850. //
  1851. if ((CredInfo->bit_mask & KERB_CRED_INFO_renew_until_present) != 0)
  1852. {
  1853. KerbConvertGeneralizedTimeToLargeInt(
  1854. &Endtime,
  1855. &CredInfo->KERB_CRED_INFO_renew_until,
  1856. 0 // no usec
  1857. );
  1858. }
  1859. else if ((CredInfo->bit_mask & endtime_present) != 0)
  1860. {
  1861. KerbConvertGeneralizedTimeToLargeInt(
  1862. &Endtime,
  1863. &CredInfo->endtime,
  1864. 0 // no usec
  1865. );
  1866. }
  1867. if (Endtime.QuadPart != 0)
  1868. {
  1869. if (LogonSession->Lifetime.QuadPart == KerbGlobalWillNeverTime.QuadPart)
  1870. {
  1871. LogonSession->Lifetime.QuadPart = Endtime.QuadPart;
  1872. }
  1873. else
  1874. {
  1875. LogonSession->Lifetime.QuadPart = max(LogonSession->Lifetime.QuadPart,Endtime.QuadPart);
  1876. }
  1877. }
  1878. RtlZeroMemory(
  1879. &FakeReplyBody,
  1880. sizeof(KERB_ENCRYPTED_KDC_REPLY)
  1881. );
  1882. FakeReplyBody.session_key = CredInfo->key;
  1883. FakeReplyBody.nonce = 0;
  1884. //
  1885. // Set the ticket flags
  1886. //
  1887. if (CredInfo->bit_mask & flags_present)
  1888. {
  1889. FakeReplyBody.flags = CredInfo->flags;
  1890. }
  1891. else
  1892. {
  1893. FakeReplyBody.flags.length = 0;
  1894. FakeReplyBody.flags.value = NULL;
  1895. }
  1896. FakeReplyBody.authtime = Ticket->authtime;
  1897. if (CredInfo->bit_mask & KERB_CRED_INFO_starttime_present)
  1898. {
  1899. FakeReplyBody.KERB_ENCRYPTED_KDC_REPLY_starttime =
  1900. CredInfo->KERB_CRED_INFO_starttime;
  1901. FakeReplyBody.bit_mask |= KERB_ENCRYPTED_KDC_REPLY_starttime_present;
  1902. }
  1903. //
  1904. // If an end time was sent, use it, otherwise assume the ticket
  1905. // lasts forever
  1906. //
  1907. if (CredInfo->bit_mask & endtime_present)
  1908. {
  1909. FakeReplyBody.endtime =
  1910. CredInfo->endtime;
  1911. }
  1912. else
  1913. {
  1914. KerbConvertLargeIntToGeneralizedTime(
  1915. &FakeReplyBody.endtime,
  1916. NULL,
  1917. &KerbGlobalWillNeverTime
  1918. );
  1919. }
  1920. if (CredInfo->bit_mask & KERB_CRED_INFO_renew_until_present)
  1921. {
  1922. FakeReplyBody.KERB_ENCRYPTED_KDC_REPLY_renew_until =
  1923. CredInfo->KERB_CRED_INFO_renew_until;
  1924. FakeReplyBody.bit_mask |= KERB_ENCRYPTED_KDC_REPLY_renew_until_present;
  1925. }
  1926. FakeReplyBody.server_name = TicketList->value.server_name;
  1927. FakeReplyBody.server_realm = TicketList->value.realm;
  1928. //
  1929. // Determine which ticket cache to use
  1930. //
  1931. if ((FakeReplyBody.server_name.name_string != NULL) &&
  1932. _stricmp(
  1933. FakeReplyBody.server_name.name_string->value,
  1934. KDC_PRINCIPAL_NAME_A) == 0)
  1935. {
  1936. TicketCache = &LogonSession->PrimaryCredentials.AuthenticationTicketCache;
  1937. //
  1938. // We only want to use the primary_tgt flag the first time through
  1939. //
  1940. CacheFlags = TgtFlags;
  1941. TgtFlags = 0;
  1942. D_DebugLog((DEB_TRACE,"Adding ticket from kerb_cred to authentication ticket cache\n"));
  1943. }
  1944. else
  1945. {
  1946. TicketCache = &LogonSession->PrimaryCredentials.ServerTicketCache;
  1947. CacheFlags = 0;
  1948. D_DebugLog((DEB_TRACE,"Adding ticket from kerb_cred to server ticket cache\n"));
  1949. }
  1950. FakeReply.client_name = Ticket->client_name;
  1951. FakeReply.ticket = TicketList->value;
  1952. FakeReply.client_realm = Ticket->client_realm;
  1953. Status = KerbCreateTicketCacheEntry(
  1954. &FakeReply,
  1955. &FakeReplyBody,
  1956. NULL, // no target name
  1957. NULL,
  1958. CacheFlags,
  1959. TicketCache,
  1960. NULL, // no credential key
  1961. &TicketCacheEntry
  1962. );
  1963. if (!NT_SUCCESS(Status))
  1964. {
  1965. D_DebugLog((DEB_ERROR, "Failed to cache ticket: 0x%x. %ws, line %d\n",
  1966. Status, THIS_FILE, __LINE__));
  1967. goto Cleanup;
  1968. }
  1969. LogonSession->LogonSessionFlags &= ~KERB_LOGON_DEFERRED;
  1970. KerbDereferenceTicketCacheEntry(TicketCacheEntry);
  1971. CredInfoList = CredInfoList->next;
  1972. TicketList = TicketList->next;
  1973. }
  1974. //
  1975. // Now that the logon session structure is filled out insert it
  1976. // into the list. After this you need to hold the logon session lock
  1977. // to read or write this logon session.
  1978. //
  1979. if (*OldLogonSession == NULL)
  1980. {
  1981. Status = KerbInsertLogonSession(LogonSession);
  1982. if (!NT_SUCCESS(Status))
  1983. {
  1984. goto Cleanup;
  1985. }
  1986. *OldLogonSession = LogonSession;
  1987. }
  1988. Cleanup:
  1989. if (LogonSessionLocked)
  1990. {
  1991. KerbUnlockLogonSessions(LogonSession);
  1992. }
  1993. if (CreatedLogonSession)
  1994. {
  1995. if (!NT_SUCCESS(Status))
  1996. {
  1997. if (LogonSession != NULL)
  1998. {
  1999. KerbFreeLogonSession(LogonSession);
  2000. }
  2001. }
  2002. }
  2003. KerbFreeString(&AccountName);
  2004. KerbFreeString(&DomainName);
  2005. return(Status);
  2006. }
  2007. //+-------------------------------------------------------------------------
  2008. //
  2009. // Function: KerbCreateLogonSessionFromTicket
  2010. //
  2011. // Synopsis: Creates a logon session from a service ticket. Later
  2012. // can be used for S4U proxy delegation.
  2013. //
  2014. // Effects:
  2015. //
  2016. // Arguments: LogonId - Logon id for the logon session
  2017. // Ticket - Ticket from the AP request containing the client's
  2018. // name and realm.
  2019. // KerbCred - KERB_CRED containing the delegation tickets
  2020. // EncryptedCred - Structure containing information about the
  2021. // tickets, such as session keys, flags, etc.
  2022. //
  2023. // Requires:
  2024. //
  2025. // Returns:
  2026. //
  2027. // Notes:
  2028. //
  2029. //
  2030. //--------------------------------------------------------------------------
  2031. NTSTATUS
  2032. KerbCreateLogonSessionFromTicket(
  2033. IN PLUID NewLuid,
  2034. IN OPTIONAL PLUID AcceptingLuid,
  2035. IN PUNICODE_STRING ClientName,
  2036. IN PUNICODE_STRING ClientRealm,
  2037. IN PKERB_AP_REQUEST ApRequest,
  2038. IN PKERB_ENCRYPTED_TICKET Ticket,
  2039. IN OUT PKERB_LOGON_SESSION *NewLogonSession // needed?
  2040. )
  2041. {
  2042. NTSTATUS Status = STATUS_SUCCESS;
  2043. PKERB_LOGON_SESSION LogonSession = NULL;
  2044. KERB_ENCRYPTED_KDC_REPLY FakeReplyBody;
  2045. KERB_KDC_REPLY FakeReply;
  2046. PKERB_TICKET_CACHE_ENTRY TicketCacheEntry = NULL;
  2047. LUID SystemLogonId = SYSTEM_LUID;
  2048. BOOLEAN IsSystemLogon = FALSE;
  2049. ULONG TicketFlags = 0;
  2050. LUID BaseLogonId;
  2051. //
  2052. // Only create these if we're allowed to do S4UToProxy. Otherwise,
  2053. // there's no reason to keep this ticket around. If it starts becoming
  2054. // available, we can always fall back to S4UToSelf to get a new service
  2055. // ticket.
  2056. //
  2057. if (ARGUMENT_PRESENT(NewLogonSession))
  2058. {
  2059. *NewLogonSession = NULL;
  2060. }
  2061. if (!ARGUMENT_PRESENT( AcceptingLuid ))
  2062. {
  2063. Status = KerbGetCallingLuid(
  2064. &BaseLogonId,
  2065. NULL // no process handle
  2066. );
  2067. if (!NT_SUCCESS( Status ))
  2068. {
  2069. goto Cleanup;
  2070. }
  2071. }
  2072. else
  2073. {
  2074. BaseLogonId = (*AcceptingLuid);
  2075. }
  2076. if (!KerbAllowedForS4UProxy( &BaseLogonId ))
  2077. {
  2078. DebugLog((DEB_TRACE_LSESS, "KerbCreateLogonSessionFromTicket NOT creating ASC logon session for %#x:%#x, accepting %#x:%#x\n",
  2079. NewLuid->HighPart, NewLuid->LowPart, BaseLogonId.HighPart, BaseLogonId.LowPart));
  2080. goto Cleanup;
  2081. }
  2082. DebugLog((DEB_TRACE_LSESS, "KerbCreateLogonSessionFromTicket creating logon session for %#x:%#x, accepting %#x:%#x, client %wZ@%wZ\n",
  2083. NewLuid->HighPart, NewLuid->LowPart, BaseLogonId.HighPart, BaseLogonId.LowPart, ClientName, ClientRealm));
  2084. //
  2085. // Allocate the new logon session
  2086. //
  2087. Status = KerbAllocateLogonSession( &LogonSession );
  2088. if (!NT_SUCCESS(Status))
  2089. {
  2090. goto Cleanup;
  2091. }
  2092. //
  2093. // Fill in the logon session components
  2094. //
  2095. LogonSession->LogonId = (*NewLuid);
  2096. LogonSession->LogonSessionFlags = KERB_LOGON_ASC_SESSION | KERB_LOGON_NO_PASSWORD | KERB_LOGON_DEFERRED;
  2097. KerbConvertGeneralizedTimeToLargeInt(
  2098. &LogonSession->Lifetime,
  2099. &Ticket->endtime,
  2100. NULL
  2101. );
  2102. if (RtlEqualLuid(
  2103. NewLuid,
  2104. &SystemLogonId
  2105. ))
  2106. {
  2107. IsSystemLogon = TRUE;
  2108. }
  2109. Status = KerbDuplicateString(
  2110. &LogonSession->PrimaryCredentials.UserName,
  2111. ClientName
  2112. );
  2113. if (!NT_SUCCESS(Status))
  2114. {
  2115. goto Cleanup;
  2116. }
  2117. //
  2118. // The system logon always comes in uppercase, so lowercase it.
  2119. //
  2120. if (IsSystemLogon)
  2121. {
  2122. RtlDowncaseUnicodeString(
  2123. &LogonSession->PrimaryCredentials.UserName,
  2124. &LogonSession->PrimaryCredentials.UserName,
  2125. FALSE
  2126. );
  2127. }
  2128. Status = KerbDuplicateString(
  2129. &LogonSession->PrimaryCredentials.DomainName,
  2130. ClientRealm
  2131. );
  2132. if (!NT_SUCCESS(Status))
  2133. {
  2134. goto Cleanup;
  2135. }
  2136. //
  2137. // We now have to make a decision about whether to store
  2138. // the ticket, and how to mark the logon session. If
  2139. // the ticket is non-fwdable, we can't do S4U using it, so we
  2140. // might as well not cache it, but we should still create
  2141. // the logon session...
  2142. //
  2143. // This allows for a bit of an optimization, as if the user's ticket
  2144. // wasn't fwdable, we shouldn't do S4U2Self for that user, as
  2145. // their account must be marked "sensitive and unable to be
  2146. // delegated", and we'd be wasting an S4UToSelf tgs_req.
  2147. //
  2148. TicketFlags = KerbConvertFlagsToUlong(&Ticket->flags);
  2149. if (( TicketFlags & KERB_TICKET_FLAGS_forwardable ) != 0)
  2150. {
  2151. ULONG ApOptions = KerbConvertFlagsToUlong(&ApRequest->ap_options);
  2152. ULONG CacheFlags = KERB_TICKET_CACHE_ASC_TICKET;
  2153. if (ApOptions & KERB_AP_OPTIONS_use_session_key)
  2154. {
  2155. D_DebugLog((DEB_TRACE_U2U, "KerbCreateLogonSessionFromTicket set TKT_ENC_IN_SKEY\n"));
  2156. CacheFlags |= KERB_TICKET_CACHE_TKT_ENC_IN_SKEY;
  2157. }
  2158. LogonSession->LogonSessionFlags |= KERB_LOGON_DELEGATE_OK;
  2159. //
  2160. // Now stick the ticket into the ticket cache. First build up a fake
  2161. // KDC reply message from the encryped cred info.
  2162. //
  2163. FakeReplyBody.authtime = Ticket->authtime;
  2164. FakeReplyBody.starttime = Ticket->starttime;
  2165. FakeReplyBody.endtime = Ticket->endtime;
  2166. FakeReplyBody.server_name = ApRequest->ticket.server_name;
  2167. FakeReplyBody.server_realm = ApRequest->ticket.realm;
  2168. FakeReplyBody.session_key = Ticket->key;
  2169. FakeReplyBody.flags = Ticket->flags;
  2170. FakeReply.client_name = Ticket->client_name;
  2171. FakeReply.ticket = ApRequest->ticket;
  2172. FakeReply.client_realm = Ticket->client_realm;
  2173. Status = KerbCreateTicketCacheEntry(
  2174. &FakeReply,
  2175. &FakeReplyBody,
  2176. NULL, // no target name
  2177. NULL,
  2178. CacheFlags,
  2179. NULL,
  2180. NULL, // no credential key
  2181. &TicketCacheEntry
  2182. );
  2183. if (!NT_SUCCESS(Status))
  2184. {
  2185. D_DebugLog((DEB_ERROR, "Failed to cache ticket: 0x%x. %ws, line %d\n",
  2186. Status, THIS_FILE, __LINE__));
  2187. goto Cleanup;
  2188. }
  2189. TicketCacheEntry->EvidenceLogonId = BaseLogonId;
  2190. KerbInsertTicketCacheEntry(
  2191. &LogonSession->PrimaryCredentials.S4UTicketCache,
  2192. TicketCacheEntry
  2193. );
  2194. }
  2195. #if DBG
  2196. else
  2197. {
  2198. DebugLog((DEB_TRACE_LSESS, "Creating non-delegatable ASC logon session\n"));
  2199. }
  2200. #endif
  2201. //
  2202. // Now that the logon session structure is filled out insert it
  2203. // into the list. After this you need to hold the logon session lock
  2204. // to read or write this logon session.
  2205. //
  2206. Status = KerbInsertLogonSession(LogonSession);
  2207. if (!NT_SUCCESS(Status))
  2208. {
  2209. goto Cleanup;
  2210. }
  2211. if (ARGUMENT_PRESENT( NewLogonSession ))
  2212. {
  2213. *NewLogonSession = LogonSession;
  2214. }
  2215. else
  2216. {
  2217. // We aren't returning it, so...
  2218. KerbDereferenceLogonSession( LogonSession );
  2219. }
  2220. Cleanup:
  2221. if (!NT_SUCCESS(Status) && (LogonSession != NULL))
  2222. {
  2223. KerbFreeLogonSession(LogonSession);
  2224. }
  2225. if (TicketCacheEntry != NULL)
  2226. {
  2227. KerbDereferenceTicketCacheEntry(TicketCacheEntry);
  2228. }
  2229. return(Status);
  2230. }
  2231. VOID
  2232. KerbFreeExtraCred(
  2233. PKERB_EXTRA_CRED Cred
  2234. )
  2235. {
  2236. if (Cred != NULL)
  2237. {
  2238. if (Cred->Passwords != NULL)
  2239. {
  2240. KerbFreeStoredCred(Cred->Passwords);
  2241. }
  2242. if (Cred->OldPasswords != NULL)
  2243. {
  2244. KerbFreeStoredCred(Cred->OldPasswords);
  2245. }
  2246. KerbFreeString(&Cred->cName);
  2247. KerbFreeString(&Cred->cRealm);
  2248. KerbFree(Cred);
  2249. }
  2250. }
  2251. VOID
  2252. KerbDereferenceExtraCred(
  2253. PKERB_EXTRA_CRED Cred
  2254. )
  2255. {
  2256. DsysAssert(Cred->ListEntry.ReferenceCount != 0);
  2257. if ( 0 == InterlockedDecrement((LONG *)&Cred->ListEntry.ReferenceCount ))
  2258. {
  2259. KerbFreeExtraCred(Cred);
  2260. }
  2261. }
  2262. VOID
  2263. KerbReferenceExtraCred(
  2264. PKERB_EXTRA_CRED Cred
  2265. )
  2266. {
  2267. InterlockedIncrement((LONG *)&Cred->ListEntry.ReferenceCount);
  2268. }
  2269. VOID
  2270. KerbRemoveExtraCredFromList(
  2271. PKERB_EXTRA_CRED Cred,
  2272. PEXTRA_CRED_LIST CredList
  2273. )
  2274. {
  2275. if ( InterlockedCompareExchange(
  2276. &Cred->Linked,
  2277. (LONG)FALSE,
  2278. (LONG)TRUE ))
  2279. {
  2280. DsysAssert(Cred->ListEntry.ReferenceCount != 0);
  2281. RemoveEntryList(&Cred->ListEntry.Next);
  2282. KerbDereferenceExtraCred(Cred);
  2283. InterlockedDecrement((LONG*) &CredList->Count );
  2284. }
  2285. }
  2286. VOID
  2287. KerbInsertExtraCred(
  2288. IN PKERB_EXTRA_CRED Cred,
  2289. IN PEXTRA_CRED_LIST Credlist
  2290. )
  2291. {
  2292. if ( !InterlockedCompareExchange(
  2293. &Cred->Linked,
  2294. (LONG)TRUE,
  2295. (LONG)FALSE ))
  2296. {
  2297. InterlockedIncrement( (LONG *)&Cred->ListEntry.ReferenceCount );
  2298. InsertHeadList(
  2299. &Credlist->CredList.List,
  2300. &Cred->ListEntry.Next
  2301. );
  2302. InterlockedIncrement((LONG*) &Credlist->Count);
  2303. }
  2304. }
  2305. VOID
  2306. KerbFreeExtraCredList(
  2307. IN PEXTRA_CRED_LIST Credlist
  2308. )
  2309. {
  2310. PKERB_EXTRA_CRED Cred;
  2311. KerbLockList(&Credlist->CredList);
  2312. while (!IsListEmpty(&Credlist->CredList.List))
  2313. {
  2314. Cred = CONTAINING_RECORD(
  2315. Credlist->CredList.List.Flink,
  2316. KERB_EXTRA_CRED,
  2317. ListEntry.Next
  2318. );
  2319. KerbRemoveExtraCredFromList(
  2320. Cred,
  2321. Credlist
  2322. );
  2323. }
  2324. DsysAssert(Credlist->Count == 0);
  2325. KerbUnlockList(&Credlist->CredList);
  2326. }
  2327. //+-------------------------------------------------------------------------
  2328. //
  2329. // Function: KerbAddExtraCredentialsToLogonSession
  2330. //
  2331. // Synopsis: Initializes logon session list
  2332. //
  2333. // Effects: allocates a resources
  2334. //
  2335. // Arguments: none
  2336. //
  2337. // Requires:
  2338. //
  2339. // Returns: STATUS_SUCCESS on success, other error codes
  2340. // on failure
  2341. //
  2342. // Notes:
  2343. //
  2344. //
  2345. //--------------------------------------------------------------------------
  2346. NTSTATUS
  2347. KerbAddExtraCredentialsToLogonSession(
  2348. IN PKERB_LOGON_SESSION LogonSession,
  2349. IN PKERB_ADD_CREDENTIALS_REQUEST AddCredRequest
  2350. )
  2351. {
  2352. NTSTATUS Status = STATUS_SUCCESS;
  2353. PKERB_STORED_CREDENTIAL DerivedPasswords = NULL;
  2354. PKERB_EXTRA_CRED Cred = NULL;
  2355. PLIST_ENTRY ListEntry;
  2356. BOOLEAN Matched = FALSE, ListLocked = FALSE;
  2357. //
  2358. // YuChen repellant
  2359. //
  2360. if ((AddCredRequest->UserName.Length > KERB_MAX_UNICODE_STRING) ||
  2361. (AddCredRequest->DomainName.Length > KERB_MAX_UNICODE_STRING))
  2362. {
  2363. return (STATUS_NAME_TOO_LONG);
  2364. }
  2365. if ((AddCredRequest->Password.Length != 0) && (AddCredRequest->Password.Length > KERB_MAX_UNICODE_STRING))
  2366. {
  2367. return (STATUS_NAME_TOO_LONG);
  2368. }
  2369. //
  2370. // See if we match an old "extra cred"
  2371. //
  2372. KerbLockList( &LogonSession->ExtraCredentials.CredList );
  2373. ListLocked = TRUE;
  2374. for (ListEntry = LogonSession->ExtraCredentials.CredList.List.Flink ;
  2375. ListEntry != &LogonSession->ExtraCredentials.CredList.List ;
  2376. ListEntry = ListEntry->Flink )
  2377. {
  2378. Cred = CONTAINING_RECORD(ListEntry, KERB_EXTRA_CRED, ListEntry.Next);
  2379. if (RtlEqualUnicodeString(
  2380. &Cred->cName,
  2381. &AddCredRequest->UserName,
  2382. TRUE) &&
  2383. RtlEqualUnicodeString(
  2384. &Cred->cRealm,
  2385. &AddCredRequest->DomainName,
  2386. TRUE))
  2387. {
  2388. D_DebugLog((DEB_TRACE, "Matched extra cred %p\n", Cred));
  2389. Matched = TRUE;
  2390. break;
  2391. }
  2392. }
  2393. if ( Matched )
  2394. {
  2395. if (( AddCredRequest->Flags & ( KERB_REQUEST_ADD_CREDENTIAL | KERB_REQUEST_REPLACE_CREDENTIAL)) != 0)
  2396. {
  2397. Status = KerbBuildPasswordList(
  2398. &AddCredRequest->Password,
  2399. &AddCredRequest->UserName,
  2400. &AddCredRequest->DomainName,
  2401. NULL,
  2402. NULL,
  2403. NULL,
  2404. UnknownAccount,
  2405. PRIMARY_CRED_CLEAR_PASSWORD,
  2406. &DerivedPasswords
  2407. );
  2408. if (!NT_SUCCESS( Status ))
  2409. {
  2410. DebugLog((DEB_ERROR, "Failed to build password list (%x) \n", Status));
  2411. goto Cleanup;
  2412. }
  2413. if (Cred->OldPasswords != NULL)
  2414. {
  2415. KerbFreeStoredCred(Cred->OldPasswords);
  2416. }
  2417. if ((AddCredRequest->Flags & KERB_REQUEST_REPLACE_CREDENTIAL) == 0)
  2418. {
  2419. Cred->OldPasswords = Cred->Passwords;
  2420. }
  2421. else if (Cred->Passwords != NULL)
  2422. {
  2423. //
  2424. // Doing a "replace" operation.
  2425. //
  2426. KerbFreeStoredCred(Cred->Passwords);
  2427. }
  2428. Cred->Passwords = DerivedPasswords;
  2429. DerivedPasswords = NULL;
  2430. }
  2431. else
  2432. {
  2433. //
  2434. // Remove the cred from the list, and deref it.
  2435. //
  2436. D_DebugLog((DEB_TRACE, "Removing extra cred %p\n", Cred));
  2437. KerbRemoveExtraCredFromList(
  2438. Cred,
  2439. &LogonSession->ExtraCredentials
  2440. );
  2441. }
  2442. }
  2443. else if (( AddCredRequest->Flags & KERB_REQUEST_REMOVE_CREDENTIAL) == 0)
  2444. {
  2445. D_DebugLog((DEB_TRACE, "Creating cred %p\n", Cred));
  2446. Cred = (PKERB_EXTRA_CRED) KerbAllocate(sizeof(KERB_EXTRA_CRED));
  2447. if (NULL == Cred)
  2448. {
  2449. Status = STATUS_NO_MEMORY;
  2450. goto Cleanup;
  2451. }
  2452. Status = KerbDuplicateString(
  2453. &Cred->cName,
  2454. &AddCredRequest->UserName
  2455. );
  2456. if (!NT_SUCCESS(Status))
  2457. {
  2458. goto Cleanup;
  2459. }
  2460. Status = KerbDuplicateString(
  2461. &Cred->cRealm,
  2462. &AddCredRequest->DomainName
  2463. );
  2464. if (!NT_SUCCESS(Status))
  2465. {
  2466. goto Cleanup;
  2467. }
  2468. Status = KerbBuildPasswordList(
  2469. &AddCredRequest->Password,
  2470. &AddCredRequest->UserName,
  2471. &AddCredRequest->DomainName,
  2472. NULL,
  2473. NULL,
  2474. NULL,
  2475. UnknownAccount,
  2476. PRIMARY_CRED_CLEAR_PASSWORD,
  2477. &DerivedPasswords
  2478. );
  2479. if (!NT_SUCCESS( Status ))
  2480. {
  2481. DebugLog((DEB_ERROR, "Failed to build password list (%x) \n", Status));
  2482. goto Cleanup;
  2483. }
  2484. Cred->Passwords = DerivedPasswords;
  2485. DerivedPasswords = NULL;
  2486. KerbInsertExtraCred(
  2487. Cred,
  2488. &LogonSession->ExtraCredentials
  2489. );
  2490. }
  2491. KerbUnlockList( &LogonSession->ExtraCredentials.CredList);
  2492. ListLocked = FALSE;
  2493. Cleanup:
  2494. if ( DerivedPasswords != NULL )
  2495. {
  2496. KerbFreeStoredCred( DerivedPasswords );
  2497. }
  2498. if (!NT_SUCCESS( Status ))
  2499. {
  2500. // only free it on error if we haven't
  2501. // matched the credential...
  2502. if ( Cred != NULL && !Matched)
  2503. {
  2504. KerbFreeExtraCred(Cred);
  2505. }
  2506. }
  2507. if ( ListLocked )
  2508. {
  2509. KerbUnlockList( &LogonSession->ExtraCredentials.CredList );
  2510. }
  2511. return Status;
  2512. }
  2513. #endif // WIN32_CHICAGO