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.

2465 lines
75 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. // This file also has all routines that access the SAM through SamR* and SamI* routines
  18. //
  19. // History: KDamour 10Mar00 Based on msv_sspi\msv1_0.c
  20. //
  21. //---------------------------------------------------------------------
  22. #include "global.h"
  23. extern "C"
  24. {
  25. #include <align.h> // ROUND_UP_COUNT
  26. #include <lsarpc.h>
  27. #include <samrpc.h>
  28. #include <logonmsv.h>
  29. #include <lsaisrv.h>
  30. }
  31. #include <pac.hxx> // MUST be outside of the Extern C since libs are exported as C++
  32. // Local prototypes
  33. NTSTATUS
  34. DigestFilterSids(
  35. IN PDIGEST_PARAMETER pDigest,
  36. IN PNETLOGON_VALIDATION_SAM_INFO3 ValidationInfo
  37. );
  38. #define SAM_CLEARTEXT_CREDENTIAL_NAME L"CLEARTEXT"
  39. #define SAM_WDIGEST_CREDENTIAL_NAME WDIGEST_SP_NAME // Name of the Supplemental (primary) cred blob for MD5 hashes
  40. const ULONG USER_ALL_DIGEST_INFO =
  41. USER_ALL_USERNAME |
  42. USER_ALL_BADPASSWORDCOUNT |
  43. USER_ALL_LOGONCOUNT |
  44. USER_ALL_PASSWORDMUSTCHANGE |
  45. USER_ALL_PASSWORDCANCHANGE |
  46. USER_ALL_WORKSTATIONS |
  47. USER_ALL_LOGONHOURS |
  48. USER_ALL_ACCOUNTEXPIRES |
  49. USER_ALL_PRIMARYGROUPID |
  50. USER_ALL_USERID |
  51. USER_ALL_USERACCOUNTCONTROL;
  52. // Local variables for access to SAM - used only on domain controller
  53. SAMPR_HANDLE l_AccountSamHandle = NULL;
  54. SAMPR_HANDLE l_AccountDomainHandle = NULL;
  55. /*++
  56. Routine Description:
  57. This routine is the dispatch routine for
  58. LsaCallAuthenticationPackage().
  59. --*/
  60. NTSTATUS
  61. LsaApCallPackage (
  62. IN PLSA_CLIENT_REQUEST ClientRequest,
  63. IN PVOID ProtocolSubmitBuffer,
  64. IN PVOID ClientBufferBase,
  65. IN ULONG SubmitBufferLength,
  66. OUT PVOID *ProtocolReturnBuffer,
  67. OUT PULONG ReturnBufferLength,
  68. OUT PNTSTATUS ProtocolStatus
  69. )
  70. {
  71. UNREFERENCED_PARAMETER(ClientRequest);
  72. UNREFERENCED_PARAMETER(ProtocolSubmitBuffer);
  73. UNREFERENCED_PARAMETER(ClientBufferBase);
  74. UNREFERENCED_PARAMETER(SubmitBufferLength);
  75. UNREFERENCED_PARAMETER(ProtocolReturnBuffer);
  76. UNREFERENCED_PARAMETER(ReturnBufferLength);
  77. UNREFERENCED_PARAMETER(ProtocolStatus);
  78. DebugLog((DEB_TRACE_FUNC, "LsaApCallPackage: Entering/Leaving \n"));
  79. return(SEC_E_UNSUPPORTED_FUNCTION);
  80. }
  81. /*++
  82. Routine Description:
  83. This routine is the dispatch routine for
  84. LsaCallAuthenticationPackage() for untrusted clients.
  85. --*/
  86. NTSTATUS
  87. LsaApCallPackageUntrusted (
  88. IN PLSA_CLIENT_REQUEST ClientRequest,
  89. IN PVOID ProtocolSubmitBuffer,
  90. IN PVOID ClientBufferBase,
  91. IN ULONG SubmitBufferLength,
  92. OUT PVOID *ProtocolReturnBuffer,
  93. OUT PULONG ReturnBufferLength,
  94. OUT PNTSTATUS ProtocolStatus
  95. )
  96. {
  97. UNREFERENCED_PARAMETER(ClientRequest);
  98. UNREFERENCED_PARAMETER(ProtocolSubmitBuffer);
  99. UNREFERENCED_PARAMETER(ClientBufferBase);
  100. UNREFERENCED_PARAMETER(SubmitBufferLength);
  101. UNREFERENCED_PARAMETER(ProtocolReturnBuffer);
  102. UNREFERENCED_PARAMETER(ReturnBufferLength);
  103. UNREFERENCED_PARAMETER(ProtocolStatus);
  104. DebugLog((DEB_TRACE_FUNC, "LsaApCallPackageUntrusted: Entering/Leaving \n"));
  105. return(SEC_E_UNSUPPORTED_FUNCTION);
  106. }
  107. /*++
  108. Routine Description:
  109. This routine is the dispatch routine for
  110. LsaCallAuthenticationPackage() for passthrough logon requests.
  111. When the passthrough is called (from AcceptSecurityCOntext)
  112. a databuffer is sent to the DC and this function is called.
  113. Arguments:
  114. ClientRequest - Is a pointer to an opaque data structure
  115. representing the client's request.
  116. ProtocolSubmitBuffer - Supplies a protocol message specific to
  117. the authentication package.
  118. ClientBufferBase - Provides the address within the client
  119. process at which the protocol message was resident.
  120. This may be necessary to fix-up any pointers within the
  121. protocol message buffer.
  122. SubmitBufferLength - Indicates the length of the submitted
  123. protocol message buffer.
  124. ProtocolReturnBuffer - Is used to return the address of the
  125. protocol buffer in the client process. The authentication
  126. package is responsible for allocating and returning the
  127. protocol buffer within the client process. This buffer is
  128. expected to have been allocated with the
  129. AllocateClientBuffer() service.
  130. The format and semantics of this buffer are specific to the
  131. authentication package.
  132. ReturnBufferLength - Receives the length (in bytes) of the
  133. returned protocol buffer.
  134. ProtocolStatus - Assuming the services completion is
  135. STATUS_SUCCESS, this parameter will receive completion status
  136. returned by the specified authentication package. The list
  137. of status values that may be returned are authentication
  138. package specific.
  139. Return Status:
  140. STATUS_SUCCESS - The call was made to the authentication package.
  141. The ProtocolStatus parameter must be checked to see what the
  142. completion status from the authentication package is.
  143. STATUS_QUOTA_EXCEEDED - This error indicates that the return
  144. buffer could not could not be allocated because the client
  145. does not have sufficient quota.
  146. --*/
  147. NTSTATUS
  148. LsaApCallPackagePassthrough (
  149. IN PLSA_CLIENT_REQUEST ClientRequest,
  150. IN PVOID ProtocolSubmitBuffer,
  151. IN PVOID ClientBufferBase,
  152. IN ULONG SubmitBufferLength,
  153. OUT PVOID *ProtocolReturnBuffer,
  154. OUT PULONG ReturnBufferLength,
  155. OUT PNTSTATUS ProtocolStatus
  156. )
  157. {
  158. NTSTATUS Status = STATUS_SUCCESS;
  159. NTSTATUS StatusProtocol = STATUS_SUCCESS;
  160. ULONG MessageType = 0;
  161. ULONG ulReturnBuffer = 0;
  162. BYTE *pReturnBuffer = NULL;
  163. UNREFERENCED_PARAMETER(ClientRequest);
  164. UNREFERENCED_PARAMETER(ClientBufferBase);
  165. ASSERT(ProtocolSubmitBuffer);
  166. ASSERT(ProtocolReturnBuffer);
  167. ASSERT(ReturnBufferLength);
  168. ASSERT(ProtocolStatus);
  169. DebugLog((DEB_TRACE_FUNC, "LsaApCallPackagePassthrough: Entering \n"));
  170. //
  171. // Get the messsage type from the protocol submit buffer.
  172. //
  173. if ( SubmitBufferLength < sizeof(DIGEST_BLOB_REQUEST) ) {
  174. DebugLog((DEB_ERROR, "LsaApCallPackagePassthrough: FAILED message size to contain Digest Request\n"));
  175. return STATUS_INVALID_PARAMETER;
  176. }
  177. memcpy((char *)&MessageType, (char *)ProtocolSubmitBuffer, sizeof(MessageType));
  178. if ( MessageType != VERIFY_DIGEST_MESSAGE)
  179. {
  180. DebugLog((DEB_ERROR, "FAILED to have correct message type\n"));
  181. return STATUS_ACCESS_DENIED;
  182. }
  183. //
  184. // Allow the DigestCalc routine to only set the return buffer information
  185. // on success conditions.
  186. //
  187. DebugLog((DEB_TRACE, "LsaApCallPackagePassthrough: setting return buffers to NULL\n"));
  188. *ProtocolReturnBuffer = NULL;
  189. *ReturnBufferLength = 0;
  190. // We will need to free any memory allocated in the Returnbuffer
  191. StatusProtocol = DigestPackagePassthrough((USHORT)SubmitBufferLength, (BYTE *)ProtocolSubmitBuffer,
  192. &ulReturnBuffer, &pReturnBuffer);
  193. if (!NT_SUCCESS(StatusProtocol))
  194. {
  195. DebugLog((DEB_ERROR,"LsaApCallPackagePassthrough: DigestPackagePassthrough failed 0x%x\n", StatusProtocol));
  196. ulReturnBuffer = 0;
  197. goto CleanUp;
  198. }
  199. // DebugLog((DEB_TRACE, "LsaApCallPackagePassthrough: setting return auth status to STATUS_SUCCEED\n"));
  200. // DebugLog((DEB_TRACE, "LsaApCallPackagePassthrough: Total Return Buffer size %ld bytes\n", ulReturnBuffer));
  201. // Now place the data back to the client (the server calling this)
  202. ASSERT(ulReturnBuffer); // we should always have data on successful logon to send back
  203. if (ulReturnBuffer)
  204. {
  205. Status = g_LsaFunctions->AllocateClientBuffer(
  206. NULL,
  207. ulReturnBuffer,
  208. ProtocolReturnBuffer
  209. );
  210. if (!NT_SUCCESS(Status))
  211. {
  212. goto CleanUp;
  213. }
  214. Status = g_LsaFunctions->CopyToClientBuffer(
  215. NULL,
  216. ulReturnBuffer,
  217. *ProtocolReturnBuffer,
  218. pReturnBuffer
  219. );
  220. if (!NT_SUCCESS(Status))
  221. { // Failed to copy over the data to the client
  222. g_LsaFunctions->FreeClientBuffer(
  223. NULL,
  224. *ProtocolReturnBuffer
  225. );
  226. *ProtocolReturnBuffer = NULL;
  227. }
  228. else
  229. {
  230. *ReturnBufferLength = ulReturnBuffer;
  231. }
  232. }
  233. else
  234. {
  235. DebugLog((DEB_ERROR, "LsaApCallPackagePassthrough: Zero length return buffer\n"));
  236. Status = STATUS_INTERNAL_ERROR;
  237. }
  238. CleanUp:
  239. *ProtocolStatus = StatusProtocol;
  240. if (pReturnBuffer)
  241. {
  242. DigestFreeMemory(pReturnBuffer);
  243. pReturnBuffer = NULL;
  244. ulReturnBuffer = 0;
  245. }
  246. DebugLog((DEB_TRACE_FUNC, "LsaApCallPackagePassthrough: Leaving Status 0x%x\n", Status));
  247. return(Status);
  248. }
  249. /*++
  250. Routine Description:
  251. This routine is used to authenticate a user logon attempt. This is
  252. the user's initial logon. A new LSA logon session will be established
  253. for the user and validation information for the user will be returned.
  254. Unused function in Digest
  255. --*/
  256. NTSTATUS
  257. LsaApLogonUserEx2 (
  258. IN PLSA_CLIENT_REQUEST ClientRequest,
  259. IN SECURITY_LOGON_TYPE LogonType,
  260. IN PVOID ProtocolSubmitBuffer,
  261. IN PVOID ClientBufferBase,
  262. IN ULONG SubmitBufferSize,
  263. OUT PVOID *ProfileBuffer,
  264. OUT PULONG ProfileBufferSize,
  265. OUT PLUID LogonId,
  266. OUT PNTSTATUS SubStatus,
  267. OUT PLSA_TOKEN_INFORMATION_TYPE TokenInformationType,
  268. OUT PVOID *TokenInformation,
  269. OUT PUNICODE_STRING *AccountName,
  270. OUT PUNICODE_STRING *AuthenticatingAuthority,
  271. OUT PUNICODE_STRING *MachineName,
  272. OUT PSECPKG_PRIMARY_CRED PrimaryCredentials,
  273. OUT PSECPKG_SUPPLEMENTAL_CRED_ARRAY * SupplementalCredentials
  274. )
  275. {
  276. UNREFERENCED_PARAMETER(ClientRequest);
  277. UNREFERENCED_PARAMETER(LogonType);
  278. UNREFERENCED_PARAMETER(ProtocolSubmitBuffer);
  279. UNREFERENCED_PARAMETER(ClientBufferBase);
  280. UNREFERENCED_PARAMETER(SubmitBufferSize);
  281. UNREFERENCED_PARAMETER(ProfileBuffer);
  282. UNREFERENCED_PARAMETER(ProfileBufferSize);
  283. UNREFERENCED_PARAMETER(LogonId);
  284. UNREFERENCED_PARAMETER(SubStatus);
  285. UNREFERENCED_PARAMETER(TokenInformationType);
  286. UNREFERENCED_PARAMETER(TokenInformation);
  287. UNREFERENCED_PARAMETER(AccountName);
  288. UNREFERENCED_PARAMETER(AuthenticatingAuthority);
  289. UNREFERENCED_PARAMETER(MachineName);
  290. UNREFERENCED_PARAMETER(PrimaryCredentials);
  291. UNREFERENCED_PARAMETER(SupplementalCredentials);
  292. DebugLog((DEB_TRACE_FUNC, "LsaApLogonUserEx2: Entering/Leaving \n"));
  293. return (SEC_E_UNSUPPORTED_FUNCTION);
  294. }
  295. /*++
  296. Routine Description:
  297. This routine is used to notify each authentication package when a logon
  298. session terminates. A logon session terminates when the last token
  299. referencing the logon session is deleted.
  300. Arguments:
  301. LogonId - Is the logon ID that just logged off.
  302. Return Status:
  303. None.
  304. --*/
  305. VOID
  306. LsaApLogonTerminated (
  307. IN PLUID pLogonId
  308. )
  309. {
  310. NTSTATUS Status = STATUS_SUCCESS;
  311. PDIGEST_LOGONSESSION pLogonSession = NULL;
  312. LONG lReferences = 0;
  313. DebugLog((DEB_TRACE_FUNC, "LsaApLogonTerminated: Entering LogonID (%x:%lx) \n",
  314. pLogonId->HighPart, pLogonId->LowPart));
  315. //
  316. // Find the entry, dereference, and de-link it from the active logon table.
  317. //
  318. Status = LogSessHandlerLogonIdToPtr(pLogonId, TRUE, &pLogonSession);
  319. if (!NT_SUCCESS(Status))
  320. {
  321. goto CleanUp; // No LongonID found in Active list - simply exit quietly
  322. }
  323. DebugLog((DEB_TRACE, "LsaApLogonTerminated: Found LogonID (%x:%lx) \n",
  324. pLogonId->HighPart, pLogonId->LowPart));
  325. // This relies on the LSA terminating all of the credentials before killing off
  326. // the LogonSession.
  327. lReferences = InterlockedDecrement(&pLogonSession->lReferences);
  328. DebugLog((DEB_TRACE, "LsaApLogonTerminated: Refcount %ld \n", lReferences));
  329. ASSERT( lReferences >= 0 );
  330. if (lReferences)
  331. {
  332. DebugLog((DEB_WARN, "LsaApLogonTerminated: WARNING Terminate LogonID (%x:%lx) non-zero RefCount!\n",
  333. pLogonId->HighPart, pLogonId->LowPart));
  334. }
  335. else
  336. {
  337. DebugLog((DEB_TRACE, "LsaApLogonTerminated: Removed LogonID (%x:%lx) from Active List! \n",
  338. pLogonId->HighPart, pLogonId->LowPart));
  339. LogonSessionFree(pLogonSession);
  340. }
  341. CleanUp:
  342. DebugLog((DEB_TRACE_FUNC, "LsaApLogonTerminated: Exiting LogonID (%x:%lx) \n",
  343. pLogonId->HighPart, pLogonId->LowPart));
  344. return;
  345. }
  346. // Routine to acquire the plaintext password for a given user
  347. // If supplemental credentials exist that contain the Digest Hash values
  348. // then return them also.
  349. // This routine runs on the domain controller
  350. // Must provide STRING strPasswd
  351. NTSTATUS
  352. DigestGetPasswd(
  353. SAMPR_HANDLE UserHandle,
  354. IN PDIGEST_PARAMETER pDigest,
  355. IN OUT PUSER_CREDENTIALS pUserCreds
  356. )
  357. {
  358. NTSTATUS Status = STATUS_SUCCESS;
  359. UNICODE_STRING ustrcPackageName = {0};
  360. UNICODE_STRING ustrcTemp = {0};
  361. STRING strcTemp = {0};
  362. PVOID pvPlainPwd = NULL;
  363. PVOID pvHashCred = NULL;
  364. ULONG ulLenPassword = 0;
  365. ULONG ulLenHash = 0;
  366. DebugLog((DEB_TRACE_FUNC,"DigestGetPasswd: Entering\n"));
  367. pUserCreds->fIsValidDigestHash = FALSE;
  368. pUserCreds->fIsValidPasswd = FALSE;
  369. ASSERT(pDigest);
  370. ASSERT(pUserCreds);
  371. if (!g_fDomainController)
  372. {
  373. DebugLog((DEB_ERROR,"DigestGetPasswd: Not on a domaincontroller - can not get credentials\n"));
  374. Status = STATUS_INVALID_SERVER_STATE;
  375. goto CleanUp;
  376. }
  377. //
  378. // Retrieve the MD5 hashed pre-calculated values if they exist for this user
  379. //
  380. // NOTE : On NT 5, this API only works on Domain Controllers !!
  381. //
  382. RtlInitUnicodeString(&ustrcPackageName, SAM_WDIGEST_CREDENTIAL_NAME);
  383. Status = SamIRetrievePrimaryCredentials( UserHandle,
  384. &ustrcPackageName,
  385. &pvHashCred,
  386. &ulLenHash);
  387. if (!NT_SUCCESS( Status ))
  388. {
  389. pvHashCred = NULL;
  390. DebugLog((DEB_TRACE,"DigestGetPasswd: NO Pre-calc Hashes were found for user\n"));
  391. }
  392. else
  393. {
  394. if ((ulLenHash >= MD5_HASH_BYTESIZE) && (ulLenHash < MAXUSHORT)) // must have a valid header size header
  395. {
  396. strcTemp.Buffer = (PCHAR) pvHashCred;
  397. strcTemp.Length = strcTemp.MaximumLength = (USHORT) ulLenHash;
  398. Status = StringDuplicate(&(pUserCreds->strDigestHash), &strcTemp);
  399. if (!NT_SUCCESS( Status ))
  400. {
  401. DebugLog((DEB_ERROR, "DigestGetPasswd: Failed to copy precalc hashes, error 0x%x\n", Status));
  402. goto CleanUp;
  403. }
  404. /* RC1 supp creds does not have SUPPCREDS_CNTLOC so use the supp cred length for now
  405. // Extract the number of pre-calculated digest hashes - just an extra check on supp cred size
  406. pUserCreds->usDigestHashCnt = (USHORT) *(strcTemp.Buffer + SUPPCREDS_CNTLOC);
  407. DebugLog((DEB_TRACE,"DigestGetPasswd: Read in %d Pre-calc Hashes size = %lu\n",
  408. pUserCreds->usDigestHashCnt, ulLenHash));
  409. if (ulLenHash != ((ULONG)(pUserCreds->usDigestHashCnt + 1) * MD5_HASH_BYTESIZE))
  410. {
  411. Status = SEC_E_NO_CREDENTIALS;
  412. DebugLog((DEB_ERROR, "DigestGetPasswd: Mismatch count with hash size count %d, error 0x%x\n",
  413. pUserCreds->usDigestHashCnt, Status));
  414. goto CleanUp;
  415. }
  416. */
  417. pUserCreds->usDigestHashCnt = (USHORT) (ulLenHash / MD5_HASH_BYTESIZE) - 1;
  418. DebugLog((DEB_TRACE,"DigestGetPasswd: Read in %d Pre-calc Hashes size = %lu\n",
  419. pUserCreds->usDigestHashCnt, ulLenHash));
  420. // setup the hashes to utilize - get format from the notify.cxx hash calcs
  421. switch (pDigest->typeName)
  422. {
  423. case NAMEFORMAT_ACCOUNTNAME:
  424. pUserCreds->sHashTags[NAME_ACCT] = 1;
  425. pUserCreds->sHashTags[NAME_ACCT_DOWNCASE] = 1;
  426. pUserCreds->sHashTags[NAME_ACCT_UPCASE] = 1;
  427. pUserCreds->sHashTags[NAME_ACCT_DUCASE] = 1;
  428. pUserCreds->sHashTags[NAME_ACCT_UDCASE] = 1;
  429. pUserCreds->sHashTags[NAME_ACCT_NUCASE] = 1;
  430. pUserCreds->sHashTags[NAME_ACCT_NDCASE] = 1;
  431. pUserCreds->sHashTags[NAME_ACCTDNS] = 1;
  432. pUserCreds->sHashTags[NAME_ACCTDNS_DOWNCASE] = 1;
  433. pUserCreds->sHashTags[NAME_ACCTDNS_UPCASE] = 1;
  434. pUserCreds->sHashTags[NAME_ACCTDNS_DUCASE] = 1;
  435. pUserCreds->sHashTags[NAME_ACCTDNS_UDCASE] = 1;
  436. pUserCreds->sHashTags[NAME_ACCTDNS_NUCASE] = 1;
  437. pUserCreds->sHashTags[NAME_ACCTDNS_NDCASE] = 1;
  438. pUserCreds->sHashTags[NAME_ACCT_FREALM] = 1;
  439. pUserCreds->sHashTags[NAME_ACCT_FREALM_DOWNCASE] = 1;
  440. pUserCreds->sHashTags[NAME_ACCT_FREALM_UPCASE] = 1;
  441. break;
  442. case NAMEFORMAT_UPN:
  443. pUserCreds->sHashTags[NAME_UPN] = 1;
  444. pUserCreds->sHashTags[NAME_UPN_DOWNCASE] = 1;
  445. pUserCreds->sHashTags[NAME_UPN_UPCASE] = 1;
  446. pUserCreds->sHashTags[NAME_UPN_FREALM] = 1;
  447. pUserCreds->sHashTags[NAME_UPN_FREALM_DOWNCASE] = 1;
  448. pUserCreds->sHashTags[NAME_UPN_FREALM_UPCASE] = 1;
  449. break;
  450. case NAMEFORMAT_NETBIOS:
  451. pUserCreds->sHashTags[NAME_NT4] = 1;
  452. pUserCreds->sHashTags[NAME_NT4_DOWNCASE] = 1;
  453. pUserCreds->sHashTags[NAME_NT4_UPCASE] = 1;
  454. pUserCreds->sHashTags[NAME_NT4_FREALM] = 1;
  455. pUserCreds->sHashTags[NAME_NT4_FREALM_DOWNCASE] = 1;
  456. pUserCreds->sHashTags[NAME_NT4_FREALM_UPCASE] = 1;
  457. break;
  458. default:
  459. break;
  460. }
  461. pUserCreds->fIsValidDigestHash = TRUE;
  462. }
  463. else
  464. {
  465. DebugLog((DEB_ERROR,"DigestGetPasswd: Invalid header on pre-calc hashes\n"));
  466. }
  467. }
  468. //
  469. // Retrieve the plaintext password
  470. //
  471. // NOTE : On NT 5, this API only works on Domain Controllers !!
  472. //
  473. RtlInitUnicodeString(&ustrcPackageName, SAM_CLEARTEXT_CREDENTIAL_NAME);
  474. // Note: Would be nice to have this as a LSAFunction
  475. Status = SamIRetrievePrimaryCredentials( UserHandle,
  476. &ustrcPackageName,
  477. &pvPlainPwd,
  478. &ulLenPassword);
  479. if (!NT_SUCCESS( Status ))
  480. {
  481. DebugLog((DEB_WARN, "DigestGetPasswd: Could not retrieve plaintext password, status 0x%x\n", Status));
  482. if (pUserCreds->fIsValidDigestHash == FALSE)
  483. {
  484. // We have no pre-computed MD5 hashes and also no cleartext password
  485. // we can not perform any Digest Auth operations
  486. //
  487. // Explicitly set the status to be "wrong password" instead of whatever
  488. // is returned by SamIRetrievePrimaryCredentials
  489. //
  490. Status = STATUS_WRONG_PASSWORD;
  491. DebugLog((DEB_ERROR,"DigestGetPasswd: Can not obtain cleartext or Hashed Creds\n"));
  492. goto CleanUp;
  493. }
  494. Status = STATUS_SUCCESS; // we have valid pre-calc hash so continue with no error
  495. }
  496. else
  497. {
  498. ustrcTemp.Buffer = (PUSHORT) pvPlainPwd;
  499. ustrcTemp.Length = ustrcTemp.MaximumLength = (USHORT) ulLenPassword;
  500. Status = UnicodeStringDuplicate(&(pUserCreds->ustrPasswd), &ustrcTemp);
  501. if (!NT_SUCCESS( Status ))
  502. {
  503. DebugLog((DEB_ERROR, "DigestGetPasswd: Failed to copy plaintext password, error 0x%x\n", Status));
  504. goto CleanUp;
  505. }
  506. // DebugLog((DEB_TRACE,"DigestGetPasswd: Have the PASSWORD %wZ\n", &(pUserCreds->ustrPasswd)));
  507. pUserCreds->fIsValidPasswd = TRUE;
  508. DebugLog((DEB_TRACE,"DigestGetPasswd: Password retrieved\n"));
  509. }
  510. CleanUp:
  511. // Release any memory from SamI* calls Would be nice to have this as a LSAFunction
  512. if (pvPlainPwd)
  513. {
  514. if (ulLenPassword > 0)
  515. {
  516. SecureZeroMemory(pvPlainPwd, ulLenPassword);
  517. }
  518. LocalFree(pvPlainPwd);
  519. pvPlainPwd = NULL;
  520. }
  521. if (pvHashCred)
  522. {
  523. if (ulLenHash > 0)
  524. {
  525. SecureZeroMemory(pvHashCred, ulLenHash);
  526. }
  527. LocalFree(pvHashCred);
  528. pvHashCred = NULL;
  529. }
  530. DebugLog((DEB_TRACE_FUNC,"DigestGetPasswd: Leaving 0x%x\n", Status));
  531. return(Status);
  532. }
  533. //+--------------------------------------------------------------------
  534. //
  535. // Function: DigestOpenSamUser
  536. //
  537. // Synopsis: Opens the Sam User database for an account
  538. //
  539. // Arguments: pusrtUserName - Unicode string for the AccountName (UPN or SAMAccountName)
  540. // pUserHandle - output handle to the opened User account
  541. //
  542. // Returns: NTSTATUS
  543. //
  544. // Notes: this call will attempt to locally open an account specified by UPN or SAMAccountName. If it is
  545. // a UPN, SAM will attemp to crackName locally. If this fails, an error will be returned.
  546. //
  547. //---------------------------------------------------------------------
  548. NTSTATUS
  549. DigestOpenSamUser(
  550. IN PDIGEST_PARAMETER pDigest,
  551. OUT SAMPR_HANDLE *ppUserHandle,
  552. OUT PUCHAR * ppucUserAuthData,
  553. OUT PULONG pulAuthDataSize
  554. )
  555. {
  556. NTSTATUS Status = STATUS_SUCCESS;
  557. NTSTATUS StatusSub = STATUS_SUCCESS;
  558. PSAMPR_USER_INFO_BUFFER UserAllInfo = NULL ;
  559. PSAMPR_USER_ALL_INFORMATION UserAll = NULL ;
  560. SID_AND_ATTRIBUTES_LIST GroupMembership = {0};
  561. LARGE_INTEGER CurrentTime = {0};
  562. LARGE_INTEGER LogoffTime = {0};
  563. LARGE_INTEGER KickoffTime = {0};
  564. PLARGE_INTEGER pTempTime = NULL;
  565. PPACTYPE Pac = NULL;
  566. ASSERT(ppUserHandle);
  567. ASSERT(ppucUserAuthData);
  568. ASSERT(pulAuthDataSize);
  569. DebugLog((DEB_TRACE_FUNC, "DigestOpenSamUser: Entering\n"));
  570. *ppucUserAuthData = NULL;
  571. *pulAuthDataSize = 0L;
  572. *ppUserHandle = NULL;
  573. GetSystemTimeAsFileTime((PFILETIME) &CurrentTime );
  574. Status = DigestOpenSam();
  575. if (!NT_SUCCESS(Status))
  576. {
  577. DebugLog((DEB_ERROR, "DigestOpenSamUser: DigestOpenSam failed 0x%x\n", Status));
  578. goto CleanUp;
  579. }
  580. // Figure out which name format is used in account
  581. // If it was NetBIOS, then this was cracked locally on the server
  582. if (pDigest->ustrCrackedAccountName.Length)
  583. {
  584. ASSERT(pDigest->typeName != NAMEFORMAT_UNKNOWN); // Should ALWAYS know format if cracked
  585. Status = SamIGetUserLogonInformationEx(l_AccountDomainHandle,
  586. 0, // Defaults to SamAccount Name
  587. &(pDigest->ustrCrackedAccountName),
  588. USER_ALL_DIGEST_INFO,
  589. &UserAllInfo,
  590. &GroupMembership,
  591. ppUserHandle);
  592. if (!NT_SUCCESS(Status))
  593. {
  594. DebugLog((DEB_ERROR, "DigestOpenSamUser: GetUserLogonInformation Cracked AccountName failed 0x%x\n", Status));
  595. goto CleanUp;
  596. }
  597. }
  598. else
  599. {
  600. // Username format could be SamAccount name or UPN
  601. // UPN could be a local UPN or need to CrackName and then make genericpassthrough to another DC
  602. // Try opening up just the SamAccount name first
  603. Status = SamIGetUserLogonInformationEx(l_AccountDomainHandle,
  604. 0,
  605. &(pDigest->ustrUsername),
  606. USER_ALL_DIGEST_INFO,
  607. &UserAllInfo,
  608. &GroupMembership,
  609. ppUserHandle);
  610. if (!NT_SUCCESS(Status))
  611. {
  612. DebugLog((DEB_TRACE, "DigestOpenSamUser: GetUserLogonInformation for SamAccount failed 0x%x\n", Status));
  613. Status = SamIGetUserLogonInformationEx(l_AccountDomainHandle,
  614. SAM_OPEN_BY_UPN_OR_ACCOUNTNAME,
  615. &(pDigest->ustrUsername),
  616. USER_ALL_DIGEST_INFO,
  617. &UserAllInfo,
  618. &GroupMembership,
  619. ppUserHandle);
  620. if (!NT_SUCCESS(Status))
  621. {
  622. DebugLog((DEB_ERROR, "DigestOpenSamUser: GetUserLogonInformation failed 0x%x\n", Status));
  623. goto CleanUp;
  624. }
  625. // Succeeded in finding account by UPN (SamAccount would have matched before)
  626. pDigest->typeName = NAMEFORMAT_UPN;
  627. }
  628. else
  629. {
  630. // We suceeded opening up account by SamAccount name
  631. pDigest->typeName = NAMEFORMAT_ACCOUNTNAME;
  632. }
  633. }
  634. UserAll = &UserAllInfo->All ;
  635. // Fill in the account name & domain
  636. UnicodeStringFree(&(pDigest->ustrCrackedAccountName));
  637. UnicodeStringFree(&(pDigest->ustrCrackedDomain));
  638. Status = UnicodeStringWCharDuplicate(&(pDigest->ustrCrackedAccountName),
  639. UserAll->UserName.Buffer,
  640. UserAll->UserName.Length / sizeof(WCHAR));
  641. if ( !NT_SUCCESS( Status ) )
  642. {
  643. DebugLog((DEB_ERROR, "DigestOpenSamUser: Account copy failed 0x%x\n", Status));
  644. goto CleanUp;
  645. }
  646. Status = UnicodeStringDuplicate(&(pDigest->ustrCrackedDomain), &g_NtDigestSecPkg.DomainName);
  647. if ( !NT_SUCCESS( Status ) )
  648. {
  649. DebugLog((DEB_ERROR, "DigestOpenSamUser: Domain copy failed 0x%x\n", Status));
  650. goto CleanUp;
  651. }
  652. pDigest->usFlags = pDigest->usFlags & (~FLAG_CRACKNAME_ON_DC); // reset - name is now cracked
  653. DebugLog((DEB_TRACE,"DigestOpenSamUser: BadPasswordCount %u Logon Count %u\n",
  654. UserAll->BadPasswordCount, UserAll->LogonCount));
  655. if ( UserAll->UserAccountControl & USER_ACCOUNT_DISABLED )
  656. {
  657. Status = STATUS_ACCOUNT_DISABLED;
  658. goto CleanUp;
  659. }
  660. if ( UserAll->UserAccountControl & USER_ACCOUNT_AUTO_LOCKED )
  661. {
  662. Status = STATUS_ACCOUNT_LOCKED_OUT;
  663. goto CleanUp ;
  664. }
  665. if ( UserAll->UserAccountControl & USER_SMARTCARD_REQUIRED )
  666. {
  667. Status = STATUS_SMARTCARD_LOGON_REQUIRED;
  668. goto CleanUp ;
  669. }
  670. //
  671. // Check the restrictions SAM doesn't:
  672. //
  673. pTempTime = (PLARGE_INTEGER) &UserAll->AccountExpires;
  674. if ((pTempTime->QuadPart != 0) &&
  675. (pTempTime->QuadPart < CurrentTime.QuadPart))
  676. {
  677. Status = STATUS_ACCOUNT_EXPIRED;
  678. goto CleanUp;
  679. }
  680. //
  681. // For user accounts, check if the password has expired.
  682. //
  683. if (UserAll->UserAccountControl & USER_NORMAL_ACCOUNT)
  684. {
  685. pTempTime = (PLARGE_INTEGER) &UserAll->PasswordMustChange;
  686. if (pTempTime->QuadPart < CurrentTime.QuadPart)
  687. {
  688. if (pTempTime->QuadPart == 0)
  689. {
  690. Status = STATUS_PASSWORD_MUST_CHANGE;
  691. }
  692. else
  693. {
  694. Status = STATUS_PASSWORD_EXPIRED;
  695. }
  696. DebugLog((DEB_ERROR, "DigestOpenSamUser: Failed PasswordMustChange 0x%x\n", Status));
  697. goto CleanUp;
  698. }
  699. }
  700. // One final check on status of password - this should be duplicate test from previous one
  701. if ( UserAll->UserAccountControl & USER_PASSWORD_EXPIRED )
  702. {
  703. Status = STATUS_PASSWORD_EXPIRED;
  704. goto CleanUp ;
  705. }
  706. Status = SamIAccountRestrictions(
  707. *ppUserHandle,
  708. &pDigest->ustrWorkstation,
  709. (PUNICODE_STRING) &UserAll->WorkStations,
  710. (PLOGON_HOURS) &UserAll->LogonHours,
  711. &LogoffTime,
  712. &KickoffTime
  713. );
  714. if (!NT_SUCCESS(Status))
  715. {
  716. DebugLog((DEB_ERROR, "DigestOpenSamUser: Failed TOD AccountRestrictions 0x%x\n", Status));
  717. goto CleanUp;
  718. }
  719. // Now create the PAC for this user
  720. Status = PAC_Init( UserAll,
  721. NULL,
  722. &GroupMembership,
  723. g_NtDigestSecPkg.DomainSid,
  724. &(g_NtDigestSecPkg.DnsDomainName),
  725. &g_ustrWorkstationName,
  726. 0, // no signature
  727. 0, // no additional data
  728. NULL, // no additional data
  729. &Pac );
  730. if ( !NT_SUCCESS( Status ) )
  731. {
  732. DebugLog((DEB_ERROR, "DigestOpenSamUser: Failed to Init PAC 0x%x\n", Status));
  733. goto CleanUp;
  734. }
  735. *pulAuthDataSize = PAC_GetSize( Pac );
  736. *ppucUserAuthData = (PUCHAR)MIDL_user_allocate( *pulAuthDataSize );
  737. if ( *ppucUserAuthData )
  738. {
  739. PAC_Marshal( Pac, *pulAuthDataSize, *ppucUserAuthData );
  740. }
  741. else
  742. {
  743. Status = SEC_E_INSUFFICIENT_MEMORY ;
  744. }
  745. MIDL_user_free( Pac );
  746. CleanUp:
  747. if ( UserAllInfo )
  748. {
  749. SamIFree_SAMPR_USER_INFO_BUFFER( UserAllInfo, UserAllInformation );
  750. }
  751. if (GroupMembership.SidAndAttributes != NULL)
  752. {
  753. SamIFreeSidAndAttributesList(&GroupMembership);
  754. }
  755. if (!NT_SUCCESS(Status))
  756. { // Cleanup functions since there was a failure
  757. if (*ppucUserAuthData)
  758. {
  759. MIDL_user_free(*ppucUserAuthData);
  760. *ppucUserAuthData = NULL;
  761. *pulAuthDataSize = 0L;
  762. }
  763. if (*ppUserHandle)
  764. {
  765. StatusSub = DigestCloseSamUser(*ppUserHandle);
  766. if (!NT_SUCCESS(StatusSub))
  767. {
  768. DebugLog((DEB_ERROR,"DigestOpenSamUser: failed DigestCloseSamUser on error 0x%x\n", StatusSub));
  769. }
  770. *ppUserHandle = NULL;
  771. }
  772. }
  773. DebugLog((DEB_TRACE_FUNC, "DigestOpenSamUser: Leaving 0x%x\n", Status));
  774. return Status;
  775. }
  776. //+--------------------------------------------------------------------
  777. //
  778. // Function: DigestCloseSamUser
  779. //
  780. // Synopsis: Closes the Sam User database for an account
  781. //
  782. // Arguments: pUserHandle - output handle to the opened User account
  783. //
  784. // Returns: NTSTATUS
  785. //
  786. // Notes:
  787. //
  788. //---------------------------------------------------------------------
  789. NTSTATUS
  790. DigestCloseSamUser(
  791. IN SAMPR_HANDLE UserHandle)
  792. {
  793. NTSTATUS Status = STATUS_SUCCESS;
  794. DebugLog((DEB_TRACE_FUNC, "DigestCloseSamUser: Entering\n"));
  795. // used to use LsaCloseSamUser()
  796. Status = SamrCloseHandle( &UserHandle );
  797. DebugLog((DEB_TRACE_FUNC, "DigestCloseSamUser: Leaving 0x%x\n", Status));
  798. return Status;
  799. }
  800. //+--------------------------------------------------------------------
  801. //
  802. // Function: DigestUpdateLogonStatistics
  803. //
  804. // Synopsis: Update the logon stats for a user (bad passwd attempts ....)
  805. //
  806. // Arguments: pUserHandle - output handle to the opened User account
  807. //
  808. // Returns: NTSTATUS
  809. //
  810. // Notes:
  811. //
  812. //---------------------------------------------------------------------
  813. NTSTATUS
  814. DigestUpdateLogonStatistics(
  815. IN SAM_HANDLE UserHandle,
  816. IN PSAM_LOGON_STATISTICS pLogonStats)
  817. {
  818. NTSTATUS Status = STATUS_SUCCESS;
  819. DebugLog((DEB_TRACE_FUNC, "DigestUpdateLogonStatistics: Entering\n"));
  820. Status = SamIUpdateLogonStatistics(
  821. UserHandle,
  822. pLogonStats );
  823. DebugLog((DEB_TRACE_FUNC, "DigestUpdateLogonStatistics: Leaving 0x%x\n", Status));
  824. return Status;
  825. }
  826. //+--------------------------------------------------------------------
  827. //
  828. // Function: DigestOpenSam
  829. //
  830. // Synopsis: Opens the Sam - done only on a domain controller
  831. //
  832. // Arguments:
  833. //
  834. // Returns: NTSTATUS
  835. //
  836. // Notes: Can be called in multithreaded mode - no need to acquire at startup
  837. //
  838. //---------------------------------------------------------------------
  839. NTSTATUS
  840. DigestOpenSam()
  841. {
  842. NTSTATUS Status = STATUS_SUCCESS;
  843. SAMPR_HANDLE SamHandle = NULL;
  844. SAMPR_HANDLE DomainHandle = NULL;
  845. // if already have valid handle to SAM
  846. if (l_AccountDomainHandle)
  847. return STATUS_SUCCESS;
  848. DebugLog((DEB_TRACE_FUNC, "DigestOpenSam: Entering\n"));
  849. //
  850. // Open SAM to get the account information
  851. //
  852. Status = SamIConnect(
  853. NULL, // no server name
  854. &SamHandle,
  855. 0, // no desired access
  856. TRUE // trusted caller
  857. );
  858. if (!NT_SUCCESS(Status))
  859. {
  860. DebugLog((DEB_ERROR, "DigestOpenSam: SamIConnect failed 0x%x\n", Status));
  861. goto CleanUp;
  862. }
  863. if(InterlockedCompareExchangePointer(
  864. &l_AccountSamHandle,
  865. SamHandle,
  866. NULL
  867. ) != NULL)
  868. {
  869. SamrCloseHandle( &SamHandle );
  870. }
  871. Status = SamrOpenDomain(
  872. l_AccountSamHandle,
  873. 0, // no desired access
  874. (PRPC_SID) g_NtDigestSecPkg.DomainSid,
  875. &DomainHandle
  876. );
  877. if (!NT_SUCCESS(Status))
  878. {
  879. goto CleanUp;
  880. }
  881. if(InterlockedCompareExchangePointer(
  882. &l_AccountDomainHandle,
  883. DomainHandle,
  884. NULL
  885. ) != NULL)
  886. {
  887. SamrCloseHandle( &DomainHandle );
  888. }
  889. CleanUp:
  890. DebugLog((DEB_TRACE_FUNC, "DigestOpenSam: Leaving 0x%x\n", Status));
  891. return Status;
  892. }
  893. //+--------------------------------------------------------------------
  894. //
  895. // Function: DigestCloseSam
  896. //
  897. // Synopsis: Closes the Sam
  898. //
  899. // Arguments:
  900. //
  901. // Returns: NTSTATUS
  902. //
  903. // Notes:
  904. //
  905. //---------------------------------------------------------------------
  906. NTSTATUS
  907. DigestCloseSam()
  908. {
  909. NTSTATUS Status = STATUS_SUCCESS;
  910. if (l_AccountDomainHandle)
  911. {
  912. SamrCloseHandle( &l_AccountDomainHandle );
  913. l_AccountDomainHandle = NULL;
  914. }
  915. if (l_AccountSamHandle)
  916. {
  917. SamrCloseHandle( &l_AccountSamHandle );
  918. l_AccountSamHandle = NULL;
  919. }
  920. DebugLog((DEB_TRACE_FUNC, "DigestCloseSamUser: Leaving\n"));
  921. return Status;
  922. }
  923. //+-------------------------------------------------------------------------
  924. //
  925. // Function: MIDL_user_allocate
  926. //
  927. // Synopsis: Allocation routine for use by RPC client stubs
  928. //
  929. // Effects: allocates memory with LsaFunctions.AllocateLsaHeap
  930. //
  931. // Arguments: BufferSize - size of buffer, in bytes, to allocate
  932. //
  933. // Requires:
  934. //
  935. // Returns: Buffer pointer or NULL on allocation failure
  936. //
  937. // Notes:
  938. //
  939. //
  940. //--------------------------------------------------------------------------
  941. PVOID
  942. MIDL_user_allocate(
  943. IN size_t BufferSize
  944. )
  945. {
  946. return (DigestAllocateMemory( ROUND_UP_COUNT((ULONG) BufferSize, 8) ) );
  947. }
  948. //+-------------------------------------------------------------------------
  949. //
  950. // Function: MIDL_user_free
  951. //
  952. // Synopsis: Memory free routine for RPC client stubs
  953. //
  954. // Effects: frees the buffer with LsaFunctions.FreeLsaHeap
  955. //
  956. // Arguments: Buffer - Buffer to free
  957. //
  958. // Requires:
  959. //
  960. // Returns: none
  961. //
  962. // Notes:
  963. //
  964. //
  965. //--------------------------------------------------------------------------
  966. VOID
  967. MIDL_user_free(
  968. IN PVOID Buffer
  969. )
  970. {
  971. DigestFreeMemory( Buffer );
  972. }
  973. // The following two routines were copied from the LSA server policy utilities dbluutil.c
  974. // LSANullTerminateUnicodeString and LsapCompareDomainNames
  975. NTSTATUS
  976. DigestNullTerminateUnicodeString(
  977. IN PUNICODE_STRING String,
  978. OUT LPWSTR *pBuffer,
  979. OUT BOOLEAN *fFreeBuffer
  980. )
  981. /*++
  982. Routine Description:
  983. This routine accepts a UNICODE_STRING and returns its internal buffer,
  984. ensuring that it is NULL terminated.
  985. If the buffer is NULL terminated it will be returned in pBuffer.
  986. If the buffer isn't NULL terminated it will be reallocated, NULL terminated,
  987. and returned in pBuffer. fFreeBuffer will be set to TRUE indicating the
  988. caller is responsible for deallocating pBuffer.
  989. If an error occurs then pBuffer will be NULL, fFreeBuffer will be FALSE, and
  990. no memory will be allocated.
  991. Arguments:
  992. String - Pointer to a UNICODE_STRING
  993. pBuffer - Pointer to a pointer to return the buffer
  994. fFreeBuffer - Pointer to a BOOLEAN to indicate whether the caller needs to
  995. deallocate pBuffer or not.
  996. Return Values:
  997. STATUS_SUCCESS - *pBuffer points to a NULL terminated version of String's
  998. internal buffer. Check *fFreeBuffer to determine if
  999. pBuffer must be freed by the caller.
  1000. STATUS_NO_MEMORY - The routine failed to NULL terminate String's internal
  1001. buffer. *pBuffer is NULL and *fFreeBuffer is FALSE.
  1002. --*/
  1003. {
  1004. BOOLEAN fNullTerminated;
  1005. NTSTATUS Status = STATUS_SUCCESS;
  1006. ASSERT(pBuffer);
  1007. ASSERT(fFreeBuffer);
  1008. //
  1009. // Initialize input parameters
  1010. //
  1011. *pBuffer = NULL;
  1012. *fFreeBuffer = FALSE;
  1013. //
  1014. // Short circuit for strings that are already NULL terminated.
  1015. //
  1016. fNullTerminated = (String->MaximumLength > String->Length &&
  1017. String->Buffer[String->Length / sizeof(WCHAR)] == UNICODE_NULL);
  1018. if (!fNullTerminated) {
  1019. //
  1020. // Allocate enough memory to include a terminating NULL character
  1021. //
  1022. *pBuffer = (WCHAR*)midl_user_allocate(String->Length + sizeof(WCHAR));
  1023. if ( NULL == *pBuffer ) {
  1024. Status = STATUS_NO_MEMORY;
  1025. }
  1026. else
  1027. {
  1028. //
  1029. // Copy the buffer into pBuffer and NULL terminate it.
  1030. //
  1031. *fFreeBuffer = TRUE;
  1032. RtlCopyMemory(*pBuffer, String->Buffer, String->Length);
  1033. (*pBuffer)[String->Length / sizeof(WCHAR)] = UNICODE_NULL;
  1034. }
  1035. }
  1036. else
  1037. {
  1038. //
  1039. // String's internal buffer is already NULL terminated, return it.
  1040. //
  1041. *pBuffer = String->Buffer;
  1042. }
  1043. return Status;
  1044. }
  1045. BOOL
  1046. DigestCompareDomainNames(
  1047. IN PUNICODE_STRING String,
  1048. IN PUNICODE_STRING AmbiguousName,
  1049. IN PUNICODE_STRING FlatName OPTIONAL
  1050. )
  1051. /*++
  1052. Routine Description:
  1053. This routine performs a case insensitive comparison between a string
  1054. and a domain name. If both the NetBIOS and Dns name forms are known then
  1055. the caller must pass the NetBIOS domain name as FlatName and the Dns domain
  1056. name as AmbiguousName. A non-NULL value for FlatName indicates the caller
  1057. knows both name forms and will result in a more optimal comparison. If the
  1058. caller has only one name form and it is ambiguous, that is it may be NetBIOS
  1059. or Dns, then the caller must pass NULL for FlatName. The routine will try
  1060. both a NetBIOS comparison using RtlEqualDomainName and a Dns comparison
  1061. using DnsNameCompare_W. If either comparison results in equality the TRUE
  1062. is returned, otherwise FALSE.
  1063. This routine provides centralized logic for robust domain name comparison
  1064. consistent with domain name semantics in Lsa data structures. Lsa trust
  1065. information structures are interpreted in the following way.
  1066. LSAPR_TRUST_INFORMATION.Name - Either NetBIOS or Dns
  1067. The following structures have both a FlatName and DomainName (or Name)
  1068. field. In this case they are interpreted as follows:
  1069. LSAPR_TRUST_INFORMATION_EX
  1070. LSAPR_TRUSTED_DOMAIN_INFORMATION_EX
  1071. LSAPR_TRUSTED_DOMAIN_INFORMATION_EX2
  1072. If the FlatName field is NULL then the other name field is ambiguous.
  1073. If the FlatName field is non NULL, then the other name field is Dns.
  1074. NetBIOS comparison is performed using RtlEqualDomainName which enforces the
  1075. proper OEM character equivelencies. DNS name comparison is performed using
  1076. DnsNameCompare_W to ensure proper handling of trailing dots and character
  1077. equivelencies.
  1078. Arguments:
  1079. String -- Potentially ambiguous domain name to compare against
  1080. AmbiguousName, and FlatName if non-NULL.
  1081. AmbiguousName -- Is treated as an ambiguous name form unless FlatName
  1082. is also specified. If FlatName is non-NULL then
  1083. AmbiguousName is treated as a Dns domain name.
  1084. FlatName -- This parameter is optional. If present it must be the
  1085. flat name of the domain. Furthermore, passing this
  1086. parameter indicates that AmbiguousName is in fact a
  1087. Dns domain name.
  1088. Return Values:
  1089. TRUE - String is equivelent to one of FlatDomainName or DnsDomainName
  1090. FALSE - String is not equivelent to either domain name
  1091. If any parameter isn't a valid UNICODE_STRING then FALSE is returned.
  1092. Notes:
  1093. The number of comparisons required to determine equivelency will depend
  1094. on the ammount of information passed in by the caller. If both the
  1095. NetBIOS and Dns domain names are known, pass them both to ensure the minimal
  1096. number of comparisons.
  1097. --*/
  1098. {
  1099. NTSTATUS Status;
  1100. BOOLEAN fEquivalent = FALSE;
  1101. LPWSTR StringBuffer = NULL;
  1102. LPWSTR AmbiguousNameBuffer = NULL;
  1103. BOOLEAN fFreeStringBuffer = FALSE;
  1104. BOOLEAN fFreeAmbiguousBuffer = FALSE;
  1105. //
  1106. // Validate input strings
  1107. //
  1108. ASSERT(String);
  1109. ASSERT(AmbiguousName);
  1110. ASSERT(NT_SUCCESS(RtlValidateUnicodeString( 0, String )));
  1111. ASSERT(NT_SUCCESS(RtlValidateUnicodeString( 0, AmbiguousName )));
  1112. //
  1113. // Ensure the UNICODE_STRING data buffers are NULL terminated before
  1114. // passing them to DnsNameCompare_W
  1115. //
  1116. Status = DigestNullTerminateUnicodeString( String,
  1117. &StringBuffer,
  1118. &fFreeStringBuffer
  1119. );
  1120. if (NT_SUCCESS(Status)) {
  1121. Status = DigestNullTerminateUnicodeString( AmbiguousName,
  1122. &AmbiguousNameBuffer,
  1123. &fFreeAmbiguousBuffer
  1124. );
  1125. }
  1126. if (NT_SUCCESS(Status)) {
  1127. if ( NULL == FlatName ) {
  1128. //
  1129. // AmbiguousName is truly ambiguous, we must perform both
  1130. // types of comparison between String
  1131. //
  1132. fEquivalent = ( RtlEqualDomainName( String, AmbiguousName ) ||
  1133. DnsNameCompare_W( StringBuffer,
  1134. AmbiguousNameBuffer )
  1135. );
  1136. }
  1137. else
  1138. {
  1139. ASSERT(NT_SUCCESS(RtlValidateUnicodeString( 0, FlatName )));
  1140. //
  1141. // We are sure of the name forms so lets just use the
  1142. // appropriate comparison routines on each.
  1143. //
  1144. fEquivalent = ( RtlEqualDomainName( String, FlatName ) ||
  1145. DnsNameCompare_W( StringBuffer,
  1146. AmbiguousNameBuffer )
  1147. );
  1148. }
  1149. }
  1150. if ( fFreeStringBuffer ) {
  1151. MIDL_user_free( StringBuffer );
  1152. }
  1153. if ( fFreeAmbiguousBuffer ) {
  1154. MIDL_user_free( AmbiguousNameBuffer );
  1155. }
  1156. if (fEquivalent)
  1157. return(TRUE);
  1158. else
  1159. return(FALSE);
  1160. }
  1161. //+-------------------------------------------------------------------------
  1162. //
  1163. // Function: DigestCheckPacForSidFiltering
  1164. //
  1165. // Synopsis: If the ticket info has a TDOSid then the function
  1166. // makes a check to make sure the SID from the TDO matches
  1167. // the client's home domain SID. A call to LsaIFilterSids
  1168. // is made to do the check. If this function fails with
  1169. // STATUS_TRUST_FAILURE then an audit log is generated.
  1170. // Otherwise the function succeeds but SIDs are filtered
  1171. // from the PAC.
  1172. //
  1173. // Effects:
  1174. //
  1175. // Arguments:
  1176. //
  1177. // Requires:
  1178. //
  1179. // Returns:
  1180. //
  1181. // Notes: taken from updated kerberos code
  1182. //
  1183. //
  1184. //--------------------------------------------------------------------------
  1185. NTSTATUS
  1186. DigestCheckPacForSidFiltering(
  1187. IN PDIGEST_PARAMETER pDigest,
  1188. IN OUT PUCHAR *PacData,
  1189. IN OUT PULONG PacSize
  1190. )
  1191. {
  1192. NTSTATUS Status = STATUS_SUCCESS;
  1193. PPAC_INFO_BUFFER LogonInfo;
  1194. PPACTYPE OldPac;
  1195. ULONG OldPacSize;
  1196. PPACTYPE NewPac = NULL;
  1197. PNETLOGON_VALIDATION_SAM_INFO3 ValidationInfo = NULL;
  1198. SAMPR_PSID_ARRAY ZeroResourceGroups;
  1199. ULONG OldExtraSidCount;
  1200. PPACTYPE RemarshalPac = NULL;
  1201. ULONG RemarshalPacSize = 0;
  1202. OldPac = (PPACTYPE) *PacData;
  1203. OldPacSize = *PacSize;
  1204. DebugLog((DEB_TRACE_FUNC,"DigestCheckPacForSidFiltering: Entering\n"));
  1205. if (PAC_UnMarshal(OldPac, OldPacSize) == 0)
  1206. {
  1207. DebugLog((DEB_ERROR,"DigestCheckPacForSidFiltering: Failed to unmarshal pac\n"));
  1208. Status = SEC_E_CANNOT_PACK;
  1209. goto Cleanup;
  1210. }
  1211. //
  1212. // Must remember to remarshal the PAC prior to returning
  1213. //
  1214. RemarshalPac = OldPac;
  1215. RemarshalPacSize = OldPacSize;
  1216. RtlZeroMemory(
  1217. &ZeroResourceGroups,
  1218. sizeof(ZeroResourceGroups)); // allows us to use PAC_InitAndUpdateGroups to remarshal the PAC
  1219. //
  1220. // First, find the logon information
  1221. //
  1222. LogonInfo = PAC_Find(
  1223. OldPac,
  1224. PAC_LOGON_INFO,
  1225. NULL
  1226. );
  1227. if (LogonInfo == NULL)
  1228. {
  1229. DebugLog((DEB_WARN,"DigestCheckPacForSidFiltering: No logon info for PAC - not making SID filtering check\n"));
  1230. goto Cleanup;
  1231. }
  1232. //
  1233. // Now unmarshall the validation information and build a list of sids
  1234. //
  1235. Status = PAC_UnmarshallValidationInfo(
  1236. &ValidationInfo,
  1237. LogonInfo->Data,
  1238. LogonInfo->cbBufferSize);
  1239. if (!NT_SUCCESS(Status))
  1240. {
  1241. DebugLog((DEB_ERROR,"DigestCheckPacForSidFiltering: Failed to unmarshall validation info! 0x%x\n", Status));
  1242. goto Cleanup;
  1243. }
  1244. //
  1245. // Save the old extra SID count (so that if KdcFilterSids compresses
  1246. // the SID array, we can avoid allocating memory for the other-org SID later)
  1247. //
  1248. OldExtraSidCount = ValidationInfo->SidCount;
  1249. //
  1250. // Call lsaifiltersids().
  1251. //
  1252. Status = DigestFilterSids(
  1253. pDigest,
  1254. ValidationInfo
  1255. );
  1256. if (!NT_SUCCESS(Status))
  1257. {
  1258. DebugLog((DEB_ERROR,"DigestCheckPacForSidFiltering: Failed filtering SIDs\n"));
  1259. goto Cleanup;
  1260. }
  1261. // Other org prcoessing was here - not supported currently for digest
  1262. //
  1263. // Now build a new pac
  1264. //
  1265. Status = PAC_InitAndUpdateGroups(
  1266. ValidationInfo,
  1267. &ZeroResourceGroups,
  1268. OldPac,
  1269. &NewPac
  1270. );
  1271. if (!NT_SUCCESS(Status))
  1272. {
  1273. DebugLog((DEB_ERROR,"DigestCheckPacForSidFiltering: Failed pac init and updating groups 0x%x\n", Status));
  1274. goto Cleanup;
  1275. }
  1276. RemarshalPacSize = PAC_GetSize(NewPac);
  1277. RemarshalPac = NewPac;
  1278. Cleanup:
  1279. if ( RemarshalPac != NULL )
  1280. {
  1281. if (!PAC_ReMarshal(RemarshalPac, RemarshalPacSize))
  1282. {
  1283. // PAC_Remarshal Failed
  1284. ASSERT(0);
  1285. Status = SEC_E_CANNOT_PACK;
  1286. }
  1287. else if ( NewPac != NULL &&
  1288. *PacData != (PBYTE)NewPac )
  1289. {
  1290. MIDL_user_free(*PacData);
  1291. *PacData = (PBYTE) NewPac;
  1292. NewPac = NULL;
  1293. *PacSize = RemarshalPacSize;
  1294. }
  1295. }
  1296. if (NewPac != NULL)
  1297. {
  1298. MIDL_user_free(NewPac);
  1299. }
  1300. if (ValidationInfo != NULL)
  1301. {
  1302. MIDL_user_free(ValidationInfo);
  1303. }
  1304. DebugLog((DEB_TRACE_FUNC,"DigestCheckPacForSidFiltering: Leaving 0x%x\n", Status));
  1305. return(Status);
  1306. }
  1307. //+-------------------------------------------------------------------------
  1308. //
  1309. // Function: DigestFilterSids
  1310. //
  1311. // Synopsis: Function that just call LsaIFilterSids.
  1312. //
  1313. // Effects:
  1314. //
  1315. // Arguments: ServerInfo structure containing attributes of the trust
  1316. // ValidationInfo authorization information to filter
  1317. //
  1318. // Requires:
  1319. //
  1320. // Returns: See LsaIFilterSids
  1321. //
  1322. // Notes:
  1323. //
  1324. //
  1325. //--------------------------------------------------------------------------
  1326. NTSTATUS
  1327. DigestFilterSids(
  1328. IN PDIGEST_PARAMETER pDigest,
  1329. IN PNETLOGON_VALIDATION_SAM_INFO3 ValidationInfo
  1330. )
  1331. {
  1332. NTSTATUS Status = STATUS_SUCCESS;
  1333. PUNICODE_STRING pustrTrustedForest = NULL;
  1334. DebugLog((DEB_TRACE_FUNC, "DigestFilterSids: Entering\n"));
  1335. // Currently we do not allow auth for domains outside forests so this should never fire
  1336. /*
  1337. if ((pDigest->ulTrustAttributes & TRUST_ATTRIBUTE_FOREST_TRANSITIVE) != 0)
  1338. {
  1339. ASSERT(0); // this should not happen until cross forest is supported
  1340. pustrTrustedForest = &(pDigest->ustrTrustedForest);
  1341. DebugLog((DEB_TRACE, "DigestFilterSids: Filtering Sids for forest %wZ\n", pustrTrustedForest));
  1342. }
  1343. */
  1344. // This call is used for both intra-forest domain filtering (where the pTrustSid is non-NULL
  1345. // and for member-to-DC boundary filtering - where the pTrustSid is NULL
  1346. Status = LsaIFilterSids(
  1347. pustrTrustedForest, // Pass domain name here
  1348. pDigest->ulTrustDirection,
  1349. pDigest->ulTrustType,
  1350. pDigest->ulTrustAttributes,
  1351. pDigest->pTrustSid,
  1352. NetlogonValidationSamInfo2,
  1353. ValidationInfo,
  1354. NULL,
  1355. NULL,
  1356. NULL
  1357. );
  1358. if (!NT_SUCCESS(Status))
  1359. {
  1360. //
  1361. // Create an audit log if it looks like the SID has been tampered with - ToDo
  1362. //
  1363. /*
  1364. if ((STATUS_DOMAIN_TRUST_INCONSISTENT == Status) &&
  1365. SecData.AuditKdcEvent(KDC_AUDIT_TGS_FAILURE))
  1366. {
  1367. DWORD Dummy = 0;
  1368. KdcLsaIAuditKdcEvent(
  1369. SE_AUDITID_TGS_TICKET_REQUEST,
  1370. &ValidationInfo->EffectiveName,
  1371. &ValidationInfo->LogonDomainName,
  1372. NULL,
  1373. &ServerInfo->AccountName,
  1374. NULL,
  1375. &Dummy,
  1376. (PULONG) &Status,
  1377. NULL,
  1378. NULL, // no preauth type
  1379. GET_CLIENT_ADDRESS(NULL),
  1380. NULL // no logon guid
  1381. );
  1382. }
  1383. */
  1384. DebugLog((DEB_ERROR,"DigestFilterSids: Failed to filter SIDS (LsaIFilterSids): 0x%x\n",Status));
  1385. }
  1386. else
  1387. {
  1388. DebugLog((DEB_TRACE,"DigestFilterSids: successfully filtered sids\n",Status));
  1389. }
  1390. DebugLog((DEB_TRACE_FUNC, "DigestFilterSids: Leaving Status 0x%x\n", Status));
  1391. return Status;
  1392. }
  1393. /*
  1394. //+---------------------------------------------------------------------------
  1395. //
  1396. // Function: LsaConvertAuthDataToToken
  1397. //
  1398. // Synopsis: Convert an opaque PAC structure into a token.
  1399. //
  1400. // Arguments: [UserAuthData] --
  1401. // [UserAuthDataSize] --
  1402. // [TokenInformation] --
  1403. // [TokenInformationType] --
  1404. //
  1405. // History: 3-14-97 RichardW Created
  1406. //
  1407. // Notes:
  1408. //
  1409. //----------------------------------------------------------------------------
  1410. NTSTATUS
  1411. NTAPI
  1412. LsaConvertAuthDataToToken(
  1413. IN PVOID UserAuthData,
  1414. IN ULONG UserAuthDataSize,
  1415. IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
  1416. IN PTOKEN_SOURCE TokenSource,
  1417. IN SECURITY_LOGON_TYPE LogonType,
  1418. IN PUNICODE_STRING AuthorityName,
  1419. OUT PHANDLE TokenHandle,
  1420. OUT PLUID LogonId,
  1421. OUT PUNICODE_STRING AccountName,
  1422. OUT PNTSTATUS SubStatus
  1423. )
  1424. {
  1425. NTSTATUS Status ;
  1426. PPACTYPE Pac = NULL ;
  1427. PPAC_INFO_BUFFER LogonInfo = NULL ;
  1428. PNETLOGON_VALIDATION_SAM_INFO3 ValidationInfo = NULL ;
  1429. PLSA_TOKEN_INFORMATION_V1 TokenInfo = NULL ;
  1430. LogonId->HighPart = LogonId->LowPart = 0;
  1431. *TokenHandle = NULL;
  1432. RtlInitUnicodeString(
  1433. AccountName,
  1434. NULL
  1435. );
  1436. *SubStatus = STATUS_SUCCESS;
  1437. Pac = (PPACTYPE) UserAuthData ;
  1438. if ( PAC_UnMarshal( Pac, UserAuthDataSize ) == 0 )
  1439. {
  1440. DebugLog(( DEB_ERROR, "Failed to unmarshall pac\n" ));
  1441. Status = STATUS_INVALID_PARAMETER ;
  1442. goto CreateToken_Cleanup ;
  1443. }
  1444. LogonInfo = PAC_Find( Pac, PAC_LOGON_INFO, NULL );
  1445. if ( !LogonInfo )
  1446. {
  1447. DebugLog(( DEB_ERROR, "Failed to find logon info in pac\n" ));
  1448. Status = STATUS_INVALID_PARAMETER ;
  1449. goto CreateToken_Cleanup ;
  1450. }
  1451. Status = PAC_UnmarshallValidationInfo(
  1452. &ValidationInfo,
  1453. LogonInfo->Data,
  1454. LogonInfo->cbBufferSize
  1455. );
  1456. if (!NT_SUCCESS(Status))
  1457. {
  1458. DebugLog((DEB_ERROR,"Failed to unmarshall validation info: 0x%x\n",
  1459. Status));
  1460. goto CreateToken_Cleanup;
  1461. }
  1462. //
  1463. // Now we need to build a LSA_TOKEN_INFORMATION_V1 from the validation
  1464. // information
  1465. //
  1466. Status = LsapMakeTokenInformationV1(
  1467. ValidationInfo,
  1468. &TokenInfo
  1469. );
  1470. if (!NT_SUCCESS(Status))
  1471. {
  1472. DebugLog((DEB_ERROR,"Failed to make token informatin v1: 0x%x\n",
  1473. Status));
  1474. goto CreateToken_Cleanup;
  1475. }
  1476. //
  1477. // Now, copy the user name.
  1478. //
  1479. Status = LsapDuplicateString( AccountName, &ValidationInfo->EffectiveName );
  1480. if ( !NT_SUCCESS( Status ) )
  1481. {
  1482. goto CreateToken_Cleanup ;
  1483. }
  1484. //
  1485. // Now create a logon session
  1486. //
  1487. Status = LsapCreateLogonSession( LogonId );
  1488. if (!NT_SUCCESS(Status))
  1489. {
  1490. goto CreateToken_Cleanup;
  1491. }
  1492. //
  1493. // Now create the token
  1494. //
  1495. Status = LsapCreateToken(
  1496. LogonId,
  1497. TokenSource,
  1498. LogonType,
  1499. ImpersonationLevel,
  1500. LsaTokenInformationV1,
  1501. TokenInfo,
  1502. NULL, // no token groups
  1503. AccountName,
  1504. AuthorityName,
  1505. NULL,
  1506. &ValidationInfo->ProfilePath,
  1507. TokenHandle,
  1508. SubStatus
  1509. );
  1510. //
  1511. // NULL out the TokenInfo pointer. LsapCreateToken will
  1512. // free the memory under all conditions
  1513. //
  1514. TokenInfo = NULL ;
  1515. if (!NT_SUCCESS(Status))
  1516. {
  1517. goto CreateToken_Cleanup;
  1518. }
  1519. //
  1520. // We don't need to free the token information because CreateToken does
  1521. // that for us.
  1522. //
  1523. MIDL_user_free(ValidationInfo);
  1524. return Status ;
  1525. CreateToken_Cleanup:
  1526. if ( TokenInfo )
  1527. {
  1528. LsaFreeTokenInfo( LsaTokenInformationV1, TokenInfo );
  1529. }
  1530. if ((LogonId->LowPart != 0) || (LogonId->HighPart != 0))
  1531. {
  1532. LsapDeleteLogonSession(LogonId);
  1533. }
  1534. LsapFreeString(
  1535. AccountName
  1536. );
  1537. if (ValidationInfo != NULL)
  1538. {
  1539. MIDL_user_free(ValidationInfo);
  1540. }
  1541. return Status ;
  1542. }
  1543. */
  1544. #ifdef ROGUE_DC
  1545. #pragma message( "COMPILING A ROGUE DC!!!" )
  1546. #pragma message( "MUST NOT SHIP THIS BUILD!!!" )
  1547. #pragma warning(disable:4127) // Disable warning/error for conditional expression is constant
  1548. #define MAX_SID_LEN (sizeof(SID) + sizeof(ULONG) * SID_MAX_SUB_AUTHORITIES)
  1549. extern "C"
  1550. {
  1551. #include "sddl.h"
  1552. #include "stdlib.h"
  1553. }
  1554. extern HKEY g_hDigestRogueKey;
  1555. NTSTATUS
  1556. DigestInstrumentRoguePac(
  1557. IN OUT PUCHAR *PacData,
  1558. IN OUT PULONG PacSize
  1559. )
  1560. {
  1561. NTSTATUS Status;
  1562. PNETLOGON_VALIDATION_SAM_INFO3 OldValidationInfo = NULL;
  1563. NETLOGON_VALIDATION_SAM_INFO3 NewValidationInfo = {0};
  1564. SAMPR_PSID_ARRAY ZeroResourceGroups = {0};
  1565. PPACTYPE NewPac = NULL;
  1566. ULONG NewPacSize;
  1567. PPAC_INFO_BUFFER LogonInfo;
  1568. PPACTYPE OldPac = NULL;
  1569. ULONG OldPacSize;
  1570. PSID LogonDomainId = NULL;
  1571. PSID ResourceGroupDomainSid = NULL;
  1572. PGROUP_MEMBERSHIP GroupIds = NULL;
  1573. PGROUP_MEMBERSHIP ResourceGroupIds = NULL;
  1574. PNETLOGON_SID_AND_ATTRIBUTES ExtraSids = NULL;
  1575. BYTE FullUserSidBuffer[MAX_SID_LEN];
  1576. SID * FullUserSid = ( SID * )FullUserSidBuffer;
  1577. CHAR * FullUserSidText = NULL;
  1578. DWORD dwType;
  1579. DWORD cbData = 0;
  1580. PCHAR Buffer;
  1581. PCHAR Value = NULL;
  1582. BOOLEAN PacChanged = FALSE;
  1583. DebugLog((DEB_TRACE_FUNC,"DigestInstrumentRoguePac: Entering\n"));
  1584. //
  1585. // Optimization: no "rogue" key in registry - nothing for us to do
  1586. //
  1587. if ( g_hDigestRogueKey == NULL )
  1588. {
  1589. return STATUS_SUCCESS;
  1590. }
  1591. OldPac = (PPACTYPE) *PacData;
  1592. OldPacSize = *PacSize;
  1593. //
  1594. // Unmarshall the old PAC
  1595. //
  1596. if ( PAC_UnMarshal(OldPac, OldPacSize) == 0 )
  1597. {
  1598. DebugLog((DEB_ERROR,"DigestInstrumentRoguePac: Failed to unmarshal pac\n"));
  1599. Status = SEC_E_CANNOT_PACK;
  1600. goto Cleanup;
  1601. }
  1602. //
  1603. // First, find the logon information
  1604. //
  1605. LogonInfo = PAC_Find(
  1606. OldPac,
  1607. PAC_LOGON_INFO,
  1608. NULL
  1609. );
  1610. if ( LogonInfo == NULL )
  1611. {
  1612. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: No logon info on PAC - not performing substitution\n"));
  1613. Status = STATUS_UNSUCCESSFUL;
  1614. goto Error;
  1615. }
  1616. //
  1617. // Now unmarshall the validation information and build a list of sids
  1618. //
  1619. if ( !NT_SUCCESS(PAC_UnmarshallValidationInfo(
  1620. &OldValidationInfo,
  1621. LogonInfo->Data,
  1622. LogonInfo->cbBufferSize )))
  1623. {
  1624. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: Unable to unmarshal validation info\n"));
  1625. Status = STATUS_UNSUCCESSFUL;
  1626. goto Error;
  1627. }
  1628. //
  1629. // Construct the text form of the full user's SID (logon domain ID + user ID)
  1630. //
  1631. DsysAssert( sizeof( FullUserSidBuffer ) >= MAX_SID_LEN );
  1632. RtlCopySid(
  1633. sizeof( FullUserSidBuffer ),
  1634. FullUserSid,
  1635. OldValidationInfo->LogonDomainId
  1636. );
  1637. FullUserSid->SubAuthority[FullUserSid->SubAuthorityCount] = OldValidationInfo->UserId;
  1638. FullUserSid->SubAuthorityCount += 1;
  1639. if ( FALSE == ConvertSidToStringSidA(
  1640. FullUserSid,
  1641. &FullUserSidText ))
  1642. {
  1643. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: Unable to convert user's SID\n"));
  1644. Status = STATUS_UNSUCCESSFUL;
  1645. goto Error;
  1646. }
  1647. //
  1648. // Now look in the registry for the SID matching the validation info
  1649. //
  1650. if ( ERROR_SUCCESS != RegQueryValueExA(
  1651. g_hDigestRogueKey,
  1652. FullUserSidText,
  1653. NULL,
  1654. &dwType,
  1655. NULL,
  1656. &cbData ) ||
  1657. dwType != REG_MULTI_SZ ||
  1658. cbData <= 1 )
  1659. {
  1660. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: No substitution info available for %s\n", FullUserSidText));
  1661. Status = STATUS_SUCCESS;
  1662. goto Error;
  1663. }
  1664. // SafeAllocaAllocate( Value, cbData );
  1665. Value = (PCHAR)DigestAllocateMemory(cbData);
  1666. if ( Value == NULL )
  1667. {
  1668. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: Out of memory allocating substitution buffer\n", FullUserSidText));
  1669. Status = STATUS_UNSUCCESSFUL;
  1670. goto Error;
  1671. }
  1672. if ( ERROR_SUCCESS != RegQueryValueExA(
  1673. g_hDigestRogueKey,
  1674. FullUserSidText,
  1675. NULL,
  1676. &dwType,
  1677. (PBYTE)Value,
  1678. &cbData ) ||
  1679. dwType != REG_MULTI_SZ ||
  1680. cbData <= 1 )
  1681. {
  1682. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: Error reading from registry\n"));
  1683. Status = STATUS_UNSUCCESSFUL;
  1684. goto Error;
  1685. }
  1686. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: Substituting the PAC for %s\n", FullUserSidText));
  1687. Buffer = Value;
  1688. //
  1689. // New validation info will be overloaded with stuff from the file
  1690. //
  1691. NewValidationInfo = *OldValidationInfo;
  1692. //
  1693. // Read the input file one line at a time
  1694. //
  1695. while ( *Buffer != '\0' )
  1696. {
  1697. switch( Buffer[0] )
  1698. {
  1699. case 'l':
  1700. case 'L': // logon domain ID
  1701. if ( LogonDomainId != NULL )
  1702. {
  1703. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: Logon domain ID specified more than once - only first one kept\n"));
  1704. break;
  1705. }
  1706. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: Substituting logon domain ID by %s\n", &Buffer[1]));
  1707. if ( FALSE == ConvertStringSidToSidA(
  1708. &Buffer[1],
  1709. &LogonDomainId ))
  1710. {
  1711. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: Unable to convert SID\n"));
  1712. Status = STATUS_UNSUCCESSFUL;
  1713. goto Error;
  1714. }
  1715. if ( LogonDomainId == NULL )
  1716. {
  1717. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: Out of memory allocating LogonDomainId\n"));
  1718. Status = STATUS_UNSUCCESSFUL;
  1719. goto Error;
  1720. }
  1721. NewValidationInfo.LogonDomainId = LogonDomainId;
  1722. LogonDomainId = NULL;
  1723. PacChanged = TRUE;
  1724. break;
  1725. case 'd':
  1726. case 'D': // resource group domain SID
  1727. if ( ResourceGroupDomainSid != NULL )
  1728. {
  1729. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: Resource group domain SID specified more than once - only first one kept\n"));
  1730. break;
  1731. }
  1732. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: Substituting resource group domain SID by %s\n", &Buffer[1]));
  1733. if ( FALSE == ConvertStringSidToSidA(
  1734. &Buffer[1],
  1735. &ResourceGroupDomainSid ))
  1736. {
  1737. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: Unable to convert SID\n"));
  1738. Status = STATUS_UNSUCCESSFUL;
  1739. goto Error;
  1740. }
  1741. if ( ResourceGroupDomainSid == NULL )
  1742. {
  1743. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: Out of memory allocating ResourceGroupDomainSid\n"));
  1744. Status = STATUS_UNSUCCESSFUL;
  1745. goto Error;
  1746. }
  1747. NewValidationInfo.ResourceGroupDomainSid = ResourceGroupDomainSid;
  1748. ResourceGroupDomainSid = NULL;
  1749. PacChanged = TRUE;
  1750. break;
  1751. case 'p':
  1752. case 'P': // primary group ID
  1753. DebugLog((DEB_WARN, "DigestInstrumentRoguePac: Substituting primary group ID by %s\n", &Buffer[1]));
  1754. NewValidationInfo.PrimaryGroupId = atoi(&Buffer[1]);
  1755. PacChanged = TRUE;
  1756. break;
  1757. case 'u':
  1758. case 'U': // User ID
  1759. DebugLog((DEB_WARN, "DigestInstrumentRoguePac: Substituting user ID by %s\n", &Buffer[1]));
  1760. NewValidationInfo.UserId = atoi(&Buffer[1]);
  1761. PacChanged = TRUE;
  1762. break;
  1763. case 'e':
  1764. case 'E': // Extra SID
  1765. DebugLog((DEB_WARN, "DigestInstrumentRoguePac: Adding an ExtraSid: %s\n", &Buffer[1]));
  1766. if ( ExtraSids == NULL )
  1767. {
  1768. NewValidationInfo.ExtraSids = NULL;
  1769. NewValidationInfo.SidCount = 0;
  1770. ExtraSids = ( PNETLOGON_SID_AND_ATTRIBUTES )HeapAlloc(
  1771. GetProcessHeap(),
  1772. 0,
  1773. sizeof( NETLOGON_SID_AND_ATTRIBUTES )
  1774. );
  1775. }
  1776. else
  1777. {
  1778. ExtraSids = ( PNETLOGON_SID_AND_ATTRIBUTES )HeapReAlloc(
  1779. GetProcessHeap(),
  1780. 0,
  1781. NewValidationInfo.ExtraSids,
  1782. ( NewValidationInfo.SidCount + 1 ) * sizeof( NETLOGON_SID_AND_ATTRIBUTES )
  1783. );
  1784. }
  1785. if ( ExtraSids == NULL )
  1786. {
  1787. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: Out of memory allocating ExtraSids\n"));
  1788. ExtraSids = NewValidationInfo.ExtraSids;
  1789. Status = STATUS_UNSUCCESSFUL;
  1790. goto Error;
  1791. }
  1792. //
  1793. // Read the actual SID
  1794. //
  1795. NewValidationInfo.ExtraSids = ExtraSids;
  1796. if ( FALSE == ConvertStringSidToSidA(
  1797. &Buffer[1],
  1798. &NewValidationInfo.ExtraSids[NewValidationInfo.SidCount].Sid ))
  1799. {
  1800. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: Unable to convert SID\n"));
  1801. Status = STATUS_UNSUCCESSFUL;
  1802. goto Error;
  1803. }
  1804. if ( NewValidationInfo.ExtraSids[NewValidationInfo.SidCount].Sid == NULL )
  1805. {
  1806. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: Out of memory allocating an extra SID\n"));
  1807. Status = STATUS_UNSUCCESSFUL;
  1808. goto Error;
  1809. }
  1810. NewValidationInfo.ExtraSids[NewValidationInfo.SidCount].Attributes =
  1811. SE_GROUP_MANDATORY |
  1812. SE_GROUP_ENABLED_BY_DEFAULT |
  1813. SE_GROUP_ENABLED;
  1814. NewValidationInfo.SidCount += 1;
  1815. PacChanged = TRUE;
  1816. break;
  1817. case 'g':
  1818. case 'G': // Group ID
  1819. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: Adding a GroupId: %s\n", &Buffer[1]));
  1820. if ( GroupIds == NULL )
  1821. {
  1822. NewValidationInfo.GroupIds = NULL;
  1823. NewValidationInfo.GroupCount = 0;
  1824. GroupIds = ( PGROUP_MEMBERSHIP )HeapAlloc(
  1825. GetProcessHeap(),
  1826. 0,
  1827. sizeof( GROUP_MEMBERSHIP )
  1828. );
  1829. }
  1830. else
  1831. {
  1832. GroupIds = ( PGROUP_MEMBERSHIP )HeapReAlloc(
  1833. GetProcessHeap(),
  1834. 0,
  1835. NewValidationInfo.GroupIds,
  1836. ( NewValidationInfo.GroupCount + 1 ) * sizeof( GROUP_MEMBERSHIP )
  1837. );
  1838. }
  1839. if ( GroupIds == NULL )
  1840. {
  1841. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: Out of memory allocating Group IDs\n"));
  1842. GroupIds = NewValidationInfo.GroupIds;
  1843. Status = STATUS_UNSUCCESSFUL;
  1844. goto Error;
  1845. }
  1846. //
  1847. // Read the actual ID
  1848. //
  1849. NewValidationInfo.GroupIds = GroupIds;
  1850. NewValidationInfo.GroupIds[NewValidationInfo.GroupCount].RelativeId = atoi(&Buffer[1]);
  1851. NewValidationInfo.GroupIds[NewValidationInfo.GroupCount].Attributes =
  1852. SE_GROUP_MANDATORY |
  1853. SE_GROUP_ENABLED_BY_DEFAULT |
  1854. SE_GROUP_ENABLED;
  1855. NewValidationInfo.GroupCount += 1;
  1856. PacChanged = TRUE;
  1857. break;
  1858. case 'r':
  1859. case 'R': // Resource groups
  1860. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: Adding a ResourceGroupId: %s\n", &Buffer[1]));
  1861. if ( ResourceGroupIds == NULL )
  1862. {
  1863. NewValidationInfo.ResourceGroupIds = NULL;
  1864. NewValidationInfo.ResourceGroupCount = 0;
  1865. ResourceGroupIds = ( PGROUP_MEMBERSHIP )HeapAlloc(
  1866. GetProcessHeap(),
  1867. 0,
  1868. sizeof( GROUP_MEMBERSHIP )
  1869. );
  1870. }
  1871. else
  1872. {
  1873. ResourceGroupIds = ( PGROUP_MEMBERSHIP )HeapReAlloc(
  1874. GetProcessHeap(),
  1875. 0,
  1876. NewValidationInfo.ResourceGroupIds,
  1877. ( NewValidationInfo.ResourceGroupCount + 1 ) * sizeof( GROUP_MEMBERSHIP )
  1878. );
  1879. }
  1880. if ( ResourceGroupIds == NULL )
  1881. {
  1882. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: Out of memory allocating Resource Group IDs\n"));
  1883. ResourceGroupIds = NewValidationInfo.ResourceGroupIds;
  1884. Status = STATUS_UNSUCCESSFUL;
  1885. goto Error;
  1886. }
  1887. //
  1888. // Read the actual ID
  1889. //
  1890. NewValidationInfo.ResourceGroupIds = ResourceGroupIds;
  1891. NewValidationInfo.ResourceGroupIds[NewValidationInfo.ResourceGroupCount].RelativeId = atoi(&Buffer[1]);
  1892. NewValidationInfo.ResourceGroupIds[NewValidationInfo.ResourceGroupCount].Attributes =
  1893. SE_GROUP_MANDATORY |
  1894. SE_GROUP_ENABLED_BY_DEFAULT |
  1895. SE_GROUP_ENABLED;
  1896. NewValidationInfo.ResourceGroupCount += 1;
  1897. PacChanged = TRUE;
  1898. break;
  1899. default: // unrecognized
  1900. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: Entry \'%c\' unrecognized\n", Buffer[0]));
  1901. break;
  1902. }
  1903. //
  1904. // Move to the next line
  1905. //
  1906. while (*Buffer++ != '\0');
  1907. }
  1908. if ( !PacChanged )
  1909. {
  1910. DebugLog((DEB_TRACE, "DigestInstrumentRoguePac: Nothing to substitute for %s\n", FullUserSidText));
  1911. Status = STATUS_SUCCESS;
  1912. goto Error;
  1913. }
  1914. //
  1915. // If resource group IDs were added, indicate that by setting the corresponding flag
  1916. //
  1917. if ( ResourceGroupIds )
  1918. {
  1919. NewValidationInfo.UserFlags |= LOGON_RESOURCE_GROUPS;
  1920. }
  1921. //
  1922. // If extra SIDs were added, indicate that by setting the corresponding flag
  1923. //
  1924. if ( ExtraSids )
  1925. {
  1926. NewValidationInfo.UserFlags |= LOGON_EXTRA_SIDS;
  1927. }
  1928. //
  1929. // Now build a new pac
  1930. //
  1931. Status = PAC_InitAndUpdateGroups(
  1932. &NewValidationInfo,
  1933. &ZeroResourceGroups,
  1934. OldPac,
  1935. &NewPac
  1936. );
  1937. if ( !NT_SUCCESS( Status ))
  1938. {
  1939. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: Error 0x%x from PAC_InitAndUpdateGroups\n"));
  1940. Status = STATUS_UNSUCCESSFUL;
  1941. goto Error;
  1942. }
  1943. NewPacSize = PAC_GetSize( NewPac );
  1944. if (!PAC_ReMarshal(NewPac, NewPacSize))
  1945. {
  1946. DsysAssert(!"DigestInstrumentRoguePac: PAC_Remarshal Failed");
  1947. Status = STATUS_UNSUCCESSFUL;
  1948. goto Error;
  1949. }
  1950. MIDL_user_free( *PacData ); // Free up the OldPac structure
  1951. *PacData = (PBYTE) NewPac;
  1952. NewPac = NULL;
  1953. *PacSize = NewPacSize;
  1954. Status = STATUS_SUCCESS;
  1955. Cleanup:
  1956. MIDL_user_free( OldValidationInfo );
  1957. LocalFree( FullUserSidText );
  1958. LocalFree( ResourceGroupDomainSid );
  1959. LocalFree( LogonDomainId );
  1960. HeapFree( GetProcessHeap(), 0, ResourceGroupIds );
  1961. HeapFree( GetProcessHeap(), 0, GroupIds );
  1962. if ( ExtraSids )
  1963. {
  1964. for ( ULONG i = 0; i < NewValidationInfo.SidCount; i++ )
  1965. {
  1966. HeapFree( GetProcessHeap(), 0, ExtraSids[i].Sid );
  1967. }
  1968. HeapFree( GetProcessHeap(), 0, ExtraSids );
  1969. }
  1970. MIDL_user_free( NewPac );
  1971. // SafeAllocaFree( Value );
  1972. DigestFreeMemory( Value );
  1973. DebugLog((DEB_TRACE_FUNC, "DigestInstrumentRoguePac: Leaving Status 0x%x\n", Status));
  1974. return Status;
  1975. Error:
  1976. if ( !NT_SUCCESS( Status ))
  1977. {
  1978. DebugLog((DEB_ERROR, "DigestInstrumentRoguePac: Substitution encountered an error, not performed\n"));
  1979. }
  1980. if ( !PAC_ReMarshal(OldPac, OldPacSize))
  1981. {
  1982. // PAC_Remarshal Failed
  1983. DsysAssert(!"DigestInstrumentRoguePac: PAC_Remarshal Failed");
  1984. Status = SEC_E_CANNOT_PACK;
  1985. }
  1986. goto Cleanup;
  1987. }
  1988. #endif