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.

622 lines
18 KiB

  1. //+--------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (c) Microsoft Corporation 2000
  6. //
  7. // File: lsaap.cxx
  8. //
  9. // Contents: Authentication package dispatch routines
  10. // LsaApInitializePackage (Not needed done in SpInitialize)
  11. // LsaApLogonUser2
  12. // LsaApCallPackage
  13. // LsaApCallPackagePassthrough
  14. // LsaApLogonTerminated
  15. //
  16. // Helper functions:
  17. //
  18. // History: KDamour 10Mar00 Stolen from msv_sspi\msv1_0.c
  19. //
  20. //---------------------------------------------------------------------
  21. #include "global.h"
  22. #include <samisrv.h>
  23. #define SAM_CLEARTEXT_CREDENTIAL_NAME L"CLEARTEXT"
  24. #define SAM_WDIGEST_CREDENTIAL_NAME WDIGEST_SP_NAME // Name of the Supplemental (primary) cred blob for MD5 hashes
  25. /*++
  26. Routine Description:
  27. This routine is the dispatch routine for
  28. LsaCallAuthenticationPackage().
  29. --*/
  30. NTSTATUS
  31. LsaApCallPackage (
  32. IN PLSA_CLIENT_REQUEST ClientRequest,
  33. IN PVOID ProtocolSubmitBuffer,
  34. IN PVOID ClientBufferBase,
  35. IN ULONG SubmitBufferLength,
  36. OUT PVOID *ProtocolReturnBuffer,
  37. OUT PULONG ReturnBufferLength,
  38. OUT PNTSTATUS ProtocolStatus
  39. )
  40. {
  41. ULONG MessageType;
  42. DebugLog((DEB_TRACE_FUNC, "LsaApCallPackage: Entering/Leaving \n"));
  43. return(SEC_E_UNSUPPORTED_FUNCTION);
  44. }
  45. /*++
  46. Routine Description:
  47. This routine is the dispatch routine for
  48. LsaCallAuthenticationPackage() for untrusted clients.
  49. --*/
  50. NTSTATUS
  51. LsaApCallPackageUntrusted (
  52. IN PLSA_CLIENT_REQUEST ClientRequest,
  53. IN PVOID ProtocolSubmitBuffer,
  54. IN PVOID ClientBufferBase,
  55. IN ULONG SubmitBufferLength,
  56. OUT PVOID *ProtocolReturnBuffer,
  57. OUT PULONG ReturnBufferLength,
  58. OUT PNTSTATUS ProtocolStatus
  59. )
  60. {
  61. ULONG MessageType;
  62. DebugLog((DEB_TRACE_FUNC, "LsaApCallPackageUntrusted: Entering/Leaving \n"));
  63. return(SEC_E_UNSUPPORTED_FUNCTION);
  64. }
  65. /*++
  66. Routine Description:
  67. This routine is the dispatch routine for
  68. LsaCallAuthenticationPackage() for passthrough logon requests.
  69. When the passthrough is called (from AcceptSecurityCOntext)
  70. a databuffer is sent to the DC and this function is called.
  71. Arguments:
  72. ClientRequest - Is a pointer to an opaque data structure
  73. representing the client's request.
  74. ProtocolSubmitBuffer - Supplies a protocol message specific to
  75. the authentication package.
  76. ClientBufferBase - Provides the address within the client
  77. process at which the protocol message was resident.
  78. This may be necessary to fix-up any pointers within the
  79. protocol message buffer.
  80. SubmitBufferLength - Indicates the length of the submitted
  81. protocol message buffer.
  82. ProtocolReturnBuffer - Is used to return the address of the
  83. protocol buffer in the client process. The authentication
  84. package is responsible for allocating and returning the
  85. protocol buffer within the client process. This buffer is
  86. expected to have been allocated with the
  87. AllocateClientBuffer() service.
  88. The format and semantics of this buffer are specific to the
  89. authentication package.
  90. ReturnBufferLength - Receives the length (in bytes) of the
  91. returned protocol buffer.
  92. ProtocolStatus - Assuming the services completion is
  93. STATUS_SUCCESS, this parameter will receive completion status
  94. returned by the specified authentication package. The list
  95. of status values that may be returned are authentication
  96. package specific.
  97. Return Status:
  98. STATUS_SUCCESS - The call was made to the authentication package.
  99. The ProtocolStatus parameter must be checked to see what the
  100. completion status from the authentication package is.
  101. STATUS_QUOTA_EXCEEDED - This error indicates that the return
  102. buffer could not could not be allocated because the client
  103. does not have sufficient quota.
  104. --*/
  105. NTSTATUS
  106. LsaApCallPackagePassthrough (
  107. IN PLSA_CLIENT_REQUEST ClientRequest,
  108. IN PVOID ProtocolSubmitBuffer,
  109. IN PVOID ClientBufferBase,
  110. IN ULONG SubmitBufferLength,
  111. OUT PVOID *ProtocolReturnBuffer,
  112. OUT PULONG ReturnBufferLength,
  113. OUT PNTSTATUS ProtocolStatus
  114. )
  115. {
  116. NTSTATUS Status = STATUS_SUCCESS;
  117. NTSTATUS StatusProtocol = STATUS_SUCCESS;
  118. PDIGEST_BLOB_REQUEST pDigestBlob = NULL;
  119. ULONG MessageType = 0;
  120. USHORT i = 0;
  121. ULONG ulReturnBuffer = 0;
  122. BYTE *pReturnBuffer = NULL;
  123. DebugLog((DEB_TRACE_FUNC, "LsaApCallPackagePassthrough: Entering \n"));
  124. //
  125. // Get the messsage type from the protocol submit buffer.
  126. //
  127. if ( SubmitBufferLength < sizeof(ULONG) ) {
  128. DebugLog((DEB_ERROR, "FAILED message size to contain MessageType\n"));
  129. return STATUS_INVALID_PARAMETER;
  130. }
  131. memcpy((char *)&MessageType, (char *)ProtocolSubmitBuffer, sizeof(MessageType));
  132. if ( MessageType != VERIFY_DIGEST_MESSAGE)
  133. {
  134. DebugLog((DEB_ERROR, "FAILED to have correct message type\n"));
  135. return STATUS_ACCESS_DENIED;
  136. }
  137. //
  138. // Allow the DigestCalc routine to only set the return buffer information
  139. // on success conditions.
  140. //
  141. DebugLog((DEB_TRACE, "LsaApCallPackagePassthrough: setting return buffers to NULL\n"));
  142. *ProtocolReturnBuffer = NULL;
  143. *ReturnBufferLength = 0;
  144. // We will need to free any memory allocated in the Returnbuffer
  145. StatusProtocol = DigestPackagePassthrough((USHORT)SubmitBufferLength, (BYTE *)ProtocolSubmitBuffer,
  146. &ulReturnBuffer, &pReturnBuffer);
  147. if (!NT_SUCCESS(StatusProtocol))
  148. {
  149. DebugLog((DEB_ERROR,"LsaApCallPackagePassthrough: DigestPackagePassthrough failed 0x%x\n",Status));
  150. ulReturnBuffer = 0;
  151. goto CleanUp;
  152. }
  153. // DebugLog((DEB_TRACE, "LsaApCallPackagePassthrough: setting return auth status to STATUS_SUCCEED\n"));
  154. // DebugLog((DEB_TRACE, "LsaApCallPackagePassthrough: Total Return Buffer size %ld bytes\n", ulReturnBuffer));
  155. // Now place the data back to the client (the server calling this)
  156. Status = g_LsaFunctions->AllocateClientBuffer(
  157. NULL,
  158. ulReturnBuffer,
  159. ProtocolReturnBuffer
  160. );
  161. if (!NT_SUCCESS(Status))
  162. {
  163. goto CleanUp;
  164. }
  165. Status = g_LsaFunctions->CopyToClientBuffer(
  166. NULL,
  167. ulReturnBuffer,
  168. *ProtocolReturnBuffer,
  169. pReturnBuffer
  170. );
  171. if (!NT_SUCCESS(Status))
  172. { // Failed to copy over the data to the client
  173. g_LsaFunctions->FreeClientBuffer(
  174. NULL,
  175. *ProtocolReturnBuffer
  176. );
  177. *ProtocolReturnBuffer = NULL;
  178. }
  179. else
  180. {
  181. *ReturnBufferLength = ulReturnBuffer;
  182. }
  183. CleanUp:
  184. *ProtocolStatus = StatusProtocol;
  185. if (pReturnBuffer)
  186. {
  187. DigestFreeMemory(pReturnBuffer);
  188. pReturnBuffer = NULL;
  189. ulReturnBuffer = 0;
  190. }
  191. DebugLog((DEB_TRACE_FUNC, "LsaApCallPackagePassthrough: Leaving Status 0x%x\n", Status));
  192. return(Status);
  193. }
  194. /*++
  195. Routine Description:
  196. This routine is used to authenticate a user logon attempt. This is
  197. the user's initial logon. A new LSA logon session will be established
  198. for the user and validation information for the user will be returned.
  199. --*/
  200. NTSTATUS
  201. LsaApLogonUserEx2 (
  202. IN PLSA_CLIENT_REQUEST ClientRequest,
  203. IN SECURITY_LOGON_TYPE LogonType,
  204. IN PVOID ProtocolSubmitBuffer,
  205. IN PVOID ClientBufferBase,
  206. IN ULONG SubmitBufferSize,
  207. OUT PVOID *ProfileBuffer,
  208. OUT PULONG ProfileBufferSize,
  209. OUT PLUID LogonId,
  210. OUT PNTSTATUS SubStatus,
  211. OUT PLSA_TOKEN_INFORMATION_TYPE TokenInformationType,
  212. OUT PVOID *TokenInformation,
  213. OUT PUNICODE_STRING *AccountName,
  214. OUT PUNICODE_STRING *AuthenticatingAuthority,
  215. OUT PUNICODE_STRING *MachineName,
  216. OUT PSECPKG_PRIMARY_CRED PrimaryCredentials,
  217. OUT PSECPKG_SUPPLEMENTAL_CRED_ARRAY * SupplementalCredentials
  218. )
  219. {
  220. NTSTATUS Status = STATUS_SUCCESS;
  221. DebugLog((DEB_TRACE_FUNC, "LsaApLogonUserEx2: Entering/Leaving \n"));
  222. //
  223. // Return status to the caller
  224. //
  225. return (SEC_E_UNSUPPORTED_FUNCTION);
  226. }
  227. /*++
  228. Routine Description:
  229. This routine is used to notify each authentication package when a logon
  230. session terminates. A logon session terminates when the last token
  231. referencing the logon session is deleted.
  232. Arguments:
  233. LogonId - Is the logon ID that just logged off.
  234. Return Status:
  235. None.
  236. --*/
  237. VOID
  238. LsaApLogonTerminated (
  239. IN PLUID pLogonId
  240. )
  241. {
  242. NTSTATUS Status = STATUS_SUCCESS;
  243. PDIGEST_LOGONSESSION pLogonSession = NULL;
  244. LONG lReferences = 0;
  245. DebugLog((DEB_TRACE_FUNC, "LsaApLogonTerminated: Entering LogonID (%x:%lx) \n",
  246. pLogonId->HighPart, pLogonId->LowPart));
  247. //
  248. // Find the entry, dereference, and de-link it from the active logon table.
  249. //
  250. Status = LogSessHandlerLogonIdToPtr(pLogonId, TRUE, &pLogonSession);
  251. if (!NT_SUCCESS(Status))
  252. {
  253. goto CleanUp; // No LongonID found in Active list - simply exit quietly
  254. }
  255. DebugLog((DEB_TRACE, "LsaApLogonTerminated: Found LogonID (%x:%lx) \n",
  256. pLogonId->HighPart, pLogonId->LowPart));
  257. // This relies on the LSA terminating all of the credentials before killing off
  258. // the LogonSession.
  259. lReferences = InterlockedDecrement(&pLogonSession->lReferences);
  260. DebugLog((DEB_TRACE, "LsaApLogonTerminated: Refcount %ld \n", lReferences));
  261. ASSERT( lReferences >= 0 );
  262. if (lReferences)
  263. {
  264. DebugLog((DEB_WARN, "LsaApLogonTerminated: WARNING Terminate LogonID (%x:%lx) non-zero RefCount!\n",
  265. pLogonId->HighPart, pLogonId->LowPart));
  266. }
  267. else
  268. {
  269. DebugLog((DEB_TRACE, "LsaApLogonTerminated: Removed LogonID (%x:%lx) from Active List! \n",
  270. pLogonId->HighPart, pLogonId->LowPart));
  271. LogonSessionFree(pLogonSession);
  272. }
  273. CleanUp:
  274. DebugLog((DEB_TRACE_FUNC, "LsaApLogonTerminated: Exiting LogonID (%x:%lx) \n",
  275. pLogonId->HighPart, pLogonId->LowPart));
  276. return;
  277. }
  278. // Routine to acquire the plaintext password for a given user
  279. // If supplemental credentials exist that contain the Digest Hash values
  280. // then return them also.
  281. // This routine runs on the domain controller
  282. // Must provide STRING strPasswd
  283. // If ppucUserAuthData pointer is NULL - do not retrieve UserAuthData
  284. NTSTATUS
  285. DigestGetPasswd(
  286. IN PUSER_CREDENTIALS pUserCreds,
  287. OUT PUCHAR * ppucUserAuthData,
  288. OUT PULONG pulAuthDataSize
  289. )
  290. {
  291. NTSTATUS Status = STATUS_SUCCESS;
  292. SAMPR_HANDLE UserHandle = NULL;
  293. UNICODE_STRING ustrcPackageName;
  294. UNICODE_STRING ustrcTemp;
  295. STRING strcTemp;
  296. PVOID pvPlainPwd = NULL;
  297. PVOID pvHashCred = NULL;
  298. ULONG ulLenPassword = 0;
  299. ULONG ulLenHash = 0;
  300. ULONG ulVersion = 0;
  301. BOOL bOpenedSAM = FALSE;
  302. DebugLog((DEB_TRACE_FUNC,"DigestGetPasswd: Entering\n"));
  303. RtlZeroMemory(&ustrcTemp, sizeof(ustrcTemp));
  304. RtlZeroMemory(&strcTemp, sizeof(strcTemp));
  305. RtlZeroMemory(&ustrcPackageName, sizeof(ustrcPackageName));
  306. pUserCreds->fIsValidDigestHash = FALSE;
  307. pUserCreds->fIsValidPasswd = FALSE;
  308. *pulAuthDataSize = 0L;
  309. *ppucUserAuthData = NULL;
  310. DebugLog((DEB_TRACE,"DigestGetPasswd: looking for username (unicode) %wZ\n", &(pUserCreds->ustrUsername)));
  311. if (!g_fDomainController)
  312. {
  313. DebugLog((DEB_ERROR,"DigestGetPasswd: Not on a domaincontroller - can not get credentials\n"));
  314. Status = STATUS_INVALID_SERVER_STATE;
  315. goto CleanUp;
  316. }
  317. // Call LsaOpenSamUser()
  318. Status = g_LsaFunctions->OpenSamUser(&(pUserCreds->ustrUsername), SecNameSamCompatible,
  319. NULL, FALSE, 0, &UserHandle);
  320. if ( !NT_SUCCESS( Status ) )
  321. {
  322. DebugLog((DEB_ERROR, "DigestGetPasswd: Failed to open SAM for user %wZ, Status = 0x%x\n",
  323. &(pUserCreds->ustrUsername), Status));
  324. goto CleanUp;
  325. }
  326. bOpenedSAM = TRUE;
  327. DebugLog((DEB_TRACE,"DigestGetPasswd: Have a valid UserHandle\n"));
  328. //
  329. // Retrieve the MD5 hashed pre-calculated values if they exist for this user
  330. //
  331. // NOTE : On NT 5, this API only works on Domain Controllers !!
  332. //
  333. RtlInitUnicodeString(&ustrcPackageName, SAM_WDIGEST_CREDENTIAL_NAME);
  334. Status = SamIRetrievePrimaryCredentials( (SAMPR_HANDLE) UserHandle,
  335. &ustrcPackageName,
  336. &pvHashCred,
  337. &ulLenHash);
  338. if (!NT_SUCCESS( Status ))
  339. {
  340. pvHashCred = NULL;
  341. DebugLog((DEB_TRACE,"DigestGetPasswd: NO Pre-calc Hashes were found for user\n"));
  342. }
  343. else
  344. {
  345. strcTemp.Buffer = (PCHAR) pvHashCred;
  346. strcTemp.Length = strcTemp.MaximumLength = (USHORT) ulLenHash;
  347. Status = StringDuplicate(&(pUserCreds->strDigestHash), &strcTemp);
  348. if (!NT_SUCCESS( Status ))
  349. {
  350. DebugLog((DEB_ERROR, "DigestGetPasswd: Failed to copy plaintext password, error 0x%x\n", Status));
  351. goto CleanUp;
  352. }
  353. // DebugLog((DEB_TRACE,"DigestGetPasswd: Have the PASSWORD %wZ\n", &(pUserCreds->ustrPasswd)));
  354. Status = RtlCharToInteger(pUserCreds->strDigestHash.Buffer, TENBASE, &ulVersion);
  355. if (!NT_SUCCESS(Status))
  356. {
  357. Status = STATUS_INVALID_PARAMETER;
  358. DebugLog((DEB_ERROR, "DigestGetPasswd: Badly formatted pre-calc version\n"));
  359. goto CleanUp;
  360. }
  361. // Check version and size of credentials
  362. if ((ulVersion != SUPPCREDS_VERSION) || (ulLenHash < (TOTALPRECALC_HEADERS * MD5_HASH_BYTESIZE)))
  363. {
  364. DebugLog((DEB_ERROR, "DigestGetPasswd: Invalid precalc version or size\n"));
  365. pUserCreds->fIsValidDigestHash = FALSE;
  366. }
  367. else
  368. {
  369. pUserCreds->fIsValidDigestHash = TRUE;
  370. // setup the hashes to utilize - get format from the notify.cxx hash calcs
  371. switch (pUserCreds->typeName)
  372. {
  373. case NAMEFORMAT_ACCOUNTNAME:
  374. pUserCreds->sHashTags[NAME_ACCT] = 1;
  375. pUserCreds->sHashTags[NAME_ACCT_DOWNCASE] = 1;
  376. pUserCreds->sHashTags[NAME_ACCT_UPCASE] = 1;
  377. break;
  378. case NAMEFORMAT_UPN:
  379. pUserCreds->sHashTags[NAME_UPN] = 1;
  380. pUserCreds->sHashTags[NAME_UPN_DOWNCASE] = 1;
  381. pUserCreds->sHashTags[NAME_UPN_UPCASE] = 1;
  382. break;
  383. case NAMEFORMAT_NETBIOS:
  384. pUserCreds->sHashTags[NAME_NT4] = 1;
  385. pUserCreds->sHashTags[NAME_NT4_DOWNCASE] = 1;
  386. pUserCreds->sHashTags[NAME_NT4_UPCASE] = 1;
  387. break;
  388. default:
  389. break;
  390. }
  391. }
  392. DebugLog((DEB_TRACE,"DigestGetPasswd: Read in Pre-calc Hashes size = %lu\n", ulLenHash));
  393. }
  394. //
  395. // Retrieve the plaintext password
  396. //
  397. // NOTE : On NT 5, this API only works on Domain Controllers !!
  398. //
  399. RtlInitUnicodeString(&ustrcPackageName, SAM_CLEARTEXT_CREDENTIAL_NAME);
  400. // Note: Would be nice to have this as a LSAFunction
  401. Status = SamIRetrievePrimaryCredentials( (SAMPR_HANDLE) UserHandle,
  402. &ustrcPackageName,
  403. &pvPlainPwd,
  404. &ulLenPassword);
  405. if (!NT_SUCCESS( Status ))
  406. {
  407. DebugLog((DEB_ERROR, "DigestGetPasswd: Failed to retrieve plaintext password, error 0x%x\n", Status));
  408. if (pUserCreds->fIsValidDigestHash == FALSE)
  409. {
  410. // We have no pre-computed MD5 hashes and also no cleartext password
  411. // we can not perform any Digest Auth operations
  412. //
  413. // Explicitly set the status to be "wrong password" instead of whatever
  414. // is returned by SamIRetrievePrimaryCredentials
  415. //
  416. Status = STATUS_WRONG_PASSWORD;
  417. DebugLog((DEB_ERROR,"DigestGetPasswd: Can not obtain cleartext or Hashed Creds\n"));
  418. goto CleanUp;
  419. }
  420. }
  421. else
  422. {
  423. ustrcTemp.Buffer = (PUSHORT) pvPlainPwd;
  424. ustrcTemp.Length = ustrcTemp.MaximumLength = (USHORT) ulLenPassword;
  425. Status = UnicodeStringDuplicate(&(pUserCreds->ustrPasswd), &ustrcTemp);
  426. if (!NT_SUCCESS( Status ))
  427. {
  428. DebugLog((DEB_ERROR, "DigestGetPasswd: Failed to copy plaintext password, error 0x%x\n", Status));
  429. goto CleanUp;
  430. }
  431. // DebugLog((DEB_TRACE,"DigestGetPasswd: Have the PASSWORD %wZ\n", &(pUserCreds->ustrPasswd)));
  432. pUserCreds->fIsValidPasswd = TRUE;
  433. DebugLog((DEB_TRACE,"DigestGetPasswd: Password retrieved\n"));
  434. }
  435. // We have some form of credentials based on password (either cleartext or pre-computed)
  436. if (ppucUserAuthData)
  437. { // Go fetch the AuthData to marshall back to the server for Token creation
  438. *ppucUserAuthData = NULL;
  439. *pulAuthDataSize = 0;
  440. // Calling LsaGetUserAuthData()
  441. Status = g_LsaFunctions->GetUserAuthData(UserHandle, ppucUserAuthData, pulAuthDataSize);
  442. if (!NT_SUCCESS(Status))
  443. {
  444. DebugLog((DEB_ERROR,"DigestGetPasswd: failed acquire UseAuthData 0x%x\n", Status));
  445. goto CleanUp;
  446. }
  447. }
  448. CleanUp:
  449. // Release any memory from SamI* calls Would be nice to have this as a LSAFunction
  450. if (pvPlainPwd)
  451. {
  452. if (ulLenPassword > 0)
  453. {
  454. ZeroMemory(pvPlainPwd, ulLenPassword);
  455. }
  456. LocalFree(pvPlainPwd);
  457. pvPlainPwd = NULL;
  458. }
  459. if (pvHashCred)
  460. {
  461. LocalFree(pvHashCred);
  462. pvHashCred = NULL;
  463. }
  464. if (bOpenedSAM == TRUE)
  465. {
  466. // LsaCloseSamUser()
  467. Status = g_LsaFunctions->CloseSamUser(UserHandle);
  468. if (!NT_SUCCESS(Status))
  469. {
  470. DebugLog((DEB_ERROR,"DigestGetPasswd: failed LsaCloseSamUser 0x%x\n", Status));
  471. }
  472. bOpenedSAM = FALSE;
  473. }
  474. if (!NT_SUCCESS(Status))
  475. { // Cleanup functions since there was a failure
  476. if (*ppucUserAuthData)
  477. {
  478. g_LsaFunctions->FreeLsaHeap(*ppucUserAuthData);
  479. *ppucUserAuthData = NULL;
  480. *pulAuthDataSize = 0L;
  481. }
  482. }
  483. DebugLog((DEB_TRACE_FUNC,"DigestGetPasswd: Leaving\n"));
  484. return(Status);
  485. }