Source code of Windows XP (NT5)
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.

612 lines
17 KiB

  1. //+-----------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (c) Microsoft Corporation 2000
  6. //
  7. // File: creds.cxx
  8. //
  9. // Contents: Credential functions:
  10. //
  11. //
  12. // History: KDamour 15Mar00 Stolen from NTLM
  13. //
  14. //------------------------------------------------------------------------
  15. #include "global.h"
  16. //
  17. // Crit Sect to protect various globals in this module.
  18. //
  19. RTL_CRITICAL_SECTION l_CredentialCritSect;
  20. LIST_ENTRY l_CredentialList;
  21. // Simple variable to make sure that the package was initialize
  22. BOOL g_bCredentialsInitialized = FALSE;
  23. //+--------------------------------------------------------------------
  24. //
  25. // Function: CredHandlerInit
  26. //
  27. // Synopsis: Initializes the credential manager package
  28. //
  29. // Arguments: none
  30. //
  31. // Returns: NTSTATUS
  32. //
  33. // Notes: Called by SpInitialize
  34. //
  35. //---------------------------------------------------------------------
  36. NTSTATUS
  37. CredHandlerInit(VOID)
  38. {
  39. NTSTATUS Status = STATUS_SUCCESS;
  40. //
  41. // Initialize the Credential list to be empty.
  42. //
  43. Status = RtlInitializeCriticalSection(&l_CredentialCritSect);
  44. if (!NT_SUCCESS(Status))
  45. {
  46. DebugLog((DEB_ERROR, "CredHandlerInit: Failed to initialize critsec 0x%x\n", Status));
  47. goto CleanUp;
  48. }
  49. InitializeListHead( &l_CredentialList );
  50. // Simple variable test to make sure all initialized;
  51. g_bCredentialsInitialized = TRUE;
  52. CleanUp:
  53. return Status;
  54. }
  55. NTSTATUS
  56. CredHandlerInsertCred(
  57. IN PDIGEST_CREDENTIAL pDigestCred
  58. )
  59. {
  60. RtlEnterCriticalSection( &l_CredentialCritSect );
  61. pDigestCred->Unlinked = FALSE;
  62. InsertHeadList( &l_CredentialList, &pDigestCred->Next );
  63. RtlLeaveCriticalSection( &l_CredentialCritSect );
  64. return STATUS_SUCCESS;
  65. }
  66. // Initialize the Credential Structure
  67. NTSTATUS
  68. CredentialInit(
  69. IN PDIGEST_CREDENTIAL pDigestCred)
  70. {
  71. NTSTATUS Status = STATUS_SUCCESS;
  72. ASSERT(pDigestCred);
  73. if (!pDigestCred)
  74. {
  75. return STATUS_INVALID_PARAMETER;
  76. }
  77. ZeroMemory(pDigestCred, sizeof(DIGEST_CREDENTIAL));
  78. pDigestCred->TimeCreated = time(NULL);
  79. pDigestCred->Unlinked = TRUE;
  80. pDigestCred->CredentialHandle = (ULONG_PTR)pDigestCred;
  81. pDigestCred->lReferences = 0;
  82. return(Status);
  83. }
  84. // Free up memory utilized by Credential the Credential Structure
  85. NTSTATUS
  86. CredentialFree(
  87. IN PDIGEST_CREDENTIAL pDigestCred)
  88. {
  89. NTSTATUS Status = STATUS_SUCCESS;
  90. ASSERT(pDigestCred);
  91. ASSERT(0 == pDigestCred->lReferences);
  92. UnicodeStringFree(&(pDigestCred->ustrAccountName));
  93. UnicodeStringFree(&(pDigestCred->ustrDownlevelName));
  94. UnicodeStringFree(&(pDigestCred->ustrDomainName));
  95. // Erase any password information
  96. if (((pDigestCred->ustrPassword).Length) &&
  97. ((pDigestCred->ustrPassword).Buffer))
  98. {
  99. ZeroMemory((pDigestCred->ustrPassword).Buffer, (pDigestCred->ustrPassword).MaximumLength);
  100. }
  101. UnicodeStringFree(&(pDigestCred->ustrPassword));
  102. UnicodeStringFree(&(pDigestCred->ustrDnsDomainName));
  103. UnicodeStringFree(&(pDigestCred->ustrUpn));
  104. UnicodeStringFree(&(pDigestCred->ustrLogonServer));
  105. DigestFreeMemory(pDigestCred);
  106. return(Status);
  107. }
  108. /*++
  109. Routine Description:
  110. This routine checks to see if the Credential Handle is from a currently
  111. active client, and references the Credential if it is valid.
  112. The caller can request that the Credential be dereferenced only.
  113. For a client's Credential to be valid, the Credential value
  114. must be on our list of active Credentials.
  115. Arguments:
  116. CredentialHandle - Points to the CredentialHandle of the Credential
  117. to be referenced.
  118. DereferenceCredential - This boolean value indicates whether the caller
  119. wants the logon process's Credential to be referenced (FALSE) or
  120. decremented (TRUE)
  121. Return Value:
  122. NULL - the Credential was not found.
  123. Otherwise - returns a pointer to the referenced credential.
  124. --*/
  125. NTSTATUS
  126. CredHandlerHandleToPtr(
  127. IN ULONG_PTR CredentialHandle,
  128. IN BOOLEAN DereferenceCredential,
  129. OUT PDIGEST_CREDENTIAL * UserCredential
  130. )
  131. {
  132. PLIST_ENTRY ListEntry = NULL;
  133. PDIGEST_CREDENTIAL Credential = NULL;
  134. NTSTATUS Status = STATUS_SUCCESS;
  135. SECPKG_CLIENT_INFO ClientInfo;
  136. SECPKG_CALL_INFO CallInfo;
  137. // LONG lDereferenceCount = 1;
  138. ULONG ulDereferenceCount = 1;
  139. LONG lReferences = 0;
  140. DebugLog((DEB_TRACE_FUNC, "CredHandlerHandleToPtr: Entering Credential 0x%x\n", CredentialHandle));
  141. // set default output
  142. ASSERT(UserCredential);
  143. *UserCredential = NULL ;
  144. ZeroMemory( &CallInfo, sizeof(CallInfo) );
  145. ZeroMemory( &ClientInfo, sizeof(ClientInfo) );
  146. if(g_LsaFunctions->GetCallInfo(&CallInfo))
  147. {
  148. ulDereferenceCount = CallInfo.CallCount;
  149. DebugLog((DEB_TRACE, "CredHandlerHandleToPtr: CallCount 0x%x\n", CallInfo.CallCount));
  150. DebugLog((DEB_TRACE, "CredHandlerHandleToPtr: Attributes 0x%x\n", CallInfo.Attributes));
  151. DebugLog((DEB_TRACE, "CredHandlerHandleToPtr: PID %d Thread %d\n", CallInfo.ProcessId, CallInfo.ThreadId));
  152. }
  153. else
  154. {
  155. ZeroMemory( &CallInfo, sizeof(CallInfo) );
  156. }
  157. Status = g_LsaFunctions->GetClientInfo(&ClientInfo);
  158. if(!NT_SUCCESS(Status))
  159. {
  160. //
  161. // this call can fail during a cleanup call. so ignore that for now,
  162. // and check for cleanup disposition.
  163. //
  164. if ((CallInfo.Attributes & SECPKG_CALL_CLEANUP) != 0)
  165. {
  166. Status = STATUS_SUCCESS;
  167. RtlZeroMemory(
  168. &ClientInfo,
  169. sizeof(SECPKG_CLIENT_INFO)
  170. );
  171. ClientInfo.HasTcbPrivilege = TRUE;
  172. ClientInfo.ProcessID = CallInfo.ProcessId;
  173. }
  174. if( !NT_SUCCESS( Status ) )
  175. {
  176. DebugLog(( DEB_ERROR, "CredHandlerHandleToPtr: GetClientInfo returned 0x%lx\n", Status));
  177. return( Status );
  178. }
  179. }
  180. if( CallInfo.Attributes & SECPKG_CALL_CLEANUP )
  181. {
  182. DebugLog(( DEB_TRACE, "CredHandlerHandleToPtr: Cleanup Called pid: 0x%lx handle: %p refcount: %lu\n",
  183. ClientInfo.ProcessID, CredentialHandle, ulDereferenceCount));
  184. }
  185. //
  186. // Acquire exclusive access to the Credential list
  187. //
  188. RtlEnterCriticalSection( &l_CredentialCritSect );
  189. //
  190. // Now walk the list of Credentials looking for a match.
  191. //
  192. for ( ListEntry = l_CredentialList.Flink;
  193. ListEntry != &l_CredentialList;
  194. ListEntry = ListEntry->Flink )
  195. {
  196. Credential = CONTAINING_RECORD( ListEntry, DIGEST_CREDENTIAL, Next );
  197. //
  198. // Found a match ... reference this Credential
  199. // (if the Credential is being removed, we would increment
  200. // and then decrement the reference, so don't bother doing
  201. // either - since they cancel each other out).
  202. //
  203. if (( Credential == (PDIGEST_CREDENTIAL) CredentialHandle))
  204. {
  205. // Make sure we have the privilege of accessing
  206. // this handle
  207. if (!ClientInfo.HasTcbPrivilege &&
  208. (Credential->ClientProcessID != ClientInfo.ProcessID)
  209. )
  210. {
  211. DebugLog((DEB_ERROR, "CredHandlerHandleToPtr: ProcessIDs are different. Access forbidden.\n"));
  212. break;
  213. }
  214. if (!DereferenceCredential)
  215. {
  216. lReferences = InterlockedIncrement(&Credential->lReferences);
  217. DebugLog((DEB_TRACE, "CredHandlerHandleToPtr: Incremented ReferenceCount %ld\n", lReferences));
  218. }
  219. else
  220. {
  221. DebugLog((DEB_TRACE, "CredHandlerHandleToPtr: Derefencing credential\n" ));
  222. ASSERT((ulDereferenceCount > 0));
  223. // Note: Subtract one off of the deref count, this avoids an extra interlock operation
  224. // After exit, SpFreeCredentialsHandle will call CredHandlerRelease
  225. ulDereferenceCount--;
  226. if( ulDereferenceCount == 1 )
  227. {
  228. DebugLog((DEB_TRACE, "CredHandlerHandleToPtr: Derefencing by one count\n" ));
  229. lReferences = InterlockedDecrement( &Credential->lReferences );
  230. ASSERT( (lReferences > 0) );
  231. }
  232. else if( ulDereferenceCount > 1 )
  233. {
  234. //
  235. // there is no equivalent to InterlockedSubtract.
  236. // so, turn it into an Add with some signed magic.
  237. //
  238. LONG lDecrementToIncrement = 0 - ulDereferenceCount;
  239. DebugLog((DEB_TRACE, "CredHandlerHandleToPtr: Derefencing by %lu count\n", ulDereferenceCount ));
  240. lReferences = InterlockedExchangeAdd( &Credential->lReferences, lDecrementToIncrement );
  241. lReferences += lDecrementToIncrement;
  242. ASSERT( (lReferences > 0) );
  243. }
  244. }
  245. // Found the Credential
  246. *UserCredential = Credential ;
  247. goto CleanUp;
  248. }
  249. }
  250. //
  251. // No match found
  252. //
  253. DebugLog((DEB_WARN, "CredHandlerHandleToCredential: Tried to reference unknown Credential 0x%lx\n",
  254. CredentialHandle ));
  255. Status = STATUS_INVALID_HANDLE;
  256. CleanUp:
  257. RtlLeaveCriticalSection( &l_CredentialCritSect );
  258. DebugLog((DEB_TRACE_FUNC, "CredHandlerHandleToPtr: Leaving Credential 0x%x\n", CredentialHandle));
  259. return(Status);
  260. }
  261. // Locate a Credential based on a LogonId, ProcessID
  262. // For either the Logon list or the Credential list
  263. NTSTATUS
  264. CredHandlerLocatePtr(
  265. IN PLUID pLogonId,
  266. IN ULONG CredentialUseFlags,
  267. OUT PDIGEST_CREDENTIAL * UserCredential
  268. )
  269. {
  270. PLIST_ENTRY ListEntry = NULL;
  271. PDIGEST_CREDENTIAL Credential = NULL;
  272. NTSTATUS Status = STATUS_SUCCESS;
  273. // SECPKG_CLIENT_INFO ClientInfo;
  274. SECPKG_CALL_INFO CallInfo;
  275. LONG lReferences = 0;
  276. DebugLog((DEB_TRACE_FUNC, "CredHandlerLocatePtr: Entering\n"));
  277. *UserCredential = NULL ;
  278. // If we do not have a LogonId
  279. if (!pLogonId)
  280. {
  281. return(STATUS_INVALID_HANDLE);
  282. }
  283. //
  284. // Match both flags
  285. //
  286. // CredentialUseFlags |= CredentialFlags;
  287. if (!g_LsaFunctions->GetCallInfo(&CallInfo))
  288. {
  289. DebugLog((DEB_ERROR,"CredHandlerLocatePtr: Failed to get call info\n"));
  290. return(STATUS_INVALID_HANDLE); // Really this is another error
  291. }
  292. //
  293. // Acquire exclusive access to the Credential list
  294. //
  295. RtlEnterCriticalSection( &l_CredentialCritSect );
  296. //
  297. // Now walk the list of Credentials looking for a match.
  298. //
  299. for ( ListEntry = l_CredentialList.Flink;
  300. ListEntry != &l_CredentialList;
  301. ListEntry = ListEntry->Flink )
  302. {
  303. Credential = CONTAINING_RECORD( ListEntry, DIGEST_CREDENTIAL, Next );
  304. //
  305. // Found a match ... reference this Credential
  306. // (if the Credential is being removed, we would increment
  307. // and then decrement the reference, so don't bother doing
  308. // either - since they cancel each other out).
  309. //
  310. // If this is a session credential then check for appropriate flags (like inbound or outbound)
  311. if ((Credential->CredentialUseFlags & DIGEST_CRED_MATCH_FLAGS) != CredentialUseFlags)
  312. {
  313. continue;
  314. }
  315. if (RtlEqualLuid(&(Credential->LogonId), pLogonId) &&
  316. (Credential->ClientProcessID == CallInfo.ProcessId))
  317. {
  318. lReferences = InterlockedIncrement(&Credential->lReferences);
  319. DebugLog((DEB_TRACE, "CredHandlerLocatePtr: ReferenceCount %ld\n", lReferences));
  320. // Found the Credential
  321. *UserCredential = Credential ;
  322. goto CleanUp;
  323. }
  324. }
  325. //
  326. // No match found
  327. //
  328. DebugLog((DEB_WARN, "CredHandlerLocatePtr: Tried to reference unknown LogonId (%x:%lx)\n",
  329. pLogonId->HighPart, pLogonId->LowPart ));
  330. Status = STATUS_INVALID_HANDLE;
  331. CleanUp:
  332. RtlLeaveCriticalSection( &l_CredentialCritSect );
  333. DebugLog((DEB_TRACE_FUNC, "CredHandlerLocatePtr: Leaving Status 0x%x\n", Status));
  334. return(Status);
  335. }
  336. //+--------------------------------------------------------------------
  337. //
  338. // Function: CredHandlerRelease
  339. //
  340. // Synopsis: Releases the Credential by decreasing reference counter
  341. // if Credential reference count drops to zero, Credential is deleted
  342. //
  343. // Arguments: pCredential - pointer to credential to de-reference
  344. //
  345. // Returns: NTSTATUS
  346. //
  347. // Notes: Called by ASC. Since multiple threads can have a credential
  348. // checked out, simply decrease the reference counter on release.
  349. //
  350. //---------------------------------------------------------------------
  351. NTSTATUS
  352. CredHandlerRelease(
  353. PDIGEST_CREDENTIAL pCredential)
  354. {
  355. NTSTATUS Status = STATUS_SUCCESS;
  356. LONG lReferences = 0;
  357. DebugLog((DEB_TRACE_FUNC, "CredHandlerRelease: Entering for Credential 0x%0x\n", pCredential));
  358. lReferences = InterlockedDecrement(&pCredential->lReferences);
  359. DebugLog((DEB_TRACE, "CredHandlerRelease: ReferenceCount %ld\n", lReferences));
  360. ASSERT( lReferences >= 0 );
  361. //
  362. // If the count has dropped to zero, then free all alloced stuff
  363. // Care must be taken since Cred is still in linked list - need to grab critsec
  364. // and then test again if zero since another thread might have taken a ref to cred
  365. //
  366. if (lReferences == 0)
  367. {
  368. RtlEnterCriticalSection( &l_CredentialCritSect );
  369. // Check to make sure no one took a reference since InterlockDecrement
  370. if (pCredential->lReferences)
  371. {
  372. DebugLog((DEB_TRACE, "CredHandlerRelease: Another thread took a reference. No action taken\n"));
  373. }
  374. else
  375. {
  376. // Safe to remove from list and delete
  377. // Check if added into linked list
  378. if (!pCredential->Unlinked)
  379. {
  380. RemoveEntryList( &pCredential->Next );
  381. DebugLog((DEB_TRACE, "CredHandlerRelease: Unlinked Credential 0x%x\n", pCredential));
  382. }
  383. DebugLog((DEB_TRACE, "CredHandlerRelease: Deleting Credential 0x%x\n", pCredential));
  384. Status = CredentialFree(pCredential);
  385. }
  386. RtlLeaveCriticalSection( &l_CredentialCritSect );
  387. }
  388. DebugLog((DEB_TRACE_FUNC, "LogSessHandlerRelease: Leaving Status 0x%x\n", Status));
  389. return(Status);
  390. }
  391. // Helper functions for processing fields within the credentials
  392. //+--------------------------------------------------------------------
  393. //
  394. // Function: CredHandlerPasswdSet
  395. //
  396. // Synopsis: Set the unicode string password in the credential
  397. //
  398. // Arguments: pCredential - pointer to credential to use
  399. // pustrPasswd - pointer to new password
  400. //
  401. // Returns: NTSTATUS
  402. //
  403. // Notes: might want to use record locking in the future instead
  404. // of an update flag on credentials
  405. //
  406. //---------------------------------------------------------------------
  407. NTSTATUS
  408. CredHandlerPasswdSet(
  409. IN OUT PDIGEST_CREDENTIAL pCredential,
  410. IN PUNICODE_STRING pustrPasswd)
  411. {
  412. NTSTATUS Status = STATUS_SUCCESS;
  413. // Protect writing the info into the credential
  414. RtlEnterCriticalSection( &l_CredentialCritSect );
  415. if (pCredential->ustrPassword.Buffer)
  416. {
  417. UnicodeStringFree(&(pCredential->ustrPassword));
  418. }
  419. Status = UnicodeStringDuplicatePassword(&(pCredential->ustrPassword),pustrPasswd);
  420. RtlLeaveCriticalSection( &l_CredentialCritSect );
  421. if (!NT_SUCCESS(Status))
  422. {
  423. DebugLog((DEB_ERROR, "CredHandlerPasswdSet: Error in setting Credential password, status 0x%0x\n", Status ));
  424. }
  425. return(Status);
  426. }
  427. //+--------------------------------------------------------------------
  428. //
  429. // Function: CredHandlerPasswdGet
  430. //
  431. // Synopsis: Get the unicode string password in the credential
  432. // Locking is only necessary for the logon creds and not the session creds
  433. // but it is just as well to keep it unifom
  434. //
  435. // Arguments: pCredential - pointer to credential to use
  436. // pustrPasswd - pointer to destination copy of password
  437. //
  438. // Returns: NTSTATUS
  439. //
  440. // Notes:
  441. //
  442. //---------------------------------------------------------------------
  443. NTSTATUS
  444. CredHandlerPasswdGet(
  445. IN PDIGEST_CREDENTIAL pCredential,
  446. OUT PUNICODE_STRING pustrPasswd)
  447. {
  448. NTSTATUS Status = STATUS_SUCCESS;
  449. // Protect reading/writing the credential password
  450. RtlEnterCriticalSection( &l_CredentialCritSect );
  451. if (pustrPasswd->Buffer)
  452. {
  453. UnicodeStringFree(pustrPasswd);
  454. }
  455. Status = UnicodeStringDuplicatePassword(pustrPasswd, &(pCredential->ustrPassword));
  456. RtlLeaveCriticalSection( &l_CredentialCritSect );
  457. if (!NT_SUCCESS(Status))
  458. {
  459. DebugLog((DEB_ERROR, "CredHandlerPasswdSet: Error in setting Credential password, status 0x%0x\n", Status ));
  460. }
  461. return(Status);
  462. }