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.

654 lines
18 KiB

  1. //+-----------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (c) Microsoft Corporation 2000
  6. //
  7. // File: ctxt.cxx
  8. //
  9. // Contents: Context manipulation functions
  10. //
  11. //
  12. // History: KDamour 15Mar00 Stolen from NTLM context.cxx
  13. //
  14. //------------------------------------------------------------------------
  15. #include "global.h"
  16. // Globals for manipulating Context Lists
  17. #define DIGEST_LIST_COUNT (16) // count of lists
  18. #define DIGEST_LIST_LOCK_COUNT_MAX (4) // count of locks
  19. LIST_ENTRY DigestContextList[ DIGEST_LIST_COUNT ]; // list array.
  20. RTL_RESOURCE DigestContextLock[ DIGEST_LIST_LOCK_COUNT_MAX ]; // lock array
  21. ULONG DigestContextCount[ DIGEST_LIST_COUNT ]; // count of active contexts
  22. ULONG DigestContextLockCount;
  23. // Indicate if completed Initialization of Credential Handler
  24. BOOL g_bContextInitialized = FALSE;
  25. ULONG
  26. HandleToListIndex(
  27. ULONG_PTR ContextHandle
  28. );
  29. ULONG
  30. __inline
  31. ListIndexToLockIndex(
  32. ULONG ListIndex
  33. );
  34. //+--------------------------------------------------------------------
  35. //
  36. // Function: SspContextInitialize
  37. //
  38. // Synopsis: Initializes the context manager package
  39. //
  40. // Arguments: none
  41. //
  42. // Returns: NTSTATUS
  43. //
  44. // Notes: Called by SpInitialize
  45. //
  46. //---------------------------------------------------------------------
  47. NTSTATUS
  48. CtxtHandlerInit(VOID)
  49. {
  50. NTSTATUS Status = STATUS_SUCCESS;
  51. NT_PRODUCT_TYPE ProductType;
  52. ULONG Index;
  53. ULONG cResourcesInitialized = 0;
  54. for( Index=0 ; Index < DIGEST_LIST_COUNT ; Index++ )
  55. {
  56. InitializeListHead (&DigestContextList[Index]);
  57. }
  58. DigestContextLockCount = 1;
  59. RtlGetNtProductType( &ProductType );
  60. if( ProductType == NtProductLanManNt ||
  61. ProductType == NtProductServer )
  62. {
  63. SYSTEM_INFO si;
  64. GetSystemInfo( &si );
  65. //
  66. // if not an even power of two, bump it up.
  67. //
  68. if( si.dwNumberOfProcessors & 1 )
  69. {
  70. si.dwNumberOfProcessors++;
  71. }
  72. //
  73. // insure it fits in the confines of the max allowed.
  74. //
  75. if( si.dwNumberOfProcessors > DIGEST_LIST_LOCK_COUNT_MAX )
  76. {
  77. si.dwNumberOfProcessors = DIGEST_LIST_LOCK_COUNT_MAX;
  78. }
  79. if( si.dwNumberOfProcessors )
  80. {
  81. DigestContextLockCount = si.dwNumberOfProcessors;
  82. }
  83. }
  84. //
  85. // list count is 1, or a power of two, for index purposes.
  86. //
  87. ASSERT( (DigestContextLockCount == 1) || ((DigestContextLockCount % 2) == 0) );
  88. for (Index=0; Index < DigestContextLockCount; Index++)
  89. {
  90. __try
  91. {
  92. RtlInitializeResource(&DigestContextLock[Index]);
  93. cResourcesInitialized++; // keep track of Resources that are initialized
  94. }
  95. __except(EXCEPTION_EXECUTE_HANDLER)
  96. {
  97. Status = STATUS_INSUFFICIENT_RESOURCES;
  98. break;
  99. }
  100. }
  101. //
  102. // avoiding deleting RTL_RESOURCEs with un-initialized fields
  103. //
  104. if (!NT_SUCCESS(Status))
  105. {
  106. for (Index = 0; Index < cResourcesInitialized; Index++)
  107. {
  108. RtlDeleteResource(&DigestContextLock[Index]);
  109. }
  110. }
  111. // Simple variable test to make sure all initialized;
  112. g_bContextInitialized = TRUE;
  113. return Status;
  114. }
  115. // Add a Context into the Context List
  116. NTSTATUS
  117. CtxtHandlerInsertCred(
  118. IN PDIGEST_CONTEXT pDigestCtxt
  119. )
  120. {
  121. ULONG ListIndex;
  122. ULONG LockIndex;
  123. DebugLog((DEB_TRACE_FUNC, "CtxtHandlerInsertCred: Entering with Context 0x%x RefCount %ld\n", pDigestCtxt, pDigestCtxt->lReferences));
  124. DebugLog((DEB_TRACE, "CtxtHandlerInsertCred: add into list\n"));
  125. ListIndex = HandleToListIndex( (ULONG_PTR)pDigestCtxt );
  126. LockIndex = ListIndexToLockIndex( ListIndex );
  127. RtlAcquireResourceExclusive(&DigestContextLock[LockIndex], TRUE);
  128. InsertHeadList( &DigestContextList[ListIndex], &pDigestCtxt->Next );
  129. RtlReleaseResource(&DigestContextLock[LockIndex]);
  130. DebugLog((DEB_TRACE_FUNC, "CtxtHandlerInsertCred: Leaving with Context 0x%x\n", pDigestCtxt));
  131. return STATUS_SUCCESS;
  132. }
  133. // Initialize a Context into the IdleState with the data from the Credential provided
  134. NTSTATUS NTAPI
  135. ContextInit(
  136. IN OUT PDIGEST_CONTEXT pContext,
  137. IN PDIGEST_CREDENTIAL pCredential
  138. )
  139. {
  140. NTSTATUS Status = STATUS_SUCCESS;
  141. DebugLog((DEB_TRACE_FUNC, "ContextInit: Entering\n"));
  142. if (!pContext || !pCredential)
  143. {
  144. return STATUS_INVALID_PARAMETER;
  145. }
  146. ZeroMemory(pContext, sizeof(DIGEST_CONTEXT));
  147. pContext->typeQOP = QOP_UNDEFINED;
  148. pContext->typeDigest = DIGEST_UNDEFINED;
  149. pContext->typeAlgorithm = ALGORITHM_UNDEFINED;
  150. pContext->typeCipher = CIPHER_UNDEFINED;
  151. pContext->typeCharset = CHARSET_UNDEFINED;
  152. pContext->lReferences = 0;
  153. pContext->ulSendMaxBuf = SASL_MAX_DATA_BUFFER;
  154. pContext->ulRecvMaxBuf = SASL_MAX_DATA_BUFFER;
  155. pContext->ContextHandle = (ULONG_PTR)pContext;
  156. pContext->ExpirationTime = g_TimeForever; // never expire
  157. // Now copy over all the info we need from the supplied credential
  158. pContext->CredentialUseFlags = pCredential->CredentialUseFlags; // Keep the info on inbound/outbound
  159. Status = UnicodeStringDuplicate(&(pContext->ustrAccountName), &(pCredential->ustrAccountName));
  160. if (!NT_SUCCESS(Status))
  161. {
  162. DebugLog((DEB_ERROR, "ContextInit: Failed to copy Domain into Context\n"));
  163. goto CleanUp;
  164. }
  165. Status = UnicodeStringDuplicate(&(pContext->ustrDomain), &(pCredential->ustrDomain));
  166. if (!NT_SUCCESS(Status))
  167. {
  168. DebugLog((DEB_ERROR, "ContextInit: Failed to copy Domain into Context\n"));
  169. goto CleanUp;
  170. }
  171. // Copy over the Credential Password if known - thread safe - this is encrypted text
  172. Status = CredHandlerPasswdGet(pCredential, &pContext->ustrPassword);
  173. if (!NT_SUCCESS(Status))
  174. {
  175. DebugLog((DEB_ERROR, "ContextInit: CredHandlerPasswdGet error status 0x%x\n", Status));
  176. goto CleanUp;
  177. }
  178. // Set context flags based on global settings
  179. if (g_dwParameter_ClientCompat & CLIENTCOMPAT_QQOP)
  180. {
  181. pContext->ulFlags |= FLAG_CONTEXT_QUOTE_QOP;
  182. }
  183. CleanUp:
  184. DebugLog((DEB_TRACE_FUNC, "ContextInit: Leaving Status 0x%x\n", Status));
  185. return Status;
  186. }
  187. // Once done with a context - release the resouces
  188. NTSTATUS NTAPI
  189. ContextFree(
  190. IN PDIGEST_CONTEXT pContext
  191. )
  192. {
  193. NTSTATUS Status = STATUS_SUCCESS;
  194. USHORT iCnt = 0;
  195. DebugLog((DEB_TRACE_FUNC, "ContextFree: Entering with Context 0x%x\n", pContext));
  196. ASSERT(pContext);
  197. ASSERT(0 == pContext->lReferences);
  198. if (!pContext)
  199. {
  200. return STATUS_INVALID_PARAMETER;
  201. }
  202. DebugLog((DEB_TRACE, "ContextFree: Context RefCount %ld\n", pContext->lReferences));
  203. DebugLog((DEB_TRACE, "ContextFree: Checking TokenHandle for LogonID (%x:%lx)\n",
  204. pContext->LoginID.HighPart, pContext->LoginID.LowPart));
  205. if (pContext->TokenHandle)
  206. {
  207. DebugLog((DEB_TRACE, "ContextFree: Closing TokenHandle for LogonID (%x:%lx)\n",
  208. pContext->LoginID.HighPart, pContext->LoginID.LowPart));
  209. NtClose(pContext->TokenHandle);
  210. pContext->TokenHandle = NULL;
  211. }
  212. StringFree(&(pContext->strNonce));
  213. StringFree(&(pContext->strCNonce));
  214. StringFree(&(pContext->strOpaque));
  215. StringFree(&(pContext->strSessionKey));
  216. UnicodeStringFree(&(pContext->ustrDomain));
  217. UnicodeStringFree(&(pContext->ustrPassword));
  218. UnicodeStringFree(&(pContext->ustrAccountName));
  219. StringFree(&(pContext->strResponseAuth));
  220. for (iCnt = 0; iCnt < MD5_AUTH_LAST; iCnt++)
  221. {
  222. StringFree(&(pContext->strDirective[iCnt]));
  223. }
  224. DigestFreeMemory(pContext);
  225. DebugLog((DEB_TRACE_FUNC, "ContextFree: Leaving with Context 0x%x\n", pContext));
  226. return Status;
  227. }
  228. /*++
  229. Routine Description:
  230. This routine checks to see if the Context is for the specified
  231. Client Connection, and references the Context if it is valid.
  232. The caller may optionally request that the Context be
  233. removed from the list of valid Contexts - preventing future
  234. requests from finding this Context.
  235. Arguments:
  236. ContextHandle - Points to the ContextHandle of the Context
  237. to be referenced.
  238. RemoveContext - This boolean value indicates whether the caller
  239. wants the Context to be removed from the list
  240. of Contexts. TRUE indicates the Context is to be removed.
  241. FALSE indicates the Context is not to be removed.
  242. Return Value:
  243. NULL - the Context was not found.
  244. Otherwise - returns a pointer to the referenced Context.
  245. --*/
  246. NTSTATUS NTAPI
  247. CtxtHandlerHandleToContext(
  248. IN ULONG_PTR ContextHandle,
  249. IN BOOLEAN RemoveContext,
  250. OUT PDIGEST_CONTEXT *ppContext
  251. )
  252. {
  253. NTSTATUS Status = STATUS_SUCCESS;
  254. PLIST_ENTRY ListEntry = NULL;
  255. PDIGEST_CONTEXT Context = NULL;
  256. LONG lReferences = 0;
  257. ULONG ListIndex;
  258. ULONG LockIndex;
  259. DebugLog((DEB_TRACE_FUNC, "CtxtHandlerHandleToContext: Entering ContextHandle 0x%lx\n", ContextHandle));
  260. //
  261. // Acquire access to the Context list
  262. //
  263. ListIndex = HandleToListIndex( ContextHandle );
  264. LockIndex = ListIndexToLockIndex( ListIndex );
  265. RtlAcquireResourceShared(&DigestContextLock[LockIndex], TRUE);
  266. //
  267. // Now walk the list of Contexts looking for a match.
  268. //
  269. for ( ListEntry = DigestContextList[ListIndex].Flink;
  270. ListEntry != &DigestContextList[ListIndex];
  271. ListEntry = ListEntry->Flink ) {
  272. Context = CONTAINING_RECORD( ListEntry, DIGEST_CONTEXT, Next );
  273. //
  274. // Found a match ... reference this Context
  275. // (if the Context is being removed, we would increment
  276. // and then decrement the reference, so don't bother doing
  277. // either - since they cancel each other out).
  278. //
  279. if ( Context == (PDIGEST_CONTEXT) ContextHandle)
  280. {
  281. if (!RemoveContext)
  282. {
  283. //
  284. // Timeout this context if caller is not trying to remove it.
  285. // We only timeout contexts that are being setup, not
  286. // fully authenticated contexts.
  287. //
  288. if (CtxtHandlerTimeHasElapsed(Context))
  289. {
  290. DebugLog((DEB_ERROR, "CtxtHandlerHandleToContext: Context 0x%lx has timed out.\n",
  291. ContextHandle ));
  292. Status = SEC_E_CONTEXT_EXPIRED;
  293. goto CleanUp;
  294. }
  295. lReferences = InterlockedIncrement(&Context->lReferences);
  296. }
  297. else
  298. {
  299. RtlConvertSharedToExclusive(&DigestContextLock[LockIndex]);
  300. RemoveEntryList( &Context->Next );
  301. DebugLog((DEB_TRACE, "CtxtHandlerHandleToContext:Delinked Context 0x%lx\n",Context ));
  302. }
  303. DebugLog((DEB_TRACE, "CtxtHandlerHandleToContext: FOUND Context = 0x%x, RemoveContext = %d, ReferenceCount = %ld\n",
  304. Context, RemoveContext, Context->lReferences));
  305. *ppContext = Context;
  306. goto CleanUp;
  307. }
  308. }
  309. //
  310. // No match found
  311. //
  312. DebugLog((DEB_WARN, "CtxtHandlerHandleToContext: Tried to reference unknown Context 0x%lx\n", ContextHandle ));
  313. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  314. CleanUp:
  315. RtlReleaseResource(&DigestContextLock[LockIndex]);
  316. DebugLog((DEB_TRACE_FUNC, "CtxtHandlerHandleToContext: Leaving\n" ));
  317. return(Status);
  318. }
  319. /*++
  320. Routine Description:
  321. This routine checks to see if the LogonId is for the specified
  322. Server Connection, and references the Context if it is valid.
  323. The caller may optionally request that the Context be
  324. removed from the list of valid Contexts - preventing future
  325. requests from finding this Context.
  326. Arguments:
  327. pstrOpaque - Opaque string that uniquely references the SecurityContext
  328. Return Value:
  329. NULL - the Context was not found.
  330. Otherwise - returns a pointer to the referenced Context.
  331. --*/
  332. NTSTATUS NTAPI
  333. CtxtHandlerOpaqueToPtr(
  334. IN PSTRING pstrOpaque,
  335. OUT PDIGEST_CONTEXT *ppContext
  336. )
  337. {
  338. NTSTATUS Status = STATUS_SUCCESS;
  339. PLIST_ENTRY ListEntry = NULL;
  340. PDIGEST_CONTEXT Context = NULL;
  341. LONG rc = 0;
  342. LONG lReferences = 0;
  343. ULONG ListIndex;
  344. ULONG LockIndex = 0;
  345. DebugLog((DEB_TRACE_FUNC, "CtxtHandlerOpaqueToPtr: Entering Opaque (%Z)\n", pstrOpaque));
  346. //
  347. // Acquire access to the Context list
  348. //
  349. for(ListIndex = 0; ListIndex < DIGEST_LIST_COUNT ; ListIndex++)
  350. {
  351. LockIndex = ListIndexToLockIndex( ListIndex );
  352. RtlAcquireResourceShared(&DigestContextLock[LockIndex], TRUE);
  353. //
  354. // Now walk the list of Contexts looking for a match.
  355. //
  356. for ( ListEntry = DigestContextList[ListIndex].Flink;
  357. ListEntry != &DigestContextList[ListIndex];
  358. ListEntry = ListEntry->Flink ) {
  359. Context = CONTAINING_RECORD( ListEntry, DIGEST_CONTEXT, Next );
  360. //
  361. // Found a match ... reference this Context
  362. // (if the Context is being removed, we would increment
  363. // and then decrement the reference, so don't bother doing
  364. // either - since they cancel each other out).
  365. //
  366. rc = RtlCompareString(pstrOpaque, &(Context->strOpaque), FALSE);
  367. if (!rc)
  368. {
  369. //
  370. // Timeout this context if caller is not trying to remove it.
  371. // We only timeout contexts that are being setup, not
  372. // fully authenticated contexts.
  373. //
  374. if (CtxtHandlerTimeHasElapsed(Context))
  375. {
  376. RtlReleaseResource(&DigestContextLock[LockIndex]);
  377. DebugLog((DEB_ERROR, "CtxtHandlerOpaqueToPtr: Context 0x%x has timed out.\n",
  378. Context ));
  379. Status = SEC_E_CONTEXT_EXPIRED;
  380. goto CleanUp;
  381. }
  382. lReferences = InterlockedIncrement(&Context->lReferences);
  383. RtlReleaseResource(&DigestContextLock[LockIndex]);
  384. DebugLog((DEB_TRACE, "CtxtHandlerOpaqueToPtr: FOUND Context = 0x%x, ReferenceCount = %ld\n",
  385. Context, Context->lReferences));
  386. *ppContext = Context;
  387. goto CleanUp;
  388. }
  389. }
  390. RtlReleaseResource(&DigestContextLock[LockIndex]);
  391. }
  392. //
  393. // No match found
  394. //
  395. DebugLog((DEB_WARN, "CtxtHandlerOpaqueToPtr: Tried to reference unknown Opaque (%Z)\n", pstrOpaque));
  396. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  397. CleanUp:
  398. DebugLog((DEB_TRACE_FUNC, "CtxtHandlerOpaqueToPtr: Leaving\n" ));
  399. return(Status);
  400. }
  401. // Check the Creation time with the Current time.
  402. // If the difference is greater than the MAX allowed, Context is no longer valid
  403. BOOL
  404. CtxtHandlerTimeHasElapsed(
  405. PDIGEST_CONTEXT pContext)
  406. {
  407. UNREFERENCED_PARAMETER(pContext);
  408. return (FALSE); // no expiration at this time
  409. }
  410. //+--------------------------------------------------------------------
  411. //
  412. // Function: CtxtHandlerRelease
  413. //
  414. // Synopsis: Releases the Context by decreasing reference counter
  415. //
  416. // Arguments: pContext - pointer to credential to de-reference
  417. //
  418. // Returns: NTSTATUS
  419. //
  420. // Notes:
  421. //
  422. //---------------------------------------------------------------------
  423. NTSTATUS
  424. CtxtHandlerRelease(
  425. PDIGEST_CONTEXT pContext,
  426. ULONG ulDereferenceCount)
  427. {
  428. NTSTATUS Status = STATUS_SUCCESS;
  429. LONG lReferences = 0;
  430. if (ulDereferenceCount == 1)
  431. {
  432. lReferences = InterlockedDecrement(&pContext->lReferences);
  433. DebugLog((DEB_TRACE, "CtxtHandlerRelease: (RefCount) deref 0x%x updated references %ld\n",
  434. pContext, lReferences));
  435. ASSERT( lReferences >= 0 );
  436. }
  437. else if (ulDereferenceCount > 0)
  438. {
  439. //
  440. // there is no equivalent to InterlockedSubtract.
  441. // so, turn it into an Add with some signed magic.
  442. //
  443. LONG lDecrementToIncrement = 0 - ulDereferenceCount;
  444. DebugLog((DEB_TRACE, "CtxtHandlerRelease: Dereferencing by %lu count\n", ulDereferenceCount ));
  445. lReferences = InterlockedExchangeAdd( &pContext->lReferences, lDecrementToIncrement );
  446. lReferences += lDecrementToIncrement;
  447. ASSERT( lReferences >= 0 );
  448. }
  449. //
  450. // If the count has dropped to zero, then free all alloced stuff
  451. //
  452. if (lReferences == 0)
  453. {
  454. DebugLog((DEB_TRACE, "CtxtHandlerRelease: (RefCount) freed 0x%x\n", pContext));
  455. Status = ContextFree(pContext);
  456. }
  457. return(Status);
  458. }
  459. ULONG
  460. HandleToListIndex(
  461. ULONG_PTR ContextHandle
  462. )
  463. {
  464. ULONG Number ;
  465. ULONG Hash;
  466. ULONG HashFinal;
  467. Number = (ULONG)ContextHandle;
  468. Hash = Number;
  469. Hash += Number >> 8;
  470. Hash += Number >> 16;
  471. Hash += Number >> 24;
  472. HashFinal = Hash;
  473. HashFinal += Hash >> 4;
  474. //
  475. // insure power of two if not one.
  476. //
  477. return ( HashFinal & (DIGEST_LIST_COUNT-1) ) ;
  478. }
  479. ULONG
  480. __inline
  481. ListIndexToLockIndex(
  482. ULONG ListIndex
  483. )
  484. {
  485. //
  486. // insure power of two if not one.
  487. //
  488. return ( ListIndex & (DigestContextLockCount-1) );
  489. }