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.

5890 lines
158 KiB

  1. /*++
  2. Copyright (c) 1991 - 1999 Microsoft Corporation
  3. Module Name:
  4. nlpcache.c
  5. Abstract:
  6. This module contains routines which implement user account caching:
  7. NlpCacheInitialize
  8. NlpCacheTerminate
  9. NlpAddCacheEntry
  10. NlpGetCacheEntry
  11. NlpDeleteCacheEntry
  12. NlpChangeCachePassword
  13. The cache contains the most recent validated logon information. There is
  14. only 1 (that's right - one) cache slot. This will probably change though
  15. Author:
  16. Richard L Firth (rfirth) 17-Dec-1991
  17. Revision History:
  18. Scott Field (sfield) 04-Jun-99
  19. Add supplemental cache data.
  20. Store all cache related data in single location.
  21. Encrypt interesting elements of cache entry using per-entry key mixed with per-machine key.
  22. MAC interesting cache elements for integrity check.
  23. Drastically reduce lock contention.
  24. Avoid NtFlushKey() for single location cache elements.
  25. Avoid persisting a new cache entry that matches an existing one.
  26. Attempt reg query with stack based buffer first.
  27. Chandana Surlu 21-Jul-96 Stolen from \\kernel\razzle3\src\security\msv1_0\nlpcache.c
  28. --*/
  29. #include <global.h>
  30. #undef EXTERN
  31. #include "msp.h"
  32. #include "nlp.h"
  33. #include "nlpcache.h"
  34. //
  35. // manifests
  36. //
  37. #if DBG
  38. #include <stdio.h>
  39. #endif
  40. //
  41. // Revision numbers
  42. //
  43. // NT 3.0 didn't explicitly store a revision number.
  44. // However, we are designating that release to be revision 0x00010000 (1.0).
  45. // NT 3.5 prior to build 622 is revision 0x00010001 (1.1).
  46. // NT 3.5 is revision 0x00010002 (1.2).
  47. // NT 4.0 SP 4 is revision 0x00010003 (1.3)
  48. // NT 5.0 build 2054+ is revision 0x00010004 (1.4)
  49. //
  50. #define NLP_CACHE_REVISION_NT_1_0 (0x00010000) // NT 3.0
  51. #define NLP_CACHE_REVISION_NT_1_0B (0x00010002) // NT 3.5
  52. #define NLP_CACHE_REVISION_NT_4_SP4 (0x00010003) // NT 4.0 SP 4 to save passwords as salted.
  53. #define NLP_CACHE_REVISION_NT_5_0 (0x00010004) // NT 5.0 to support opaque cache data and single location data storage.
  54. #define NLP_CACHE_REVISION (NLP_CACHE_REVISION_NT_5_0)
  55. //
  56. // The logon cache may be controlled via a value in the registry.
  57. // If the registry key does not exist, then this default constant defines
  58. // how many logon cache entries will be active. The max constant
  59. // places an upper limit on how many cache entries we will support.
  60. // If the user specifies more than the max value, we will use the
  61. // max value instead.
  62. //
  63. #define NLP_DEFAULT_LOGON_CACHE_COUNT (10)
  64. #define NLP_MAX_LOGON_CACHE_COUNT (50)
  65. //
  66. // length of per-machine cache encryption key.
  67. //
  68. #define NLP_CACHE_ENCRYPTION_KEY_LEN (64)
  69. //
  70. // name of LSA secret containing cache encryption key.
  71. //
  72. #define NLP_CACHE_ENCRYPTION_KEY_NAME L"NL$KM"
  73. //
  74. // macros
  75. //
  76. #define AllocateCacheEntry(n) (PLOGON_CACHE_ENTRY)I_NtLmAllocate(n)
  77. #define FreeCacheEntry(p) I_NtLmFree((PVOID)p)
  78. #define AllocateFromHeap(n) I_NtLmAllocate(n)
  79. #define FreeToHeap(p) I_NtLmFree((PVOID)p)
  80. //
  81. // guard against simultaneous access
  82. //
  83. #define READ_CACHE() RtlAcquireResourceShared(&NlpLogonCacheCritSec, TRUE)
  84. #define WRITE_CACHE() RtlAcquireResourceExclusive(&NlpLogonCacheCritSec, TRUE)
  85. #define READ_TO_WRITE_CACHE() RtlConvertSharedToExclusive(&NlpLogonCacheCritSec)
  86. #define LEAVE_CACHE() RtlReleaseResource(&NlpLogonCacheCritSec)
  87. #define INVALIDATE_HANDLE(handle) (*((PHANDLE)(&handle)) = INVALID_HANDLE_VALUE)
  88. #define IS_VALID_HANDLE(handle) (handle != INVALID_HANDLE_VALUE)
  89. ////////////////////////////////////////////////////////////////////////
  90. // //
  91. // datatypes //
  92. // //
  93. ////////////////////////////////////////////////////////////////////////
  94. typedef enum _NLP_SET_TIME_HINT {
  95. NLP_SMALL_TIME,
  96. NLP_BIG_TIME,
  97. NLP_NOW_TIME
  98. } NLP_SET_TIME_HINT, *PNLP_SET_TIME_HINT;
  99. #define BIG_PART_1 0x7fffffff // largest positive large int is 63 bits on
  100. #define BIG_PART_2 0xffffffff
  101. #define SMALL_PART_1 0x0 // smallest positive large int is 64 bits off
  102. #define SMALL_PART_2 0x0
  103. //
  104. // This structure is saved on disk and provides information
  105. // about the rest of the cache. This structure is in a value
  106. // named "NL$Control" under the cache registry key.
  107. //
  108. typedef struct _NLP_CACHE_CONTROL {
  109. //
  110. // Revision of the cache on-disk structure
  111. //
  112. ULONG Revision;
  113. //
  114. // The current on-disk size of the cache (number of entries)
  115. //
  116. ULONG Entries;
  117. } NLP_CACHE_CONTROL, *PNLP_CACHE_CONTROL;
  118. //
  119. // This data structure is a single cache table entry (CTE)
  120. // Each entry in the cache has a corresponding CTE.
  121. //
  122. typedef struct _NLP_CTE {
  123. //
  124. // CTEs are linked on either an invalid list (in any order)
  125. // or on a valid list (in ascending order of time).
  126. // This makes it easy to figure out which entry is to be
  127. // flushed when adding to the cache.
  128. //
  129. LIST_ENTRY Link;
  130. //
  131. // Time the cache entry was established.
  132. // This is used to determine which cache
  133. // entry is the oldest, and therefore will
  134. // be flushed from the cache first to make
  135. // room for new entries.
  136. //
  137. LARGE_INTEGER Time;
  138. //
  139. // This field contains the index of the CTE within the
  140. // CTE table. This index is used to generate the names
  141. // of the entrie's secret key and cache key in the registry.
  142. // This field is valid even if the entry is marked Inactive.
  143. //
  144. ULONG Index;
  145. //
  146. // Normally, we walk the active and inactive lists
  147. // to find entries. When growing or shrinking the
  148. // cache, however, it is nice to be able to walk the
  149. // table using indexes. In this case, it is nice to
  150. // have a local way of determining whether an entry
  151. // is on the active or inactive list. This field
  152. // provides that capability.
  153. //
  154. // TRUE ==> on active list
  155. // FALSE ==> not on active list
  156. //
  157. BOOLEAN Active;
  158. } NLP_CTE, *PNLP_CTE;
  159. //
  160. // This structure is used for keeping track of all information that
  161. // is stored on backing store.
  162. //
  163. typedef struct _NLP_CACHE_AND_SECRETS {
  164. PLOGON_CACHE_ENTRY CacheEntry;
  165. ULONG EntrySize;
  166. PLSAPR_CR_CIPHER_VALUE NewSecret;
  167. PLSAPR_CR_CIPHER_VALUE OldSecret;
  168. BOOLEAN Active;
  169. } NLP_CACHE_AND_SECRETS, *PNLP_CACHE_AND_SECRETS;
  170. ////////////////////////////////////////////////////////////////////////
  171. // //
  172. // Local Prototypes //
  173. // //
  174. ////////////////////////////////////////////////////////////////////////
  175. NTSTATUS
  176. NlpInternalCacheInitialize(
  177. VOID
  178. );
  179. NTSTATUS
  180. NlpOpenCache( VOID );
  181. VOID
  182. NlpCloseCache( VOID );
  183. NTSTATUS
  184. NlpGetCacheControlInfo( VOID );
  185. NTSTATUS
  186. NlpCacheKeyInitialize(
  187. VOID
  188. );
  189. NTSTATUS
  190. NlpBuildCteTable( VOID );
  191. NTSTATUS
  192. NlpChangeCacheSizeIfNecessary( VOID );
  193. NTSTATUS
  194. NlpWriteCacheControl( VOID );
  195. VOID
  196. NlpMakeCacheEntryName(
  197. IN ULONG EntryIndex,
  198. OUT PUNICODE_STRING Name
  199. );
  200. NTSTATUS
  201. NlpMakeNewCacheEntry(
  202. ULONG Index
  203. );
  204. NTSTATUS
  205. NlpEliminateCacheEntry(
  206. IN ULONG Index
  207. );
  208. NTSTATUS
  209. NlpReadCacheEntryByIndex(
  210. IN ULONG Index,
  211. OUT PLOGON_CACHE_ENTRY* CacheEntry,
  212. OUT PULONG EntrySize
  213. );
  214. VOID
  215. NlpAddEntryToActiveList(
  216. IN ULONG Index
  217. );
  218. VOID
  219. NlpAddEntryToInactiveList(
  220. IN ULONG Index
  221. );
  222. VOID
  223. NlpGetFreeEntryIndex(
  224. OUT PULONG Index
  225. );
  226. NTSTATUS
  227. NlpBuildCacheEntry(
  228. IN PNETLOGON_INTERACTIVE_INFO LogonInfo,
  229. IN PNETLOGON_VALIDATION_SAM_INFO4 AccountInfo,
  230. IN ULONG CacheFlags,
  231. IN ULONG SupplementalCacheDataLength,
  232. IN PBYTE SupplementalCacheData,
  233. OUT PLOGON_CACHE_ENTRY* ppCacheEntry,
  234. OUT PULONG pEntryLength
  235. );
  236. BOOLEAN
  237. NlpCompareCacheEntry(
  238. IN PLOGON_CACHE_ENTRY CacheEntry1,
  239. IN ULONG EntrySize1,
  240. IN PLOGON_CACHE_ENTRY CacheEntry2,
  241. IN ULONG EntrySize2
  242. );
  243. NTSTATUS
  244. NlpEncryptCacheEntry(
  245. IN PLOGON_CACHE_ENTRY CacheEntry,
  246. IN ULONG EntrySize
  247. );
  248. NTSTATUS
  249. NlpDecryptCacheEntry(
  250. IN PLOGON_CACHE_ENTRY CacheEntry,
  251. IN ULONG EntrySize
  252. );
  253. NTSTATUS
  254. NlpAddSupplementalCacheData(
  255. IN PVOID SupplementalCacheData,
  256. IN ULONG SupplementalCacheDataLength,
  257. IN OUT PLOGON_CACHE_ENTRY *ppCacheEntry,
  258. IN OUT PULONG pEntryLength
  259. );
  260. NTSTATUS
  261. NlpOpenCache( VOID );
  262. VOID
  263. NlpCloseCache( VOID );
  264. NTSTATUS
  265. NlpOpenSecret(
  266. IN ULONG Index
  267. );
  268. VOID
  269. NlpCloseSecret( VOID );
  270. NTSTATUS
  271. NlpWriteSecret(
  272. IN PLSAPR_CR_CIPHER_VALUE NewSecret,
  273. IN PLSAPR_CR_CIPHER_VALUE OldSecret
  274. );
  275. NTSTATUS
  276. NlpReadSecret(
  277. OUT PLSAPR_CR_CIPHER_VALUE * NewSecret,
  278. OUT PLSAPR_CR_CIPHER_VALUE * OldSecret
  279. );
  280. NTSTATUS
  281. NlpMakeSecretPassword(
  282. OUT PLSAPR_CR_CIPHER_VALUE Passwords,
  283. IN PUNICODE_STRING UserName,
  284. IN PNT_OWF_PASSWORD NtOwfPassword OPTIONAL,
  285. IN PLM_OWF_PASSWORD LmOwfPassword OPTIONAL
  286. );
  287. NTSTATUS
  288. NlpMakeSecretPasswordNT5(
  289. IN OUT PCACHE_PASSWORDS Passwords,
  290. IN PUNICODE_STRING UserName,
  291. IN PNT_OWF_PASSWORD NtOwfPassword OPTIONAL,
  292. IN PLM_OWF_PASSWORD LmOwfPassword OPTIONAL
  293. );
  294. NTSTATUS
  295. NlpGetCredentialNamesFromMitCacheEntry(
  296. IN PLOGON_CACHE_ENTRY CacheEntry,
  297. OUT PUNICODE_STRING DomainName,
  298. OUT PUNICODE_STRING UserName
  299. );
  300. NTSTATUS
  301. NlpGetCredentialNamesFromCacheEntry(
  302. IN PLOGON_CACHE_ENTRY CacheEntry,
  303. IN PNETLOGON_VALIDATION_SAM_INFO4 AccountInfo,
  304. OUT PUNICODE_STRING DomainName,
  305. OUT PUNICODE_STRING UserName
  306. );
  307. NTSTATUS
  308. NlpGetCredentialNamesFromMitCacheSupplementalCacheData(
  309. IN ULONG SupplementalCacheDataLength,
  310. IN PBYTE SupplementalCacheData,
  311. OUT PUNICODE_STRING DomainName,
  312. OUT PUNICODE_STRING UserName
  313. );
  314. NTSTATUS
  315. NlpReadCacheEntry(
  316. IN PUNICODE_STRING DomainName,
  317. IN PUNICODE_STRING UserName,
  318. OUT PULONG Index,
  319. OUT PLOGON_CACHE_ENTRY* CacheEntry,
  320. OUT PULONG EntrySize
  321. );
  322. NTSTATUS
  323. NlpWriteCacheEntry(
  324. IN ULONG Index,
  325. IN PLOGON_CACHE_ENTRY Entry,
  326. IN ULONG EntrySize
  327. );
  328. VOID
  329. NlpCopyAndUpdateAccountInfo(
  330. IN USHORT Length,
  331. IN PUNICODE_STRING pUnicodeString,
  332. IN OUT PUCHAR* pSource,
  333. IN OUT PUCHAR* pDest
  334. );
  335. VOID
  336. NlpSetTimeField(
  337. OUT POLD_LARGE_INTEGER pTimeField,
  338. IN NLP_SET_TIME_HINT Hint
  339. );
  340. NTSTATUS
  341. NlpBuildAccountInfo(
  342. IN PLOGON_CACHE_ENTRY pCacheEntry,
  343. IN ULONG EntryLength,
  344. OUT PNETLOGON_VALIDATION_SAM_INFO4* AccountInfo
  345. );
  346. /////////////////////////////////////////////////////////////////////////
  347. // //
  348. // Diagnostic support services prototypes //
  349. // //
  350. /////////////////////////////////////////////////////////////////////////
  351. #if DBG
  352. PCHAR
  353. DumpOwfPasswordToString(
  354. OUT PCHAR Buffer,
  355. IN PLM_OWF_PASSWORD Password
  356. );
  357. VOID
  358. DumpLogonInfo(
  359. IN PNETLOGON_LOGON_IDENTITY_INFO LogonInfo
  360. );
  361. char*
  362. MapWeekday(
  363. IN CSHORT Weekday
  364. );
  365. VOID
  366. DumpTime(
  367. IN LPSTR String,
  368. IN POLD_LARGE_INTEGER OldTime
  369. );
  370. VOID
  371. DumpGroupIds(
  372. IN LPSTR String,
  373. IN ULONG Count,
  374. IN PGROUP_MEMBERSHIP GroupIds
  375. );
  376. VOID
  377. DumpSessKey(
  378. IN LPSTR String,
  379. IN PUSER_SESSION_KEY Key
  380. );
  381. VOID
  382. DumpSid(
  383. LPSTR String,
  384. PISID Sid
  385. );
  386. VOID
  387. DumpAccountInfo(
  388. IN PNETLOGON_VALIDATION_SAM_INFO4 AccountInfo
  389. );
  390. VOID
  391. DumpCacheEntry(
  392. IN ULONG Index,
  393. IN PLOGON_CACHE_ENTRY pEntry
  394. );
  395. #endif //DBG
  396. ////////////////////////////////////////////////////////////////////////
  397. // //
  398. // global data //
  399. // //
  400. ////////////////////////////////////////////////////////////////////////
  401. //
  402. // This boolean indicates whether or not we have been able to
  403. // initialize caching yet. It turn out that during authentication
  404. // package load time, we can't do everything we would like to (like
  405. // call LSA RPC routines). So, we delay initializing until we can
  406. // call LSA. All publicly exposed interfaces must check this value
  407. // before assuming work can be done.
  408. //
  409. BOOLEAN NlpInitializationNotYetPerformed = TRUE;
  410. RTL_RESOURCE NlpLogonCacheCritSec;
  411. HANDLE NlpCacheHandle = (HANDLE) INVALID_HANDLE_VALUE;
  412. LSAPR_HANDLE NlpSecretHandle = (LSAPR_HANDLE) INVALID_HANDLE_VALUE;
  413. //
  414. // control information about the cache (number of entries, etc).
  415. //
  416. NLP_CACHE_CONTROL NlpCacheControl;
  417. //
  418. // This structure is generated and maintained only in memory.
  419. // It indicates which cache entries are valid and which aren't.
  420. // It also indicates what time each entry was established so we
  421. // know which order to discard them in.
  422. //
  423. // This field is a pointer to an array of CTEs. The number of CTEs
  424. // in the array is in NlpCacheControl.Entries. This structure is
  425. // allocated at initialization time.
  426. //
  427. PNLP_CTE NlpCteTable;
  428. //
  429. // The Cache Table Entries in NlpCteTable are linked on either an
  430. // active or inactive list. The entries on the active list are in
  431. // ascending time order - so the last one on the list is the first
  432. // one to be discarded when a flush is needed to add a new entry.
  433. //
  434. LIST_ENTRY NlpActiveCtes;
  435. LIST_ENTRY NlpInactiveCtes;
  436. //
  437. // global, per-machine key used for encrypting NT_5_0 version cache
  438. // entries.
  439. //
  440. CHAR NlpCacheEncryptionKey[ NLP_CACHE_ENCRYPTION_KEY_LEN ];
  441. #if DBG
  442. #ifdef DUMP_CACHE_INFO
  443. ULONG DumpCacheInfo = 1;
  444. #else
  445. ULONG DumpCacheInfo = 0;
  446. #endif
  447. #endif
  448. ////////////////////////////////////////////////////////////////////////
  449. // //
  450. // Services Exported by this module //
  451. // //
  452. ////////////////////////////////////////////////////////////////////////
  453. NTSTATUS
  454. NlpCacheInitialize(
  455. VOID
  456. )
  457. /*++
  458. Routine Description:
  459. This routine is called to initialize cached logon processing.
  460. Unfortunately, there isn't much we can do when we are called.
  461. (we can't open LSA, for example). So, defer initialization
  462. until later.
  463. Arguments:
  464. None.
  465. Return Value:
  466. NTSTATUS
  467. --*/
  468. {
  469. RtlInitializeResource(&NlpLogonCacheCritSec);
  470. return STATUS_SUCCESS;
  471. }
  472. NTSTATUS
  473. NlpCacheTerminate(
  474. VOID
  475. )
  476. /*++
  477. Routine Description:
  478. Called when process detaches
  479. Arguments:
  480. None.
  481. Return Value:
  482. NTSTATUS
  483. --*/
  484. {
  485. #if DBG
  486. if (DumpCacheInfo) {
  487. DbgPrint("NlpCacheTerminate\n");
  488. }
  489. #endif
  490. if (!NlpInitializationNotYetPerformed) {
  491. NlpCloseCache();
  492. NlpCloseSecret();
  493. if (IS_VALID_HANDLE(NlpCacheHandle)) {
  494. NtClose( NlpCacheHandle );
  495. }
  496. FreeToHeap( NlpCteTable );
  497. }
  498. RtlDeleteResource(&NlpLogonCacheCritSec);
  499. return STATUS_SUCCESS;
  500. }
  501. NTSTATUS
  502. NlpGetCacheEntry(
  503. IN PNETLOGON_LOGON_IDENTITY_INFO LogonInfo,
  504. IN ULONG CacheLookupFlags,
  505. OUT OPTIONAL PUNICODE_STRING CredentialDomainName, // domain/realm name
  506. OUT OPTIONAL PUNICODE_STRING CredentialUserName,
  507. OUT PNETLOGON_VALIDATION_SAM_INFO4* AccountInfo,
  508. OUT PCACHE_PASSWORDS Passwords,
  509. OUT OPTIONAL PVOID *ppSupplementalCacheData,
  510. OUT OPTIONAL PULONG pSupplementalCacheDataLength
  511. )
  512. /*++
  513. Routine Description:
  514. If the user logging on has information stored in the cache,
  515. then it is retrieved. Also returns the cached password from
  516. 'secret' storage
  517. Arguments:
  518. LogonInfo - pointer to NETLOGON_IDENTITY_INFO structure which contains
  519. the domain name, user name for this user
  520. CacheLookupFlags - flags used to lookup
  521. CredentialDomainName - domain name in primary credentials
  522. CredentialUserName - user name in primary credentials
  523. AccountInfo - pointer to NETLOGON_VALIDATION_SAM_INFO4 structure to
  524. receive this user's specific interactive logon information
  525. Passwords - pointer to CACHE_PASSWORDS structure to receive passwords
  526. returned from secret storage
  527. ppSupplementalCacheData - supplemental cache data
  528. pSupplementalCacheDataLength - lenght of supplemental cache data
  529. Return Value:
  530. NTSTATUS
  531. Success = STATUS_SUCCESS
  532. *AccountInfo points to a NETLOGON_VALIDATION_SAM_INFO4
  533. structure. This must be freed by caller
  534. *Passwords contain USER_INTERNAL1_INFORMATION structure
  535. which contains NT OWF password and LM OWF password. These
  536. must be used to validate the logon
  537. Failure = STATUS_LOGON_FAILURE
  538. The user logging on isn't in the cache.
  539. --*/
  540. {
  541. NTSTATUS
  542. NtStatus;
  543. PNETLOGON_VALIDATION_SAM_INFO4
  544. SamInfo = NULL;
  545. PLOGON_CACHE_ENTRY
  546. CacheEntry = NULL;
  547. ULONG
  548. EntrySize,
  549. Index;
  550. PLSAPR_CR_CIPHER_VALUE
  551. CurrentSecret = NULL,
  552. OldSecret = NULL;
  553. BOOLEAN fCacheLocked = FALSE;
  554. *AccountInfo = NULL;
  555. if ( ppSupplementalCacheData )
  556. *ppSupplementalCacheData = NULL;
  557. #if DBG
  558. if (DumpCacheInfo) {
  559. DbgPrint("NlpGetCacheEntry\n");
  560. DumpLogonInfo(LogonInfo);
  561. }
  562. #endif
  563. if (NlpInitializationNotYetPerformed) {
  564. NtStatus = NlpInternalCacheInitialize();
  565. if (!NT_SUCCESS(NtStatus)) {
  566. return(NtStatus);
  567. }
  568. }
  569. if (NlpCacheControl.Entries == 0) {
  570. return(STATUS_LOGON_FAILURE);
  571. }
  572. //
  573. // TODO: consider comparing LogonDomainName to NlpSamDomainName
  574. // and failing cached logon attempts at local machine.
  575. //
  576. READ_CACHE();
  577. fCacheLocked = TRUE;
  578. //
  579. // Find the cache entry and open its secret (if found)
  580. //
  581. NtStatus = NlpReadCacheEntry(
  582. &LogonInfo->LogonDomainName,
  583. &LogonInfo->UserName,
  584. &Index,
  585. &CacheEntry,
  586. &EntrySize
  587. );
  588. if (!NT_SUCCESS(NtStatus)) {
  589. LEAVE_CACHE();
  590. return (NtStatus);
  591. }
  592. //
  593. // exclude smartcard only cache entries for NTLM.
  594. // check for earlier versions prior to nt_5_0 where SC is not
  595. // supported and CacheFlags is not part of the cache entry
  596. //
  597. if ( (CacheEntry->Revision >= NLP_CACHE_REVISION_NT_5_0)
  598. && ((CacheLookupFlags & MSV1_0_CACHE_LOGON_REQUEST_SMARTCARD_ONLY) == 0)
  599. && ((CacheEntry->CacheFlags & MSV1_0_CACHE_LOGON_REQUEST_SMARTCARD_ONLY)
  600. || ((0 == (CacheEntry->CacheFlags & MSV1_0_CACHE_LOGON_REQUEST_MIT_LOGON))
  601. && CacheEntry->SupplementalCacheDataLength)) )
  602. {
  603. LEAVE_CACHE();
  604. SspPrint((SSP_CRITICAL, "NlpGetCacheEntry can not use smartcard only cacne entry: CacheFlags %#x, LookupFlags %#x\n", CacheEntry->CacheFlags, CacheLookupFlags));
  605. return STATUS_LOGON_FAILURE;
  606. }
  607. if ( CacheEntry->Revision >= NLP_CACHE_REVISION_NT_5_0 ) {
  608. //
  609. // for NT5, we can release the cache lock now, since all data
  610. // stored in one place.
  611. //
  612. LEAVE_CACHE();
  613. fCacheLocked = FALSE;
  614. //
  615. // if caller wanted supplemental data, give it to them.
  616. //
  617. if ( ppSupplementalCacheData && pSupplementalCacheDataLength )
  618. {
  619. LPBYTE Source;
  620. *pSupplementalCacheDataLength = CacheEntry->SupplementalCacheDataLength;
  621. *ppSupplementalCacheData = MIDL_user_allocate( *pSupplementalCacheDataLength );
  622. if ( *ppSupplementalCacheData == NULL ) {
  623. NtStatus = STATUS_NO_MEMORY;
  624. goto Cleanup;
  625. }
  626. //
  627. // note: the decrypt operation that occurred during the
  628. // ReadCacheEntry validates any data and pointers through
  629. // integrity checking via HMAC. Having said that, we can be
  630. // lazy and not do boundry checking.
  631. //
  632. Source = ((LPBYTE)CacheEntry + CacheEntry->SupplementalCacheDataOffset);
  633. CopyMemory( *ppSupplementalCacheData,
  634. Source,
  635. *pSupplementalCacheDataLength
  636. );
  637. }
  638. }
  639. NtStatus = NlpBuildAccountInfo(CacheEntry, EntrySize, &SamInfo);
  640. if (!NT_SUCCESS(NtStatus))
  641. {
  642. goto Cleanup;
  643. }
  644. if (CredentialDomainName && CredentialUserName)
  645. {
  646. NtStatus = NlpGetCredentialNamesFromCacheEntry(
  647. CacheEntry,
  648. SamInfo,
  649. CredentialDomainName,
  650. CredentialUserName
  651. );
  652. if (!NT_SUCCESS(NtStatus))
  653. {
  654. goto Cleanup;
  655. }
  656. SspPrint((SSP_CRED, "NlpGetCacheEntry getting cred %wZ\\%wZ, account %wZ\\%wZ, logon %wZ\\%wZ\n",
  657. CredentialDomainName, CredentialUserName,
  658. &SamInfo->LogonDomainName, &SamInfo->EffectiveName,
  659. &LogonInfo->LogonDomainName, &LogonInfo->UserName));
  660. }
  661. if ( CacheEntry->Revision >= NLP_CACHE_REVISION_NT_5_0 ) {
  662. //
  663. // for NT5, the Passwords are stored in the CacheEntry.
  664. // note: passwords are assumed to be salted.
  665. //
  666. RtlCopyMemory( Passwords, &(CacheEntry->CachePasswords), sizeof(*Passwords) );
  667. } else {
  668. //
  669. // prior to NT5, the Passwords are stored separately in their
  670. // own LSA secret.
  671. //
  672. NtStatus = NlpReadSecret(&CurrentSecret, &OldSecret);
  673. if (!NT_SUCCESS(NtStatus))
  674. {
  675. goto Cleanup;
  676. }
  677. if ( CurrentSecret == NULL )
  678. {
  679. NtStatus = STATUS_LOGON_FAILURE;
  680. goto Cleanup;
  681. }
  682. //
  683. // can release the cache lock now, since second data item fetched.
  684. //
  685. LEAVE_CACHE();
  686. fCacheLocked = FALSE;
  687. //
  688. // Check to see which version of the passwords are stored
  689. // here - the normal or the salted.
  690. //
  691. RtlCopyMemory((PVOID)Passwords,
  692. (PVOID)CurrentSecret->Buffer,
  693. (ULONG)CurrentSecret->Length
  694. );
  695. if ( CacheEntry->Revision < NLP_CACHE_REVISION_NT_4_SP4 )
  696. {
  697. if (Passwords->SecretPasswords.NtPasswordPresent)
  698. {
  699. NtStatus = NlpComputeSaltedHashedPassword(
  700. &Passwords->SecretPasswords.NtOwfPassword,
  701. &Passwords->SecretPasswords.NtOwfPassword,
  702. &SamInfo->EffectiveName
  703. );
  704. if (!NT_SUCCESS(NtStatus))
  705. {
  706. goto Cleanup;
  707. }
  708. }
  709. if (Passwords->SecretPasswords.LmPasswordPresent)
  710. {
  711. NtStatus = NlpComputeSaltedHashedPassword(
  712. &Passwords->SecretPasswords.LmOwfPassword,
  713. &Passwords->SecretPasswords.LmOwfPassword,
  714. &SamInfo->EffectiveName
  715. );
  716. if (!NT_SUCCESS(NtStatus))
  717. {
  718. goto Cleanup;
  719. }
  720. }
  721. }
  722. }
  723. Cleanup:
  724. if ( fCacheLocked ) {
  725. LEAVE_CACHE();
  726. }
  727. //
  728. // free structure allocated by NlpReadCacheEntry
  729. //
  730. if ( CacheEntry ) {
  731. ZeroMemory( CacheEntry, EntrySize );
  732. FreeToHeap(CacheEntry);
  733. }
  734. //
  735. // free structures allocated by NlpReadSecret
  736. //
  737. if (CurrentSecret) {
  738. MIDL_user_free(CurrentSecret);
  739. }
  740. if (OldSecret) {
  741. MIDL_user_free(OldSecret);
  742. }
  743. if ( NT_SUCCESS( NtStatus ) ) {
  744. *AccountInfo = SamInfo;
  745. } else {
  746. if ( SamInfo != NULL ) {
  747. MIDL_user_free( SamInfo );
  748. }
  749. if( ppSupplementalCacheData && *ppSupplementalCacheData ) {
  750. if (pSupplementalCacheDataLength)
  751. {
  752. RtlZeroMemory(*ppSupplementalCacheData, *pSupplementalCacheDataLength);
  753. }
  754. MIDL_user_free( *ppSupplementalCacheData );
  755. *ppSupplementalCacheData = NULL;
  756. }
  757. }
  758. return(NtStatus);
  759. }
  760. NTSTATUS
  761. NlpAddCacheEntry(
  762. IN PNETLOGON_INTERACTIVE_INFO LogonInfo,
  763. IN PNETLOGON_VALIDATION_SAM_INFO4 AccountInfo,
  764. IN PVOID SupplementalCacheData,
  765. IN ULONG SupplementalCacheDataLength,
  766. IN ULONG CacheFlags
  767. )
  768. /*++
  769. Routine Description:
  770. Adds this domain:user interactive logon information to the cache.
  771. Arguments:
  772. LogonInfo - pointer to NETLOGON_INTERACTIVE_INFO structure which contains
  773. the domain name, user name and password for this user. These
  774. are what the user typed to WinLogon
  775. AccountInfo - pointer to NETLOGON_VALIDATION_SAM_INFO4 structure which
  776. contains this user's specific interactive logon information
  777. Return Value:
  778. NTSTATUS
  779. Success = STATUS_SUCCESS
  780. AccountInfo successfully added to cache
  781. Failure = STATUS_NO_MEMORY
  782. --*/
  783. {
  784. NTSTATUS
  785. NtStatus;
  786. PLOGON_CACHE_ENTRY
  787. CacheEntry = NULL;
  788. PLOGON_CACHE_ENTRY
  789. CacheEntryExisting = NULL;
  790. ULONG
  791. EntrySize = 0,
  792. EntrySizeExisting = 0,
  793. Index;
  794. BOOLEAN fCacheLocked = FALSE;
  795. #if DBG
  796. if (DumpCacheInfo) {
  797. DbgPrint("NlpAddCacheEntry\n");
  798. DumpLogonInfo(&LogonInfo->Identity);
  799. DumpAccountInfo(AccountInfo);
  800. }
  801. #endif
  802. if (NlpInitializationNotYetPerformed) {
  803. NtStatus = NlpInternalCacheInitialize();
  804. if (!NT_SUCCESS(NtStatus)) {
  805. return(NtStatus);
  806. }
  807. }
  808. if (NlpCacheControl.Entries == 0) {
  809. return(STATUS_SUCCESS);
  810. }
  811. //
  812. // LogonUser() allows for a NULL domain name to be supplied, which
  813. // causes netlogon search logic to kick in. this can result to logon
  814. // packages requesting to cache local account information.
  815. // In this case, use the LogonDomainName that SAM provides to make
  816. // a decision about whether to allow caching. In the same scenario,
  817. // if we decide caching is allowed, the cache entry target domain
  818. // is also set based on what SAM returned.
  819. //
  820. if ( (CacheFlags & MSV1_0_CACHE_LOGON_REQUEST_MIT_LOGON) == 0 )
  821. {
  822. //
  823. // If Sam is not yet initialized,
  824. // do it now.
  825. //
  826. if ( !NlpSamInitialized ) {
  827. NtStatus = NlSamInitialize( SAM_STARTUP_TIME );
  828. if ( !NT_SUCCESS(NtStatus) ) {
  829. goto Cleanup;
  830. }
  831. }
  832. ASSERT( NlpSamDomainId != NULL );
  833. ASSERT( AccountInfo->LogonDomainId != NULL );
  834. if( RtlEqualSid( NlpSamDomainId, AccountInfo->LogonDomainId ) )
  835. {
  836. #if DBG
  837. if (DumpCacheInfo) {
  838. DbgPrint("NlpAddCacheEntry: attempt to cache against local account skipped.\n");
  839. }
  840. #endif
  841. return STATUS_SUCCESS;
  842. }
  843. }
  844. //
  845. // build base cache entry.
  846. //
  847. NtStatus = NlpBuildCacheEntry(
  848. LogonInfo,
  849. AccountInfo,
  850. CacheFlags,
  851. SupplementalCacheDataLength,
  852. SupplementalCacheData,
  853. &CacheEntry,
  854. &EntrySize
  855. );
  856. if(!NT_SUCCESS(NtStatus) )
  857. {
  858. return (NtStatus);
  859. }
  860. //
  861. // add in salted OWFs.
  862. //
  863. NtStatus = NlpMakeSecretPasswordNT5(
  864. &CacheEntry->CachePasswords,
  865. &AccountInfo->EffectiveName,
  866. &LogonInfo->NtOwfPassword,
  867. &LogonInfo->LmOwfPassword
  868. );
  869. if(!NT_SUCCESS(NtStatus)) {
  870. goto Cleanup;
  871. }
  872. READ_CACHE();
  873. fCacheLocked = TRUE;
  874. //
  875. // See if this entry already exists in the cache.
  876. // If so, use the same index.
  877. //
  878. NtStatus = NlpReadCacheEntry( &LogonInfo->Identity.LogonDomainName,
  879. &LogonInfo->Identity.UserName,
  880. &Index,
  881. &CacheEntryExisting,
  882. &EntrySizeExisting
  883. );
  884. //
  885. // If we didn't find an entry, then we need to allocate an
  886. // entry.
  887. //
  888. if (!NT_SUCCESS(NtStatus)) {
  889. NlpGetFreeEntryIndex( &Index );
  890. CacheEntryExisting = NULL;
  891. } else {
  892. //
  893. // We already have an entry for this user.
  894. // Discard the structure we got back but
  895. // use the same index.
  896. //
  897. // TODO: check if existing entry matches new built entry.
  898. // if so, avoid write.
  899. BOOLEAN fMatchesExisting;
  900. fMatchesExisting = NlpCompareCacheEntry(
  901. CacheEntry,
  902. EntrySize,
  903. CacheEntryExisting,
  904. EntrySizeExisting
  905. );
  906. if ( fMatchesExisting )
  907. {
  908. SspPrint((SSP_CRED, "NlpAddCacheEntry avoid matching entry for logon %wZ\\%wZ\n",
  909. &LogonInfo->Identity.LogonDomainName,
  910. &LogonInfo->Identity.UserName));
  911. goto Cleanup;
  912. }
  913. }
  914. //
  915. // encrypt sensitive portions of the cache entry.
  916. // note: this was done prior to locking the cache, but, in the interest
  917. // of allowing for cache compare above, the encryption is deferred until
  918. // now.
  919. //
  920. NtStatus = NlpEncryptCacheEntry(CacheEntry, EntrySize);
  921. if(!NT_SUCCESS(NtStatus)) {
  922. goto Cleanup;
  923. }
  924. //
  925. // we already have the read lock, convert it to write-lock.
  926. //
  927. READ_TO_WRITE_CACHE();
  928. //
  929. // now, write the entry out...
  930. //
  931. NtStatus = NlpWriteCacheEntry(Index, CacheEntry, EntrySize);
  932. if (NT_SUCCESS(NtStatus)) {
  933. SspPrint((SSP_CRED, "NlpAddCacheEntry entry %d written: %wZ\\%wZ (update? %s)\n",
  934. Index, &LogonInfo->Identity.LogonDomainName, &LogonInfo->Identity.UserName,
  935. CacheEntryExisting ? "true" : "false"));
  936. NlpCteTable[Index].Time = CacheEntry->Time;
  937. NlpAddEntryToActiveList( Index );
  938. }
  939. Cleanup:
  940. if ( fCacheLocked )
  941. {
  942. LEAVE_CACHE();
  943. }
  944. if ( CacheEntry ) {
  945. ZeroMemory( CacheEntry, EntrySize );
  946. FreeCacheEntry( CacheEntry );
  947. }
  948. if( CacheEntryExisting ) {
  949. ZeroMemory( CacheEntryExisting, EntrySizeExisting );
  950. FreeCacheEntry( CacheEntryExisting );
  951. }
  952. return(NtStatus);
  953. }
  954. NTSTATUS
  955. NlpAddSupplementalCacheData(
  956. IN PVOID SupplementalCacheData,
  957. IN ULONG SupplementalCacheDataLength,
  958. IN OUT PLOGON_CACHE_ENTRY *ppCacheEntry,
  959. IN OUT PULONG pEntryLength
  960. )
  961. /*++
  962. Routine Description:
  963. Extends the supplied LOGON_CACHE_ENTRY with opaque authentication package
  964. SupplementalCacheData (eg: smart-card logon cache info).
  965. Return Value:
  966. NTSTATUS
  967. Success = STATUS_SUCCESS
  968. Failure =
  969. --*/
  970. {
  971. PLOGON_CACHE_ENTRY NewCacheEntry = NULL;
  972. if( (*ppCacheEntry)->Revision < NLP_CACHE_REVISION_NT_5_0 ) {
  973. return STATUS_SUCCESS;
  974. }
  975. (*ppCacheEntry)->SupplementalCacheDataLength = SupplementalCacheDataLength;
  976. (*ppCacheEntry)->SupplementalCacheDataOffset = *pEntryLength;
  977. if ( SupplementalCacheData == NULL || SupplementalCacheDataLength == 0 ) {
  978. return STATUS_SUCCESS;
  979. }
  980. //
  981. // allocate new entry, and copy existing entry + supplemental data to end.
  982. //
  983. NewCacheEntry = AllocateCacheEntry( *pEntryLength + SupplementalCacheDataLength );
  984. if( NewCacheEntry == NULL ) {
  985. return STATUS_NO_MEMORY;
  986. }
  987. CopyMemory( NewCacheEntry, *ppCacheEntry, *pEntryLength );
  988. CopyMemory( ((PBYTE)(NewCacheEntry) + *pEntryLength),
  989. SupplementalCacheData,
  990. SupplementalCacheDataLength
  991. );
  992. ZeroMemory( *ppCacheEntry, *pEntryLength );
  993. FreeCacheEntry( *ppCacheEntry );
  994. *ppCacheEntry = NewCacheEntry;
  995. *pEntryLength += SupplementalCacheDataLength;
  996. return STATUS_SUCCESS;
  997. }
  998. NTSTATUS
  999. NlpDeleteCacheEntry(
  1000. IN NTSTATUS FailedStatus,
  1001. IN USHORT Authoritative,
  1002. IN USHORT LogonType,
  1003. IN BOOLEAN InvalidatedByNtlm,
  1004. IN PNETLOGON_INTERACTIVE_INFO LogonInfo
  1005. )
  1006. /*++
  1007. Routine Description:
  1008. Deletes a user account from the local user account cache, if the corresponding
  1009. entry can be found. We actually just null out the current contents instead of
  1010. destroying the storage - this should save us some time when we next come to
  1011. add an entry to the cache
  1012. Arguments:
  1013. FailedStatus - Status that caused the logon cache entry to be deleted
  1014. Authoritative - Was the error code authorative?
  1015. LogonType - LogonType that caused the logon cache entry to be deleted
  1016. InvalidatedByNtlm - whether entry is validated by NTLM
  1017. LogonInfo - pointer to NETLOGON_INTERACTIVE_INFO structure which contains
  1018. the domain name, user name and password for this user
  1019. Return Value:
  1020. NTSTATUS
  1021. Success = STATUS_SUCCESS
  1022. Failure =
  1023. --*/
  1024. {
  1025. NTSTATUS
  1026. NtStatus;
  1027. PLOGON_CACHE_ENTRY
  1028. CacheEntry = NULL;
  1029. ULONG
  1030. EntrySize,
  1031. Index;
  1032. if (NlpInitializationNotYetPerformed) {
  1033. NtStatus = NlpInternalCacheInitialize();
  1034. if (!NT_SUCCESS(NtStatus)) {
  1035. return(NtStatus);
  1036. }
  1037. }
  1038. if (NlpCacheControl.Entries == 0) {
  1039. return(STATUS_SUCCESS);
  1040. }
  1041. WRITE_CACHE();
  1042. //
  1043. // See if this entry exists in the cache.
  1044. //
  1045. NtStatus = NlpReadCacheEntry( &LogonInfo->Identity.LogonDomainName,
  1046. &LogonInfo->Identity.UserName,
  1047. &Index,
  1048. &CacheEntry,
  1049. &EntrySize
  1050. );
  1051. //
  1052. // If we didn't find an entry, then there is nothing to do.
  1053. //
  1054. if (!NT_SUCCESS(NtStatus)) {
  1055. LEAVE_CACHE();
  1056. return(STATUS_SUCCESS);
  1057. }
  1058. //
  1059. // ealier revisons of cache entries do not have the CacheFlags field
  1060. //
  1061. if (InvalidatedByNtlm && (CacheEntry->Revision >= NLP_CACHE_REVISION_NT_5_0) && (CacheEntry->CacheFlags & MSV1_0_CACHE_LOGON_REQUEST_MIT_LOGON)) {
  1062. SspPrint((SSP_WARNING,
  1063. "NlpDeleteCacheEntry can not invalidate MIT cache entry %wZ\\%wZ\n",
  1064. &LogonInfo->Identity.LogonDomainName,
  1065. &LogonInfo->Identity.UserName
  1066. ));
  1067. LEAVE_CACHE();
  1068. return STATUS_NO_LOGON_SERVERS; // MIT kdc is not available
  1069. }
  1070. //
  1071. // Mark it as invalid.
  1072. //
  1073. CacheEntry->Valid = FALSE;
  1074. ASSERT(sizeof(FailedStatus) == 4 && sizeof(Authoritative) == 2 && sizeof(LogonType) == 2 && sizeof(CacheEntry->RandomKey) == 16);
  1075. RtlCopyMemory(CacheEntry->RandomKey, &FailedStatus, 4);
  1076. RtlCopyMemory(CacheEntry->RandomKey + 4, &Authoritative, 2);
  1077. RtlCopyMemory(CacheEntry->RandomKey + 6, &LogonType, 2);
  1078. NtQuerySystemTime((PLARGE_INTEGER) (CacheEntry->RandomKey + 8)), // 8 bytes
  1079. NtStatus = NlpWriteCacheEntry( Index, CacheEntry, EntrySize );
  1080. if (NT_SUCCESS(NtStatus)) {
  1081. //
  1082. // Put the CTE entry on the inactive list.
  1083. //
  1084. NlpAddEntryToInactiveList( Index );
  1085. }
  1086. LEAVE_CACHE();
  1087. //
  1088. // Free the structure returned from NlpReadCacheEntry()
  1089. //
  1090. if ( CacheEntry ) {
  1091. ZeroMemory( CacheEntry, EntrySize );
  1092. FreeToHeap( CacheEntry );
  1093. }
  1094. return(NtStatus);
  1095. }
  1096. NTSTATUS
  1097. NlpChangeCachePassword(
  1098. IN BOOLEAN Validated,
  1099. IN PUNICODE_STRING DomainName,
  1100. IN PUNICODE_STRING UserName,
  1101. IN PLM_OWF_PASSWORD LmOwfPassword,
  1102. IN PNT_OWF_PASSWORD NtOwfPassword
  1103. )
  1104. /*++
  1105. Routine Description:
  1106. Update a cached password to the specified value, if we have
  1107. the specified account cached.
  1108. Arguments:
  1109. DomainName - The name of the domain in which the account exists.
  1110. UserName - The name of the account whose password is to be changed.
  1111. LmOwfPassword - The new LM compatible password.
  1112. NtOwfPassword - The new NT compatible password.
  1113. Return Value:
  1114. None.
  1115. --*/
  1116. {
  1117. NTSTATUS NtStatus = STATUS_SUCCESS;
  1118. PLOGON_CACHE_ENTRY
  1119. CacheEntry = NULL;
  1120. ULONG
  1121. EntrySize,
  1122. Index;
  1123. PLSAPR_CR_CIPHER_VALUE
  1124. CurrentSecret = NULL,
  1125. OldSecret = NULL;
  1126. LSAPR_CR_CIPHER_VALUE
  1127. Passwords;
  1128. PNETLOGON_VALIDATION_SAM_INFO4 NlpUser = NULL;
  1129. SECPKG_CALL_INFO CallInfo = {0};
  1130. PSID CacheEntryUserSid = NULL;
  1131. #if DBG
  1132. if (DumpCacheInfo) {
  1133. DbgPrint("NlpChangeCachePassword\n");
  1134. }
  1135. #endif
  1136. if (NlpInitializationNotYetPerformed) {
  1137. NtStatus = NlpInternalCacheInitialize();
  1138. if (!NT_SUCCESS(NtStatus)) {
  1139. return NtStatus;
  1140. }
  1141. }
  1142. if (NlpCacheControl.Entries == 0) {
  1143. return STATUS_NOT_FOUND;
  1144. }
  1145. WRITE_CACHE();
  1146. NtStatus = NlpReadCacheEntry( DomainName,
  1147. UserName,
  1148. &Index,
  1149. &CacheEntry,
  1150. &EntrySize );
  1151. if (!NT_SUCCESS( NtStatus) )
  1152. {
  1153. SspPrint((SSP_WARNING, "NlpChangeCachePassword no cache entry found %#x\n", NtStatus));
  1154. //
  1155. // NlpReadCacheEntry returns STATUS_LOGON_FAILURE if no cache entry is
  1156. // found, remap the error code
  1157. //
  1158. if (STATUS_LOGON_FAILURE == NtStatus)
  1159. {
  1160. NtStatus = STATUS_NOT_FOUND;
  1161. }
  1162. goto Cleanup;
  1163. }
  1164. if (!Validated)
  1165. {
  1166. if (!LsaFunctions->GetCallInfo(&CallInfo))
  1167. {
  1168. NtStatus = STATUS_INTERNAL_ERROR;
  1169. goto Cleanup;
  1170. }
  1171. //
  1172. // disallow un-trusted callers to change cached passwords other than
  1173. // their own cache entries
  1174. //
  1175. if (0 == (CallInfo.Attributes & SECPKG_CALL_IS_TCB))
  1176. {
  1177. BOOL IsMember = FALSE;
  1178. NtStatus = NlpBuildAccountInfo(
  1179. CacheEntry,
  1180. EntrySize,
  1181. &NlpUser
  1182. );
  1183. if (!NT_SUCCESS(NtStatus))
  1184. {
  1185. goto Cleanup;
  1186. }
  1187. CacheEntryUserSid = NlpMakeDomainRelativeSid(
  1188. NlpUser->LogonDomainId,
  1189. NlpUser->UserId
  1190. );
  1191. if (!CacheEntryUserSid)
  1192. {
  1193. NtStatus = STATUS_NO_MEMORY;
  1194. goto Cleanup;
  1195. }
  1196. //
  1197. // now impersonating, need to revert to self later
  1198. //
  1199. NtStatus = LsaFunctions->ImpersonateClient();
  1200. if (!NT_SUCCESS(NtStatus))
  1201. {
  1202. goto Cleanup;
  1203. }
  1204. if (!CheckTokenMembership(NULL, CacheEntryUserSid, &IsMember))
  1205. {
  1206. SspPrint((SSP_CRITICAL, "NlpChangeCachePassword failed to check token membership, last error %#x\n", GetLastError()));
  1207. IsMember = FALSE;
  1208. }
  1209. RevertToSelf();
  1210. if (!IsMember)
  1211. {
  1212. NtStatus = STATUS_PRIVILEGE_NOT_HELD;
  1213. SspPrint((SSP_CRITICAL, "NlpChangeCachePassword user sids do not mismatched\n"));
  1214. goto Cleanup;
  1215. }
  1216. }
  1217. }
  1218. if ( CacheEntry->Revision >= NLP_CACHE_REVISION_NT_5_0 ) {
  1219. UNICODE_STRING CachedUser;
  1220. CachedUser.Length =
  1221. CachedUser.MaximumLength = CacheEntry->UserNameLength;
  1222. CachedUser.Buffer = (PWSTR) ((PBYTE) CacheEntry + sizeof(LOGON_CACHE_ENTRY));
  1223. //
  1224. // update timestamp in cache entry
  1225. //
  1226. NtQuerySystemTime(&CacheEntry->Time);
  1227. NtStatus = NlpMakeSecretPasswordNT5( &CacheEntry->CachePasswords,
  1228. &CachedUser,
  1229. NtOwfPassword,
  1230. LmOwfPassword );
  1231. if (NT_SUCCESS(NtStatus)) {
  1232. //
  1233. // encrypt the entry...
  1234. //
  1235. NtStatus = NlpEncryptCacheEntry( CacheEntry, EntrySize );
  1236. }
  1237. if (NT_SUCCESS( NtStatus )) {
  1238. //
  1239. // now, write the entry out...
  1240. //
  1241. NtStatus = NlpWriteCacheEntry(Index, CacheEntry, EntrySize);
  1242. #ifdef DBG
  1243. if(DumpCacheInfo) {
  1244. if( NT_SUCCESS( NtStatus ) ) {
  1245. DbgPrint("NlpChangeCachePassword: SUCCEED write NT5 version cache entry.\n");
  1246. } else {
  1247. DbgPrint("NlpChangeCachePassword: FAIL write NT5 version cache entry.\n");
  1248. }
  1249. }
  1250. #endif
  1251. }
  1252. } else {
  1253. NtStatus = NlpOpenSecret( Index );
  1254. if (NT_SUCCESS(NtStatus)) {
  1255. NtStatus = NlpReadSecret(&CurrentSecret, &OldSecret);
  1256. if (NT_SUCCESS(NtStatus)) {
  1257. UNICODE_STRING CachedUser;
  1258. //
  1259. // Grab the various strings from the cache entry.
  1260. //
  1261. ASSERT( CacheEntry->Revision >= NLP_CACHE_REVISION_NT_1_0B );
  1262. CachedUser.Length =
  1263. CachedUser.MaximumLength = CacheEntry->UserNameLength;
  1264. CachedUser.Buffer = (PWSTR) ((PBYTE) CacheEntry + sizeof(LOGON_CACHE_ENTRY_NT_4_SP4));
  1265. NtStatus = NlpMakeSecretPassword( &Passwords,
  1266. &CachedUser,
  1267. NtOwfPassword,
  1268. LmOwfPassword );
  1269. if (NT_SUCCESS(NtStatus)) {
  1270. NtStatus = NlpWriteSecret(&Passwords, CurrentSecret);
  1271. //
  1272. // free the buffer allocated to store the passwords
  1273. //
  1274. RtlZeroMemory(Passwords.Buffer, Passwords.Length);
  1275. FreeToHeap(Passwords.Buffer);
  1276. }
  1277. //
  1278. // free strings returned by NlpReadSecret
  1279. //
  1280. if (CurrentSecret) {
  1281. RtlZeroMemory(CurrentSecret->Buffer, CurrentSecret->Length);
  1282. MIDL_user_free(CurrentSecret);
  1283. }
  1284. if (OldSecret) {
  1285. RtlZeroMemory(OldSecret->Buffer, OldSecret->Length);
  1286. MIDL_user_free(OldSecret);
  1287. }
  1288. }
  1289. }
  1290. }
  1291. Cleanup:
  1292. LEAVE_CACHE();
  1293. //
  1294. // free structure allocated by NlpReadCacheEntry
  1295. //
  1296. if ( CacheEntry )
  1297. {
  1298. ZeroMemory( CacheEntry, EntrySize );
  1299. FreeToHeap(CacheEntry);
  1300. }
  1301. if (CacheEntryUserSid)
  1302. {
  1303. LsaFunctions->FreeLsaHeap(CacheEntryUserSid);
  1304. }
  1305. if (NlpUser)
  1306. {
  1307. MIDL_user_free(NlpUser);
  1308. }
  1309. return NtStatus;
  1310. }
  1311. ////////////////////////////////////////////////////////////////////////
  1312. // //
  1313. // Services Internal to this module //
  1314. // //
  1315. ////////////////////////////////////////////////////////////////////////
  1316. NTSTATUS
  1317. NlpInternalCacheInitialize(
  1318. VOID
  1319. )
  1320. /*++
  1321. Routine Description:
  1322. This routine is called to initialize cached logon processing.
  1323. This routine will automatically adjust the size of the logon
  1324. cache if necessary to accomodate a new user-specified length
  1325. (specified in the Winlogon part of the registry).
  1326. NOTE: If called too early, this routine won't be able to call
  1327. LSA's RPC routines. In this case, initialization is
  1328. defered until later.
  1329. Arguments:
  1330. None.
  1331. Return Value:
  1332. NTSTATUS
  1333. --*/
  1334. {
  1335. NTSTATUS
  1336. NtStatus;
  1337. // DbgPrint("\n\n\n REMEMBER TO TAKE THIS BREAKPOINT OUT BEFORE CHECKIN.\n\n\n");
  1338. // DumpCacheInfo = 1; // Remember to take this out too !!!!!!
  1339. // DbgBreakPoint(); // Remember to take this out before checking
  1340. #if DBG
  1341. if (DumpCacheInfo) {
  1342. DbgPrint("NlpCacheInitialize\n");
  1343. }
  1344. #endif
  1345. //
  1346. // Upon return from this routine, if logon caching is enabled,
  1347. // the following will be true:
  1348. //
  1349. // A handle to the registry key in which all cache entries
  1350. // are held will be open (NlpCacheHandle).
  1351. //
  1352. // A global structure defining how many cache entries there are
  1353. // will be initialized (NlpCacheControl).
  1354. //
  1355. // The Cache Table Entry table (CTE table) will be initialized
  1356. // (NlpCteTable).
  1357. //
  1358. // The active and inactive CTE lists will be built
  1359. // (NlpActiveCtes and NlpInactiveCtes).
  1360. //
  1361. // A global cache encryption key will be initialized.
  1362. //
  1363. WRITE_CACHE();
  1364. //
  1365. // Check again if the cache is initialized now that the crit sect is locked.
  1366. //
  1367. if (NlpInitializationNotYetPerformed) {
  1368. //
  1369. // Open the local system's policy object
  1370. //
  1371. //
  1372. // Successfully, or unsucessfully,
  1373. // The definition of "initialized" is we could call LSA's RPC
  1374. // routines.
  1375. //
  1376. NlpInitializationNotYetPerformed = FALSE;
  1377. //
  1378. // Open the registry key containing cache entries.
  1379. // This will remain open.
  1380. //
  1381. NtStatus = NlpOpenCache();
  1382. if (NT_SUCCESS(NtStatus)) {
  1383. //
  1384. // Get information on the current cache structure
  1385. // (number of entries, et cetera). This information is
  1386. // placed in a global variable for use throughout this
  1387. // module.
  1388. //
  1389. NtStatus = NlpGetCacheControlInfo();
  1390. //
  1391. // Initialize the per-machine cache encryption key.
  1392. //
  1393. if(NT_SUCCESS( NtStatus) ) {
  1394. NtStatus = NlpCacheKeyInitialize();
  1395. }
  1396. //
  1397. // Now build the CTE table
  1398. //
  1399. if (NT_SUCCESS(NtStatus)) {
  1400. NtStatus = NlpBuildCteTable();
  1401. }
  1402. //
  1403. // If we were successful, then see if we need to change
  1404. // the cache due to new user-specified cache size.
  1405. //
  1406. if (NT_SUCCESS(NtStatus)) {
  1407. NtStatus = NlpChangeCacheSizeIfNecessary();
  1408. }
  1409. if (!NT_SUCCESS(NtStatus)) {
  1410. NlpCloseCache();
  1411. }
  1412. }
  1413. //
  1414. // If we had an error, then set our entry count to zero
  1415. // to prevent using any cache information.
  1416. //
  1417. if (!NT_SUCCESS(NtStatus)) {
  1418. NlpCacheControl.Entries = 0;
  1419. }
  1420. } else {
  1421. NtStatus = STATUS_SUCCESS;
  1422. }
  1423. LEAVE_CACHE();
  1424. return(NtStatus);
  1425. }
  1426. NTSTATUS
  1427. NlpCacheKeyInitialize(
  1428. VOID
  1429. )
  1430. /*++
  1431. Routine Description:
  1432. Initializes the Global variable NlpCacheEncryptionKey with a per-machine
  1433. cache encryption key. If the per-machine key does not exist as an LSA
  1434. secret, it is created.
  1435. --*/
  1436. {
  1437. LSAPR_HANDLE SecretHandle;
  1438. UNICODE_STRING ValueName;
  1439. BOOLEAN SecretCreationNeeded = FALSE;
  1440. NTSTATUS NtStatus;
  1441. RtlInitUnicodeString( &ValueName, NLP_CACHE_ENCRYPTION_KEY_NAME );
  1442. NtStatus = I_LsarOpenSecret(NtLmGlobalPolicyHandle,
  1443. (PLSAPR_UNICODE_STRING) &ValueName,
  1444. SECRET_QUERY_VALUE | SECRET_SET_VALUE,
  1445. &SecretHandle
  1446. );
  1447. if (!NT_SUCCESS(NtStatus)) {
  1448. //
  1449. // create new key, if not present.
  1450. //
  1451. if (NtStatus != STATUS_OBJECT_NAME_NOT_FOUND) {
  1452. return (NtStatus);
  1453. }
  1454. NtStatus = I_LsarCreateSecret(NtLmGlobalPolicyHandle,
  1455. (PLSAPR_UNICODE_STRING) &ValueName,
  1456. SECRET_SET_VALUE,
  1457. &SecretHandle
  1458. );
  1459. if (!NT_SUCCESS(NtStatus)) {
  1460. return (NtStatus);
  1461. }
  1462. SecretCreationNeeded = TRUE;
  1463. } else {
  1464. //
  1465. // query current value...
  1466. //
  1467. LARGE_INTEGER
  1468. CurrentTime;
  1469. PLSAPR_CR_CIPHER_VALUE CurrentSecret = NULL;
  1470. NtStatus = I_LsarQuerySecret(SecretHandle,
  1471. &CurrentSecret,
  1472. &CurrentTime,
  1473. NULL,
  1474. NULL
  1475. );
  1476. if (NT_SUCCESS( NtStatus ) ) {
  1477. if( CurrentSecret == NULL ) {
  1478. //
  1479. // non existing data, create it.
  1480. //
  1481. SecretCreationNeeded = TRUE;
  1482. } else {
  1483. //
  1484. // size of data is wrong, bail now and leave things as-is.
  1485. //
  1486. if( CurrentSecret->Length != sizeof( NlpCacheEncryptionKey ) ) {
  1487. NtStatus = STATUS_SECRET_TOO_LONG;
  1488. } else {
  1489. //
  1490. // capture existing data into global.
  1491. //
  1492. CopyMemory( NlpCacheEncryptionKey, CurrentSecret->Buffer, CurrentSecret->Length );
  1493. }
  1494. RtlSecureZeroMemory(CurrentSecret->Buffer, CurrentSecret->Length);
  1495. MIDL_user_free(CurrentSecret);
  1496. }
  1497. }
  1498. }
  1499. if ( SecretCreationNeeded ) {
  1500. LSAPR_CR_CIPHER_VALUE SecretValue;
  1501. NtStatus = SspGenerateRandomBits( NlpCacheEncryptionKey, sizeof(NlpCacheEncryptionKey) );
  1502. if (NT_SUCCESS(NtStatus)) {
  1503. //
  1504. // write out secret...
  1505. //
  1506. SecretValue.Length = sizeof(NlpCacheEncryptionKey);
  1507. SecretValue.MaximumLength = SecretValue.Length;
  1508. SecretValue.Buffer = (PBYTE)NlpCacheEncryptionKey;
  1509. NtStatus = I_LsarSetSecret(SecretHandle,
  1510. &SecretValue,
  1511. NULL
  1512. );
  1513. }
  1514. }
  1515. I_LsarClose( &SecretHandle );
  1516. return (NtStatus);
  1517. }
  1518. BOOLEAN
  1519. NlpCompareCacheEntry(
  1520. IN PLOGON_CACHE_ENTRY CacheEntry1,
  1521. IN ULONG EntrySize1,
  1522. IN PLOGON_CACHE_ENTRY CacheEntry2,
  1523. IN ULONG EntrySize2
  1524. )
  1525. /*++
  1526. Routine Description:
  1527. Compare two in-memory cache entries, for the purpose of avoiding
  1528. un-necessary cache updates.
  1529. Certain fields are not taken into account during the compare,
  1530. ie: the Random encryption key.
  1531. --*/
  1532. {
  1533. LARGE_INTEGER Time1;
  1534. LARGE_INTEGER Time2;
  1535. CHAR RandomKey1[16];
  1536. CHAR RandomKey2[16];
  1537. CHAR MAC1[16];
  1538. CHAR MAC2[16];
  1539. BOOLEAN fEqual = FALSE;
  1540. if ( EntrySize1 != EntrySize2 )
  1541. {
  1542. return FALSE;
  1543. }
  1544. if ( CacheEntry1->Revision != CacheEntry2->Revision )
  1545. {
  1546. return FALSE;
  1547. }
  1548. //
  1549. // scoop up the current values of the 'volatile' fields,
  1550. // whack them to zero,
  1551. // do the memory compare,
  1552. // put the saved values back.
  1553. //
  1554. ASSERT(( sizeof(RandomKey1) == sizeof(CacheEntry1->RandomKey) ));
  1555. ASSERT(( sizeof(MAC1) == sizeof(CacheEntry1->MAC) ));
  1556. ASSERT(( sizeof(Time1) == sizeof(CacheEntry1->Time) ));
  1557. Time1 = CacheEntry1->Time;
  1558. Time2 = CacheEntry2->Time;
  1559. RtlZeroMemory(&CacheEntry1->Time, sizeof(CacheEntry1->Time));
  1560. RtlZeroMemory(&CacheEntry2->Time, sizeof(CacheEntry2->Time));
  1561. RtlCopyMemory(RandomKey1, CacheEntry1->RandomKey, sizeof(RandomKey1));
  1562. RtlCopyMemory(RandomKey2, CacheEntry2->RandomKey, sizeof(RandomKey2));
  1563. ZeroMemory(CacheEntry1->RandomKey, sizeof(CacheEntry1->RandomKey));
  1564. ZeroMemory(CacheEntry2->RandomKey, sizeof(CacheEntry2->RandomKey));
  1565. RtlCopyMemory(MAC1, CacheEntry1->MAC, sizeof(MAC1));
  1566. RtlCopyMemory(MAC2, CacheEntry2->MAC, sizeof(MAC2));
  1567. ZeroMemory(CacheEntry1->MAC, sizeof(CacheEntry1->MAC));
  1568. ZeroMemory(CacheEntry2->MAC, sizeof(CacheEntry2->MAC));
  1569. if ( memcmp(CacheEntry1, CacheEntry2, EntrySize1) == 0 )
  1570. {
  1571. fEqual = TRUE;
  1572. }
  1573. CacheEntry1->Time = Time1;
  1574. CacheEntry2->Time = Time2;
  1575. RtlCopyMemory(CacheEntry1->RandomKey, RandomKey1, sizeof(RandomKey1));
  1576. RtlCopyMemory(CacheEntry2->RandomKey, RandomKey2, sizeof(RandomKey2));
  1577. RtlCopyMemory(CacheEntry1->MAC, MAC1, sizeof(MAC1));
  1578. RtlCopyMemory(CacheEntry2->MAC, MAC2, sizeof(MAC2));
  1579. return fEqual;
  1580. }
  1581. NTSTATUS
  1582. NlpEncryptCacheEntry(
  1583. IN PLOGON_CACHE_ENTRY CacheEntry,
  1584. IN ULONG EntrySize
  1585. )
  1586. /*++
  1587. Routine Description:
  1588. Encrypts the sensitive portions of the input CacheEntry.
  1589. --*/
  1590. {
  1591. HMACMD5_CTX hmacCtx;
  1592. RC4_KEYSTRUCT rc4key;
  1593. CHAR DerivedKey[ MD5DIGESTLEN ];
  1594. PBYTE pbData;
  1595. ULONG cbData;
  1596. if( CacheEntry->Revision < NLP_CACHE_REVISION_NT_5_0 ) {
  1597. return STATUS_SUCCESS;
  1598. }
  1599. //
  1600. // derive encryption key from global machine LSA secret, and random
  1601. // cache entry key.
  1602. //
  1603. HMACMD5Init(&hmacCtx, (PUCHAR) NlpCacheEncryptionKey, sizeof(NlpCacheEncryptionKey));
  1604. HMACMD5Update(&hmacCtx, (PUCHAR) CacheEntry->RandomKey, sizeof(CacheEntry->RandomKey));
  1605. HMACMD5Final(&hmacCtx, (PUCHAR) DerivedKey);
  1606. //
  1607. // begin encrypting at the cachepasswords field.
  1608. //
  1609. pbData = (PBYTE) &(CacheEntry->CachePasswords);
  1610. //
  1611. // data length is EntrySize - header up to CachePasswords.
  1612. //
  1613. cbData = EntrySize - (ULONG)( pbData - (PBYTE)CacheEntry );
  1614. //
  1615. // MAC the data for integrity checking.
  1616. //
  1617. HMACMD5Init(&hmacCtx, (PUCHAR) DerivedKey, sizeof(DerivedKey));
  1618. HMACMD5Update(&hmacCtx, pbData, cbData);
  1619. HMACMD5Final(&hmacCtx, (PUCHAR) CacheEntry->MAC);
  1620. //
  1621. // now encrypt it...
  1622. //
  1623. rc4_key( &rc4key, sizeof(DerivedKey), (PUCHAR) DerivedKey );
  1624. rc4( &rc4key, cbData, pbData );
  1625. RtlSecureZeroMemory( DerivedKey, sizeof(DerivedKey) );
  1626. return STATUS_SUCCESS;
  1627. }
  1628. NTSTATUS
  1629. NlpDecryptCacheEntry(
  1630. IN PLOGON_CACHE_ENTRY CacheEntry,
  1631. IN ULONG EntrySize
  1632. )
  1633. /*++
  1634. Routine Description:
  1635. Decrypts the sensitive portions of the input CacheEntry, and verified
  1636. integrity of decrypted data.
  1637. --*/
  1638. {
  1639. HMACMD5_CTX hmacCtx;
  1640. RC4_KEYSTRUCT rc4key;
  1641. CHAR DerivedKey[ MD5DIGESTLEN ];
  1642. CHAR MAC[ MD5DIGESTLEN ];
  1643. PBYTE pbData;
  1644. ULONG cbData;
  1645. if( CacheEntry->Revision < NLP_CACHE_REVISION_NT_5_0 ) {
  1646. return STATUS_SUCCESS;
  1647. }
  1648. //
  1649. // derive encryption key from global machine LSA secret, and random
  1650. // cache entry key.
  1651. //
  1652. HMACMD5Init(&hmacCtx, (PUCHAR) NlpCacheEncryptionKey, sizeof(NlpCacheEncryptionKey));
  1653. HMACMD5Update(&hmacCtx, (PUCHAR) CacheEntry->RandomKey, sizeof(CacheEntry->RandomKey));
  1654. HMACMD5Final(&hmacCtx, (PUCHAR) DerivedKey);
  1655. //
  1656. // begin decrypting at the cachepasswords field.
  1657. //
  1658. pbData = (PBYTE)&(CacheEntry->CachePasswords);
  1659. //
  1660. // data length is EntrySize - header up to CachePasswords.
  1661. //
  1662. cbData = EntrySize - (ULONG)( pbData - (PBYTE)CacheEntry );
  1663. //
  1664. // now decrypt it...
  1665. //
  1666. rc4_key( &rc4key, sizeof(DerivedKey), (PUCHAR) DerivedKey );
  1667. rc4( &rc4key, cbData, pbData );
  1668. //
  1669. // compute MAC on decrypted data for integrity checking.
  1670. //
  1671. HMACMD5Init(&hmacCtx, (PUCHAR) DerivedKey, sizeof(DerivedKey));
  1672. HMACMD5Update(&hmacCtx, pbData, cbData);
  1673. HMACMD5Final(&hmacCtx, (PUCHAR) MAC);
  1674. RtlSecureZeroMemory( DerivedKey, sizeof(DerivedKey) );
  1675. //
  1676. // verify MAC.
  1677. //
  1678. if ( memcmp( MAC, CacheEntry->MAC, sizeof(MAC) ) != 0 ) {
  1679. return STATUS_LOGON_FAILURE;
  1680. }
  1681. return STATUS_SUCCESS;
  1682. }
  1683. NTSTATUS
  1684. NlpBuildCacheEntry(
  1685. IN PNETLOGON_INTERACTIVE_INFO LogonInfo,
  1686. IN PNETLOGON_VALIDATION_SAM_INFO4 AccountInfo,
  1687. IN ULONG CacheFlags,
  1688. IN ULONG SupplementalCacheDataLength,
  1689. IN PBYTE SupplementalCacheData,
  1690. OUT PLOGON_CACHE_ENTRY* ppCacheEntry,
  1691. OUT PULONG pEntryLength
  1692. )
  1693. /*++
  1694. Routine Description:
  1695. Builds a LOGON_CACHE_ENTRY from a NETLOGON_VALIDATION_SAM_INFO4 structure.
  1696. We only cache those fields that we cannot easily re-invent
  1697. Arguments:
  1698. LogonInfo - pointer to NETLOGON_INTERACTIVE_INFO structure containing
  1699. user's name and logon domain name
  1700. AccountInfo - pointer to NETLOGON_VALIDATION_SAM_INFO4 from successful
  1701. logon
  1702. CacheFlags - cache flags
  1703. SupplementalCacheDataLength - supplemental cache data length
  1704. SupplementalCacheData - supplemental cache data
  1705. ppCacheEntry - pointer to place to return pointer to allocated
  1706. LOGON_CACHE_ENTRY
  1707. pEntryLength - size of the buffer returned in *ppCacheEntry
  1708. Return Value:
  1709. NTSTATUS
  1710. Success = STATUS_SUCCESS
  1711. *ppCacheEntry contains pointer to allocated LOGON_CACHE_ENTRY
  1712. structure
  1713. Failure = STATUS_NO_MEMORY
  1714. *ppCacheEntry undefined
  1715. --*/
  1716. {
  1717. PLOGON_CACHE_ENTRY pEntry = NULL;
  1718. ULONG EntryLength = 0;
  1719. ULONG length;
  1720. PCHAR dataptr;
  1721. UNICODE_STRING SamAccountName;
  1722. UNICODE_STRING NetbiosDomainName;
  1723. UNICODE_STRING DnsDomainName;
  1724. UNICODE_STRING Upn;
  1725. NTSTATUS Status = STATUS_SUCCESS;
  1726. UNICODE_STRING UserNameToUse = {0};
  1727. UNICODE_STRING DomainNameToUse = {0};
  1728. UNICODE_STRING UpnForMitUser = {0};
  1729. *ppCacheEntry = NULL;
  1730. *pEntryLength = 0;
  1731. //
  1732. // Grab the various forms of the account name
  1733. //
  1734. NlpGetAccountNames( &LogonInfo->Identity,
  1735. AccountInfo,
  1736. &SamAccountName,
  1737. &NetbiosDomainName,
  1738. &DnsDomainName,
  1739. &Upn );
  1740. //
  1741. // two kinds of cached logonon: 1) UPN logon looked up by UPNs, 2) non UPN
  1742. // logon looked up by LogonInfo UserName and NetbiosDomainName or DNS doman
  1743. // name.
  1744. //
  1745. // Cache entries created by kerberos for MIT princ cached logon should
  1746. // have DnsDomainName populated as MIT realm name (FQDN) from kerberos
  1747. // primary credential. In the authorization data LogonDomainName refers
  1748. // to MS domain where the PAC is created while DnsLogonDomainName refers
  1749. // to the MIT realm
  1750. //
  1751. if (CacheFlags & MSV1_0_CACHE_LOGON_REQUEST_MIT_LOGON) {
  1752. Status = NlpGetCredentialNamesFromMitCacheSupplementalCacheData(
  1753. SupplementalCacheDataLength,
  1754. SupplementalCacheData,
  1755. &DomainNameToUse,
  1756. &UserNameToUse
  1757. );
  1758. if (!NT_SUCCESS(Status)) {
  1759. goto Cleanup;
  1760. }
  1761. if (!RtlEqualUnicodeString(
  1762. &DomainNameToUse,
  1763. &DnsDomainName,
  1764. FALSE // case sensitive
  1765. )) {
  1766. Status = STATUS_INVALID_PARAMETER;
  1767. goto Cleanup;
  1768. }
  1769. if (Upn.Length == 0) { // construct an UPN
  1770. UpnForMitUser.Length = UserNameToUse.Length + sizeof(WCHAR) + DomainNameToUse.Length;
  1771. UpnForMitUser.MaximumLength = UpnForMitUser.Length + sizeof(WCHAR);
  1772. UpnForMitUser.Buffer = NtLmAllocatePrivateHeap(UpnForMitUser.MaximumLength);
  1773. if (UpnForMitUser.Buffer == NULL) {
  1774. Status = STATUS_NO_MEMORY;
  1775. goto Cleanup;
  1776. }
  1777. RtlCopyMemory(
  1778. UpnForMitUser.Buffer,
  1779. UserNameToUse.Buffer,
  1780. UserNameToUse.Length
  1781. );
  1782. RtlCopyMemory(
  1783. ((PBYTE) UpnForMitUser.Buffer) + UserNameToUse.Length,
  1784. L"@",
  1785. sizeof(WCHAR)
  1786. );
  1787. RtlCopyMemory(
  1788. ((PBYTE) UpnForMitUser.Buffer) + UserNameToUse.Length + sizeof(WCHAR),
  1789. DomainNameToUse.Buffer,
  1790. DomainNameToUse.Length
  1791. );
  1792. Upn = UpnForMitUser;
  1793. }
  1794. RtlZeroMemory(&DomainNameToUse, sizeof(DomainNameToUse)); // no need to store dumplicate domain names
  1795. } else {
  1796. UserNameToUse = SamAccountName;
  1797. DomainNameToUse = NetbiosDomainName;
  1798. }
  1799. SspPrint((SSP_CRED,
  1800. "NlpBuildCacheEntry domain (%wZ), dnsDomain (%wZ), upn (%wZ), user (%wZ), flags %#x\n",
  1801. &DomainNameToUse,
  1802. &DnsDomainName,
  1803. &Upn,
  1804. &UserNameToUse,
  1805. CacheFlags));
  1806. //
  1807. // assumes GROUP_MEMBERSHIP is integral multiple of DWORDs
  1808. //
  1809. length = ROUND_UP_COUNT(sizeof(LOGON_CACHE_ENTRY), sizeof(ULONG))
  1810. + ROUND_UP_COUNT(DomainNameToUse.Length, sizeof(ULONG))
  1811. + ROUND_UP_COUNT(UserNameToUse.Length, sizeof(ULONG))
  1812. + ROUND_UP_COUNT(DnsDomainName.Length, sizeof(ULONG))
  1813. + ROUND_UP_COUNT(Upn.Length, sizeof(ULONG))
  1814. + ROUND_UP_COUNT(AccountInfo->EffectiveName.Length, sizeof(ULONG))
  1815. + ROUND_UP_COUNT(AccountInfo->FullName.Length, sizeof(ULONG))
  1816. + ROUND_UP_COUNT(AccountInfo->LogonScript.Length, sizeof(ULONG))
  1817. + ROUND_UP_COUNT(AccountInfo->ProfilePath.Length, sizeof(ULONG))
  1818. + ROUND_UP_COUNT(AccountInfo->HomeDirectory.Length, sizeof(ULONG))
  1819. + ROUND_UP_COUNT(AccountInfo->HomeDirectoryDrive.Length, sizeof(ULONG))
  1820. + ROUND_UP_COUNT(AccountInfo->LogonDomainName.Length, sizeof(ULONG))
  1821. + ROUND_UP_COUNT(AccountInfo->GroupCount * sizeof(GROUP_MEMBERSHIP), sizeof(ULONG))
  1822. + ROUND_UP_COUNT(RtlLengthSid(AccountInfo->LogonDomainId), sizeof(ULONG))
  1823. + ROUND_UP_COUNT(AccountInfo->LogonServer.Length, sizeof(ULONG))
  1824. + ROUND_UP_COUNT(SupplementalCacheDataLength, sizeof(ULONG));
  1825. if (AccountInfo->UserFlags & LOGON_EXTRA_SIDS) {
  1826. if (AccountInfo->SidCount) {
  1827. ULONG i;
  1828. length += ROUND_UP_COUNT(AccountInfo->SidCount * sizeof(ULONG), sizeof(ULONG));
  1829. for (i = 0; i < AccountInfo->SidCount; i++) {
  1830. length += ROUND_UP_COUNT(RtlLengthSid(AccountInfo->ExtraSids[i].Sid), sizeof(ULONG));
  1831. }
  1832. }
  1833. }
  1834. pEntry = AllocateCacheEntry(length);
  1835. if (pEntry == NULL) {
  1836. return STATUS_NO_MEMORY;
  1837. }
  1838. RtlZeroMemory( pEntry, length );
  1839. pEntry->Revision = NLP_CACHE_REVISION;
  1840. NtQuerySystemTime( &pEntry->Time );
  1841. pEntry->Valid = TRUE;
  1842. pEntry->LogonPackage = LogonInfo->Identity.ParameterControl;
  1843. dataptr = (PCHAR)(pEntry + 1);
  1844. EntryLength = length;
  1845. ASSERT(!((ULONG_PTR)dataptr & (sizeof(ULONG) - 1)));
  1846. //
  1847. // each of these (unicode) strings and other structures are copied to the
  1848. // end of the fixed LOGON_CACHE_ENTRY structure, each aligned on DWORD
  1849. // boundaries
  1850. //
  1851. length = pEntry->UserNameLength = UserNameToUse.Length;
  1852. RtlCopyMemory(dataptr, UserNameToUse.Buffer, length);
  1853. dataptr = ROUND_UP_POINTER(dataptr + length, sizeof(ULONG));
  1854. length = pEntry->DomainNameLength = DomainNameToUse.Length;
  1855. if (length) {
  1856. RtlCopyMemory(dataptr, DomainNameToUse.Buffer, length);
  1857. dataptr = ROUND_UP_POINTER(dataptr + length, sizeof(ULONG));
  1858. }
  1859. length = pEntry->DnsDomainNameLength = DnsDomainName.Length;
  1860. if (length) {
  1861. RtlCopyMemory(dataptr, DnsDomainName.Buffer, length);
  1862. dataptr = ROUND_UP_POINTER(dataptr + length, sizeof(ULONG));
  1863. }
  1864. length = pEntry->UpnLength = Upn.Length;
  1865. if (length) {
  1866. RtlCopyMemory(dataptr, Upn.Buffer, length);
  1867. dataptr = ROUND_UP_POINTER(dataptr + length, sizeof(ULONG));
  1868. }
  1869. length = pEntry->EffectiveNameLength = AccountInfo->EffectiveName.Length;
  1870. if (length) {
  1871. RtlCopyMemory(dataptr, AccountInfo->EffectiveName.Buffer, length);
  1872. dataptr = ROUND_UP_POINTER(dataptr + length, sizeof(ULONG));
  1873. }
  1874. length = pEntry->FullNameLength = AccountInfo->FullName.Length;
  1875. if (length) {
  1876. RtlCopyMemory(dataptr, AccountInfo->FullName.Buffer, length);
  1877. dataptr = ROUND_UP_POINTER(dataptr + length, sizeof(ULONG));
  1878. }
  1879. length = pEntry->LogonScriptLength = AccountInfo->LogonScript.Length;
  1880. if (length) {
  1881. RtlCopyMemory(dataptr, AccountInfo->LogonScript.Buffer, length);
  1882. dataptr = ROUND_UP_POINTER(dataptr + length, sizeof(ULONG));
  1883. }
  1884. length = pEntry->ProfilePathLength = AccountInfo->ProfilePath.Length;
  1885. if (length) {
  1886. RtlCopyMemory(dataptr, AccountInfo->ProfilePath.Buffer, length);
  1887. dataptr = ROUND_UP_POINTER(dataptr + length, sizeof(ULONG));
  1888. }
  1889. length = pEntry->HomeDirectoryLength = AccountInfo->HomeDirectory.Length;
  1890. if (length) {
  1891. RtlCopyMemory(dataptr, AccountInfo->HomeDirectory.Buffer, length);
  1892. dataptr = ROUND_UP_POINTER(dataptr + length, sizeof(ULONG));
  1893. }
  1894. length = pEntry->HomeDirectoryDriveLength = AccountInfo->HomeDirectoryDrive.Length;
  1895. if (length) {
  1896. RtlCopyMemory(dataptr, AccountInfo->HomeDirectoryDrive.Buffer, length);
  1897. dataptr = ROUND_UP_POINTER(dataptr + length, sizeof(ULONG));
  1898. }
  1899. pEntry->UserId = AccountInfo->UserId;
  1900. pEntry->PrimaryGroupId = AccountInfo->PrimaryGroupId;
  1901. length = pEntry->GroupCount = AccountInfo->GroupCount;
  1902. length *= sizeof(GROUP_MEMBERSHIP);
  1903. if (length) {
  1904. RtlCopyMemory(dataptr, AccountInfo->GroupIds, length);
  1905. dataptr = ROUND_UP_POINTER(dataptr + length, sizeof(ULONG));
  1906. }
  1907. length = pEntry->LogonDomainNameLength = AccountInfo->LogonDomainName.Length;
  1908. if (length) {
  1909. RtlCopyMemory(dataptr, AccountInfo->LogonDomainName.Buffer, length);
  1910. dataptr = ROUND_UP_POINTER(dataptr + length, sizeof(ULONG));
  1911. }
  1912. if (AccountInfo->UserFlags & LOGON_EXTRA_SIDS) {
  1913. length = pEntry->SidCount = AccountInfo->SidCount;
  1914. length *= sizeof(ULONG);
  1915. if (length) {
  1916. ULONG i, sidLength;
  1917. PULONG sidAttributes = (PULONG) dataptr;
  1918. dataptr = ROUND_UP_POINTER(dataptr + length, sizeof(ULONG));
  1919. //
  1920. // Now copy over all the SIDs
  1921. //
  1922. for (i = 0; i < AccountInfo->SidCount ; i++ ) {
  1923. sidAttributes[i] = AccountInfo->ExtraSids[i].Attributes;
  1924. sidLength = RtlLengthSid(AccountInfo->ExtraSids[i].Sid);
  1925. RtlCopySid(sidLength,(PSID) dataptr, AccountInfo->ExtraSids[i].Sid);
  1926. dataptr = ROUND_UP_POINTER(dataptr + sidLength, sizeof(ULONG));
  1927. }
  1928. pEntry->SidLength = (ULONG) (dataptr - (PCHAR) sidAttributes);
  1929. } else {
  1930. pEntry->SidLength = 0;
  1931. }
  1932. } else {
  1933. pEntry->SidCount = 0;
  1934. pEntry->SidLength = 0;
  1935. }
  1936. length = pEntry->LogonDomainIdLength = (USHORT) RtlLengthSid(AccountInfo->LogonDomainId);
  1937. Status = RtlCopySid(pEntry->LogonDomainIdLength,
  1938. (PSID)dataptr,
  1939. AccountInfo->LogonDomainId
  1940. );
  1941. if (!NT_SUCCESS(Status)) {
  1942. goto Cleanup;
  1943. }
  1944. dataptr = ROUND_UP_POINTER(dataptr + length, sizeof(ULONG));
  1945. //
  1946. // copy in the LogonServer
  1947. //
  1948. length = pEntry->LogonServerLength = AccountInfo->LogonServer.Length;
  1949. if (length) {
  1950. RtlCopyMemory(dataptr, AccountInfo->LogonServer.Buffer, length);
  1951. dataptr = ROUND_UP_POINTER(dataptr + length, sizeof(ULONG));
  1952. }
  1953. length = pEntry->SupplementalCacheDataLength = SupplementalCacheDataLength;
  1954. pEntry->SupplementalCacheDataOffset = RtlPointerToOffset(pEntry, dataptr);
  1955. if (length) {
  1956. RtlCopyMemory(dataptr, SupplementalCacheData, length);
  1957. dataptr = ROUND_UP_POINTER(dataptr + length, sizeof(ULONG));
  1958. }
  1959. //
  1960. // fill in randomkey for this cache entry.
  1961. //
  1962. SspGenerateRandomBits( pEntry->RandomKey, sizeof(pEntry->RandomKey) );
  1963. pEntry->CacheFlags = CacheFlags;
  1964. #if DBG
  1965. if (DumpCacheInfo) {
  1966. DbgPrint("BuildCacheEntry:\n");
  1967. DumpCacheEntry(999, pEntry);
  1968. }
  1969. #endif
  1970. *ppCacheEntry = pEntry;
  1971. *pEntryLength = EntryLength;
  1972. pEntry = NULL;
  1973. Cleanup:
  1974. if (pEntry) {
  1975. FreeCacheEntry(pEntry);
  1976. }
  1977. if (UpnForMitUser.Buffer) {
  1978. NtLmFreePrivateHeap(UpnForMitUser.Buffer);
  1979. }
  1980. return Status;
  1981. }
  1982. NTSTATUS
  1983. NlpOpenCache( VOID )
  1984. /*++
  1985. Routine Description:
  1986. Opens the registry node for read or write (depending on Switch) and opens
  1987. the secret storage in the same mode. If successful, the NlpCacheHandle
  1988. is valid.
  1989. Arguments:
  1990. Return Value:
  1991. NTSTATUS
  1992. Success = STATUS_SUCCESS
  1993. NlpCacheHandle contains handle to use for reading/writing
  1994. registry
  1995. Failure =
  1996. --*/
  1997. {
  1998. NTSTATUS NtStatus;
  1999. ULONG Disposition;
  2000. OBJECT_ATTRIBUTES ObjectAttributes;
  2001. UNICODE_STRING ObjectName;
  2002. ObjectName.Length = ObjectName.MaximumLength = CACHE_NAME_SIZE;
  2003. ObjectName.Buffer = CACHE_NAME;
  2004. InitializeObjectAttributes(&ObjectAttributes,
  2005. &ObjectName,
  2006. OBJ_CASE_INSENSITIVE,
  2007. 0, // RootDirectory
  2008. NULL // default is reasonable from SYSTEM context
  2009. );
  2010. NtStatus = NtCreateKey(&NlpCacheHandle,
  2011. (KEY_WRITE | KEY_READ),
  2012. &ObjectAttributes,
  2013. CACHE_TITLE_INDEX,
  2014. NULL, // class name
  2015. 0, // create options
  2016. &Disposition
  2017. );
  2018. return NtStatus;
  2019. }
  2020. VOID
  2021. NlpCloseCache( VOID )
  2022. /*++
  2023. Routine Description:
  2024. Closes handles opened by NlpOpenCache
  2025. Arguments:
  2026. None.
  2027. Return Value:
  2028. None.
  2029. --*/
  2030. {
  2031. #if DBG
  2032. NTSTATUS NtStatus;
  2033. if (DumpCacheInfo) {
  2034. DbgPrint("CloseCache: Closing NlpCacheHandle (%#08x)\n", NlpCacheHandle);
  2035. }
  2036. if (IS_VALID_HANDLE(NlpCacheHandle)) {
  2037. NtStatus = NtClose(NlpCacheHandle);
  2038. if (DumpCacheInfo) {
  2039. DbgPrint("CloseCache: NtClose returns %#08x\n", NtStatus);
  2040. }
  2041. ASSERT(NT_SUCCESS(NtStatus));
  2042. INVALIDATE_HANDLE(NlpCacheHandle);
  2043. }
  2044. #else
  2045. if (IS_VALID_HANDLE(NlpCacheHandle)) {
  2046. NtClose(NlpCacheHandle);
  2047. INVALIDATE_HANDLE(NlpCacheHandle);
  2048. }
  2049. #endif
  2050. }
  2051. NTSTATUS
  2052. NlpOpenSecret(
  2053. IN ULONG Index
  2054. )
  2055. /*++
  2056. Routine Description:
  2057. Opens a cache entry's secret storage object for read (in order to LsaQuerySecret) and
  2058. write (in order to LsaSetSecret). If successful, the handle value
  2059. is placed in the global variable NlpSecretHandle.
  2060. If the secret does not exist, it will be created.
  2061. Arguments:
  2062. Index - The index of the entry being opened. This is used to build
  2063. a name of the object.
  2064. Return Value:
  2065. NTSTATUS
  2066. Success = STATUS_SUCCESS
  2067. NlpSecretHandle can be used to read/write secret storage
  2068. Failure =
  2069. --*/
  2070. {
  2071. NTSTATUS
  2072. NtStatus;
  2073. UNICODE_STRING
  2074. ValueName;
  2075. WCHAR
  2076. NameBuffer[32];
  2077. //
  2078. // Close previous handle if necessary
  2079. //
  2080. if (IS_VALID_HANDLE(NlpSecretHandle)) {
  2081. I_LsarClose( &NlpSecretHandle );
  2082. }
  2083. ValueName.Buffer = &NameBuffer[0];
  2084. ValueName.MaximumLength = 32;
  2085. ValueName.Length = 0;
  2086. NlpMakeCacheEntryName( Index, &ValueName );
  2087. NtStatus = I_LsarOpenSecret(NtLmGlobalPolicyHandle,
  2088. (PLSAPR_UNICODE_STRING) &ValueName,
  2089. SECRET_QUERY_VALUE | SECRET_SET_VALUE,
  2090. &NlpSecretHandle
  2091. );
  2092. if (!NT_SUCCESS(NtStatus)) {
  2093. if (NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) {
  2094. NtStatus = I_LsarCreateSecret(NtLmGlobalPolicyHandle,
  2095. (PLSAPR_UNICODE_STRING) &ValueName,
  2096. SECRET_SET_VALUE | SECRET_QUERY_VALUE,
  2097. &NlpSecretHandle
  2098. );
  2099. if (!NT_SUCCESS(NtStatus)) {
  2100. INVALIDATE_HANDLE(NlpSecretHandle);
  2101. }
  2102. } else {
  2103. INVALIDATE_HANDLE(NlpSecretHandle);
  2104. }
  2105. }
  2106. return(NtStatus);
  2107. }
  2108. VOID
  2109. NlpCloseSecret( VOID )
  2110. /*++
  2111. Routine Description:
  2112. Closes the handles opened via NlpOpenSecret
  2113. Arguments:
  2114. None.
  2115. Return Value:
  2116. None.
  2117. --*/
  2118. {
  2119. NTSTATUS
  2120. NtStatus;
  2121. if (IS_VALID_HANDLE(NlpSecretHandle)) {
  2122. NtStatus = I_LsarClose(&NlpSecretHandle);
  2123. #if DBG
  2124. if (DumpCacheInfo) {
  2125. DbgPrint("CloseSecret: LsaClose returns %#08x\n", NtStatus);
  2126. }
  2127. #endif
  2128. ASSERT(NT_SUCCESS(NtStatus));
  2129. INVALIDATE_HANDLE(NlpSecretHandle);
  2130. }
  2131. }
  2132. NTSTATUS
  2133. NlpWriteSecret(
  2134. IN PLSAPR_CR_CIPHER_VALUE NewSecret,
  2135. IN PLSAPR_CR_CIPHER_VALUE OldSecret
  2136. )
  2137. /*++
  2138. Routine Description:
  2139. Writes the password (and optionally the previous password) to the LSA
  2140. secret storage
  2141. Arguments:
  2142. NewSecret - pointer to UNICODE_STRING containing current password
  2143. OldSecret - pointer to UNICODE_STRING containing previous password
  2144. Return Value:
  2145. NTSTATUS
  2146. Success =
  2147. Failure =
  2148. --*/
  2149. {
  2150. return I_LsarSetSecret(NlpSecretHandle, NewSecret, OldSecret);
  2151. }
  2152. NTSTATUS
  2153. NlpReadSecret(
  2154. OUT PLSAPR_CR_CIPHER_VALUE * NewSecret,
  2155. OUT PLSAPR_CR_CIPHER_VALUE * OldSecret
  2156. )
  2157. /*++
  2158. Routine Description:
  2159. Reads the new and old secrets (UNICODE_STRINGs) for the
  2160. currently open LSA secret
  2161. The Lsa routine returns us pointers to UNICODE strings
  2162. Arguments:
  2163. NewSecret - pointer to returned pointer to UNICODE_STRING containing
  2164. most recent password (if any)
  2165. OldSecret - pointer to returned pointer to UNICODE_STRING containing
  2166. previous password (if any)
  2167. Return Value:
  2168. NTSTATUS
  2169. Success
  2170. Failure
  2171. --*/
  2172. {
  2173. NTSTATUS
  2174. NtStatus;
  2175. LARGE_INTEGER
  2176. NewTime,
  2177. OldTime;
  2178. NtStatus = I_LsarQuerySecret(NlpSecretHandle,
  2179. NewSecret,
  2180. &NewTime,
  2181. OldSecret,
  2182. &OldTime
  2183. );
  2184. #if DBG
  2185. {
  2186. char newNt[80];
  2187. char newLm[80];
  2188. char oldNt[80];
  2189. char oldLm[80];
  2190. if (DumpCacheInfo) {
  2191. DbgPrint("NlpReadSecret: NewSecret.Nt = \"%s\"\n"
  2192. " NewSecret.Lm = \"%s\"\n"
  2193. " OldSecret.Nt = \"%s\"\n"
  2194. " OldSecret.Lm = \"%s\"\n",
  2195. *NewSecret
  2196. ? DumpOwfPasswordToString(newNt, (PLM_OWF_PASSWORD)((*NewSecret)->Buffer))
  2197. : "",
  2198. *NewSecret
  2199. ? DumpOwfPasswordToString(newLm, (PLM_OWF_PASSWORD)((*NewSecret)->Buffer)+1)
  2200. : "",
  2201. *OldSecret
  2202. ? DumpOwfPasswordToString(oldNt, (PLM_OWF_PASSWORD)((*OldSecret)->Buffer))
  2203. : "",
  2204. *OldSecret
  2205. ? DumpOwfPasswordToString(oldLm, (PLM_OWF_PASSWORD)((*OldSecret)->Buffer)+1)
  2206. : ""
  2207. );
  2208. }
  2209. }
  2210. #endif
  2211. return NtStatus;
  2212. }
  2213. NTSTATUS
  2214. NlpComputeSaltedHashedPassword(
  2215. OUT PNT_OWF_PASSWORD SaltedOwfPassword,
  2216. IN PNT_OWF_PASSWORD OwfPassword,
  2217. IN PUNICODE_STRING UserName
  2218. )
  2219. /*++
  2220. Routine Description:
  2221. Computes the salted hash of a password by concatenating the user name
  2222. with the OWF and computing the OWF of the combination.
  2223. Arguments:
  2224. SaltedOwfPassword - receives the LM or NT salted password/
  2225. OwfPassword - Contains the NT or LM owf password.
  2226. UserName - Contains the name of the user, used for salt.
  2227. Return Value:
  2228. NTSTATUS
  2229. Success = STATUS_SUCCESS
  2230. Passwords created OK
  2231. Failure = STATUS_NO_MEMORY
  2232. Not enough storage to create Passwords
  2233. --*/
  2234. {
  2235. NTSTATUS Status;
  2236. UNICODE_STRING TempString;
  2237. UNICODE_STRING LowerUserName;
  2238. //
  2239. // Compute the lower case user name.
  2240. //
  2241. Status = RtlDowncaseUnicodeString( &LowerUserName,
  2242. UserName,
  2243. TRUE );
  2244. if ( !NT_SUCCESS(Status)) {
  2245. return Status;
  2246. }
  2247. //
  2248. // Build a string that is a concatenation of the OWF and LowerCase username.
  2249. //
  2250. TempString.Length = TempString.MaximumLength = LowerUserName.Length + sizeof(NT_OWF_PASSWORD);
  2251. TempString.Buffer = AllocateFromHeap( TempString.Length );
  2252. if (TempString.Buffer == NULL) {
  2253. RtlFreeUnicodeString( &LowerUserName );
  2254. return(STATUS_INSUFFICIENT_RESOURCES);
  2255. }
  2256. RtlCopyMemory(
  2257. TempString.Buffer,
  2258. OwfPassword,
  2259. sizeof(NT_OWF_PASSWORD) );
  2260. RtlCopyMemory(
  2261. (PUCHAR) TempString.Buffer + sizeof(NT_OWF_PASSWORD),
  2262. LowerUserName.Buffer,
  2263. LowerUserName.Length );
  2264. //
  2265. // The Salted hash is the OWF of that.
  2266. //
  2267. Status = RtlCalculateNtOwfPassword(
  2268. &TempString,
  2269. SaltedOwfPassword
  2270. );
  2271. FreeToHeap(TempString.Buffer);
  2272. RtlFreeUnicodeString( &LowerUserName );
  2273. return(Status);
  2274. }
  2275. NTSTATUS
  2276. NlpMakeSecretPassword(
  2277. OUT PLSAPR_CR_CIPHER_VALUE Passwords,
  2278. IN PUNICODE_STRING UserName,
  2279. IN PNT_OWF_PASSWORD NtOwfPassword OPTIONAL,
  2280. IN PLM_OWF_PASSWORD LmOwfPassword OPTIONAL
  2281. )
  2282. /*++
  2283. Routine Description:
  2284. Converts a (fixed length structure) NT_OWF_PASSWORD and a LM_OWF_PASSWORD
  2285. to a UNICODE_STRING. Allocates memory for the unicode string in this function
  2286. The calling function must free up the string buffer allocated in this routine.
  2287. The caller uses FreeToHeap (RtlFreeHeap)
  2288. Arguments:
  2289. Passwords - returned UNICODE_STRING which actually contains a
  2290. CACHE_PASSWORDS structure
  2291. NtOwfPassword - pointer to encrypted, fixed-length NT password
  2292. LmOwfPassword - pointer to encrypted, fixed-length LM password
  2293. Return Value:
  2294. NTSTATUS
  2295. Success = STATUS_SUCCESS
  2296. Passwords created OK
  2297. Failure = STATUS_NO_MEMORY
  2298. Not enough storage to create Passwords
  2299. --*/
  2300. {
  2301. NTSTATUS Status = STATUS_SUCCESS;
  2302. PCACHE_PASSWORDS pwd;
  2303. Passwords->Buffer = NULL;
  2304. pwd = (PCACHE_PASSWORDS)AllocateFromHeap(sizeof(*pwd));
  2305. if (pwd == NULL) {
  2306. return STATUS_NO_MEMORY;
  2307. }
  2308. //
  2309. // concatenate the fixed length NT_OWF_PASSWORD and LM_OWF_PASSWORD structures
  2310. // into a buffer which we then use as a UNICODE_STRING
  2311. //
  2312. if (ARGUMENT_PRESENT(NtOwfPassword)) {
  2313. Status = NlpComputeSaltedHashedPassword(
  2314. &pwd->SecretPasswords.NtOwfPassword,
  2315. NtOwfPassword,
  2316. UserName
  2317. );
  2318. if (!NT_SUCCESS(Status)) {
  2319. goto Cleanup;
  2320. }
  2321. pwd->SecretPasswords.NtPasswordPresent = TRUE;
  2322. } else {
  2323. RtlZeroMemory((PVOID)&pwd->SecretPasswords.NtOwfPassword,
  2324. sizeof(pwd->SecretPasswords.NtOwfPassword)
  2325. );
  2326. pwd->SecretPasswords.NtPasswordPresent = FALSE;
  2327. }
  2328. if (ARGUMENT_PRESENT(LmOwfPassword)) {
  2329. Status = NlpComputeSaltedHashedPassword(
  2330. &pwd->SecretPasswords.LmOwfPassword,
  2331. LmOwfPassword,
  2332. UserName
  2333. );
  2334. if (!NT_SUCCESS(Status)) {
  2335. goto Cleanup;
  2336. }
  2337. pwd->SecretPasswords.LmPasswordPresent = TRUE;
  2338. } else {
  2339. RtlZeroMemory((PVOID)&pwd->SecretPasswords.LmOwfPassword,
  2340. sizeof(pwd->SecretPasswords.LmOwfPassword)
  2341. );
  2342. pwd->SecretPasswords.LmPasswordPresent = FALSE;
  2343. }
  2344. Passwords->Length = Passwords->MaximumLength = sizeof(*pwd);
  2345. Passwords->Buffer = (PUCHAR)pwd;
  2346. Cleanup:
  2347. if( !NT_SUCCESS( Status ) ) {
  2348. if( pwd != NULL )
  2349. FreeToHeap( pwd );
  2350. }
  2351. return Status;
  2352. }
  2353. NTSTATUS
  2354. NlpMakeSecretPasswordNT5(
  2355. IN OUT PCACHE_PASSWORDS Passwords,
  2356. IN PUNICODE_STRING UserName,
  2357. IN PNT_OWF_PASSWORD NtOwfPassword OPTIONAL,
  2358. IN PLM_OWF_PASSWORD LmOwfPassword OPTIONAL
  2359. )
  2360. /*++
  2361. Routine Description:
  2362. Populates CACHE_PASSWORDS structure with salted forms of NtOwfPassword
  2363. and LmOwfPassword.
  2364. Arguments:
  2365. Passwords - populated CACHE_PASSWORDS structure.
  2366. NtOwfPassword - pointer to encrypted, fixed-length NT password
  2367. LmOwfPassword - pointer to encrypted, fixed-length LM password
  2368. Return Value:
  2369. NTSTATUS
  2370. Success = STATUS_SUCCESS
  2371. Passwords created OK
  2372. Failure = STATUS_NO_MEMORY
  2373. Not enough storage to create Passwords
  2374. --*/
  2375. {
  2376. NTSTATUS Status = STATUS_SUCCESS;
  2377. PCACHE_PASSWORDS pwd;
  2378. pwd = Passwords;
  2379. //
  2380. // concatenate the fixed length NT_OWF_PASSWORD and LM_OWF_PASSWORD structures
  2381. // into a buffer which we then use as a UNICODE_STRING
  2382. //
  2383. if (ARGUMENT_PRESENT(NtOwfPassword)) {
  2384. Status = NlpComputeSaltedHashedPassword(
  2385. &pwd->SecretPasswords.NtOwfPassword,
  2386. NtOwfPassword,
  2387. UserName
  2388. );
  2389. if (!NT_SUCCESS(Status)) {
  2390. goto Cleanup;
  2391. }
  2392. pwd->SecretPasswords.NtPasswordPresent = TRUE;
  2393. } else {
  2394. RtlZeroMemory((PVOID)&pwd->SecretPasswords.NtOwfPassword,
  2395. sizeof(pwd->SecretPasswords.NtOwfPassword)
  2396. );
  2397. pwd->SecretPasswords.NtPasswordPresent = FALSE;
  2398. }
  2399. //
  2400. // Windows2000:
  2401. // never store LMOWF -- since we never need it, and, this would
  2402. // be the first thing attacked once a cache entry is unwrapped.
  2403. //
  2404. #if 0
  2405. if (ARGUMENT_PRESENT(LmOwfPassword)) {
  2406. Status = NlpComputeSaltedHashedPassword(
  2407. &pwd->SecretPasswords.LmOwfPassword,
  2408. LmOwfPassword,
  2409. UserName
  2410. );
  2411. if (!NT_SUCCESS(Status)) {
  2412. goto Cleanup;
  2413. }
  2414. pwd->SecretPasswords.LmPasswordPresent = TRUE;
  2415. } else
  2416. #else
  2417. UNREFERENCED_PARAMETER( LmOwfPassword );
  2418. #endif
  2419. {
  2420. RtlZeroMemory((PVOID)&pwd->SecretPasswords.LmOwfPassword,
  2421. sizeof(pwd->SecretPasswords.LmOwfPassword)
  2422. );
  2423. pwd->SecretPasswords.LmPasswordPresent = FALSE;
  2424. }
  2425. Cleanup:
  2426. return Status;
  2427. }
  2428. NTSTATUS
  2429. NlpGetCredentialNamesFromCacheEntry(
  2430. IN PLOGON_CACHE_ENTRY CacheEntry,
  2431. IN PNETLOGON_VALIDATION_SAM_INFO4 AccountInfo,
  2432. OUT PUNICODE_STRING DomainName,
  2433. OUT PUNICODE_STRING UserName
  2434. )
  2435. /*++
  2436. Routine Description:
  2437. Uses the supplemental data found in a cached MIT logon to
  2438. to get the MIT princ names
  2439. Arguments:
  2440. CacheEntry - Cache entry
  2441. DomainName - Realm name
  2442. UserName - Princ name
  2443. Return Value:
  2444. NTSATUS
  2445. --*/
  2446. {
  2447. NTSTATUS Status = STATUS_SUCCESS;
  2448. UNICODE_STRING User = {0};
  2449. UNICODE_STRING Domain = {0};
  2450. //
  2451. // ealier revisons of cache entries do not have the CacheFlags field
  2452. //
  2453. if ((CacheEntry->Revision >= NLP_CACHE_REVISION_NT_5_0) && (CacheEntry->CacheFlags & MSV1_0_CACHE_LOGON_REQUEST_MIT_LOGON))
  2454. {
  2455. Status = NlpGetCredentialNamesFromMitCacheEntry(CacheEntry, &Domain, &User);
  2456. if (!NT_SUCCESS(Status))
  2457. {
  2458. goto Cleanup;
  2459. }
  2460. }
  2461. else
  2462. {
  2463. User = AccountInfo->EffectiveName;
  2464. Domain = AccountInfo->LogonDomainName;
  2465. }
  2466. Status = NtLmDuplicateUnicodeString(DomainName, &Domain);
  2467. if (!NT_SUCCESS(Status))
  2468. {
  2469. goto Cleanup;
  2470. }
  2471. Status = NtLmDuplicateUnicodeString(UserName, &User);
  2472. Cleanup:
  2473. return Status;
  2474. }
  2475. NTSTATUS
  2476. NlpGetCredentialNamesFromMitCacheEntry(
  2477. IN PLOGON_CACHE_ENTRY CacheEntry,
  2478. OUT PUNICODE_STRING DomainName,
  2479. OUT PUNICODE_STRING UserName
  2480. )
  2481. /*++
  2482. Routine Description:
  2483. Uses the supplemental data found in a cached MIT logon to
  2484. to get the MIT princ names
  2485. Arguments:
  2486. CacheEntry - Cache entry
  2487. DomainName - Realm name
  2488. UserName - Princ name
  2489. Return Value:
  2490. NTSATUS
  2491. --*/
  2492. {
  2493. if ( (CacheEntry->SupplementalCacheDataOffset < sizeof(LOGON_CACHE_ENTRY)) )
  2494. {
  2495. return STATUS_INVALID_PARAMETER;
  2496. }
  2497. return NlpGetCredentialNamesFromMitCacheSupplementalCacheData(
  2498. CacheEntry->SupplementalCacheDataLength,
  2499. (PBYTE) RtlOffsetToPointer(
  2500. CacheEntry,
  2501. CacheEntry->SupplementalCacheDataOffset
  2502. ),
  2503. DomainName,
  2504. UserName
  2505. );
  2506. }
  2507. NTSTATUS
  2508. NlpGetCredentialNamesFromMitCacheSupplementalCacheData(
  2509. IN ULONG SupplementalCacheDataLength,
  2510. IN PBYTE SupplementalCacheData,
  2511. OUT PUNICODE_STRING DomainName,
  2512. OUT PUNICODE_STRING UserName
  2513. )
  2514. /*++
  2515. Routine Description:
  2516. Uses the supplemental data found in a cached MIT logon to
  2517. to get the MIT princ names
  2518. Arguments:
  2519. SupplementalCacheDataLength - SupplementalCacheDataLength
  2520. SupplementalCacheData - SupplementalCacheData
  2521. DomainName - Realm name
  2522. UserName - Princ name
  2523. Return Value:
  2524. NTSATUS
  2525. --*/
  2526. {
  2527. PBYTE Tmp = SupplementalCacheData;
  2528. if ( SupplementalCacheDataLength < (2 * sizeof(UNICODE_STRING)) )
  2529. {
  2530. return STATUS_INVALID_PARAMETER;
  2531. }
  2532. //
  2533. // Supplemental data will contain 2 UNICODE_STRINGs & buffers, in format
  2534. // MIT User <buffer> MIT Realm <buffer>. All buffers are offset from
  2535. // beginning of supplemental data.
  2536. //
  2537. RtlCopyMemory(
  2538. UserName,
  2539. Tmp,
  2540. sizeof(UNICODE_STRING)
  2541. );
  2542. Tmp += sizeof(UNICODE_STRING);
  2543. UserName->Buffer = (PWSTR) RtlOffsetToPointer(SupplementalCacheData, UserName->Buffer);
  2544. Tmp += ROUND_UP_COUNT(UserName->Length, ALIGN_LONG);
  2545. RtlCopyMemory(
  2546. DomainName,
  2547. Tmp,
  2548. sizeof(UNICODE_STRING)
  2549. );
  2550. DomainName->Buffer = (PWSTR) RtlOffsetToPointer(SupplementalCacheData, DomainName->Buffer);
  2551. return STATUS_SUCCESS;
  2552. }
  2553. NTSTATUS
  2554. NlpReadCacheEntry(
  2555. IN PUNICODE_STRING DomainName,
  2556. IN PUNICODE_STRING UserName,
  2557. OUT PULONG Index,
  2558. OUT PLOGON_CACHE_ENTRY* CacheEntry,
  2559. OUT PULONG EntrySize
  2560. )
  2561. /*++
  2562. Routine Description:
  2563. Searches the active entry list for a domain\username
  2564. match in the cache. If a match is found, then it
  2565. is returned.
  2566. Arguments:
  2567. DomainName - The name of the domain in which the account exists.
  2568. This can be the Netbios or Dns Domain Name.
  2569. UserName - The name of the account whose password is to be changed.
  2570. This can be the Sam Account Name.
  2571. If DomainName is empty, this is the UPN of the account
  2572. Index - receives the index of the entry retrieved.
  2573. CacheEntry - pointer to place to return pointer to LOGON_CACHE_ENTRY
  2574. EntrySize - size of returned LOGON_CACHE_ENTRY
  2575. Return Value:
  2576. NTSTATUS
  2577. Success = STATUS_SUCCESS
  2578. *ppEntry points to allocated LOGON_CACHE_ENTRY
  2579. *EntrySize is size of returned data
  2580. Failure = STATUS_NO_MEMORY
  2581. Couldn't allocate buffer for LOGON_CACHE_ENTRY
  2582. --*/
  2583. {
  2584. NTSTATUS NtStatus = STATUS_SUCCESS;
  2585. UNICODE_STRING CachedUser;
  2586. UNICODE_STRING CachedDomain;
  2587. UNICODE_STRING CachedDnsDomain;
  2588. UNICODE_STRING CachedUpn;
  2589. BOOLEAN CaseInSensitive = TRUE;
  2590. PNLP_CTE Next;
  2591. SspPrint((SSP_CRED, "NlpReadCacheEntry looking for: %wZ\\%wZ\n", DomainName, UserName));
  2592. //
  2593. // Walk the active list looking for a domain/name match
  2594. //
  2595. Next = (PNLP_CTE)NlpActiveCtes.Flink;
  2596. while (Next != (PNLP_CTE)&NlpActiveCtes) {
  2597. ASSERT(CacheEntry && (!*CacheEntry) && L"*CacheEntry must be null");
  2598. NtStatus = NlpReadCacheEntryByIndex(
  2599. Next->Index,
  2600. CacheEntry,
  2601. EntrySize
  2602. );
  2603. if (!NT_SUCCESS(NtStatus)) {
  2604. break; // out of while-loop
  2605. }
  2606. //
  2607. // Grab the various strings from the cache entry.
  2608. //
  2609. ASSERT((*CacheEntry)->Revision >= NLP_CACHE_REVISION_NT_1_0B );
  2610. //
  2611. // decrypt the cache entry...
  2612. //
  2613. NtStatus = NlpDecryptCacheEntry( *CacheEntry, *EntrySize );
  2614. if (!NT_SUCCESS(NtStatus)) {
  2615. //
  2616. // for failed decrypt, continue the search.
  2617. // the reason for this is because the decrypt does an integrity
  2618. // check. We don't want one corrupt cache entry to cause (possibly)
  2619. // the whole cache to be invalidated.
  2620. //
  2621. FreeToHeap( (*CacheEntry) );
  2622. *CacheEntry = NULL;
  2623. Next = (PNLP_CTE)(Next->Link.Flink);
  2624. continue;
  2625. }
  2626. CachedUser.Length =
  2627. CachedUser.MaximumLength = (*CacheEntry)->UserNameLength;
  2628. if ( (*CacheEntry)->Revision >= NLP_CACHE_REVISION_NT_5_0 ) {
  2629. CachedUser.Buffer = (PWSTR) ((PBYTE) *CacheEntry + sizeof(LOGON_CACHE_ENTRY));
  2630. } else {
  2631. CachedUser.Buffer = (PWSTR) ((PBYTE) *CacheEntry + sizeof(LOGON_CACHE_ENTRY_NT_4_SP4));
  2632. }
  2633. CachedDomain.Length =
  2634. CachedDomain.MaximumLength = (*CacheEntry)->DomainNameLength;
  2635. CachedDomain.Buffer = (PWSTR)((LPBYTE)CachedUser.Buffer +
  2636. ROUND_UP_COUNT((*CacheEntry)->UserNameLength, sizeof(ULONG)));
  2637. CachedDnsDomain.Length =
  2638. CachedDnsDomain.MaximumLength = (*CacheEntry)->DnsDomainNameLength;
  2639. CachedDnsDomain.Buffer = (PWSTR)((LPBYTE)CachedDomain.Buffer +
  2640. ROUND_UP_COUNT((*CacheEntry)->DomainNameLength, sizeof(ULONG)));
  2641. CachedUpn.Length =
  2642. CachedUpn.MaximumLength = (*CacheEntry)->UpnLength;
  2643. CachedUpn.Buffer = (PWSTR)((LPBYTE)CachedDnsDomain.Buffer +
  2644. ROUND_UP_COUNT((*CacheEntry)->DnsDomainNameLength, sizeof(ULONG)));
  2645. //
  2646. // ealier revisons of cache entries do not have the CacheFlags field
  2647. //
  2648. if (((*CacheEntry)->Revision >= NLP_CACHE_REVISION_NT_5_0)) {
  2649. //
  2650. // MIT user name and realm name are case sensitive
  2651. //
  2652. CaseInSensitive = (BOOLEAN) (0 == ((*CacheEntry)->CacheFlags & MSV1_0_CACHE_LOGON_REQUEST_MIT_LOGON));
  2653. SspPrint((SSP_CRED,
  2654. "NlpReadCacheEntry checking entry %d: domain (%wZ), dnsDomain (%wZ), upn (%wZ), user (%wZ), flags %#x\n",
  2655. Next->Index,
  2656. &CachedDomain,
  2657. &CachedDnsDomain,
  2658. &CachedUpn,
  2659. &CachedUser,
  2660. (*CacheEntry)->CacheFlags));
  2661. }
  2662. if (DomainName->Length != 0) {
  2663. //
  2664. // smartcard only cache entries use UPN only, skip it.
  2665. // check for earlier versions prior to nt_5_0 where SC is not
  2666. // supported and CacheFlags is not part of the cache entry
  2667. //
  2668. if (((*CacheEntry)->Revision < NLP_CACHE_REVISION_NT_5_0) || (0 == ((*CacheEntry)->CacheFlags & MSV1_0_CACHE_LOGON_REQUEST_SMARTCARD_ONLY)))
  2669. {
  2670. if (RtlEqualUnicodeString(UserName, &CachedUser, CaseInSensitive)) {
  2671. if ( RtlEqualDomainName(DomainName, &CachedDomain) ||
  2672. RtlEqualUnicodeString(DomainName, &CachedDnsDomain, CaseInSensitive) ) {
  2673. //
  2674. // found it !
  2675. //
  2676. SspPrint((SSP_CRED, "NlpReadCacheEntry domain and user names matched\n"));
  2677. break; // out of while-loop
  2678. }
  2679. }
  2680. }
  2681. //
  2682. // If no domain name was passed in,
  2683. // the user name is the UPN.
  2684. //
  2685. } else {
  2686. if ( RtlEqualUnicodeString(UserName, &CachedUpn, CaseInSensitive) ) {
  2687. //
  2688. // found it !
  2689. //
  2690. SspPrint((SSP_CRED, "NlpReadCacheEntry UPNs matched\n"));
  2691. break; // out of while-loop
  2692. }
  2693. }
  2694. //
  2695. // Not the right entry, free the registry structure
  2696. // and go on to the next one.
  2697. //
  2698. FreeToHeap( (*CacheEntry) );
  2699. *CacheEntry = NULL;
  2700. Next = (PNLP_CTE)(Next->Link.Flink);
  2701. }
  2702. if (Next != (PNLP_CTE)&NlpActiveCtes && NT_SUCCESS(NtStatus)) {
  2703. //
  2704. // We found a match - Open the corresponding secret
  2705. //
  2706. (*Index) = Next->Index;
  2707. if( (*CacheEntry)->Revision < NLP_CACHE_REVISION_NT_5_0 ) {
  2708. //
  2709. // versions prior to NT5 require us open the corresponding LSA secret.
  2710. //
  2711. NtStatus = NlpOpenSecret(Next->Index);
  2712. if (!NT_SUCCESS(NtStatus)) {
  2713. FreeToHeap( (*CacheEntry) );
  2714. *CacheEntry = NULL;
  2715. return(NtStatus);
  2716. }
  2717. }
  2718. } else {
  2719. NtStatus = STATUS_LOGON_FAILURE;
  2720. }
  2721. return(NtStatus);
  2722. }
  2723. NTSTATUS
  2724. NlpWriteCacheEntry(
  2725. IN ULONG Index,
  2726. IN PLOGON_CACHE_ENTRY Entry,
  2727. IN ULONG EntrySize
  2728. )
  2729. /*++
  2730. Routine Description:
  2731. Writes a LOGON_CACHE_ENTRY to the registry cache.
  2732. It is the caller's responsibility to place the corresponding
  2733. CTE table entry in the correct active/inactive list.
  2734. Arguments:
  2735. Index - Index of entry to write out.
  2736. Entry - pointer to LOGON_CACHE_ENTRY to write to cache
  2737. EntrySize - size of this entry (in bytes (must be multiple of 4 thoough))
  2738. Return Value:
  2739. NTSTATUS
  2740. Success = STATUS_SUCCESS
  2741. The LOGON_CACHE_ENTRY is now in the registry (hopefully
  2742. on disk)
  2743. Failure =
  2744. --*/
  2745. {
  2746. NTSTATUS
  2747. NtStatus;
  2748. UNICODE_STRING
  2749. ValueName;
  2750. WCHAR
  2751. NameBuffer[32];
  2752. ValueName.MaximumLength = 32;
  2753. ValueName.Length = 0;
  2754. ValueName.Buffer = &NameBuffer[0];
  2755. NlpMakeCacheEntryName( Index, &ValueName );
  2756. SspPrint((SSP_CRED, "NlpWriteCacheEntry cache entry index: %d, value name: %wZ, valid %s\n",
  2757. Index, &ValueName, Entry->Valid ? "true" : "false"));
  2758. NtStatus = NtSetValueKey(NlpCacheHandle,
  2759. &ValueName,
  2760. 0, // TitleIndex
  2761. REG_BINARY, // Type
  2762. (PVOID)Entry,
  2763. EntrySize
  2764. );
  2765. return(NtStatus);
  2766. }
  2767. VOID
  2768. NlpCopyAndUpdateAccountInfo(
  2769. IN USHORT Length,
  2770. IN PUNICODE_STRING pUnicodeString,
  2771. IN OUT PUCHAR* pSource,
  2772. IN OUT PUCHAR* pDest
  2773. )
  2774. /*++
  2775. Routine Description:
  2776. Updates a UNICODE_STRING structure and copies the associated buffer to
  2777. *pDest, if Length is non-zero
  2778. Arguments:
  2779. Length - length of UNICODE_STRING.Buffer to copy
  2780. pUnicodeString - pointer to UNICODE_STRING structure to update
  2781. pSource - pointer to pointer to source WCHAR string
  2782. pDest - pointer to pointer to place to copy WCHAR string
  2783. Return Value:
  2784. None.
  2785. if string was copied, *Source and *Dest are updated to point to the next
  2786. naturally aligned (DWORD) positions in the input and output buffers resp.
  2787. --*/
  2788. {
  2789. PUCHAR source = *pSource;
  2790. PUCHAR dest = *pDest;
  2791. pUnicodeString->Length = Length;
  2792. pUnicodeString->MaximumLength = Length;
  2793. pUnicodeString->Buffer = (PWSTR)dest;
  2794. if (Length) {
  2795. RtlCopyMemory(dest, source, Length);
  2796. *pSource = ROUND_UP_POINTER(source + Length, sizeof(ULONG));
  2797. *pDest = ROUND_UP_POINTER(dest + Length, sizeof(ULONG));
  2798. }
  2799. }
  2800. VOID
  2801. NlpSetTimeField(
  2802. OUT POLD_LARGE_INTEGER pTimeField,
  2803. IN NLP_SET_TIME_HINT Hint
  2804. )
  2805. /*++
  2806. Routine Description:
  2807. Sets a LARGE_INTEGER time field to one of 3 values:
  2808. NLP_BIG_TIME = maximum positive large integer (0x7fffffffffffffff)
  2809. NLP_SMALL_TIME = smallest positive large integer (0)
  2810. NLP_NOW_TIME = current system time
  2811. Arguments:
  2812. pTimeField - pointer to LARGE_INTEGER structure to update
  2813. Hint - NLP_BIG_TIME, NLP_SMALL_TIME or NLP_NOW_TIME
  2814. Return Value:
  2815. None.
  2816. --*/
  2817. {
  2818. LARGE_INTEGER Time;
  2819. switch (Hint) {
  2820. case NLP_SMALL_TIME:
  2821. pTimeField->HighPart = SMALL_PART_1;
  2822. pTimeField->LowPart = SMALL_PART_2;
  2823. break;
  2824. case NLP_BIG_TIME:
  2825. pTimeField->HighPart = BIG_PART_1;
  2826. pTimeField->LowPart = BIG_PART_2;
  2827. break;
  2828. case NLP_NOW_TIME:
  2829. NtQuerySystemTime(&Time);
  2830. NEW_TO_OLD_LARGE_INTEGER( Time, (*pTimeField) );
  2831. break;
  2832. }
  2833. }
  2834. NTSTATUS
  2835. NlpBuildAccountInfo(
  2836. IN PLOGON_CACHE_ENTRY pCacheEntry,
  2837. IN ULONG EntryLength,
  2838. OUT PNETLOGON_VALIDATION_SAM_INFO4 *AccountInfo
  2839. )
  2840. /*++
  2841. Routine Description:
  2842. Performs the reverse of NlpBuildCacheEntry - creates a NETLOGON_VALIDATION_SAM_INFO4
  2843. structure from a cache entry
  2844. Arguments:
  2845. pCacheEntry - pointer to LOGON_CACHE_ENTRY
  2846. EntryLength - inclusive size of *pCacheEntry, including variable data
  2847. AccountInfo - pointer to place to create NETLOGON_VALIDATION_SAM_INFO4
  2848. Return Value:
  2849. NTSTATUS
  2850. Success = STATUS_SUCCESS
  2851. Failure = STATUS_NO_MEMORY
  2852. --*/
  2853. {
  2854. PNETLOGON_VALIDATION_SAM_INFO4 pSamInfo;
  2855. PUCHAR source;
  2856. PUCHAR dest;
  2857. ULONG length;
  2858. ULONG sidLength;
  2859. ULONG commonBits;
  2860. LPWSTR computerName = NULL;
  2861. ULONG computerNameLength = 0;
  2862. //
  2863. // commonBits is the size of the variable data area common to both the
  2864. // LOGON_CACHE_ENTRY and NETLOGON_VALIDATION_SAM_INFO4 structures
  2865. //
  2866. commonBits = ROUND_UP_COUNT(pCacheEntry->EffectiveNameLength, sizeof(ULONG))
  2867. + ROUND_UP_COUNT(pCacheEntry->FullNameLength, sizeof(ULONG))
  2868. + ROUND_UP_COUNT(pCacheEntry->LogonScriptLength, sizeof(ULONG))
  2869. + ROUND_UP_COUNT(pCacheEntry->ProfilePathLength, sizeof(ULONG))
  2870. + ROUND_UP_COUNT(pCacheEntry->HomeDirectoryLength, sizeof(ULONG))
  2871. + ROUND_UP_COUNT(pCacheEntry->HomeDirectoryDriveLength, sizeof(ULONG))
  2872. + ROUND_UP_COUNT(pCacheEntry->GroupCount * sizeof(GROUP_MEMBERSHIP), sizeof(ULONG))
  2873. + ROUND_UP_COUNT(pCacheEntry->LogonDomainNameLength, sizeof(ULONG));
  2874. if ( pCacheEntry->Revision >= NLP_CACHE_REVISION_NT_5_0 )
  2875. {
  2876. commonBits += ROUND_UP_COUNT(pCacheEntry->DnsDomainNameLength, sizeof(ULONG))
  2877. + ROUND_UP_COUNT(pCacheEntry->UpnLength, sizeof(ULONG));
  2878. if( pCacheEntry->LogonServerLength != 0 )
  2879. {
  2880. computerNameLength = pCacheEntry->LogonServerLength;
  2881. computerName = NULL;
  2882. }
  2883. }
  2884. if ( computerNameLength == 0 )
  2885. {
  2886. //
  2887. // will GetComputerName ever fail??? Its only used to fake the logon
  2888. // server name when we logon using the cached information, so its
  2889. // probably ok to use a NULL string if GetComputerName phones in sick
  2890. //
  2891. computerName = NlpComputerName.Buffer;
  2892. computerNameLength = NlpComputerName.Length / sizeof(WCHAR);
  2893. ASSERT( computerName );
  2894. ASSERT( computerNameLength );
  2895. #if DBG
  2896. if (DumpCacheInfo) {
  2897. DbgPrint("ComputerName = %ws, length = %d\n", computerName, computerNameLength);
  2898. }
  2899. #endif
  2900. }
  2901. ASSERT(pCacheEntry->Revision >= NLP_CACHE_REVISION_NT_1_0B);
  2902. //
  2903. // Account for possible roundup for NETLOGON_SID_AND_ATTRIBUTES structure
  2904. //
  2905. commonBits += sizeof(PVOID);
  2906. commonBits += ROUND_UP_COUNT(sizeof(NETLOGON_SID_AND_ATTRIBUTES) * pCacheEntry->SidCount, sizeof(ULONG))
  2907. + ROUND_UP_COUNT(pCacheEntry->SidLength, sizeof(ULONG));
  2908. sidLength = pCacheEntry->LogonDomainIdLength;
  2909. //
  2910. // length is the required amount of buffer in which to build a working
  2911. // NETLOGON_VALIDATION_SAM_INFO4 structure
  2912. //
  2913. length = ROUND_UP_COUNT(sizeof(NETLOGON_VALIDATION_SAM_INFO4), sizeof(ULONG))
  2914. + commonBits
  2915. + sidLength
  2916. + computerNameLength * sizeof(WCHAR);
  2917. #if DBG
  2918. if (DumpCacheInfo) {
  2919. DbgPrint("NlpBuildAccountInfo: %d bytes required\n", length);
  2920. }
  2921. #endif
  2922. // MIDL_user_allocate zeros the buffer. This routine depends on that.
  2923. pSamInfo = (PNETLOGON_VALIDATION_SAM_INFO4)MIDL_user_allocate(length);
  2924. if (pSamInfo == NULL) {
  2925. return STATUS_NO_MEMORY;
  2926. }
  2927. //
  2928. // point dest at the first (aligned) byte at the start of the variable data
  2929. // area at the end of the sam info structure
  2930. //
  2931. dest = (PUCHAR)(pSamInfo + 1);
  2932. //
  2933. // point source at the first string to be copied out of the variable length
  2934. // data area at the end of the cache entry
  2935. //
  2936. ASSERT(pCacheEntry->Revision >= NLP_CACHE_REVISION_NT_1_0B );
  2937. if ( pCacheEntry->Revision >= NLP_CACHE_REVISION_NT_5_0 ) {
  2938. source = (PUCHAR)(pCacheEntry + 1);
  2939. } else {
  2940. source = (PUCHAR)( (PLOGON_CACHE_ENTRY_NT_4_SP4)pCacheEntry + 1 );
  2941. }
  2942. source += ROUND_UP_COUNT(pCacheEntry->UserNameLength, sizeof(ULONG))
  2943. + ROUND_UP_COUNT(pCacheEntry->DomainNameLength, sizeof(ULONG));
  2944. if ( pCacheEntry->Revision >= NLP_CACHE_REVISION_NT_5_0 )
  2945. {
  2946. NlpCopyAndUpdateAccountInfo(pCacheEntry->DnsDomainNameLength,
  2947. &pSamInfo->DnsLogonDomainName,
  2948. &source,
  2949. &dest
  2950. );
  2951. NlpCopyAndUpdateAccountInfo(pCacheEntry->UpnLength,
  2952. &pSamInfo->Upn,
  2953. &source,
  2954. &dest
  2955. );
  2956. } else {
  2957. //
  2958. // Fill in the new field for the PNETLOGON_VALIDATION_SAM_INFO4 structure
  2959. //
  2960. RtlInitUnicodeString( &pSamInfo->DnsLogonDomainName, NULL );
  2961. RtlInitUnicodeString( &pSamInfo->Upn, NULL );
  2962. source += ROUND_UP_COUNT(pCacheEntry->DnsDomainNameLength, sizeof(ULONG))
  2963. + ROUND_UP_COUNT(pCacheEntry->UpnLength, sizeof(ULONG));
  2964. }
  2965. //
  2966. // pull out the variable length data from the end of the LOGON_CACHE_ENTRY
  2967. // and stick them at the end of the NETLOGON_VALIDATION_SAM_INFO4 structure.
  2968. // These must be copied out IN THE SAME ORDER as NlpBuildCacheEntry put them
  2969. // in. If we want to change the order of things in the buffer, the order
  2970. // must be changed in both routines (this & NlpBuildCacheEntry)
  2971. //
  2972. //
  2973. // create the UNICODE_STRING structures in the NETLOGON_VALIDATION_SAM_INFO4
  2974. // structure and copy the strings to the end of the buffer. 0 length strings
  2975. // will get a pointer which should be ignored
  2976. //
  2977. NlpCopyAndUpdateAccountInfo(pCacheEntry->EffectiveNameLength,
  2978. &pSamInfo->EffectiveName,
  2979. &source,
  2980. &dest
  2981. );
  2982. NlpCopyAndUpdateAccountInfo(pCacheEntry->FullNameLength,
  2983. &pSamInfo->FullName,
  2984. &source,
  2985. &dest
  2986. );
  2987. NlpCopyAndUpdateAccountInfo(pCacheEntry->LogonScriptLength,
  2988. &pSamInfo->LogonScript,
  2989. &source,
  2990. &dest
  2991. );
  2992. NlpCopyAndUpdateAccountInfo(pCacheEntry->ProfilePathLength,
  2993. &pSamInfo->ProfilePath,
  2994. &source,
  2995. &dest
  2996. );
  2997. NlpCopyAndUpdateAccountInfo(pCacheEntry->HomeDirectoryLength,
  2998. &pSamInfo->HomeDirectory,
  2999. &source,
  3000. &dest
  3001. );
  3002. NlpCopyAndUpdateAccountInfo(pCacheEntry->HomeDirectoryDriveLength,
  3003. &pSamInfo->HomeDirectoryDrive,
  3004. &source,
  3005. &dest
  3006. );
  3007. //
  3008. // copy the group membership array
  3009. //
  3010. pSamInfo->GroupIds = (PGROUP_MEMBERSHIP)dest;
  3011. length = pCacheEntry->GroupCount * sizeof(GROUP_MEMBERSHIP);
  3012. RtlCopyMemory(dest, source, length);
  3013. dest = ROUND_UP_POINTER(dest + length, sizeof(ULONG));
  3014. source = ROUND_UP_POINTER(source + length, sizeof(ULONG));
  3015. //
  3016. // final UNICODE_STRING from LOGON_CACHE_ENTRY. Reorganize this to:
  3017. // strings, groups, SID?
  3018. //
  3019. NlpCopyAndUpdateAccountInfo(pCacheEntry->LogonDomainNameLength,
  3020. &pSamInfo->LogonDomainName,
  3021. &source,
  3022. &dest
  3023. );
  3024. //
  3025. // Copy all the SIDs
  3026. //
  3027. if (pCacheEntry->Revision >= NLP_CACHE_REVISION_NT_1_0B ) {
  3028. pSamInfo->SidCount = pCacheEntry->SidCount;
  3029. if (pCacheEntry->SidCount) {
  3030. ULONG i, sidLength;
  3031. PULONG SidAttributes = (PULONG) source;
  3032. source = ROUND_UP_POINTER(source + pCacheEntry->SidCount * sizeof(ULONG), sizeof(ULONG));
  3033. //
  3034. // Structures containing pointers must start on 8-byte boundries
  3035. //
  3036. dest = ROUND_UP_POINTER(dest, sizeof(PVOID));
  3037. pSamInfo->ExtraSids = (PNETLOGON_SID_AND_ATTRIBUTES) dest;
  3038. dest = ROUND_UP_POINTER(dest + pCacheEntry->SidCount * sizeof(NETLOGON_SID_AND_ATTRIBUTES), sizeof(ULONG));
  3039. for (i = 0; i < pCacheEntry->SidCount ; i++ ) {
  3040. pSamInfo->ExtraSids[i].Attributes = SidAttributes[i];
  3041. sidLength = RtlLengthSid((PSID) source);
  3042. RtlCopySid(sidLength, (PSID) dest, (PSID) source);
  3043. pSamInfo->ExtraSids[i].Sid = (PSID) dest;
  3044. dest = ROUND_UP_POINTER(dest + sidLength, sizeof(ULONG));
  3045. source = ROUND_UP_POINTER(source + sidLength, sizeof(ULONG));
  3046. }
  3047. ASSERT((ULONG) (source - (PUCHAR) SidAttributes) == pCacheEntry->SidLength);
  3048. } else {
  3049. pSamInfo->ExtraSids = NULL;
  3050. }
  3051. } else {
  3052. pSamInfo->ExtraSids = NULL;
  3053. pSamInfo->SidCount = 0;
  3054. }
  3055. //
  3056. // copy the LogonDomainId SID
  3057. //
  3058. RtlCopySid(sidLength, (PSID)dest, (PSID)source);
  3059. pSamInfo->LogonDomainId = (PSID)dest;
  3060. dest = ROUND_UP_POINTER(dest + sidLength, sizeof(ULONG));
  3061. source = ROUND_UP_POINTER(source + sidLength, sizeof(ULONG));
  3062. if ( computerName != NULL )
  3063. {
  3064. //
  3065. // final UNICODE_STRING. This one from stack. Note that we have finished
  3066. // with source
  3067. //
  3068. source = (PUCHAR)computerName;
  3069. NlpCopyAndUpdateAccountInfo((USHORT)(computerNameLength * sizeof(WCHAR)),
  3070. &pSamInfo->LogonServer,
  3071. &source,
  3072. &dest
  3073. );
  3074. } else {
  3075. //
  3076. // Sanity check that we have a proper cache revision.
  3077. //
  3078. if ( pCacheEntry->Revision >= NLP_CACHE_REVISION_NT_5_0 )
  3079. {
  3080. //
  3081. // final UNICODE_STRING from LOGON_CACHE_ENTRY.
  3082. //
  3083. NlpCopyAndUpdateAccountInfo((USHORT)pCacheEntry->LogonServerLength,
  3084. &pSamInfo->LogonServer,
  3085. &source,
  3086. &dest
  3087. );
  3088. }
  3089. }
  3090. //
  3091. // copy the non-variable fields
  3092. //
  3093. pSamInfo->UserId = pCacheEntry->UserId;
  3094. pSamInfo->PrimaryGroupId = pCacheEntry->PrimaryGroupId;
  3095. pSamInfo->GroupCount = pCacheEntry->GroupCount;
  3096. //
  3097. // finally, invent some fields
  3098. //
  3099. NlpSetTimeField(&pSamInfo->LogonTime, NLP_NOW_TIME);
  3100. NlpSetTimeField(&pSamInfo->LogoffTime, NLP_BIG_TIME);
  3101. NlpSetTimeField(&pSamInfo->KickOffTime, NLP_BIG_TIME);
  3102. NlpSetTimeField(&pSamInfo->PasswordLastSet, NLP_SMALL_TIME);
  3103. NlpSetTimeField(&pSamInfo->PasswordCanChange, NLP_BIG_TIME);
  3104. NlpSetTimeField(&pSamInfo->PasswordMustChange, NLP_BIG_TIME);
  3105. pSamInfo->LogonCount = 0;
  3106. pSamInfo->BadPasswordCount = 0;
  3107. pSamInfo->UserFlags = LOGON_EXTRA_SIDS;
  3108. if (pCacheEntry->LogonPackage != 0) {
  3109. pSamInfo->UserFlags |= pCacheEntry->LogonPackage << PRIMARY_CRED_LOGON_PACKAGE_SHIFT;
  3110. }
  3111. // RtlZeroMemory(&pSamInfo->UserSessionKey, sizeof(pSamInfo->UserSessionKey));
  3112. #if DBG
  3113. if (DumpCacheInfo) {
  3114. DbgPrint("NlpBuildAccountInfo:\n");
  3115. DumpAccountInfo(pSamInfo);
  3116. }
  3117. #endif
  3118. *AccountInfo = pSamInfo;
  3119. return STATUS_SUCCESS;
  3120. UNREFERENCED_PARAMETER( EntryLength );
  3121. }
  3122. NTSTATUS
  3123. NlpGetCacheControlInfo( VOID )
  3124. /*++
  3125. Routine Description:
  3126. This function retrieves the cache control information from the
  3127. registry. This information is placed in global data for use
  3128. throughout this module. The Cache Table Entry table will also
  3129. be initialized.
  3130. If this routine returns success, then it may be assumed that
  3131. everything completed successfully.
  3132. Arguments:
  3133. None.
  3134. Return Value:
  3135. --*/
  3136. {
  3137. NTSTATUS
  3138. NtStatus;
  3139. UNICODE_STRING
  3140. CacheControlValueName;
  3141. ULONG
  3142. RequiredSize;
  3143. PKEY_VALUE_PARTIAL_INFORMATION
  3144. RegInfo = NULL;
  3145. //
  3146. // read the current control info, if it is there.
  3147. // If it is not there, then we may be dealing with a down-level
  3148. // system and might have a single cache entry in the registry.
  3149. //
  3150. RtlInitUnicodeString( &CacheControlValueName, L"NL$Control" );
  3151. NtStatus = NtQueryValueKey(NlpCacheHandle,
  3152. &CacheControlValueName,
  3153. KeyValuePartialInformation,
  3154. NULL,
  3155. 0,
  3156. &RequiredSize
  3157. );
  3158. if (NT_SUCCESS(NtStatus) || NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) {
  3159. NTSTATUS TempStatus;
  3160. //
  3161. // Hmmm - no entry, that means we are dealing with a
  3162. // first release system here (that didn't have
  3163. // this value).
  3164. //
  3165. //
  3166. // Set up for 1 cache entry.
  3167. // create the secret and cache key entry
  3168. //
  3169. TempStatus = NlpMakeNewCacheEntry( 0 );
  3170. if ( NT_SUCCESS(TempStatus) ) {
  3171. //
  3172. // Now flush out the control information
  3173. //
  3174. NlpCacheControl.Revision = NLP_CACHE_REVISION;
  3175. NlpCacheControl.Entries = 1;
  3176. TempStatus = NlpWriteCacheControl();
  3177. if ( NT_SUCCESS(TempStatus) ) {
  3178. //
  3179. // If a version 1.0 entry exists,
  3180. // copy the old form of cache entry to the new structure.
  3181. //
  3182. // if (NT_SUCCESS(NtStatus)) {
  3183. // TempStatus = NlpConvert1_0To1_0B();
  3184. // }
  3185. }
  3186. }
  3187. NtStatus = TempStatus;
  3188. } else if ( NtStatus == STATUS_BUFFER_TOO_SMALL ) {
  3189. //
  3190. // allocate buffer then do query again, this time receiving data
  3191. //
  3192. RegInfo = (PKEY_VALUE_PARTIAL_INFORMATION)AllocateFromHeap(RequiredSize);
  3193. if (RegInfo == NULL) {
  3194. NtStatus = STATUS_NO_MEMORY;
  3195. goto Cleanup;
  3196. }
  3197. NtStatus = NtQueryValueKey(NlpCacheHandle,
  3198. &CacheControlValueName,
  3199. KeyValuePartialInformation,
  3200. (PVOID)RegInfo,
  3201. RequiredSize,
  3202. &RequiredSize
  3203. );
  3204. if (!NT_SUCCESS(NtStatus)) {
  3205. goto Cleanup;
  3206. }
  3207. //
  3208. // check the revision - we can't deal with up-level revisions.
  3209. //
  3210. if (RegInfo->DataLength < sizeof(NLP_CACHE_CONTROL)) {
  3211. NtStatus = STATUS_UNKNOWN_REVISION;
  3212. goto Cleanup;
  3213. }
  3214. RtlCopyMemory( &NlpCacheControl, &(RegInfo->Data[0]), sizeof(NLP_CACHE_CONTROL) );
  3215. if (NlpCacheControl.Revision > NLP_CACHE_REVISION) {
  3216. NtStatus = STATUS_UNKNOWN_REVISION;
  3217. goto Cleanup;
  3218. }
  3219. //
  3220. // If this is an older cache, update it with the latest revision
  3221. //
  3222. if (NlpCacheControl.Revision != NLP_CACHE_REVISION) {
  3223. // There is no conversion. All the version of cache control have
  3224. // been the same.
  3225. NlpCacheControl.Revision = NLP_CACHE_REVISION;
  3226. NtStatus = NlpWriteCacheControl();
  3227. if (!NT_SUCCESS(NtStatus)) {
  3228. goto Cleanup;
  3229. }
  3230. }
  3231. NtStatus = STATUS_SUCCESS;
  3232. }
  3233. Cleanup:
  3234. if (!NT_SUCCESS(NtStatus)) {
  3235. NlpCacheControl.Entries = 0; // Disable logon cache
  3236. }
  3237. if( RegInfo ) {
  3238. FreeToHeap( RegInfo );
  3239. }
  3240. return(NtStatus);
  3241. }
  3242. NTSTATUS
  3243. NlpBuildCteTable( VOID )
  3244. /*++
  3245. Routine Description:
  3246. This function initializes the CTE table from the contents of
  3247. the cache in the registry.
  3248. Arguments:
  3249. None.
  3250. Return Value:
  3251. STATUS_SUCCESS - the cache is initialized.
  3252. Other - The cache has been disabled.
  3253. --*/
  3254. {
  3255. NTSTATUS
  3256. NtStatus = STATUS_SUCCESS;
  3257. PLOGON_CACHE_ENTRY
  3258. CacheEntry;
  3259. ULONG
  3260. EntrySize,
  3261. i;
  3262. //
  3263. // Initialize the active and inactive CTE lists
  3264. //
  3265. InitializeListHead( &NlpActiveCtes );
  3266. InitializeListHead( &NlpInactiveCtes );
  3267. //
  3268. // Allocate a CTE table
  3269. //
  3270. NlpCteTable = AllocateFromHeap( sizeof( NLP_CTE ) *
  3271. NlpCacheControl.Entries );
  3272. if (NlpCteTable == NULL) {
  3273. //
  3274. // Can't allocate table, disable caching
  3275. //
  3276. NlpCacheControl.Entries = 0; // Disable cache
  3277. return(STATUS_NO_MEMORY);
  3278. }
  3279. for (i = 0; i < NlpCacheControl.Entries; i++) {
  3280. NtStatus = NlpReadCacheEntryByIndex( i,
  3281. &CacheEntry,
  3282. &EntrySize);
  3283. if (!NT_SUCCESS(NtStatus) ) {
  3284. NlpCacheControl.Entries = 0; // Disable cache
  3285. return(NtStatus);
  3286. }
  3287. //
  3288. //
  3289. if (EntrySize < sizeof(LOGON_CACHE_ENTRY_NT_4_SP4)) {
  3290. //
  3291. // Hmmm, something is bad.
  3292. // disable caching and return an error
  3293. //
  3294. NlpCacheControl.Entries = 0; // Disable cache
  3295. FreeToHeap( CacheEntry );
  3296. return( STATUS_INTERNAL_DB_CORRUPTION );
  3297. }
  3298. if (CacheEntry->Revision > NLP_CACHE_REVISION) {
  3299. NlpCacheControl.Entries = 0; // Disable cache
  3300. FreeToHeap( CacheEntry );
  3301. return(STATUS_UNKNOWN_REVISION);
  3302. }
  3303. NlpCteTable[i].Index = i;
  3304. NlpCteTable[i].Active = CacheEntry->Valid;
  3305. NlpCteTable[i].Time = CacheEntry->Time;
  3306. InsertTailList( &NlpInactiveCtes, &NlpCteTable[i].Link );
  3307. if (NlpCteTable[i].Active) {
  3308. NlpAddEntryToActiveList( i );
  3309. }
  3310. FreeToHeap( CacheEntry );
  3311. }
  3312. return(NtStatus);
  3313. }
  3314. NTSTATUS
  3315. NlpChangeCacheSizeIfNecessary( VOID )
  3316. /*++
  3317. Routine Description:
  3318. This function checks to see if the user has requested a
  3319. different cache size than what we currently have.
  3320. If so, then we try to grow or shrink our cache appropriately.
  3321. If this succeeds, then the global cache control information is
  3322. updated appropriately. If it fails then one of two things will
  3323. happen:
  3324. 1) If the user was trying to shrink the cache, then it will
  3325. be disabled (entries set to zero).
  3326. 2) If the user was trying to grow the cache, then we will leave
  3327. it as it is.
  3328. In either of these two failure conditions, an error is returned.
  3329. Arguments:
  3330. None.
  3331. Return Value:
  3332. STATUS_SUCCESS
  3333. --*/
  3334. {
  3335. NTSTATUS
  3336. NtStatus;
  3337. UINT
  3338. CachedLogonsCount;
  3339. PNLP_CTE
  3340. NewCteTable,
  3341. Next;
  3342. LIST_ENTRY
  3343. NewActive,
  3344. NewInactive;
  3345. PNLP_CACHE_AND_SECRETS
  3346. CacheAndSecrets;
  3347. ULONG
  3348. ErrorCacheSize,
  3349. i,
  3350. j;
  3351. // Find out how many logons to cache.
  3352. // This is a user setable value and it may be different than
  3353. // the last time we booted.
  3354. //
  3355. CachedLogonsCount = GetProfileInt(
  3356. TEXT("Winlogon"),
  3357. TEXT("CachedLogonsCount"),
  3358. NLP_DEFAULT_LOGON_CACHE_COUNT // Default value
  3359. );
  3360. //
  3361. // Minimize the user-supplied value with the maximum allowable
  3362. // value.
  3363. //
  3364. if (CachedLogonsCount > NLP_MAX_LOGON_CACHE_COUNT) {
  3365. CachedLogonsCount = NLP_MAX_LOGON_CACHE_COUNT;
  3366. }
  3367. //
  3368. // Compare it to what we already have and see if we need
  3369. // to change the size of the cache
  3370. //
  3371. if (CachedLogonsCount == NlpCacheControl.Entries) {
  3372. //
  3373. // No change
  3374. //
  3375. return(STATUS_SUCCESS);
  3376. }
  3377. //
  3378. // Set the size of the cache to be used in case of error
  3379. // changing the size. If we are trying to grow the cache,
  3380. // then use the existing cache on error. If we are trying
  3381. // to shrink the cache, then disable caching on error.
  3382. //
  3383. if (CachedLogonsCount > NlpCacheControl.Entries) {
  3384. ErrorCacheSize = NlpCacheControl.Entries;
  3385. } else {
  3386. ErrorCacheSize = 0;
  3387. }
  3388. //
  3389. // Allocate a CTE table the size of the new table
  3390. //
  3391. NewCteTable = AllocateFromHeap( sizeof( NLP_CTE ) *
  3392. CachedLogonsCount );
  3393. if (NewCteTable == NULL) {
  3394. //
  3395. // Can't shrink table, disable caching
  3396. //
  3397. NlpCacheControl.Entries = ErrorCacheSize;
  3398. return(STATUS_NO_MEMORY);
  3399. }
  3400. //
  3401. // Now the tricky parts ...
  3402. //
  3403. if (CachedLogonsCount > NlpCacheControl.Entries) {
  3404. //
  3405. // Try to grow the cache -
  3406. // Create additional secrets and cache entries.
  3407. //
  3408. // Copy time fields and set index
  3409. //
  3410. for (i=0; i < NlpCacheControl.Entries; i++) {
  3411. NewCteTable[i].Index = i;
  3412. NewCteTable[i].Time = NlpCteTable[i].Time;
  3413. }
  3414. //
  3415. // Place existing entries on either the active or inactive list
  3416. //
  3417. InitializeListHead( &NewActive );
  3418. for (Next = (PNLP_CTE)NlpActiveCtes.Flink;
  3419. Next != (PNLP_CTE)(&NlpActiveCtes);
  3420. Next = (PNLP_CTE)Next->Link.Flink
  3421. ) {
  3422. InsertTailList( &NewActive, &NewCteTable[Next->Index].Link );
  3423. NewCteTable[Next->Index].Active = TRUE;
  3424. }
  3425. InitializeListHead( &NewInactive );
  3426. for (Next = (PNLP_CTE)NlpInactiveCtes.Flink;
  3427. Next != (PNLP_CTE)(&NlpInactiveCtes);
  3428. Next = (PNLP_CTE)Next->Link.Flink
  3429. ) {
  3430. InsertTailList( &NewInactive, &NewCteTable[Next->Index].Link );
  3431. NewCteTable[Next->Index].Active = FALSE;
  3432. }
  3433. //
  3434. // Make all the new table entries.
  3435. // Mark them as invalid.
  3436. //
  3437. for (i=NlpCacheControl.Entries; i<CachedLogonsCount; i++) {
  3438. //
  3439. // Add the CTE entry to the inactive list
  3440. //
  3441. InsertTailList( &NewInactive, &NewCteTable[i].Link );
  3442. NewCteTable[i].Active = FALSE;
  3443. NewCteTable[i].Index = i;
  3444. NtStatus = NlpMakeNewCacheEntry( i );
  3445. if (!NT_SUCCESS(NtStatus)) {
  3446. FreeToHeap( NewCteTable );
  3447. return(NtStatus);
  3448. }
  3449. }
  3450. } else {
  3451. //
  3452. // Try to shrink the cache.
  3453. //
  3454. if (CachedLogonsCount != 0) {
  3455. //
  3456. // 0 size implies disabling the cache.
  3457. // That is a degenerate case of shrinking that
  3458. // requires only the last few steps of shrinking.
  3459. //
  3460. //
  3461. // Allocate an array of pointers for reading registry and secret
  3462. // info into. Clear it to assist in cleanup.
  3463. //
  3464. CacheAndSecrets = (PNLP_CACHE_AND_SECRETS)
  3465. AllocateFromHeap( sizeof( NLP_CACHE_AND_SECRETS ) *
  3466. CachedLogonsCount );
  3467. if (CacheAndSecrets == NULL) {
  3468. FreeToHeap( NlpCteTable );
  3469. NlpCacheControl.Entries = ErrorCacheSize;
  3470. return(STATUS_NO_MEMORY);
  3471. }
  3472. RtlZeroMemory( CacheAndSecrets,
  3473. (sizeof( NLP_CACHE_AND_SECRETS ) * CachedLogonsCount) );
  3474. //
  3475. // Set up the new CTE table to be inactive
  3476. //
  3477. InitializeListHead( &NewActive );
  3478. InitializeListHead( &NewInactive );
  3479. for (i=0; i<CachedLogonsCount; i++) {
  3480. InsertTailList( &NewInactive, &NewCteTable[i].Link );
  3481. NewCteTable[i].Index = i;
  3482. NewCteTable[i].Active = FALSE;
  3483. }
  3484. //
  3485. // Walk the current active list, reading
  3486. // entries and copying information into the new CTE table.
  3487. //
  3488. i = 0;
  3489. Next = (PNLP_CTE)NlpActiveCtes.Flink;
  3490. while (Next != (PNLP_CTE)&NlpActiveCtes && i<CachedLogonsCount) {
  3491. NtStatus = NlpReadCacheEntryByIndex( Next->Index,
  3492. &CacheAndSecrets[i].CacheEntry,
  3493. &CacheAndSecrets[i].EntrySize
  3494. // &EntrySize
  3495. );
  3496. if (NT_SUCCESS(NtStatus)) {
  3497. //
  3498. // for pre-Win2000 cache entries, read the associated secret.
  3499. //
  3500. if( CacheAndSecrets[i].CacheEntry->Revision < NLP_CACHE_REVISION_NT_5_0 ) {
  3501. NtStatus = NlpOpenSecret( Next->Index );
  3502. if (NT_SUCCESS(NtStatus)) {
  3503. NtStatus = NlpReadSecret( &CacheAndSecrets[i].NewSecret,
  3504. &CacheAndSecrets[i].OldSecret);
  3505. NlpCloseSecret();
  3506. }
  3507. }
  3508. if (NT_SUCCESS(NtStatus)) {
  3509. //
  3510. // Only make this entry active if everything was
  3511. // successfully read in.
  3512. //
  3513. CacheAndSecrets[i].Active = TRUE;
  3514. i++; // advance our new CTE table index
  3515. }
  3516. }
  3517. Next = (PNLP_CTE)(Next->Link.Flink);
  3518. } // end-while
  3519. //
  3520. // At this point "i" indicates how many CacheAndSecrets entries
  3521. // are active. Furthermore, the entries were assembled
  3522. // in the CacheAndSecrets array in ascending time order, which
  3523. // is the order they need to be placed in the new CTE table.
  3524. //
  3525. for ( j=0; j<i; j++) {
  3526. Next = &NewCteTable[j];
  3527. //
  3528. // The Time field in the original cache entry is not aligned
  3529. // properly, so copy each field individually.
  3530. //
  3531. Next->Time.LowPart = CacheAndSecrets[j].CacheEntry->Time.LowPart;
  3532. Next->Time.HighPart = CacheAndSecrets[j].CacheEntry->Time.HighPart;
  3533. //
  3534. // Try writing out the new entry's information
  3535. //
  3536. NtStatus = NlpWriteCacheEntry( j,
  3537. CacheAndSecrets[j].CacheEntry,
  3538. CacheAndSecrets[j].EntrySize
  3539. );
  3540. if (NT_SUCCESS(NtStatus)) {
  3541. if ( CacheAndSecrets[j].CacheEntry->Revision < NLP_CACHE_REVISION_NT_5_0 ) {
  3542. //
  3543. // for pre-Win2000 cache entries, write the secret back out.
  3544. // note: we don't bother to try to migrate pre-win2000 -> Win2000
  3545. // here, because this will happen later, as a side-effect
  3546. // of updating cache entry during successful DC validated logon.
  3547. //
  3548. NtStatus = NlpOpenSecret( j );
  3549. if (NT_SUCCESS(NtStatus)) {
  3550. NtStatus = NlpWriteSecret(CacheAndSecrets[j].NewSecret,
  3551. CacheAndSecrets[j].OldSecret);
  3552. }
  3553. }
  3554. if (NT_SUCCESS(NtStatus)) {
  3555. //
  3556. // move the corresponding entry into the new CTEs
  3557. // active list.
  3558. //
  3559. Next->Active = TRUE;
  3560. RemoveEntryList( &Next->Link );
  3561. InsertTailList( &NewActive, &Next->Link );
  3562. }
  3563. }
  3564. //
  3565. // Free the CacheEntry and secret information
  3566. //
  3567. if (CacheAndSecrets[j].CacheEntry != NULL) {
  3568. FreeToHeap( CacheAndSecrets[j].CacheEntry );
  3569. }
  3570. if (CacheAndSecrets[j].NewSecret != NULL) {
  3571. RtlZeroMemory(CacheAndSecrets[j].NewSecret->Buffer, CacheAndSecrets[j].NewSecret->Length);
  3572. MIDL_user_free( CacheAndSecrets[j].NewSecret );
  3573. }
  3574. if (CacheAndSecrets[j].OldSecret != NULL) {
  3575. RtlZeroMemory(CacheAndSecrets[j].OldSecret->Buffer, CacheAndSecrets[j].OldSecret->Length);
  3576. MIDL_user_free( CacheAndSecrets[j].OldSecret );
  3577. }
  3578. }
  3579. //
  3580. // Free the CacheAndSecrets array
  3581. // (everything in it has already been freed)
  3582. //
  3583. if (CacheAndSecrets != NULL) {
  3584. FreeToHeap( CacheAndSecrets );
  3585. }
  3586. //
  3587. // Change remaining entries to invalid (on disk)
  3588. //
  3589. for (j=i; j<CachedLogonsCount; j++) {
  3590. NlpMakeNewCacheEntry( j );
  3591. }
  3592. } // end-if (CachedLogonsCount != 0)
  3593. //
  3594. // Now get rid of extra (no longer needed) entries
  3595. //
  3596. for (j=CachedLogonsCount; j<NlpCacheControl.Entries; j++) {
  3597. NlpEliminateCacheEntry( j );
  3598. }
  3599. }
  3600. //
  3601. // We have successfully:
  3602. //
  3603. // Allocated the new CTE table.
  3604. //
  3605. // Filled the CTE table with copies of the currently
  3606. // active CTEs (including putting each CTE on an active
  3607. // or inactive list).
  3608. //
  3609. // Established new CTE entries, including the corresponding
  3610. // secrets and cache keys in the registry, for the
  3611. // new CTEs.
  3612. //
  3613. //
  3614. // All we have left to do is:
  3615. //
  3616. //
  3617. // Update the cache control structure in the registry
  3618. // to indicate we have a new length
  3619. //
  3620. // move the new CTE over to the real Active and Inactive
  3621. // list heads (rather than the local ones we've used so far)
  3622. //
  3623. // deallocate the old CTE table.
  3624. //
  3625. // Re-set the entries count in the in-memory
  3626. // cache-control structure NlpCacheControl.
  3627. //
  3628. NlpCacheControl.Entries = CachedLogonsCount;
  3629. NtStatus = NlpWriteCacheControl();
  3630. if (CachedLogonsCount > 0) { // Only necessary if there is a new CTE table
  3631. if (!NT_SUCCESS(NtStatus)) {
  3632. FreeToHeap( NewCteTable );
  3633. NlpCacheControl.Entries = ErrorCacheSize;
  3634. return(NtStatus);
  3635. }
  3636. InsertHeadList( &NewActive, &NlpActiveCtes );
  3637. RemoveEntryList( &NewActive );
  3638. InsertHeadList( &NewInactive, &NlpInactiveCtes );
  3639. RemoveEntryList( &NewInactive );
  3640. FreeToHeap( NlpCteTable );
  3641. NlpCteTable = NewCteTable;
  3642. }
  3643. return(NtStatus);
  3644. }
  3645. NTSTATUS
  3646. NlpWriteCacheControl( VOID )
  3647. /*++
  3648. Routine Description:
  3649. This function writes a new cache length out to the
  3650. cache control structure stored in the registry.
  3651. Note:
  3652. When lengthening the cache, call this routine after the cache
  3653. entries and corresponding secrets have been established for
  3654. the new length.
  3655. When shortening the cache, call this routine before the cache
  3656. entries and corresponding secrets being discarded have actually
  3657. been discarded.
  3658. This ensures that if the system crashes during the resizing
  3659. operation, it will be in a valid state when the system comes
  3660. back up.
  3661. Arguments:
  3662. None.
  3663. Return Value:
  3664. STATUS_SUCCESS
  3665. --*/
  3666. {
  3667. NTSTATUS
  3668. NtStatus;
  3669. UNICODE_STRING
  3670. CacheControlValueName;
  3671. RtlInitUnicodeString( &CacheControlValueName, L"NL$Control" );
  3672. NtStatus = NtSetValueKey( NlpCacheHandle,
  3673. &CacheControlValueName, // Name
  3674. 0, // TitleIndex
  3675. REG_BINARY, // Type
  3676. &NlpCacheControl, // Data
  3677. sizeof(NLP_CACHE_CONTROL) // DataLength
  3678. );
  3679. return(NtStatus);
  3680. }
  3681. VOID
  3682. NlpMakeCacheEntryName(
  3683. IN ULONG EntryIndex,
  3684. OUT PUNICODE_STRING Name
  3685. )
  3686. /*++
  3687. Routine Description:
  3688. This function builds a name of a cache entry value or secret name
  3689. for a cached entry. The name is based upon the index of the cache
  3690. entry.
  3691. Names are of the form:
  3692. "NLP1" through "NLPnnn"
  3693. where "nnn" is the largest allowable entry count (see
  3694. NLP_MAX_LOGON_CACHE_COUNT).
  3695. The output UNICODE_STRING buffer is expected to be large enough
  3696. to accept this string with a null termination on it.
  3697. Arguments:
  3698. EntryIndex - The index of the cache entry whose name is desired.
  3699. Name - A unicode string large enough to accept the name.
  3700. Return Value:
  3701. STATUS_SUCCESS
  3702. --*/
  3703. {
  3704. NTSTATUS
  3705. NtStatus;
  3706. UNICODE_STRING
  3707. TmpString;
  3708. WCHAR
  3709. TmpStringBuffer[17];
  3710. ASSERT(Name->MaximumLength >= 7*sizeof(WCHAR) );
  3711. ASSERT( EntryIndex <= NLP_MAX_LOGON_CACHE_COUNT );
  3712. Name->Length = 0;
  3713. RtlAppendUnicodeToString( Name, L"NL$" );
  3714. TmpString.MaximumLength = 16;
  3715. TmpString.Length = 0;
  3716. TmpString.Buffer = TmpStringBuffer;
  3717. NtStatus = RtlIntegerToUnicodeString ( (EntryIndex+1), // make 1 based index
  3718. 10, // Base 10
  3719. &TmpString
  3720. );
  3721. ASSERT(NT_SUCCESS(NtStatus));
  3722. RtlAppendUnicodeStringToString( Name, &TmpString );
  3723. return;
  3724. }
  3725. NTSTATUS
  3726. NlpMakeNewCacheEntry(
  3727. ULONG Index
  3728. )
  3729. /*++
  3730. Routine Description:
  3731. This routine creates a secret and a cache entry value for a
  3732. new cache entry with the specified index.
  3733. The secret handle is NOT left open.
  3734. Arguments:
  3735. Index - The index of the cache entry whose name is desired.
  3736. Name - A unicode string large enough to accept the name.
  3737. Return Value:
  3738. STATUS_SUCCESS
  3739. --*/
  3740. {
  3741. NTSTATUS
  3742. NtStatus;
  3743. LOGON_CACHE_ENTRY
  3744. Entry;
  3745. UNICODE_STRING
  3746. ValueName;
  3747. WCHAR
  3748. NameBuffer[32];
  3749. LSAPR_HANDLE
  3750. SecretHandle;
  3751. ValueName.Length = 0;
  3752. ValueName.MaximumLength = 32;
  3753. ValueName.Buffer = &NameBuffer[0];
  3754. NlpMakeCacheEntryName( Index, &ValueName );
  3755. NtStatus = I_LsarOpenSecret( NtLmGlobalPolicyHandle,
  3756. (PLSAPR_UNICODE_STRING) &ValueName,
  3757. DELETE,
  3758. &SecretHandle
  3759. );
  3760. if( NT_SUCCESS( NtStatus ) ) {
  3761. //
  3762. // for Windows2000, we remove old style cache entry related
  3763. // LSA secrets.
  3764. //
  3765. //
  3766. // Deleting and object causes its handle to be closed
  3767. //
  3768. I_LsarDelete( SecretHandle );
  3769. // I_LsarClose( &SecretHandle );
  3770. }
  3771. //
  3772. // Create the cache entry marked as invalid
  3773. //
  3774. RtlZeroMemory( &Entry, sizeof(Entry) );
  3775. Entry.Revision = NLP_CACHE_REVISION;
  3776. Entry.Valid = FALSE;
  3777. NtStatus = NtSetValueKey( NlpCacheHandle,
  3778. &ValueName, // Name
  3779. 0, // TitleIndex
  3780. REG_BINARY, // Type
  3781. &Entry, // Data
  3782. sizeof(LOGON_CACHE_ENTRY) // DataLength
  3783. );
  3784. return(NtStatus);
  3785. }
  3786. NTSTATUS
  3787. NlpEliminateCacheEntry(
  3788. IN ULONG Index
  3789. )
  3790. /*++
  3791. Routine Description:
  3792. Delete the registry value and secret object related to a
  3793. CTE entry.
  3794. Arguments:
  3795. Index - The index of the entry whose value and secret are to
  3796. be deleted. This value is used only to build a name with
  3797. (not to reference the CTE table).
  3798. Return Value:
  3799. --*/
  3800. {
  3801. NTSTATUS
  3802. NtStatus;
  3803. UNICODE_STRING
  3804. ValueName;
  3805. WCHAR
  3806. NameBuffer[32];
  3807. LSAPR_HANDLE
  3808. SecretHandle;
  3809. ValueName.Buffer = &NameBuffer[0];
  3810. ValueName.MaximumLength = 32;
  3811. ValueName.Length = 0;
  3812. NlpMakeCacheEntryName( Index, &ValueName );
  3813. NtStatus = I_LsarOpenSecret(NtLmGlobalPolicyHandle,
  3814. (PLSAPR_UNICODE_STRING) &ValueName,
  3815. DELETE,
  3816. &SecretHandle
  3817. );
  3818. if (NT_SUCCESS(NtStatus)) {
  3819. //
  3820. // Deleting and object causes its handle to be closed
  3821. //
  3822. NtStatus = I_LsarDelete( SecretHandle );
  3823. }
  3824. //
  3825. // Now delete the registry value
  3826. //
  3827. NtStatus = NtDeleteValueKey( NlpCacheHandle, &ValueName );
  3828. return(NtStatus);
  3829. }
  3830. NTSTATUS
  3831. NlpReadCacheEntryByIndex(
  3832. IN ULONG Index,
  3833. OUT PLOGON_CACHE_ENTRY* CacheEntry,
  3834. OUT PULONG EntrySize
  3835. )
  3836. /*++
  3837. Routine Description:
  3838. Reads a cache entry from registry
  3839. Arguments:
  3840. Index - CTE table index of the entry to open.
  3841. This is used to build the entry's value and secret names.
  3842. CacheEntry - pointer to place to return pointer to LOGON_CACHE_ENTRY
  3843. EntrySize - size of returned LOGON_CACHE_ENTRY
  3844. Return Value:
  3845. NTSTATUS
  3846. Success = STATUS_SUCCESS
  3847. *ppEntry points to allocated LOGON_CACHE_ENTRY
  3848. *EntrySize is size of returned data
  3849. Failure = STATUS_NO_MEMORY
  3850. Couldn't allocate buffer for LOGON_CACHE_ENTRY
  3851. --*/
  3852. {
  3853. NTSTATUS
  3854. NtStatus;
  3855. UNICODE_STRING
  3856. ValueName;
  3857. WCHAR
  3858. NameBuffer[32];
  3859. ULONG
  3860. RequiredSize;
  3861. PKEY_VALUE_FULL_INFORMATION
  3862. RegInfo;
  3863. PLOGON_CACHE_ENTRY
  3864. RCacheEntry; // CacheEntry in registry buffer
  3865. BYTE FastBuffer[ 512 ];
  3866. PBYTE SlowBuffer = NULL;
  3867. ValueName.Buffer = &NameBuffer[0];
  3868. ValueName.MaximumLength = 32;
  3869. ValueName.Length = 0;
  3870. NlpMakeCacheEntryName( Index, &ValueName );
  3871. RegInfo = (PKEY_VALUE_FULL_INFORMATION)FastBuffer;
  3872. RequiredSize = sizeof(FastBuffer);
  3873. //
  3874. // perform first query to find out how much buffer to allocate
  3875. //
  3876. NtStatus = NtQueryValueKey(NlpCacheHandle,
  3877. &ValueName,
  3878. KeyValueFullInformation,
  3879. (PVOID)RegInfo,
  3880. RequiredSize,
  3881. &RequiredSize
  3882. );
  3883. if( (NtStatus == STATUS_BUFFER_TOO_SMALL) ||
  3884. (NtStatus == STATUS_BUFFER_OVERFLOW) ) {
  3885. //
  3886. // allocate buffer then do query again, this time receiving data
  3887. //
  3888. SlowBuffer = (PBYTE)AllocateFromHeap(RequiredSize);
  3889. if (SlowBuffer == NULL) {
  3890. return(STATUS_NO_MEMORY);
  3891. }
  3892. RegInfo = (PKEY_VALUE_FULL_INFORMATION)SlowBuffer;
  3893. NtStatus = NtQueryValueKey(NlpCacheHandle,
  3894. &ValueName,
  3895. KeyValueFullInformation,
  3896. (PVOID)RegInfo,
  3897. RequiredSize,
  3898. &RequiredSize
  3899. );
  3900. }
  3901. if (NT_SUCCESS(NtStatus)) {
  3902. #if DBG
  3903. if (DumpCacheInfo) {
  3904. DbgPrint("NlpReadCacheEntryByIndex: Index : %d\n"
  3905. " NtQueryValueKey returns: %d bytes\n"
  3906. " DataOffset=%d\n"
  3907. " DataLength=%d\n",
  3908. Index, RequiredSize, RegInfo->DataOffset, RegInfo->DataLength);
  3909. }
  3910. #endif
  3911. if( RegInfo->DataLength == 0 ) {
  3912. NtStatus = STATUS_INTERNAL_DB_CORRUPTION;
  3913. *CacheEntry = NULL;
  3914. *EntrySize = 0;
  3915. } else {
  3916. RCacheEntry = (PLOGON_CACHE_ENTRY)((PCHAR)RegInfo + RegInfo->DataOffset);
  3917. *EntrySize = RegInfo->DataLength;
  3918. (*CacheEntry) = (PLOGON_CACHE_ENTRY)AllocateFromHeap( (*EntrySize) );
  3919. if ((*CacheEntry) == NULL) {
  3920. NtStatus = STATUS_NO_MEMORY;
  3921. } else {
  3922. RtlCopyMemory( (*CacheEntry),
  3923. RCacheEntry,
  3924. (*EntrySize) );
  3925. }
  3926. }
  3927. }
  3928. if ( SlowBuffer )
  3929. FreeToHeap( SlowBuffer );
  3930. return(NtStatus);
  3931. }
  3932. VOID
  3933. NlpAddEntryToActiveList(
  3934. IN ULONG Index
  3935. )
  3936. /*++
  3937. Routine Description:
  3938. Place a CTE entry in the active CTE list.
  3939. This requires placing the entry in the right location in
  3940. the list chronologically. The beginning of the list is
  3941. the most recently updated (or referenced) cache entry.
  3942. The end of the list is the oldest active cache entry.
  3943. Note - The entry may be already in the active list (but
  3944. in the wrong place), or may be on the inactive list.
  3945. It will be removed from whichever list it is on.
  3946. Arguments:
  3947. Index - CTE table index of the entry to make active..
  3948. Return Value:
  3949. None.
  3950. --*/
  3951. {
  3952. PNLP_CTE
  3953. Next;
  3954. //
  3955. // Remove the entry from its current list, and then place it
  3956. // in the active list.
  3957. //
  3958. RemoveEntryList( &NlpCteTable[Index].Link );
  3959. //
  3960. // Now walk the active list until we find a place to insert
  3961. // the entry. It must follow all entries with more recent
  3962. // time stamps.
  3963. //
  3964. Next = (PNLP_CTE)NlpActiveCtes.Flink;
  3965. while (Next != (PNLP_CTE)&NlpActiveCtes) {
  3966. if ( NlpCteTable[Index].Time.QuadPart > Next->Time.QuadPart ) {
  3967. //
  3968. // More recent than this entry - add it here
  3969. //
  3970. break; // out of while-loop
  3971. }
  3972. Next = (PNLP_CTE)(Next->Link.Flink); // Advance to next entry
  3973. }
  3974. //
  3975. // Use the preceding entry as the list head.
  3976. //
  3977. InsertHeadList( Next->Link.Blink, &NlpCteTable[Index].Link );
  3978. //
  3979. // Mark the entry as valid
  3980. //
  3981. NlpCteTable[Index].Active = TRUE;
  3982. return;
  3983. }
  3984. VOID
  3985. NlpAddEntryToInactiveList(
  3986. IN ULONG Index
  3987. )
  3988. /*++
  3989. Routine Description:
  3990. Move the CTE entry to the inactive list.
  3991. It doesn't matter if the entry is already inactive.
  3992. Arguments:
  3993. Index - CTE table index of the entry to make inactive.
  3994. Return Value:
  3995. None.
  3996. --*/
  3997. {
  3998. //
  3999. // Remove the entry from its current list, and then place it
  4000. // in the inactive list.
  4001. //
  4002. RemoveEntryList( &NlpCteTable[Index].Link );
  4003. InsertTailList( &NlpInactiveCtes, &NlpCteTable[Index].Link );
  4004. //
  4005. // Mark the entry as invalid
  4006. //
  4007. NlpCteTable[Index].Active = FALSE;
  4008. return;
  4009. }
  4010. VOID
  4011. NlpGetFreeEntryIndex(
  4012. OUT PULONG Index
  4013. )
  4014. /*++
  4015. Routine Description:
  4016. This routine returns the index of either a free entry,
  4017. or, lacking any free entries, the oldest active entry.
  4018. The entry is left on the list it is already on. If it
  4019. is used by the caller, then the caller must ensure it is
  4020. re-assigned to the active list (using NlpAddEntryToActiveList()).
  4021. This routine is only callable if the cache is enabled (that is,
  4022. NlpCacheControl.Entries != 0).
  4023. Arguments:
  4024. Index - Receives the index of the next available entry.
  4025. Return Value:
  4026. None.
  4027. --*/
  4028. {
  4029. //
  4030. // See if the Inactive list is empty.
  4031. //
  4032. if (NlpInactiveCtes.Flink != &NlpInactiveCtes) {
  4033. (*Index) = ((PNLP_CTE)(NlpInactiveCtes.Flink))->Index;
  4034. } else {
  4035. //
  4036. // Have to return the oldest active entry.
  4037. //
  4038. (*Index) = ((PNLP_CTE)(NlpActiveCtes.Blink))->Index;
  4039. }
  4040. return;
  4041. }
  4042. /////////////////////////////////////////////////////////////////////////
  4043. // //
  4044. // Diagnostic support services //
  4045. // //
  4046. /////////////////////////////////////////////////////////////////////////
  4047. //
  4048. // diagnostic dump routines
  4049. //
  4050. #if DBG
  4051. PCHAR
  4052. DumpOwfPasswordToString(
  4053. OUT PCHAR Buffer,
  4054. IN PLM_OWF_PASSWORD Password
  4055. )
  4056. {
  4057. int i;
  4058. PCHAR bufptr;
  4059. for (i = 0, bufptr = Buffer; i < sizeof(*Password); ++i) {
  4060. sprintf(bufptr, "%02.2x ", ((PCHAR)Password)[i] & 0xff);
  4061. bufptr += 3;
  4062. }
  4063. return Buffer;
  4064. }
  4065. VOID
  4066. DumpLogonInfo(
  4067. IN PNETLOGON_LOGON_IDENTITY_INFO LogonInfo
  4068. )
  4069. {
  4070. DbgPrint( "\n"
  4071. "NETLOGON_INTERACTIVE_INFO:\n"
  4072. "DomainName : \"%*.*ws\"\n"
  4073. "UserName : \"%*.*ws\"\n"
  4074. "Parm Ctrl : %u (%x)\n"
  4075. "LogonId : %u.%u (%x.%x)\n"
  4076. "Workstation : \"%*.*ws\"\n",
  4077. LogonInfo->LogonDomainName.Length/sizeof(WCHAR),
  4078. LogonInfo->LogonDomainName.Length/sizeof(WCHAR),
  4079. LogonInfo->LogonDomainName.Buffer,
  4080. LogonInfo->UserName.Length/sizeof(WCHAR),
  4081. LogonInfo->UserName.Length/sizeof(WCHAR),
  4082. LogonInfo->UserName.Buffer,
  4083. LogonInfo->ParameterControl,
  4084. LogonInfo->ParameterControl,
  4085. LogonInfo->LogonId.HighPart,
  4086. LogonInfo->LogonId.LowPart,
  4087. LogonInfo->LogonId.HighPart,
  4088. LogonInfo->LogonId.LowPart,
  4089. LogonInfo->Workstation.Length/sizeof(WCHAR),
  4090. LogonInfo->Workstation.Length/sizeof(WCHAR),
  4091. LogonInfo->Workstation.Buffer
  4092. );
  4093. }
  4094. char*
  4095. MapWeekday(
  4096. IN CSHORT Weekday
  4097. )
  4098. {
  4099. switch (Weekday) {
  4100. case 0: return "Sunday";
  4101. case 1: return "Monday";
  4102. case 2: return "Tuesday";
  4103. case 3: return "Wednesday";
  4104. case 4: return "Thursday";
  4105. case 5: return "Friday";
  4106. case 6: return "Saturday";
  4107. }
  4108. return "???";
  4109. }
  4110. VOID
  4111. DumpTime(
  4112. IN LPSTR String,
  4113. IN POLD_LARGE_INTEGER OldTime
  4114. )
  4115. {
  4116. TIME_FIELDS tf;
  4117. LARGE_INTEGER Time;
  4118. OLD_TO_NEW_LARGE_INTEGER( (*OldTime), Time );
  4119. RtlTimeToTimeFields(&Time, &tf);
  4120. DbgPrint("%s%02d:%02d:%02d.%03d %02d/%02d/%d (%s [%d])\n",
  4121. String,
  4122. tf.Hour,
  4123. tf.Minute,
  4124. tf.Second,
  4125. tf.Milliseconds,
  4126. tf.Month,
  4127. tf.Day,
  4128. tf.Year,
  4129. MapWeekday(tf.Weekday),
  4130. tf.Weekday
  4131. );
  4132. }
  4133. VOID
  4134. DumpGroupIds(
  4135. IN LPSTR String,
  4136. IN ULONG Count,
  4137. IN PGROUP_MEMBERSHIP GroupIds
  4138. )
  4139. {
  4140. DbgPrint(String);
  4141. if (!Count) {
  4142. DbgPrint("No group IDs!\n");
  4143. } else {
  4144. char tab[80];
  4145. memset(tab, ' ', strlen(String));
  4146. // tab[strcspn(String, "%")] = 0;
  4147. tab[strlen(String)] = 0;
  4148. while (Count--) {
  4149. DbgPrint("%d, %d\n", GroupIds->RelativeId, GroupIds->Attributes);
  4150. if (Count) {
  4151. DbgPrint(tab);
  4152. }
  4153. ++GroupIds;
  4154. }
  4155. }
  4156. }
  4157. VOID
  4158. DumpSessKey(
  4159. IN LPSTR String,
  4160. IN PUSER_SESSION_KEY Key
  4161. )
  4162. {
  4163. int len;
  4164. DbgPrint(String);
  4165. DbgPrint("%02.2x-%02.2x-%02.2x-%02.2x-%02.2x-%02.2x-%02.2x-%02.2x\n",
  4166. ((PUCHAR)&Key->data[0])[0],
  4167. ((PUCHAR)&Key->data[0])[1],
  4168. ((PUCHAR)&Key->data[0])[2],
  4169. ((PUCHAR)&Key->data[0])[3],
  4170. ((PUCHAR)&Key->data[0])[4],
  4171. ((PUCHAR)&Key->data[0])[5],
  4172. ((PUCHAR)&Key->data[0])[6],
  4173. ((PUCHAR)&Key->data[0])[7]
  4174. );
  4175. len = (int) strlen(String);
  4176. DbgPrint("%-*.*s", len, len, "");
  4177. DbgPrint("%02.2x-%02.2x-%02.2x-%02.2x-%02.2x-%02.2x-%02.2x-%02.2x\n",
  4178. ((PUCHAR)&Key->data[1])[0],
  4179. ((PUCHAR)&Key->data[1])[1],
  4180. ((PUCHAR)&Key->data[1])[2],
  4181. ((PUCHAR)&Key->data[1])[3],
  4182. ((PUCHAR)&Key->data[1])[4],
  4183. ((PUCHAR)&Key->data[1])[5],
  4184. ((PUCHAR)&Key->data[1])[6],
  4185. ((PUCHAR)&Key->data[1])[7]
  4186. );
  4187. }
  4188. VOID
  4189. DumpSid(
  4190. LPSTR String,
  4191. PISID Sid
  4192. )
  4193. {
  4194. DbgPrint(String);
  4195. if ( Sid == NULL ) {
  4196. DbgPrint(0, "(null)\n");
  4197. } else {
  4198. UNICODE_STRING SidString;
  4199. NTSTATUS Status;
  4200. Status = RtlConvertSidToUnicodeString( &SidString, Sid, TRUE );
  4201. if ( !NT_SUCCESS(Status) ) {
  4202. DbgPrint("Invalid 0x%lX\n", Status );
  4203. } else {
  4204. DbgPrint( "%wZ\n", &SidString );
  4205. RtlFreeUnicodeString( &SidString );
  4206. }
  4207. }
  4208. }
  4209. VOID
  4210. DumpAccountInfo(
  4211. IN PNETLOGON_VALIDATION_SAM_INFO4 AccountInfo
  4212. )
  4213. {
  4214. DbgPrint( "\n"
  4215. "NETLOGON_VALIDATION_SAM_INFO:\n");
  4216. DumpTime( "LogonTime : ", &AccountInfo->LogonTime);
  4217. DumpTime( "LogoffTime : ", &AccountInfo->LogoffTime);
  4218. DumpTime( "KickOffTime : ", &AccountInfo->KickOffTime);
  4219. DumpTime( "PasswordLastSet : ", &AccountInfo->PasswordLastSet);
  4220. DumpTime( "PasswordCanChange : ", &AccountInfo->PasswordCanChange);
  4221. DumpTime( "PasswordMustChange : ", &AccountInfo->PasswordMustChange);
  4222. DbgPrint( "EffectiveName : \"%*.*ws\"\n"
  4223. "Upn : \"%*.*ws\"\n"
  4224. "FullName : \"%*.*ws\"\n"
  4225. "LogonScript : \"%*.*ws\"\n"
  4226. "ProfilePath : \"%*.*ws\"\n"
  4227. "HomeDirectory : \"%*.*ws\"\n"
  4228. "HomeDirectoryDrive : \"%*.*ws\"\n"
  4229. "LogonCount : %d\n"
  4230. "BadPasswordCount : %d\n"
  4231. "UserId : %d\n"
  4232. "PrimaryGroupId : %d\n"
  4233. "GroupCount : %d\n",
  4234. AccountInfo->EffectiveName.Length/sizeof(WCHAR),
  4235. AccountInfo->EffectiveName.Length/sizeof(WCHAR),
  4236. AccountInfo->EffectiveName.Buffer,
  4237. AccountInfo->Upn.Length/sizeof(WCHAR),
  4238. AccountInfo->Upn.Length/sizeof(WCHAR),
  4239. AccountInfo->Upn.Buffer,
  4240. AccountInfo->FullName.Length/sizeof(WCHAR),
  4241. AccountInfo->FullName.Length/sizeof(WCHAR),
  4242. AccountInfo->FullName.Buffer,
  4243. AccountInfo->LogonScript.Length/sizeof(WCHAR),
  4244. AccountInfo->LogonScript.Length/sizeof(WCHAR),
  4245. AccountInfo->LogonScript.Buffer,
  4246. AccountInfo->ProfilePath.Length/sizeof(WCHAR),
  4247. AccountInfo->ProfilePath.Length/sizeof(WCHAR),
  4248. AccountInfo->ProfilePath.Buffer,
  4249. AccountInfo->HomeDirectory.Length/sizeof(WCHAR),
  4250. AccountInfo->HomeDirectory.Length/sizeof(WCHAR),
  4251. AccountInfo->HomeDirectory.Buffer,
  4252. AccountInfo->HomeDirectoryDrive.Length/sizeof(WCHAR),
  4253. AccountInfo->HomeDirectoryDrive.Length/sizeof(WCHAR),
  4254. AccountInfo->HomeDirectoryDrive.Buffer,
  4255. AccountInfo->LogonCount,
  4256. AccountInfo->BadPasswordCount,
  4257. AccountInfo->UserId,
  4258. AccountInfo->PrimaryGroupId,
  4259. AccountInfo->GroupCount
  4260. );
  4261. DumpGroupIds("GroupIds : ",
  4262. AccountInfo->GroupCount,
  4263. AccountInfo->GroupIds
  4264. );
  4265. DbgPrint( "UserFlags : 0x%08x\n",
  4266. AccountInfo->UserFlags
  4267. );
  4268. DumpSessKey("UserSessionKey : ", &AccountInfo->UserSessionKey);
  4269. DbgPrint( "LogonServer : \"%*.*ws\"\n"
  4270. "LogonDomainName : \"%*.*ws\"\n"
  4271. "DnsLogonDomainName : \"%*.*ws\"\n",
  4272. AccountInfo->LogonServer.Length/sizeof(WCHAR),
  4273. AccountInfo->LogonServer.Length/sizeof(WCHAR),
  4274. AccountInfo->LogonServer.Buffer,
  4275. AccountInfo->LogonDomainName.Length/sizeof(WCHAR),
  4276. AccountInfo->LogonDomainName.Length/sizeof(WCHAR),
  4277. AccountInfo->LogonDomainName.Buffer,
  4278. AccountInfo->DnsLogonDomainName.Length/sizeof(WCHAR),
  4279. AccountInfo->DnsLogonDomainName.Length/sizeof(WCHAR),
  4280. AccountInfo->DnsLogonDomainName.Buffer
  4281. );
  4282. DumpSid( "LogonDomainId : ", (PISID)AccountInfo->LogonDomainId);
  4283. }
  4284. VOID
  4285. DumpCacheEntry(
  4286. IN ULONG Index,
  4287. IN PLOGON_CACHE_ENTRY pEntry
  4288. )
  4289. {
  4290. PUCHAR dataptr;
  4291. ULONG length;
  4292. DbgPrint( "\n"
  4293. "LOGON_CACHE_ENTRY:\n"
  4294. "CTE Index : %d\n", Index);
  4295. if (pEntry->Valid != TRUE) {
  4296. DbgPrint( "State : INVALID\n");
  4297. return;
  4298. }
  4299. dataptr = (PUCHAR)(pEntry+1);
  4300. length = pEntry->UserNameLength;
  4301. DbgPrint( "State : VALID\n");
  4302. DbgPrint( "UserName : \"%*.*ws\"\n", length/2, length/2, dataptr);
  4303. dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
  4304. length = pEntry->DomainNameLength;
  4305. DbgPrint( "DomainName : \"%*.*ws\"\n", length/2, length/2, dataptr);
  4306. dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
  4307. length = pEntry->DnsDomainNameLength;
  4308. DbgPrint( "DnsDomainname : \"%*.*ws\"\n", length/2, length/2, dataptr);
  4309. dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
  4310. length = pEntry->UpnLength;
  4311. DbgPrint( "Upn : \"%*.*ws\"\n", length/2, length/2, dataptr);
  4312. dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
  4313. length = pEntry->EffectiveNameLength;
  4314. DbgPrint( "EffectiveName : \"%*.*ws\"\n", length/2, length/2, dataptr);
  4315. dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
  4316. length = pEntry->FullNameLength;
  4317. DbgPrint( "FullName : \"%*.*ws\"\n", length/2, length/2, dataptr);
  4318. dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
  4319. length = pEntry->LogonScriptLength;
  4320. DbgPrint( "LogonScript : \"%*.*ws\"\n", length/2, length/2, dataptr);
  4321. dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
  4322. length = pEntry->ProfilePathLength;
  4323. DbgPrint( "ProfilePath : \"%*.*ws\"\n", length/2, length/2, dataptr);
  4324. dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
  4325. length = pEntry->HomeDirectoryLength;
  4326. DbgPrint( "HomeDirectory : \"%*.*ws\"\n", length/2, length/2, dataptr);
  4327. dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
  4328. length = pEntry->HomeDirectoryDriveLength;
  4329. DbgPrint( "HomeDirectoryDrive : \"%*.*ws\"\n", length/2, length/2, dataptr);
  4330. dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
  4331. DbgPrint( "UserId : %d\n"
  4332. "PrimaryGroupId : %d\n"
  4333. "GroupCount : %d\n",
  4334. pEntry->UserId,
  4335. pEntry->PrimaryGroupId,
  4336. pEntry->GroupCount
  4337. );
  4338. DumpGroupIds(
  4339. "GroupIds : ",
  4340. pEntry->GroupCount,
  4341. (PGROUP_MEMBERSHIP)dataptr
  4342. );
  4343. dataptr = ROUND_UP_POINTER((dataptr+pEntry->GroupCount * sizeof(GROUP_MEMBERSHIP)), sizeof(ULONG));
  4344. length = pEntry->LogonDomainNameLength;
  4345. DbgPrint( "LogonDomainName : \"%*.*ws\"\n", length/2, length/2, dataptr);
  4346. dataptr = ROUND_UP_POINTER(dataptr+length, sizeof(ULONG));
  4347. if (pEntry->SidCount) {
  4348. ULONG i, sidLength;
  4349. PULONG SidAttributes = (PULONG) dataptr;
  4350. dataptr = ROUND_UP_POINTER(dataptr + pEntry->SidCount * sizeof(ULONG), sizeof(ULONG));
  4351. for (i = 0; i < pEntry->SidCount ; i++ ) {
  4352. sidLength = RtlLengthSid ((PSID) dataptr);
  4353. DumpSid("Sid : ",(PISID) dataptr);
  4354. DbgPrint("\tAttributes = 0x%x\n",SidAttributes[i]);
  4355. dataptr = ROUND_UP_POINTER(dataptr + sidLength, sizeof(ULONG));
  4356. }
  4357. }
  4358. DumpSid( "LogonDomainId : ", (PISID)dataptr);
  4359. }
  4360. #endif