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.

555 lines
14 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. cache.cxx
  5. Abstract:
  6. Injectee
  7. Author:
  8. Larry Zhu (LZhu) December 1, 2001 Created
  9. Environment:
  10. User Mode
  11. Revision History:
  12. --*/
  13. #include "precomp.hxx"
  14. #pragma hdrstop
  15. #include <lmcons.h>
  16. #include <ntsamp.h>
  17. #include <md5.h>
  18. #include <hmac.h>
  19. #include <rc4.h>
  20. #include "cache.hxx"
  21. BOOL
  22. DllMain(
  23. IN HANDLE hModule,
  24. IN DWORD dwReason,
  25. IN DWORD dwReserved
  26. )
  27. {
  28. BOOL bRet;
  29. switch (dwReason)
  30. {
  31. case DLL_THREAD_ATTACH:
  32. break;
  33. case DLL_THREAD_DETACH:
  34. break;
  35. case DLL_PROCESS_DETACH:
  36. break;
  37. case DLL_PROCESS_ATTACH:
  38. break;
  39. default:
  40. break;
  41. }
  42. bRet = DllMainDefaultHandler(hModule, dwReason, dwReserved);
  43. DebugPrintf(SSPI_LOG, "DllMain leaving %#x\n", bRet);
  44. return bRet;
  45. }
  46. int
  47. RunIt(
  48. IN ULONG cbParameters,
  49. IN VOID* pvParameters
  50. )
  51. {
  52. //
  53. // RunItDefaultHandler calls Start() and adds try except
  54. //
  55. DWORD dwErr;
  56. dwErr = RunItDefaultHandler(cbParameters, pvParameters);
  57. DebugPrintf(SSPI_LOG, "RunIt leaving %#x\n", dwErr);
  58. return dwErr;
  59. }
  60. #if 0
  61. Return Values for Start():
  62. ERROR_NO_MORE_USER_HANDLES unload repeatedly
  63. ERROR_SERVER_HAS_OPEN_HANDLES no unload at all
  64. others unload once
  65. #endif 0
  66. int
  67. Start(
  68. IN ULONG cbParameters,
  69. IN VOID* pvParameters
  70. )
  71. {
  72. DWORD dwErr = ERROR_SUCCESS;
  73. CHAR* pNlpCacheEncryptionKey = NULL;
  74. LIST_ENTRY* pNlpActiveCtes = NULL;
  75. SspiPrintHex(SSPI_LOG, TEXT("Start Parameters"), cbParameters, pvParameters);
  76. if (cbParameters == sizeof(ULONG_PTR) + sizeof(ULONG_PTR))
  77. {
  78. TNtStatus Status;
  79. pNlpCacheEncryptionKey = * ((CHAR**) pvParameters);
  80. pNlpActiveCtes = * ((LIST_ENTRY**) ((CHAR*)pvParameters + sizeof(ULONG_PTR)));
  81. Status DBGCHK = EnumerateNlpCacheEntries(pNlpCacheEncryptionKey, pNlpActiveCtes);
  82. if (NT_SUCCESS(Status))
  83. {
  84. SspiPrint(SSPI_LOG, TEXT("Operation succeeded\n"));
  85. }
  86. else
  87. {
  88. SspiPrint(SSPI_LOG, TEXT("Operation failed\n"));
  89. }
  90. }
  91. else
  92. {
  93. dwErr = ERROR_INVALID_PARAMETER;
  94. SspiPrint(SSPI_LOG, TEXT("Start received invalid parameter\n"));
  95. }
  96. return dwErr;
  97. }
  98. int
  99. Init(
  100. IN ULONG argc,
  101. IN PCSTR argv[],
  102. OUT ULONG* pcbParameters,
  103. OUT VOID** ppvParameters
  104. )
  105. {
  106. DWORD dwErr = ERROR_SUCCESS;
  107. CHAR Parameters[REMOTE_PACKET_SIZE] = {0};
  108. ULONG cbBuffer = sizeof(Parameters);
  109. ULONG cbParameter = 0;
  110. CHAR* pNlpCacheEncryptionKey = NULL;
  111. LIST_ENTRY* pNlpActiveCtes = NULL;
  112. *pcbParameters = 0;
  113. *ppvParameters = NULL;
  114. if (argc == 2)
  115. {
  116. DebugPrintf(SSPI_LOG, "argv[0] %s, argv[1] %s\n", argv[0], argv[1]);
  117. pNlpCacheEncryptionKey = (CHAR*) (ULONG_PTR) strtol(argv[0], NULL, 0);
  118. pNlpActiveCtes = (LIST_ENTRY*) (ULONG_PTR) strtol(argv[1], NULL, 0);
  119. SspiPrint(SSPI_LOG, TEXT("msv1_0!NlpCacheEncryptionKey %p, addr of msv1_0!NlpActiveCtes %p\n"), pNlpCacheEncryptionKey, pNlpActiveCtes);
  120. memcpy(Parameters, &pNlpCacheEncryptionKey, sizeof(ULONG_PTR));
  121. cbParameter = sizeof(ULONG_PTR);
  122. memcpy(Parameters + cbParameter, &pNlpActiveCtes, sizeof(ULONG_PTR));
  123. cbParameter += sizeof(ULONG_PTR);
  124. }
  125. else // return "Usage" in ppvParameters, must be a NULL terminated string
  126. {
  127. strcpy(Parameters, "<value of msv1_0!NlpCacheEncryptionKey> <addr of msv1_0!NlpActiveCtes>");
  128. cbParameter = strlen(Parameters) + 1;
  129. dwErr = ERROR_INVALID_PARAMETER;
  130. }
  131. *ppvParameters = new CHAR[cbParameter];
  132. if (*ppvParameters)
  133. {
  134. *pcbParameters = cbParameter;
  135. memcpy(*ppvParameters, Parameters, *pcbParameters);
  136. }
  137. else
  138. {
  139. dwErr = ERROR_OUTOFMEMORY;
  140. goto Cleanup;
  141. }
  142. Cleanup:
  143. return dwErr;
  144. }
  145. NTSTATUS
  146. NlpMakeCacheEntryName(
  147. IN ULONG EntryIndex,
  148. OUT UNICODE_STRING* pName
  149. )
  150. {
  151. TNtStatus NtStatus = STATUS_SUCCESS;
  152. UNICODE_STRING TmpString = {0};
  153. WCHAR TmpStringBuffer[256] = {0};
  154. if (EntryIndex > NLP_MAX_LOGON_CACHE_COUNT)
  155. {
  156. DebugPrintf(SSPI_ERROR, "NlpMakeCacheEntryName EntryIndex %#x exceeds NLP_MAX_LOGON_CACHE_COUNT %#x\n", EntryIndex, NLP_MAX_LOGON_CACHE_COUNT);
  157. NtStatus DBGCHK = STATUS_INVALID_PARAMETER;
  158. }
  159. if (NT_SUCCESS(NtStatus))
  160. {
  161. pName->Length = 0;
  162. NtStatus DBGCHK = RtlAppendUnicodeToString(pName, L"NL$");
  163. }
  164. if (NT_SUCCESS(NtStatus))
  165. {
  166. TmpString.MaximumLength = 16;
  167. TmpString.Length = 0;
  168. TmpString.Buffer = TmpStringBuffer;
  169. NtStatus DBGCHK = RtlIntegerToUnicodeString(
  170. (EntryIndex + 1), // make 1 based index
  171. 10, // Base 10
  172. &TmpString
  173. );
  174. }
  175. if (NT_SUCCESS(NtStatus))
  176. {
  177. NtStatus DBGCHK = RtlAppendUnicodeStringToString(pName, &TmpString);
  178. }
  179. return NtStatus;
  180. }
  181. NTSTATUS
  182. NlpOpenCache(
  183. OUT HANDLE* phNlpCache
  184. )
  185. {
  186. TNtStatus NtStatus;
  187. OBJECT_ATTRIBUTES ObjectAttributes;
  188. UNICODE_STRING ObjectName;
  189. ObjectName.Length = ObjectName.MaximumLength = CACHE_NAME_SIZE;
  190. ObjectName.Buffer = CACHE_NAME;
  191. InitializeObjectAttributes(
  192. &ObjectAttributes,
  193. &ObjectName,
  194. OBJ_CASE_INSENSITIVE,
  195. 0, // RootDirectory
  196. NULL // default is reasonable from SYSTEM context
  197. );
  198. NtStatus DBGCHK = NtOpenKey(
  199. phNlpCache,
  200. KEY_READ,
  201. &ObjectAttributes
  202. );
  203. return NtStatus;
  204. }
  205. NTSTATUS
  206. NlpReadCacheEntryByIndex(
  207. IN ULONG Index,
  208. OUT PLOGON_CACHE_ENTRY* ppCacheEntry,
  209. OUT PULONG pcbEntrySize
  210. )
  211. {
  212. TNtStatus NtStatus = STATUS_SUCCESS;
  213. UNICODE_STRING ValueName = {0};
  214. HANDLE hNlpCache = NULL;
  215. WCHAR szNameBuffer[256] = {0};
  216. ULONG cbRequiredSize = 0;
  217. PKEY_VALUE_FULL_INFORMATION pRegInfo = NULL;
  218. PLOGON_CACHE_ENTRY pRCacheEntry = NULL; // CacheEntry in registry buffer
  219. BYTE FastBuffer[4098] = {0};
  220. PBYTE pSlowBuffer = NULL;
  221. ValueName.Buffer = szNameBuffer;
  222. ValueName.MaximumLength = sizeof(szNameBuffer) - sizeof(WCHAR);
  223. ValueName.Length = 0;
  224. NtStatus DBGCHK = NlpMakeCacheEntryName(Index, &ValueName);
  225. if (NT_SUCCESS(NtStatus))
  226. {
  227. SspiPrint(SSPI_LOG, TEXT("NlpReadCacheEntryByIndex %#x, ValueName %wZ\n"), Index, &ValueName);
  228. NtStatus DBGCHK = NlpOpenCache(&hNlpCache);
  229. }
  230. if (NT_SUCCESS(NtStatus))
  231. {
  232. pRegInfo = (PKEY_VALUE_FULL_INFORMATION)FastBuffer;
  233. cbRequiredSize = sizeof(FastBuffer);
  234. DBGCFG2(NtStatus, STATUS_BUFFER_TOO_SMALL, STATUS_BUFFER_OVERFLOW);
  235. //
  236. // perform first query to find out how much buffer to allocate
  237. //
  238. NtStatus DBGCHK = NtQueryValueKey(
  239. hNlpCache,
  240. &ValueName,
  241. KeyValueFullInformation,
  242. pRegInfo,
  243. cbRequiredSize,
  244. &cbRequiredSize
  245. );
  246. if ( ( ((NTSTATUS) NtStatus) == STATUS_BUFFER_TOO_SMALL )
  247. || ( ((NTSTATUS) NtStatus) == STATUS_BUFFER_OVERFLOW ) )
  248. {
  249. SspiPrint(SSPI_WARN, TEXT("NlpReadCacheEntryByIndex NtQueryValueKey requires %#x bytes\n"), cbRequiredSize);
  250. //
  251. // allocate buffer then do query again, this time receiving data
  252. //
  253. pSlowBuffer = new BYTE[cbRequiredSize];
  254. NtStatus DBGCHK = pSlowBuffer ? STATUS_SUCCESS : STATUS_NO_MEMORY;
  255. if (NT_SUCCESS(NtStatus))
  256. {
  257. pRegInfo = (PKEY_VALUE_FULL_INFORMATION)pSlowBuffer;
  258. NtStatus DBGCHK = NtQueryValueKey(
  259. hNlpCache,
  260. &ValueName,
  261. KeyValueFullInformation,
  262. pRegInfo,
  263. cbRequiredSize,
  264. &cbRequiredSize
  265. );
  266. }
  267. }
  268. }
  269. if (NT_SUCCESS(NtStatus))
  270. {
  271. if (pRegInfo->DataLength == 0 )
  272. {
  273. NtStatus DBGCHK = STATUS_INTERNAL_DB_CORRUPTION;
  274. *ppCacheEntry = NULL;
  275. *pcbEntrySize = 0;
  276. }
  277. else
  278. {
  279. pRCacheEntry = (PLOGON_CACHE_ENTRY) ((PCHAR)pRegInfo + pRegInfo->DataOffset);
  280. *pcbEntrySize = pRegInfo->DataLength;
  281. (*ppCacheEntry) = (PLOGON_CACHE_ENTRY) new CHAR[*pcbEntrySize];
  282. NtStatus DBGCHK = *ppCacheEntry ? STATUS_SUCCESS : STATUS_NO_MEMORY;
  283. if (NT_SUCCESS(NtStatus))
  284. {
  285. RtlCopyMemory((*ppCacheEntry), pRCacheEntry, (*pcbEntrySize) );
  286. }
  287. }
  288. }
  289. if (pSlowBuffer)
  290. {
  291. delete [] pSlowBuffer;
  292. }
  293. if (hNlpCache)
  294. {
  295. NtClose(hNlpCache);
  296. }
  297. return NtStatus;
  298. }
  299. NTSTATUS
  300. NlpDecryptCacheEntry(
  301. IN CHAR NlpCacheEncryptionKey[NLP_CACHE_ENCRYPTION_KEY_LEN],
  302. IN ULONG EntrySize,
  303. IN OUT PLOGON_CACHE_ENTRY pCacheEntry
  304. )
  305. {
  306. TNtStatus NtStatus = STATUS_SUCCESS;
  307. HMACMD5_CTX hmacCtx;
  308. RC4_KEYSTRUCT rc4key;
  309. CHAR DerivedKey[ MD5DIGESTLEN ];
  310. CHAR MAC[ MD5DIGESTLEN ];
  311. PBYTE pbData;
  312. ULONG cbData;
  313. // DebugPrintHex(SSPI_LOG, "NlpDecryptCacheEntry NlpCacheEncryptionKey",
  314. // NLP_CACHE_ENCRYPTION_KEY_LEN, NlpCacheEncryptionKey);
  315. if ( pCacheEntry->Revision < NLP_CACHE_REVISION_NT_5_0 )
  316. {
  317. NtStatus DBGCHK = STATUS_UNSUCCESSFUL;
  318. }
  319. //
  320. // derive encryption key from global machine LSA secret, and random
  321. // cache entry key.
  322. //
  323. if (NT_SUCCESS(NtStatus))
  324. {
  325. HMACMD5Init(&hmacCtx, (PUCHAR) NlpCacheEncryptionKey, NLP_CACHE_ENCRYPTION_KEY_LEN);
  326. HMACMD5Update(&hmacCtx, (PUCHAR) pCacheEntry->RandomKey, sizeof(pCacheEntry->RandomKey));
  327. HMACMD5Final(&hmacCtx, (PUCHAR) DerivedKey);
  328. //
  329. // begin decrypting at the cachepasswords field.
  330. //
  331. pbData = (PBYTE)&(pCacheEntry->CachePasswords);
  332. //
  333. // data length is EntrySize - header up to CachePasswords.
  334. //
  335. cbData = EntrySize - (ULONG)( pbData - (PBYTE)pCacheEntry );
  336. //
  337. // now decrypt it...
  338. //
  339. rc4_key( &rc4key, sizeof(DerivedKey), (PUCHAR) DerivedKey );
  340. rc4( &rc4key, cbData, pbData );
  341. //
  342. // compute MAC on decrypted data for integrity checking.
  343. //
  344. HMACMD5Init(&hmacCtx, (PUCHAR) DerivedKey, sizeof(DerivedKey));
  345. HMACMD5Update(&hmacCtx, pbData, cbData);
  346. HMACMD5Final(&hmacCtx, (PUCHAR) MAC);
  347. RtlZeroMemory( DerivedKey, sizeof(DerivedKey) );
  348. //
  349. // verify MAC.
  350. //
  351. if (memcmp( MAC, pCacheEntry->MAC, sizeof(MAC) ) != 0)
  352. {
  353. NtStatus DBGCHK = STATUS_LOGON_FAILURE;
  354. }
  355. }
  356. return NtStatus;
  357. }
  358. NTSTATUS
  359. EnumerateNlpCacheEntries(
  360. IN CHAR NlpCacheEncryptionKey[NLP_CACHE_ENCRYPTION_KEY_LEN],
  361. IN LIST_ENTRY* pNlpActiveCtes
  362. )
  363. {
  364. TNtStatus NtStatus = STATUS_SUCCESS;
  365. ULONG i = 0;
  366. SspiPrint(SSPI_LOG, TEXT("EnumerateNlpCacheEntries NlpCacheEncryptionKey %p, NlpActiveCtes %p\n"),
  367. NlpCacheEncryptionKey, pNlpActiveCtes);
  368. for (PNLP_CTE pNext = (PNLP_CTE) pNlpActiveCtes->Flink;
  369. NT_SUCCESS(NtStatus) && (pNext != (PNLP_CTE)pNlpActiveCtes);
  370. pNext = (PNLP_CTE)pNext->Link.Flink)
  371. {
  372. LOGON_CACHE_ENTRY* pCacheEntry = NULL;
  373. ULONG cbEntrySize = 0;
  374. UNICODE_STRING CachedUser = {0};
  375. UNICODE_STRING CachedDomain = {0};
  376. UNICODE_STRING CachedDnsDomain = {0};
  377. UNICODE_STRING CachedUpn = {0};
  378. SspiPrint(SSPI_LOG, TEXT("*************#%#x) _NLP_CTE %p, Index %#x*******\n"), i++, pNext, pNext->Index);
  379. NtStatus DBGCHK = NlpReadCacheEntryByIndex(
  380. pNext->Index,
  381. &pCacheEntry,
  382. &cbEntrySize
  383. );
  384. if (NT_SUCCESS(NtStatus))
  385. {
  386. NtStatus DBGCHK = (pCacheEntry->Revision >= NLP_CACHE_REVISION_NT_1_0B) ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR;
  387. }
  388. if (NT_SUCCESS(NtStatus))
  389. {
  390. NtStatus DBGCHK = NlpDecryptCacheEntry(
  391. NlpCacheEncryptionKey,
  392. cbEntrySize,
  393. pCacheEntry
  394. );
  395. }
  396. if (NT_SUCCESS(NtStatus))
  397. {
  398. CachedUser.Length = CachedUser.MaximumLength = pCacheEntry->UserNameLength;
  399. NtStatus DBGCHK = (pCacheEntry->Revision >= NLP_CACHE_REVISION_NT_5_0) ? STATUS_SUCCESS : STATUS_INTERNAL_ERROR;
  400. }
  401. if (NT_SUCCESS(NtStatus))
  402. {
  403. CachedUser.Buffer = (PWSTR) ((PBYTE) pCacheEntry + sizeof(LOGON_CACHE_ENTRY));
  404. CachedDomain.Length = CachedDomain.MaximumLength = pCacheEntry->DomainNameLength;
  405. CachedDomain.Buffer = (PWSTR)((LPBYTE)CachedUser.Buffer
  406. + ROUND_UP_COUNT(pCacheEntry->UserNameLength, sizeof(ULONG)));
  407. CachedDnsDomain.Length = CachedDnsDomain.MaximumLength = pCacheEntry->DnsDomainNameLength;
  408. CachedDnsDomain.Buffer = (PWSTR)((PBYTE)CachedDomain.Buffer
  409. + ROUND_UP_COUNT(pCacheEntry->DomainNameLength, sizeof(ULONG)));
  410. CachedUpn.Length = CachedUpn.MaximumLength = pCacheEntry->UpnLength;
  411. CachedUpn.Buffer = (PWSTR)((PBYTE)CachedDnsDomain.Buffer
  412. + ROUND_UP_COUNT(pCacheEntry->DnsDomainNameLength, sizeof(ULONG)));
  413. SspiPrint(SSPI_LOG,
  414. TEXT("domain \"%wZ\", dns domain \"%wZ\", upn \"%wZ\", user \"%wZ\", flags %#x, ")
  415. TEXT("UserId %#x, PrimaryGroupId %#x, GroupCount %#x, LogonPackage %#x\n"),
  416. &CachedDomain, &CachedDnsDomain, &CachedUpn, &CachedUser, pCacheEntry->CacheFlags,
  417. pCacheEntry->UserId, pCacheEntry->PrimaryGroupId, pCacheEntry->GroupCount, pCacheEntry->LogonPackage);
  418. }
  419. if (pCacheEntry)
  420. {
  421. delete [] pCacheEntry;
  422. }
  423. }
  424. return NtStatus;
  425. }