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.

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