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.

6965 lines
209 KiB

  1. /*++
  2. Copyright (c) 1989 - 1999 Microsoft Corporation
  3. Module Name:
  4. nlmain.c
  5. Abstract:
  6. This file contains the initialization and dispatch routines
  7. for the LAN Manager portions of the MSV1_0 authentication package.
  8. Author:
  9. Jim Kelly 11-Apr-1991
  10. Revision History:
  11. 25-Apr-1991 (cliffv)
  12. Added interactive logon support for PDK.
  13. Chandana Surlu 21-Jul-1996
  14. Stolen from \\kernel\razzle3\src\security\msv1_0\nlmain.c
  15. JClark 28-Jun-2000
  16. Added WMI Trace Logging Support
  17. --*/
  18. #include <global.h>
  19. #include "msp.h"
  20. #undef EXTERN
  21. #define NLP_ALLOCATE
  22. #include "nlp.h"
  23. #undef NLP_ALLOCATE
  24. #include <lmsname.h> // Service Names
  25. #include <safeboot.h>
  26. #include <confname.h> // NETSETUPP_NETLOGON_JD_STOPPED
  27. #include "nlpcache.h" // logon cache prototypes
  28. #include "trace.h" // wmi tracing goo
  29. #include "msvwow.h"
  30. NTSTATUS
  31. NlpMapLogonDomain(
  32. OUT PUNICODE_STRING MappedDomain,
  33. IN PUNICODE_STRING LogonDomain
  34. );
  35. NTSTATUS
  36. NlInitialize(
  37. VOID
  38. )
  39. /*++
  40. Routine Description:
  41. Initialize NETLOGON portion of msv1_0 authentication package.
  42. Arguments:
  43. None.
  44. Return Status:
  45. STATUS_SUCCESS - Indicates NETLOGON successfully initialized.
  46. --*/
  47. {
  48. NTSTATUS Status = STATUS_SUCCESS;
  49. LPWSTR ComputerName;
  50. DWORD ComputerNameLength = MAX_COMPUTERNAME_LENGTH + 1;
  51. NT_PRODUCT_TYPE NtProductType;
  52. UNICODE_STRING TempUnicodeString;
  53. HKEY Key;
  54. int err;
  55. ULONG Size;
  56. ULONG Type;
  57. ULONG Value;
  58. //
  59. // Initialize global data
  60. //
  61. NlpEnumerationHandle = 0;
  62. NlpLogonAttemptCount = 0;
  63. NlpComputerName.Buffer = NULL;
  64. RtlInitUnicodeString( &NlpPrimaryDomainName, NULL );
  65. NlpSamDomainName.Buffer = NULL;
  66. NlpSamDomainId = NULL;
  67. NlpSamDomainHandle = NULL;
  68. //
  69. // Get the name of this machine.
  70. //
  71. ComputerName = I_NtLmAllocate(
  72. ComputerNameLength * sizeof(WCHAR) );
  73. if (ComputerName == NULL ||
  74. !GetComputerNameW( ComputerName, &ComputerNameLength )) {
  75. SspPrint((SSP_MISC, "Cannot get computername %lX\n", GetLastError() ));
  76. NlpLanmanInstalled = FALSE;
  77. I_NtLmFree( ComputerName );
  78. ComputerName = NULL;
  79. } else {
  80. NlpLanmanInstalled = TRUE;
  81. }
  82. //
  83. // For Safe mode boot (minimal, no networking)
  84. // turn off the lanmaninstalled flag, since no network components will
  85. // be started.
  86. //
  87. err = RegOpenKeyExW(
  88. HKEY_LOCAL_MACHINE,
  89. L"System\\CurrentControlSet\\Control\\SafeBoot\\Option",
  90. 0,
  91. KEY_READ,
  92. &Key );
  93. if ( err == ERROR_SUCCESS )
  94. {
  95. Value = 0 ;
  96. Size = sizeof( ULONG );
  97. err = RegQueryValueExW(
  98. Key,
  99. L"OptionValue",
  100. 0,
  101. &Type,
  102. (PUCHAR) &Value,
  103. &Size );
  104. RegCloseKey( Key );
  105. if ( err == ERROR_SUCCESS )
  106. {
  107. NtLmGlobalSafeBoot = TRUE;
  108. if ( Value == SAFEBOOT_MINIMAL )
  109. {
  110. NlpLanmanInstalled = FALSE ;
  111. }
  112. }
  113. }
  114. RtlInitUnicodeString( &NlpComputerName, ComputerName );
  115. //
  116. // Determine if this machine is running Windows NT or Lanman NT.
  117. // LanMan NT runs on a domain controller.
  118. //
  119. if ( !RtlGetNtProductType( &NtProductType ) ) {
  120. SspPrint((SSP_MISC, "Nt Product Type undefined (WinNt assumed)\n" ));
  121. NtProductType = NtProductWinNt;
  122. }
  123. NlpWorkstation = (BOOLEAN)(NtProductType != NtProductLanManNt);
  124. InitializeListHead(&NlpActiveLogonListAnchor);
  125. //
  126. // Initialize any locks.
  127. //
  128. __try
  129. {
  130. RtlInitializeResource(&NlpActiveLogonLock);
  131. }
  132. __except(EXCEPTION_EXECUTE_HANDLER)
  133. {
  134. Status = STATUS_INSUFFICIENT_RESOURCES;
  135. }
  136. ASSERT( NT_SUCCESS(Status) );
  137. //
  138. // initialize the cache - creates a critical section is all
  139. //
  140. NlpCacheInitialize();
  141. //
  142. // Attempt to load Netlogon.dll
  143. //
  144. NlpLoadNetlogonDll();
  145. #ifdef COMPILED_BY_DEVELOPER
  146. SspPrint((SSP_CRITICAL, "COMPILED_BY_DEVELOPER breakpoint.\n"));
  147. DbgBreakPoint();
  148. #endif // COMPILED_BY_DEVELOPER
  149. //
  150. // Initialize useful encryption constants
  151. //
  152. Status = RtlCalculateLmOwfPassword( "", &NlpNullLmOwfPassword );
  153. ASSERT( NT_SUCCESS(Status) );
  154. RtlInitUnicodeString(&TempUnicodeString, NULL);
  155. Status = RtlCalculateNtOwfPassword(&TempUnicodeString,
  156. &NlpNullNtOwfPassword);
  157. ASSERT( NT_SUCCESS(Status) );
  158. //
  159. // Initialize the SubAuthentication Dlls
  160. //
  161. Msv1_0SubAuthenticationInitialization();
  162. #ifdef notdef
  163. //
  164. // If we weren't successful,
  165. // Clean up global resources we intended to initialize.
  166. //
  167. if ( !NT_SUCCESS(Status) ) {
  168. if ( NlpComputerName.Buffer != NULL ) {
  169. MIDL_user_free( NlpComputerName.Buffer );
  170. }
  171. }
  172. #endif // notdef
  173. return STATUS_SUCCESS;
  174. }
  175. NTSTATUS
  176. NlWaitForEvent(
  177. LPWSTR EventName,
  178. ULONG Timeout
  179. )
  180. /*++
  181. Routine Description:
  182. Wait up to Timeout seconds for EventName to be triggered.
  183. Arguments:
  184. EventName - Name of event to wait on
  185. Timeout - Timeout for event (in seconds).
  186. Return Status:
  187. STATUS_SUCCESS - Indicates NETLOGON successfully initialized.
  188. STATUS_NETLOGON_NOT_STARTED - Timeout occurred.
  189. --*/
  190. {
  191. NTSTATUS Status;
  192. HANDLE EventHandle;
  193. OBJECT_ATTRIBUTES EventAttributes;
  194. UNICODE_STRING EventNameString;
  195. LARGE_INTEGER LocalTimeout;
  196. //
  197. // Create an event for us to wait on.
  198. //
  199. RtlInitUnicodeString( &EventNameString, EventName );
  200. InitializeObjectAttributes( &EventAttributes, &EventNameString, 0, 0, NULL );
  201. Status = NtCreateEvent(
  202. &EventHandle,
  203. SYNCHRONIZE,
  204. &EventAttributes,
  205. NotificationEvent,
  206. (BOOLEAN) FALSE // The event is initially not signaled
  207. );
  208. if ( !NT_SUCCESS(Status)) {
  209. //
  210. // If the event already exists, the server beat us to creating it.
  211. // Just open it.
  212. //
  213. if ( Status == STATUS_OBJECT_NAME_EXISTS ||
  214. Status == STATUS_OBJECT_NAME_COLLISION ) {
  215. Status = NtOpenEvent( &EventHandle,
  216. SYNCHRONIZE,
  217. &EventAttributes );
  218. }
  219. if ( !NT_SUCCESS(Status)) {
  220. SspPrint((SSP_MISC, "OpenEvent failed %lx\n", Status ));
  221. return Status;
  222. }
  223. }
  224. //
  225. // Wait for NETLOGON to initialize. Wait a maximum of Timeout seconds.
  226. //
  227. LocalTimeout.QuadPart = ((LONGLONG)(Timeout)) * (-10000000);
  228. Status = NtWaitForSingleObject( EventHandle, (BOOLEAN)FALSE, &LocalTimeout);
  229. (VOID) NtClose( EventHandle );
  230. if ( !NT_SUCCESS(Status) || Status == STATUS_TIMEOUT ) {
  231. if ( Status == STATUS_TIMEOUT ) {
  232. Status = STATUS_NETLOGON_NOT_STARTED; // Map to an error condition
  233. }
  234. return Status;
  235. }
  236. return STATUS_SUCCESS;
  237. }
  238. BOOLEAN
  239. NlDoingSetup(
  240. VOID
  241. )
  242. /*++
  243. Routine Description:
  244. Returns TRUE if we're running setup.
  245. Arguments:
  246. NONE.
  247. Return Status:
  248. TRUE - We're currently running setup
  249. FALSE - We're not running setup or aren't sure.
  250. --*/
  251. {
  252. LONG RegStatus;
  253. HKEY KeyHandle = NULL;
  254. DWORD ValueType;
  255. DWORD Value;
  256. DWORD ValueSize;
  257. //
  258. // Open the key for HKLM\SYSTEM\Setup
  259. //
  260. RegStatus = RegOpenKeyExA(
  261. HKEY_LOCAL_MACHINE,
  262. "SYSTEM\\Setup",
  263. 0, //Reserved
  264. KEY_QUERY_VALUE,
  265. &KeyHandle );
  266. if ( RegStatus != ERROR_SUCCESS ) {
  267. SspPrint((SSP_INIT, "NlDoingSetup: Cannot open registy key 'HKLM\\SYSTEM\\Setup' %ld.\n",
  268. RegStatus ));
  269. return FALSE;
  270. }
  271. //
  272. // Get the value that says whether we're doing setup.
  273. //
  274. ValueSize = sizeof(Value);
  275. RegStatus = RegQueryValueExA(
  276. KeyHandle,
  277. "SystemSetupInProgress",
  278. 0,
  279. &ValueType,
  280. (LPBYTE)&Value,
  281. &ValueSize );
  282. RegCloseKey( KeyHandle );
  283. if ( RegStatus != ERROR_SUCCESS ) {
  284. SspPrint((SSP_INIT, "NlDoingSetup: Cannot query value of 'HKLM\\SYSTEM\\Setup\\SystemSetupInProgress' %ld.\n",
  285. RegStatus ));
  286. return FALSE;
  287. }
  288. if ( ValueType != REG_DWORD ) {
  289. SspPrint((SSP_INIT, "NlDoingSetup: value of 'HKLM\\SYSTEM\\Setup\\SystemSetupInProgress'is not a REG_DWORD %ld.\n",
  290. ValueType ));
  291. return FALSE;
  292. }
  293. if ( ValueSize != sizeof(Value) ) {
  294. SspPrint((SSP_INIT, "NlDoingSetup: value size of 'HKLM\\SYSTEM\\Setup\\SystemSetupInProgress'is not 4 %ld.\n",
  295. ValueSize ));
  296. return FALSE;
  297. }
  298. if ( Value != 1 ) {
  299. // KdPrint(( "NlDoingSetup: not doing setup\n" ));
  300. return FALSE;
  301. }
  302. SspPrint((SSP_INIT, "NlDoingSetup: doing setup\n" ));
  303. return TRUE;
  304. }
  305. NTSTATUS
  306. NlWaitForNetlogon(
  307. ULONG Timeout
  308. )
  309. /*++
  310. Routine Description:
  311. Wait up to Timeout seconds for the netlogon service to start.
  312. Arguments:
  313. Timeout - Timeout for event (in seconds).
  314. Return Status:
  315. STATUS_SUCCESS - Indicates NETLOGON successfully initialized.
  316. STATUS_NETLOGON_NOT_STARTED - Timeout occurred.
  317. --*/
  318. {
  319. NTSTATUS Status;
  320. NET_API_STATUS NetStatus;
  321. SC_HANDLE ScManagerHandle = NULL;
  322. SC_HANDLE ServiceHandle = NULL;
  323. SERVICE_STATUS ServiceStatus;
  324. LPQUERY_SERVICE_CONFIG ServiceConfig;
  325. LPQUERY_SERVICE_CONFIG AllocServiceConfig = NULL;
  326. QUERY_SERVICE_CONFIG DummyServiceConfig;
  327. DWORD ServiceConfigSize;
  328. //
  329. // If the netlogon service is currently running,
  330. // skip the rest of the tests.
  331. //
  332. Status = NlWaitForEvent( L"\\NETLOGON_SERVICE_STARTED", 0 );
  333. if ( NT_SUCCESS(Status) ) {
  334. return Status;
  335. }
  336. //
  337. // If we're in setup,
  338. // don't bother waiting for netlogon to start.
  339. //
  340. if ( NlDoingSetup() ) {
  341. return STATUS_NETLOGON_NOT_STARTED;
  342. }
  343. //
  344. // Open a handle to the Netlogon Service.
  345. //
  346. ScManagerHandle = OpenSCManager(
  347. NULL,
  348. NULL,
  349. SC_MANAGER_CONNECT );
  350. if (ScManagerHandle == NULL) {
  351. SspPrint((SSP_MISC, "NlWaitForNetlogon: OpenSCManager failed: "
  352. "%lu\n", GetLastError()));
  353. Status = STATUS_NETLOGON_NOT_STARTED;
  354. goto Cleanup;
  355. }
  356. ServiceHandle = OpenService(
  357. ScManagerHandle,
  358. SERVICE_NETLOGON,
  359. SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG );
  360. if ( ServiceHandle == NULL ) {
  361. SspPrint((SSP_MISC, "NlWaitForNetlogon: OpenService failed: "
  362. "%lu\n", GetLastError()));
  363. Status = STATUS_NETLOGON_NOT_STARTED;
  364. goto Cleanup;
  365. }
  366. //
  367. // If the Netlogon service isn't configured to be automatically started
  368. // by the service controller, don't bother waiting for it to start.
  369. //
  370. // ?? Pass "DummyServiceConfig" and "sizeof(..)" since QueryService config
  371. // won't allow a null pointer, yet.
  372. if ( QueryServiceConfig(
  373. ServiceHandle,
  374. &DummyServiceConfig,
  375. sizeof(DummyServiceConfig),
  376. &ServiceConfigSize )) {
  377. ServiceConfig = &DummyServiceConfig;
  378. } else {
  379. NetStatus = GetLastError();
  380. if ( NetStatus != ERROR_INSUFFICIENT_BUFFER ) {
  381. SspPrint((SSP_MISC, "NlWaitForNetlogon: QueryServiceConfig failed: "
  382. "%lu\n", NetStatus));
  383. Status = STATUS_NETLOGON_NOT_STARTED;
  384. goto Cleanup;
  385. }
  386. AllocServiceConfig = I_NtLmAllocate( ServiceConfigSize );
  387. ServiceConfig = AllocServiceConfig;
  388. if ( AllocServiceConfig == NULL ) {
  389. Status = STATUS_NO_MEMORY;
  390. goto Cleanup;
  391. }
  392. if ( !QueryServiceConfig(
  393. ServiceHandle,
  394. ServiceConfig,
  395. ServiceConfigSize,
  396. &ServiceConfigSize )) {
  397. SspPrint((SSP_MISC, "NlWaitForNetlogon: QueryServiceConfig "
  398. "failed again: %lu\n", GetLastError()));
  399. Status = STATUS_NETLOGON_NOT_STARTED;
  400. goto Cleanup;
  401. }
  402. }
  403. if ( ServiceConfig->dwStartType != SERVICE_AUTO_START ) {
  404. SspPrint((SSP_MISC, "NlWaitForNetlogon: Netlogon start type invalid:"
  405. "%lu\n", ServiceConfig->dwStartType ));
  406. Status = STATUS_NETLOGON_NOT_STARTED;
  407. goto Cleanup;
  408. }
  409. //
  410. // Loop waiting for the netlogon service to start.
  411. // (Convert Timeout to a number of 10 second iterations)
  412. //
  413. Timeout = (Timeout+9)/10;
  414. for (;;) {
  415. //
  416. // Query the status of the Netlogon service.
  417. //
  418. if (! QueryServiceStatus( ServiceHandle, &ServiceStatus )) {
  419. SspPrint((SSP_MISC, "NlWaitForNetlogon: QueryServiceStatus failed: "
  420. "%lu\n", GetLastError() ));
  421. Status = STATUS_NETLOGON_NOT_STARTED;
  422. goto Cleanup;
  423. }
  424. //
  425. // Return or continue waiting depending on the state of
  426. // the netlogon service.
  427. //
  428. switch( ServiceStatus.dwCurrentState) {
  429. case SERVICE_RUNNING:
  430. Status = STATUS_SUCCESS;
  431. goto Cleanup;
  432. case SERVICE_STOPPED:
  433. //
  434. // If Netlogon failed to start,
  435. // error out now. The caller has waited long enough to start.
  436. //
  437. if ( ServiceStatus.dwWin32ExitCode != ERROR_SERVICE_NEVER_STARTED ){
  438. SspPrint((SSP_MISC, "NlWaitForNetlogon: "
  439. "Netlogon service couldn't start: %lu %lx\n",
  440. ServiceStatus.dwWin32ExitCode,
  441. ServiceStatus.dwWin32ExitCode ));
  442. if ( ServiceStatus.dwWin32ExitCode == ERROR_SERVICE_SPECIFIC_ERROR ) {
  443. SspPrint((SSP_MISC, " Service specific error code: %lu %lx\n",
  444. ServiceStatus.dwServiceSpecificExitCode,
  445. ServiceStatus.dwServiceSpecificExitCode ));
  446. }
  447. Status = STATUS_NETLOGON_NOT_STARTED;
  448. goto Cleanup;
  449. }
  450. //
  451. // If Netlogon has never been started on this boot,
  452. // continue waiting for it to start.
  453. //
  454. break;
  455. //
  456. // If Netlogon is trying to start up now,
  457. // continue waiting for it to start.
  458. //
  459. case SERVICE_START_PENDING:
  460. break;
  461. //
  462. // Any other state is bogus.
  463. //
  464. default:
  465. SspPrint((SSP_MISC, "NlWaitForNetlogon: "
  466. "Invalid service state: %lu\n",
  467. ServiceStatus.dwCurrentState ));
  468. Status = STATUS_NETLOGON_NOT_STARTED;
  469. goto Cleanup;
  470. }
  471. //
  472. // Wait ten seconds for the netlogon service to start.
  473. // If it has successfully started, just return now.
  474. //
  475. Status = NlWaitForEvent( L"\\NETLOGON_SERVICE_STARTED", 10 );
  476. if ( Status != STATUS_NETLOGON_NOT_STARTED ) {
  477. goto Cleanup;
  478. }
  479. //
  480. // If we've waited long enough for netlogon to start,
  481. // time out now.
  482. //
  483. if ( (--Timeout) == 0 ) {
  484. Status = STATUS_NETLOGON_NOT_STARTED;
  485. goto Cleanup;
  486. }
  487. }
  488. /* NOT REACHED */
  489. Cleanup:
  490. if ( ScManagerHandle != NULL ) {
  491. (VOID) CloseServiceHandle(ScManagerHandle);
  492. }
  493. if ( ServiceHandle != NULL ) {
  494. (VOID) CloseServiceHandle(ServiceHandle);
  495. }
  496. if ( AllocServiceConfig != NULL ) {
  497. I_NtLmFree( AllocServiceConfig );
  498. }
  499. return Status;
  500. }
  501. NTSTATUS
  502. NlSamInitialize(
  503. ULONG Timeout
  504. )
  505. /*++
  506. Routine Description:
  507. Initialize the MSV1_0 Authentication Package's communication to the SAM
  508. database. This initialization will take place once immediately prior
  509. to the first actual use of the SAM database.
  510. Arguments:
  511. Timeout - Timeout for event (in seconds).
  512. Return Status:
  513. STATUS_SUCCESS - Indicates NETLOGON successfully initialized.
  514. --*/
  515. {
  516. NTSTATUS Status;
  517. //
  518. // locals that are staging area for globals.
  519. //
  520. UNICODE_STRING PrimaryDomainName;
  521. PSID SamDomainId = NULL;
  522. UNICODE_STRING SamDomainName;
  523. SAMPR_HANDLE SamDomainHandle = NULL;
  524. UNICODE_STRING DnsTreeName;
  525. PLSAPR_POLICY_INFORMATION PolicyPrimaryDomainInfo = NULL;
  526. PLSAPR_POLICY_INFORMATION PolicyAccountDomainInfo = NULL;
  527. SAMPR_HANDLE SamHandle = NULL;
  528. #ifdef SAM
  529. PSAMPR_DOMAIN_INFO_BUFFER DomainInfo = NULL;
  530. #endif // SAM
  531. PrimaryDomainName.Buffer = NULL;
  532. SamDomainName.Buffer = NULL;
  533. DnsTreeName.Buffer = NULL;
  534. //
  535. // Wait for SAM to finish initialization.
  536. //
  537. Status = NlWaitForEvent( L"\\SAM_SERVICE_STARTED", Timeout );
  538. if ( !NT_SUCCESS(Status) ) {
  539. goto Cleanup;
  540. }
  541. //
  542. // Determine the DomainName and DomainId of the Account Database
  543. //
  544. Status = I_LsarQueryInformationPolicy( NtLmGlobalPolicyHandle,
  545. PolicyAccountDomainInformation,
  546. &PolicyAccountDomainInfo );
  547. if ( !NT_SUCCESS(Status) ) {
  548. goto Cleanup;
  549. }
  550. if ( PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainSid == NULL ||
  551. PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainName.Length == 0 ) {
  552. SspPrint((SSP_MISC, "Account domain info from LSA invalid.\n"));
  553. Status = STATUS_NO_SUCH_DOMAIN;
  554. goto Cleanup;
  555. }
  556. Status = I_LsarQueryInformationPolicy(
  557. NtLmGlobalPolicyHandle,
  558. PolicyPrimaryDomainInformation,
  559. &PolicyPrimaryDomainInfo );
  560. if ( !NT_SUCCESS(Status) ) {
  561. goto Cleanup;
  562. }
  563. if ( PolicyPrimaryDomainInfo->PolicyPrimaryDomainInfo.Name.Length == 0 )
  564. {
  565. SspPrint((SSP_CRITICAL, "Primary domain info from LSA invalid.\n"));
  566. Status = STATUS_NO_SUCH_DOMAIN;
  567. goto Cleanup;
  568. }
  569. //
  570. // save PrimaryDomainName
  571. //
  572. PrimaryDomainName.Length = PolicyPrimaryDomainInfo->PolicyPrimaryDomainInfo.Name.Length;
  573. PrimaryDomainName.MaximumLength = PrimaryDomainName.Length;
  574. PrimaryDomainName.Buffer =
  575. (PWSTR)I_NtLmAllocate( PrimaryDomainName.MaximumLength );
  576. if ( PrimaryDomainName.Buffer == NULL ) {
  577. Status = STATUS_NO_MEMORY;
  578. goto Cleanup;
  579. }
  580. RtlCopyMemory( PrimaryDomainName.Buffer,
  581. PolicyPrimaryDomainInfo->PolicyPrimaryDomainInfo.Name.Buffer,
  582. PrimaryDomainName.Length );
  583. //
  584. // Save the domain id of this domain
  585. //
  586. SamDomainId = I_NtLmAllocate(
  587. RtlLengthSid( PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainSid )
  588. );
  589. if ( SamDomainId == NULL ) {
  590. Status = STATUS_NO_MEMORY;
  591. goto Cleanup;
  592. }
  593. RtlCopyMemory( SamDomainId,
  594. PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainSid,
  595. RtlLengthSid( PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainSid ));
  596. //
  597. // Save the name of the account database on this machine.
  598. //
  599. // On a workstation, the account database is refered to by the machine
  600. // name and not the database name.
  601. // The above being true, the machine name is set to MACHINENAME during
  602. // setup and for the duration when the machine has a real machine name
  603. // until the end of setup, NlpSamDomainName will still have MACHINENAME.
  604. // This is not what the caller expects to authenticate against, so we
  605. // force a look from the Lsa all the time.
  606. // We assume that NlpSamDomainName will get the right info from the Lsa
  607. SamDomainName.Length = PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainName.Length;
  608. SamDomainName.MaximumLength = (USHORT)
  609. (SamDomainName.Length + sizeof(WCHAR));
  610. SamDomainName.Buffer =
  611. I_NtLmAllocate( SamDomainName.MaximumLength );
  612. if ( SamDomainName.Buffer == NULL ) {
  613. Status = STATUS_NO_MEMORY;
  614. goto Cleanup;
  615. }
  616. RtlCopyMemory( SamDomainName.Buffer,
  617. PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainName.Buffer,
  618. SamDomainName.MaximumLength );
  619. //
  620. // Open our connection with SAM
  621. //
  622. Status = I_SamIConnect( NULL, // No server name
  623. &SamHandle,
  624. SAM_SERVER_CONNECT,
  625. (BOOLEAN) TRUE ); // Indicate we are privileged
  626. if ( !NT_SUCCESS(Status) ) {
  627. SamHandle = NULL;
  628. SspPrint((SSP_CRITICAL, "Cannot SamIConnect %lX\n", Status));
  629. goto Cleanup;
  630. }
  631. //
  632. // Open the domain.
  633. //
  634. Status = I_SamrOpenDomain( SamHandle,
  635. DOMAIN_ALL_ACCESS,
  636. SamDomainId,
  637. &SamDomainHandle );
  638. if ( !NT_SUCCESS(Status) ) {
  639. SamDomainHandle = NULL;
  640. SspPrint((SSP_CRITICAL, "Cannot SamrOpenDomain %lX\n", Status));
  641. goto Cleanup;
  642. }
  643. //
  644. // query the TreeName (since SAM was not up during package initialization)
  645. // update the various globals.
  646. //
  647. if( !NlpSamInitialized )
  648. {
  649. //
  650. // make the query before taking the exclusive lock, to avoid possible
  651. // deadlock conditions.
  652. //
  653. SsprQueryTreeName( &DnsTreeName );
  654. }
  655. Status = STATUS_SUCCESS;
  656. RtlAcquireResourceExclusive(&NtLmGlobalCritSect, TRUE);
  657. if( !NlpSamInitialized ) {
  658. NlpPrimaryDomainName = PrimaryDomainName;
  659. NlpSamDomainId = SamDomainId;
  660. NlpSamDomainName = SamDomainName;
  661. NlpSamDomainHandle = SamDomainHandle;
  662. if( NtLmGlobalUnicodeDnsTreeName.Buffer )
  663. {
  664. NtLmFree( NtLmGlobalUnicodeDnsTreeName.Buffer );
  665. }
  666. NtLmGlobalUnicodeDnsTreeName = DnsTreeName;
  667. SsprUpdateTargetInfo();
  668. NlpSamInitialized = TRUE;
  669. //
  670. // mark locals invalid so they don't get freed.
  671. //
  672. PrimaryDomainName.Buffer = NULL;
  673. SamDomainId = NULL;
  674. SamDomainName.Buffer = NULL;
  675. SamDomainHandle = NULL;
  676. DnsTreeName.Buffer = NULL;
  677. }
  678. RtlReleaseResource(&NtLmGlobalCritSect);
  679. Cleanup:
  680. if( DnsTreeName.Buffer )
  681. {
  682. NtLmFree( DnsTreeName.Buffer );
  683. }
  684. if ( PrimaryDomainName.Buffer != NULL ) {
  685. I_NtLmFree( PrimaryDomainName.Buffer );
  686. }
  687. if ( SamDomainName.Buffer != NULL ) {
  688. I_NtLmFree( SamDomainName.Buffer );
  689. }
  690. if ( SamDomainHandle != NULL ) {
  691. (VOID) I_SamrCloseHandle( &SamDomainHandle );
  692. }
  693. if ( SamDomainId != NULL ) {
  694. I_NtLmFree( SamDomainId );
  695. }
  696. if ( PolicyAccountDomainInfo != NULL ) {
  697. I_LsaIFree_LSAPR_POLICY_INFORMATION( PolicyAccountDomainInformation,
  698. PolicyAccountDomainInfo );
  699. }
  700. if ( PolicyPrimaryDomainInfo != NULL ) {
  701. I_LsaIFree_LSAPR_POLICY_INFORMATION( PolicyPrimaryDomainInformation,
  702. PolicyPrimaryDomainInfo );
  703. }
  704. if ( SamHandle != NULL ) {
  705. (VOID) I_SamrCloseHandle( &SamHandle );
  706. }
  707. return Status;
  708. }
  709. NTSTATUS
  710. MspLm20Challenge (
  711. IN PLSA_CLIENT_REQUEST ClientRequest,
  712. IN PVOID ProtocolSubmitBuffer,
  713. IN PVOID ClientBufferBase,
  714. IN ULONG SubmitBufferSize,
  715. OUT PVOID *ProtocolReturnBuffer,
  716. OUT PULONG ReturnBufferSize,
  717. OUT PNTSTATUS ProtocolStatus
  718. )
  719. /*++
  720. Routine Description:
  721. This routine is the dispatch routine for LsaCallAuthenticationPackage()
  722. with a message type of MsV1_0Lm20ChallengeRequest. It is called by
  723. the LanMan server to determine the Challenge to pass back to a
  724. redirector trying to establish a connection to the server. The server
  725. is responsible remembering this Challenge and passing in back to this
  726. authentication package on a subsequent MsV1_0Lm20Logon request.
  727. Arguments:
  728. The arguments to this routine are identical to those of LsaApCallPackage.
  729. Only the special attributes of these parameters as they apply to
  730. this routine are mentioned here.
  731. Return Value:
  732. STATUS_SUCCESS - Indicates the service completed successfully.
  733. STATUS_QUOTA_EXCEEDED - This error indicates that the logon
  734. could not be completed because the client does not have
  735. sufficient quota to allocate the return buffer.
  736. --*/
  737. {
  738. NTSTATUS Status;
  739. PMSV1_0_LM20_CHALLENGE_REQUEST ChallengeRequest;
  740. PMSV1_0_LM20_CHALLENGE_RESPONSE ChallengeResponse;
  741. CLIENT_BUFFER_DESC ClientBufferDesc;
  742. UNREFERENCED_PARAMETER( ClientBufferBase );
  743. ASSERT( sizeof(LM_CHALLENGE) == MSV1_0_CHALLENGE_LENGTH );
  744. NlpInitClientBuffer( &ClientBufferDesc, ClientRequest );
  745. //
  746. // Ensure the specified Submit Buffer is of reasonable size and
  747. // relocate all of the pointers to be relative to the LSA allocated
  748. // buffer.
  749. //
  750. if ( SubmitBufferSize < sizeof(MSV1_0_LM20_CHALLENGE_REQUEST) ) {
  751. Status = STATUS_INVALID_PARAMETER;
  752. goto Cleanup;
  753. }
  754. ChallengeRequest = (PMSV1_0_LM20_CHALLENGE_REQUEST) ProtocolSubmitBuffer;
  755. ASSERT( ChallengeRequest->MessageType == MsV1_0Lm20ChallengeRequest );
  756. //
  757. // Allocate a buffer to return to the caller.
  758. //
  759. *ReturnBufferSize = sizeof(MSV1_0_LM20_CHALLENGE_RESPONSE);
  760. Status = NlpAllocateClientBuffer( &ClientBufferDesc,
  761. sizeof(MSV1_0_LM20_CHALLENGE_RESPONSE),
  762. *ReturnBufferSize );
  763. if ( !NT_SUCCESS( Status ) ) {
  764. goto Cleanup;
  765. }
  766. ChallengeResponse = (PMSV1_0_LM20_CHALLENGE_RESPONSE) ClientBufferDesc.MsvBuffer;
  767. //
  768. // Fill in the return buffer.
  769. //
  770. ChallengeResponse->MessageType = MsV1_0Lm20ChallengeRequest;
  771. //
  772. // Compute a random seed.
  773. //
  774. Status = SspGenerateRandomBits(
  775. ChallengeResponse->ChallengeToClient,
  776. MSV1_0_CHALLENGE_LENGTH
  777. );
  778. if ( !NT_SUCCESS( Status ) ) {
  779. goto Cleanup;
  780. }
  781. //
  782. // Flush the buffer to the client's address space.
  783. //
  784. Status = NlpFlushClientBuffer( &ClientBufferDesc,
  785. ProtocolReturnBuffer );
  786. Cleanup:
  787. //
  788. // If we weren't successful, free the buffer in the clients address space.
  789. //
  790. if ( !NT_SUCCESS(Status) ) {
  791. NlpFreeClientBuffer( &ClientBufferDesc );
  792. }
  793. //
  794. // Return status to the caller.
  795. //
  796. *ProtocolStatus = Status;
  797. return STATUS_SUCCESS;
  798. }
  799. NTSTATUS
  800. MspLm20GetChallengeResponse (
  801. IN PLSA_CLIENT_REQUEST ClientRequest,
  802. IN PVOID ProtocolSubmitBuffer,
  803. IN PVOID ClientBufferBase,
  804. IN ULONG SubmitBufferSize,
  805. OUT PVOID *ProtocolReturnBuffer,
  806. OUT PULONG ReturnBufferSize,
  807. OUT PNTSTATUS ProtocolStatus
  808. )
  809. /*++
  810. Routine Description:
  811. This routine is the dispatch routine for LsaCallAuthenticationPackage()
  812. with a message type of MsV1_0Lm20GetChallengeResponse. It is called by
  813. the LanMan redirector to determine the Challenge Response to pass to a
  814. server when trying to establish a connection to the server.
  815. This routine is passed a Challenge from the server. This routine encrypts
  816. the challenge with either the specified password or with the password
  817. implied by the specified Logon Id.
  818. Two Challenge responses are returned. One is based on the Unicode password
  819. as given to the Authentication package. The other is based on that
  820. password converted to a multi-byte character set (e.g., ASCII) and upper
  821. cased. The redirector should use whichever (or both) challenge responses
  822. as it needs them.
  823. Arguments:
  824. The arguments to this routine are identical to those of LsaApCallPackage.
  825. Only the special attributes of these parameters as they apply to
  826. this routine are mentioned here.
  827. Return Value:
  828. STATUS_SUCCESS - Indicates the service completed successfully.
  829. STATUS_QUOTA_EXCEEDED - This error indicates that the logon
  830. could not be completed because the client does not have
  831. sufficient quota to allocate the return buffer.
  832. --*/
  833. {
  834. NTSTATUS Status;
  835. PMSV1_0_GETCHALLENRESP_REQUEST GetRespRequest;
  836. CLIENT_BUFFER_DESC ClientBufferDesc;
  837. PMSV1_0_GETCHALLENRESP_RESPONSE GetRespResponse;
  838. PMSV1_0_PRIMARY_CREDENTIAL Credential = NULL;
  839. PMSV1_0_PRIMARY_CREDENTIAL PrimaryCredential = NULL;
  840. MSV1_0_PRIMARY_CREDENTIAL BuiltCredential = {0};
  841. //
  842. // Responses to return to the caller.
  843. //
  844. LM_RESPONSE LmResponse;
  845. STRING LmResponseString;
  846. NT_RESPONSE NtResponse;
  847. STRING NtResponseString;
  848. PMSV1_0_NTLM3_RESPONSE pNtlm3Response = NULL;
  849. UNICODE_STRING UserName;
  850. UNICODE_STRING LogonDomainName;
  851. USER_SESSION_KEY UserSessionKey;
  852. UCHAR LanmanSessionKey[MSV1_0_LANMAN_SESSION_KEY_LENGTH];
  853. UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH];
  854. UCHAR ChallengeFromClient[MSV1_0_CHALLENGE_LENGTH];
  855. ULONG NtLmProtocolSupported;
  856. //
  857. // Initialization
  858. //
  859. NlpInitClientBuffer( &ClientBufferDesc, ClientRequest );
  860. RtlInitUnicodeString( &UserName, NULL );
  861. RtlInitUnicodeString( &LogonDomainName, NULL );
  862. RtlZeroMemory( &UserSessionKey, sizeof(UserSessionKey) );
  863. RtlZeroMemory( LanmanSessionKey, sizeof(LanmanSessionKey) );
  864. //
  865. // If no credentials are associated with the client, a null session
  866. // will be used. For a downlevel server, the null session response is
  867. // a 1-byte null string (\0). Initialize LmResponseString to the
  868. // null session response.
  869. //
  870. RtlInitString( &LmResponseString, "" );
  871. LmResponseString.Length = 1;
  872. //
  873. // Initialize the NT response to the NT null session credentials,
  874. // which are zero length.
  875. //
  876. RtlInitString( &NtResponseString, NULL );
  877. //
  878. // Ensure the specified Submit Buffer is of reasonable size and
  879. // relocate all of the pointers to be relative to the LSA allocated
  880. // buffer.
  881. //
  882. if ( SubmitBufferSize < sizeof(MSV1_0_GETCHALLENRESP_REQUEST_V1) ) {
  883. Status = STATUS_INVALID_PARAMETER;
  884. goto Cleanup;
  885. }
  886. GetRespRequest = (PMSV1_0_GETCHALLENRESP_REQUEST) ProtocolSubmitBuffer;
  887. ASSERT( GetRespRequest->MessageType == MsV1_0Lm20GetChallengeResponse );
  888. if ( (GetRespRequest->ParameterControl & USE_PRIMARY_PASSWORD) == 0 ) {
  889. RELOCATE_ONE( &GetRespRequest->Password );
  890. }
  891. //
  892. // If we don't support the request (such as the caller is asking for an
  893. // LM challenge response and we do't support it, return an error here.
  894. //
  895. NtLmProtocolSupported = NtLmGlobalLmProtocolSupported;
  896. //
  897. // allow protocol to be downgraded to NTLM from NTLMv2 if so requested.
  898. //
  899. if ( (NtLmProtocolSupported >= UseNtlm3)
  900. && (ClientRequest == (PLSA_CLIENT_REQUEST) -1)
  901. && (GetRespRequest->ParameterControl & GCR_ALLOW_NTLM) )
  902. {
  903. NtLmProtocolSupported = NoLm;
  904. }
  905. if ( (GetRespRequest->ParameterControl & RETURN_NON_NT_USER_SESSION_KEY)
  906. && (NtLmProtocolSupported >= NoLm)
  907. // datagram can not negotiate, allow LM key
  908. && !( (ClientRequest == (PLSA_CLIENT_REQUEST) -1)
  909. && (GetRespRequest->ParameterControl & GCR_ALLOW_LM) ) )
  910. {
  911. Status = STATUS_NOT_SUPPORTED;
  912. SspPrint((SSP_CRITICAL,
  913. "MspLm20GetChallengeResponse: cannot do non NT user session key: "
  914. "ClientRequest %p, NtLmProtocolSupported %#x, ParameterControl %#x\n",
  915. ClientRequest, NtLmProtocolSupported, GetRespRequest->ParameterControl));
  916. goto Cleanup;
  917. }
  918. if( GetRespRequest->ParameterControl & GCR_MACHINE_CREDENTIAL )
  919. {
  920. SECPKG_CLIENT_INFO ClientInfo;
  921. LUID SystemLuid = SYSTEM_LUID;
  922. //
  923. // if caller wants machine cred, check they are SYSTEM.
  924. // if so, whack the LogonId to point at the machine logon.
  925. //
  926. Status = LsaFunctions->GetClientInfo( &ClientInfo );
  927. if( !NT_SUCCESS(Status) )
  928. {
  929. goto Cleanup;
  930. }
  931. if(!RtlEqualLuid( &ClientInfo.LogonId, &SystemLuid ))
  932. {
  933. Status = STATUS_ACCESS_DENIED;
  934. goto Cleanup;
  935. }
  936. GetRespRequest->LogonId = NtLmGlobalLuidMachineLogon;
  937. }
  938. //
  939. // if caller wants NTLM++, so be it...
  940. //
  941. if ( (GetRespRequest->ParameterControl & GCR_NTLM3_PARMS) ) {
  942. PMSV1_0_AV_PAIR pAV;
  943. UCHAR TargetInfoBuffer[3*sizeof(MSV1_0_AV_PAIR) + (DNS_MAX_NAME_LENGTH+CNLEN+2)*sizeof(WCHAR)];
  944. NULL_RELOCATE_ONE( &GetRespRequest->UserName );
  945. NULL_RELOCATE_ONE( &GetRespRequest->LogonDomainName );
  946. NULL_RELOCATE_ONE( &GetRespRequest->ServerName );
  947. // if target is just a domain name or domain name followed by
  948. // server name, make it into an AV pair list
  949. if (!(GetRespRequest->ParameterControl & GCR_TARGET_INFO)) {
  950. UNICODE_STRING DomainName;
  951. UNICODE_STRING ServerName = { 0, 0, NULL};
  952. unsigned int i;
  953. //
  954. // check length of name to make sure it fits in my buffer
  955. //
  956. if (GetRespRequest->ServerName.Length > (DNS_MAX_NAME_LENGTH+CNLEN+2)*sizeof(WCHAR)) {
  957. Status = STATUS_INVALID_PARAMETER;
  958. goto Cleanup;
  959. }
  960. //
  961. // init AV list in temp buffer
  962. //
  963. pAV = MsvpAvlInit(TargetInfoBuffer);
  964. //
  965. // see if there's a NULL in the middle of the server name
  966. // that indicates that it's really a domain name followed by a server name
  967. //
  968. DomainName = GetRespRequest->ServerName;
  969. for (i = 0; i < (DomainName.Length/sizeof(WCHAR)); i++) {
  970. if ( DomainName.Buffer[i] == L'\0' )
  971. {
  972. // take length of domain name without the NULL
  973. DomainName.Length = (USHORT) i*sizeof(WCHAR);
  974. // adjust server name and length to point after the domain name
  975. ServerName.Length = (USHORT) (GetRespRequest->ServerName.Length - (i+1) * sizeof(WCHAR));
  976. ServerName.Buffer = GetRespRequest->ServerName.Buffer + (i+1);
  977. break;
  978. }
  979. }
  980. //
  981. // strip off possible trailing null after the server name
  982. //
  983. for (i = 0; i < (ServerName.Length / sizeof(WCHAR)); i++) {
  984. if (ServerName.Buffer[i] == L'\0')
  985. {
  986. ServerName.Length = (USHORT)i*sizeof(WCHAR);
  987. break;
  988. }
  989. }
  990. //
  991. // put both names in the AV list (if both exist)
  992. //
  993. MsvpAvlAdd(pAV, MsvAvNbDomainName, &DomainName, sizeof(TargetInfoBuffer));
  994. if (ServerName.Length > 0) {
  995. MsvpAvlAdd(pAV, MsvAvNbComputerName, &ServerName, sizeof(TargetInfoBuffer));
  996. }
  997. //
  998. // make the request point at AV list instead of names.
  999. //
  1000. GetRespRequest->ServerName.Length = (USHORT)MsvpAvlLen(pAV, sizeof(TargetInfoBuffer));
  1001. GetRespRequest->ServerName.Buffer = (PWCHAR)pAV;
  1002. }
  1003. //
  1004. // if we're only using NTLMv2 or better, then complain if either
  1005. // computer name or server name missing
  1006. //
  1007. if (NtLmProtocolSupported >= RefuseNtlm3NoTarget) {
  1008. pAV = (PMSV1_0_AV_PAIR)GetRespRequest->ServerName.Buffer;
  1009. if ((pAV==NULL) ||
  1010. MsvpAvlGet(pAV, MsvAvNbDomainName, GetRespRequest->ServerName.Length) == NULL ||
  1011. MsvpAvlGet(pAV, MsvAvNbComputerName, GetRespRequest->ServerName.Length) == NULL) {
  1012. Status = STATUS_INVALID_PARAMETER;
  1013. goto Cleanup;
  1014. }
  1015. }
  1016. }
  1017. //
  1018. // If the caller wants information from the credentials of a specified
  1019. // LogonId, get those credentials from the LSA.
  1020. //
  1021. // If there are no such credentials,
  1022. // tell the caller to use the NULL session.
  1023. //
  1024. if ( ((GetRespRequest->ParameterControl & PRIMARY_CREDENTIAL_NEEDED) != 0 ) && ((GetRespRequest->ParameterControl & NULL_SESSION_REQUESTED) == 0)) {
  1025. Status = NlpGetPrimaryCredential(
  1026. &GetRespRequest->LogonId,
  1027. &PrimaryCredential,
  1028. NULL );
  1029. if ( NT_SUCCESS(Status) ) {
  1030. if ( GetRespRequest->ParameterControl & RETURN_PRIMARY_USERNAME ) {
  1031. UserName = PrimaryCredential->UserName;
  1032. }
  1033. if ( GetRespRequest->ParameterControl &
  1034. RETURN_PRIMARY_LOGON_DOMAINNAME ) {
  1035. #ifndef DONT_MAP_DOMAIN_ON_REQUEST
  1036. //
  1037. // Map the user's logon domain against the current mapping
  1038. // in the registry.
  1039. //
  1040. Status = NlpMapLogonDomain(
  1041. &LogonDomainName,
  1042. &PrimaryCredential->LogonDomainName
  1043. );
  1044. if (!NT_SUCCESS(Status)) {
  1045. goto Cleanup;
  1046. }
  1047. #else
  1048. LogonDomainName = PrimaryCredential->LogonDomainName;
  1049. #endif
  1050. }
  1051. } else if ( Status == STATUS_NO_SUCH_LOGON_SESSION ||
  1052. Status == STATUS_UNSUCCESSFUL ) {
  1053. //
  1054. // Clean up the status code
  1055. //
  1056. Status = STATUS_NO_SUCH_LOGON_SESSION;
  1057. //
  1058. // If the caller wants at least the password from the primary
  1059. // credential, just use a NULL session primary credential.
  1060. //
  1061. if ( (GetRespRequest->ParameterControl & USE_PRIMARY_PASSWORD ) ==
  1062. USE_PRIMARY_PASSWORD ) {
  1063. PrimaryCredential = NULL;
  1064. //
  1065. // If part of the information was supplied by the caller,
  1066. // report the error to the caller.
  1067. //
  1068. } else {
  1069. SspPrint((SSP_CRITICAL, "MspLm20GetChallengeResponse: cannot "
  1070. " GetPrimaryCredential %lx\n", Status ));
  1071. goto Cleanup;
  1072. }
  1073. } else {
  1074. SspPrint((SSP_CRITICAL, "MspLm20GetChallengeResponse: cannot "
  1075. " GetPrimaryCredential %lx\n", Status ));
  1076. goto Cleanup;
  1077. }
  1078. Credential = PrimaryCredential;
  1079. }
  1080. //
  1081. // If the caller passed in a password to use,
  1082. // use it to build a credential.
  1083. //
  1084. if ( (GetRespRequest->ParameterControl & USE_PRIMARY_PASSWORD) == 0 ) {
  1085. NlpPutOwfsInPrimaryCredential( &(GetRespRequest->Password),
  1086. (BOOLEAN) ((GetRespRequest->ParameterControl & GCR_USE_OWF_PASSWORD) != 0),
  1087. &BuiltCredential );
  1088. //
  1089. // Use the newly allocated credential to get the password information
  1090. // from.
  1091. //
  1092. Credential = &BuiltCredential;
  1093. }
  1094. //
  1095. // Build the appropriate response.
  1096. //
  1097. if ( Credential != NULL ) {
  1098. SspPrint((SSP_CRED, "MspLm20GetChallengeResponse: LogonId %#x:%#x, ParameterControl %#x, %wZ\\%wZ; Credential %wZ\\%wZ; %wZ\\%wZ\n",
  1099. GetRespRequest->LogonId.HighPart, GetRespRequest->LogonId.LowPart, GetRespRequest->ParameterControl,
  1100. &GetRespRequest->LogonDomainName, &GetRespRequest->UserName,
  1101. &Credential->LogonDomainName, &Credential->UserName,
  1102. &LogonDomainName, &UserName));
  1103. //
  1104. // If the DC is asserted to have been upgraded, we should use NTLM3
  1105. // if caller supplies the NTLM3 parameters
  1106. //
  1107. if ((NtLmProtocolSupported >= UseNtlm3) &&
  1108. (GetRespRequest->ParameterControl & GCR_NTLM3_PARMS)
  1109. ) {
  1110. USHORT Ntlm3ResponseSize;
  1111. UNICODE_STRING Ntlm3UserName;
  1112. UNICODE_STRING Ntlm3LogonDomainName;
  1113. UNICODE_STRING Ntlm3ServerName;
  1114. // use the server name supplied by the caller
  1115. Ntlm3ServerName = GetRespRequest->ServerName;
  1116. // even if user name and domain are supplied, use current logged
  1117. // in user if so requested
  1118. if (GetRespRequest->ParameterControl & USE_PRIMARY_PASSWORD) {
  1119. Ntlm3UserName = Credential->UserName;
  1120. Ntlm3LogonDomainName = Credential->LogonDomainName;
  1121. } else {
  1122. Ntlm3UserName = GetRespRequest->UserName;
  1123. Ntlm3LogonDomainName = GetRespRequest->LogonDomainName;
  1124. }
  1125. //
  1126. // Allocate the response
  1127. //
  1128. Ntlm3ResponseSize =
  1129. sizeof(MSV1_0_NTLM3_RESPONSE) + Ntlm3ServerName.Length;
  1130. pNtlm3Response = (*Lsa.AllocatePrivateHeap)( Ntlm3ResponseSize );
  1131. if ( pNtlm3Response == NULL ) {
  1132. SspPrint((SSP_CRITICAL, "MspLm20GetChallengeResponse: No memory %ld\n",
  1133. Ntlm3ResponseSize ));
  1134. Status = STATUS_INSUFFICIENT_RESOURCES;
  1135. goto Cleanup;
  1136. }
  1137. //
  1138. // Upcase LogonDomainName and UserName if outbound buffers use OEM
  1139. // character set
  1140. //
  1141. if (GetRespRequest->ParameterControl & GCR_USE_OEM_SET) {
  1142. Status = RtlUpcaseUnicodeString(&Ntlm3LogonDomainName, &Ntlm3LogonDomainName, FALSE);
  1143. if (NT_SUCCESS(Status)) {
  1144. RtlUpcaseUnicodeString(&Ntlm3UserName, &Ntlm3UserName, FALSE);
  1145. }
  1146. if (!NT_SUCCESS(Status)) {
  1147. goto Cleanup;
  1148. }
  1149. }
  1150. MsvpLm20GetNtlm3ChallengeResponse(
  1151. &Credential->NtOwfPassword,
  1152. &Ntlm3UserName,
  1153. &Ntlm3LogonDomainName,
  1154. &Ntlm3ServerName,
  1155. GetRespRequest->ChallengeToClient,
  1156. pNtlm3Response,
  1157. (PMSV1_0_LM3_RESPONSE)&LmResponse,
  1158. &UserSessionKey,
  1159. (PLM_SESSION_KEY)LanmanSessionKey
  1160. );
  1161. NtResponseString.Buffer = (PCHAR) pNtlm3Response;
  1162. NtResponseString.Length = Ntlm3ResponseSize;
  1163. LmResponseString.Buffer = (PCHAR) &LmResponse;
  1164. LmResponseString.Length = sizeof(LmResponse);
  1165. } else {
  1166. //
  1167. // if requested, generate our own challenge, and mix it with that
  1168. // of the server's
  1169. //
  1170. if (GetRespRequest->ParameterControl & GENERATE_CLIENT_CHALLENGE) {
  1171. Status = SspGenerateRandomBits(ChallengeFromClient, MSV1_0_CHALLENGE_LENGTH);
  1172. if (!NT_SUCCESS(Status)) {
  1173. goto Cleanup;
  1174. }
  1175. #ifdef USE_CONSTANT_CHALLENGE
  1176. RtlZeroMemory(ChallengeFromClient, MSV1_0_CHALLENGE_LENGTH);
  1177. #endif
  1178. RtlCopyMemory(
  1179. ChallengeToClient,
  1180. GetRespRequest->ChallengeToClient,
  1181. MSV1_0_CHALLENGE_LENGTH
  1182. );
  1183. MsvpCalculateNtlm2Challenge (
  1184. GetRespRequest->ChallengeToClient,
  1185. ChallengeFromClient,
  1186. GetRespRequest->ChallengeToClient
  1187. );
  1188. }
  1189. Status = RtlCalculateNtResponse(
  1190. (PNT_CHALLENGE) GetRespRequest->ChallengeToClient,
  1191. &Credential->NtOwfPassword,
  1192. &NtResponse );
  1193. if ( !NT_SUCCESS( Status ) ) {
  1194. goto Cleanup;
  1195. }
  1196. //
  1197. // send the client challenge back in the LM response slot if we made one
  1198. //
  1199. if (GetRespRequest->ParameterControl & GENERATE_CLIENT_CHALLENGE) {
  1200. RtlZeroMemory(
  1201. &LmResponse,
  1202. sizeof(LmResponse)
  1203. );
  1204. RtlCopyMemory(
  1205. &LmResponse,
  1206. ChallengeFromClient,
  1207. MSV1_0_CHALLENGE_LENGTH
  1208. );
  1209. //
  1210. // Return the LM response if policy set that way for backwards compatibility.
  1211. //
  1212. } else if ((NtLmProtocolSupported <= AllowLm) ) {
  1213. Status = RtlCalculateLmResponse(
  1214. (PLM_CHALLENGE) GetRespRequest->ChallengeToClient,
  1215. &Credential->LmOwfPassword,
  1216. &LmResponse );
  1217. if ( !NT_SUCCESS( Status ) ) {
  1218. goto Cleanup;
  1219. }
  1220. //
  1221. //
  1222. // Can't return LM response -- so use NT response
  1223. // (to allow LM_KEY generatation)
  1224. //
  1225. } else {
  1226. RtlCopyMemory(
  1227. &LmResponse,
  1228. &NtResponse,
  1229. sizeof(LmResponse)
  1230. );
  1231. }
  1232. NtResponseString.Buffer = (PCHAR) &NtResponse;
  1233. NtResponseString.Length = sizeof(NtResponse);
  1234. LmResponseString.Buffer = (PCHAR) &LmResponse;
  1235. LmResponseString.Length = sizeof(LmResponse);
  1236. //
  1237. // Compute the session keys
  1238. //
  1239. if (GetRespRequest->ParameterControl & GENERATE_CLIENT_CHALLENGE) {
  1240. //
  1241. // assert: we're talking to an NT4-SP4 or later server
  1242. // and the user's DC hasn't been upgraded to NTLM++
  1243. // generate session key from MD4(NT hash) -
  1244. // aka NtUserSessionKey - that is different for each session
  1245. //
  1246. Status = RtlCalculateUserSessionKeyNt(
  1247. &NtResponse,
  1248. &Credential->NtOwfPassword,
  1249. &UserSessionKey );
  1250. if ( !NT_SUCCESS( Status ) ) {
  1251. goto Cleanup;
  1252. }
  1253. MsvpCalculateNtlm2SessionKeys(
  1254. &UserSessionKey,
  1255. ChallengeToClient,
  1256. ChallengeFromClient,
  1257. (PUSER_SESSION_KEY)&UserSessionKey,
  1258. (PLM_SESSION_KEY)LanmanSessionKey
  1259. );
  1260. } else if ( GetRespRequest->ParameterControl & RETURN_NON_NT_USER_SESSION_KEY){
  1261. //
  1262. // If the redir didn't negotiate an NT protocol with the server,
  1263. // use the lanman session key.
  1264. //
  1265. if ( Credential->LmPasswordPresent ) {
  1266. ASSERT( sizeof(UserSessionKey) >= sizeof(LanmanSessionKey) );
  1267. RtlCopyMemory( &UserSessionKey,
  1268. &Credential->LmOwfPassword,
  1269. sizeof(LanmanSessionKey) );
  1270. }
  1271. } else {
  1272. if ( !Credential->NtPasswordPresent ) {
  1273. RtlCopyMemory( &Credential->NtOwfPassword,
  1274. &NlpNullNtOwfPassword,
  1275. sizeof(Credential->NtOwfPassword) );
  1276. }
  1277. Status = RtlCalculateUserSessionKeyNt(
  1278. &NtResponse,
  1279. &Credential->NtOwfPassword,
  1280. &UserSessionKey );
  1281. if ( !NT_SUCCESS( Status ) ) {
  1282. goto Cleanup;
  1283. }
  1284. }
  1285. if ( Credential->LmPasswordPresent ) {
  1286. RtlCopyMemory( LanmanSessionKey,
  1287. &Credential->LmOwfPassword,
  1288. sizeof(LanmanSessionKey) );
  1289. }
  1290. } // UseNtlm3
  1291. }
  1292. //
  1293. // Allocate a buffer to return to the caller.
  1294. //
  1295. *ReturnBufferSize = sizeof(MSV1_0_GETCHALLENRESP_RESPONSE) +
  1296. LogonDomainName.Length + sizeof(WCHAR) +
  1297. UserName.Length + sizeof(WCHAR) +
  1298. NtResponseString.Length + sizeof(WCHAR) +
  1299. LmResponseString.Length + sizeof(WCHAR);
  1300. Status = NlpAllocateClientBuffer( &ClientBufferDesc,
  1301. sizeof(MSV1_0_GETCHALLENRESP_RESPONSE),
  1302. *ReturnBufferSize );
  1303. if ( !NT_SUCCESS( Status ) ) {
  1304. goto Cleanup;
  1305. }
  1306. GetRespResponse = (PMSV1_0_GETCHALLENRESP_RESPONSE) ClientBufferDesc.MsvBuffer;
  1307. //
  1308. // Fill in the return buffer.
  1309. //
  1310. GetRespResponse->MessageType = MsV1_0Lm20GetChallengeResponse;
  1311. RtlCopyMemory( GetRespResponse->UserSessionKey,
  1312. &UserSessionKey,
  1313. sizeof(UserSessionKey));
  1314. RtlCopyMemory( GetRespResponse->LanmanSessionKey,
  1315. LanmanSessionKey,
  1316. sizeof(LanmanSessionKey) );
  1317. //
  1318. // Copy the logon domain name (the string may be empty)
  1319. //
  1320. NlpPutClientString( &ClientBufferDesc,
  1321. &GetRespResponse->LogonDomainName,
  1322. &LogonDomainName );
  1323. //
  1324. // Copy the user name (the string may be empty)
  1325. //
  1326. NlpPutClientString( &ClientBufferDesc,
  1327. &GetRespResponse->UserName,
  1328. &UserName );
  1329. //
  1330. // Copy the Challenge Responses to the client buffer.
  1331. //
  1332. NlpPutClientString(
  1333. &ClientBufferDesc,
  1334. (PUNICODE_STRING)
  1335. &GetRespResponse->CaseSensitiveChallengeResponse,
  1336. (PUNICODE_STRING) &NtResponseString );
  1337. NlpPutClientString(
  1338. &ClientBufferDesc,
  1339. (PUNICODE_STRING)
  1340. &GetRespResponse->CaseInsensitiveChallengeResponse,
  1341. (PUNICODE_STRING)&LmResponseString );
  1342. //
  1343. // Flush the buffer to the client's address space.
  1344. //
  1345. Status = NlpFlushClientBuffer( &ClientBufferDesc,
  1346. ProtocolReturnBuffer );
  1347. Cleanup:
  1348. //
  1349. // If we weren't successful, free the buffer in the clients address space.
  1350. //
  1351. if ( !NT_SUCCESS(Status) ) {
  1352. NlpFreeClientBuffer( &ClientBufferDesc );
  1353. }
  1354. //
  1355. // Cleanup locally used resources
  1356. //
  1357. if ( PrimaryCredential != NULL ) {
  1358. RtlZeroMemory(PrimaryCredential, sizeof(*PrimaryCredential));
  1359. (*Lsa.FreeLsaHeap)( PrimaryCredential );
  1360. }
  1361. #ifndef DONT_MAP_DOMAIN_ON_REQUEST
  1362. if (LogonDomainName.Buffer != NULL) {
  1363. NtLmFree(LogonDomainName.Buffer);
  1364. }
  1365. #endif
  1366. if ( pNtlm3Response != NULL ) {
  1367. (*Lsa.FreePrivateHeap)( pNtlm3Response );
  1368. }
  1369. RtlSecureZeroMemory(&BuiltCredential, sizeof(BuiltCredential));
  1370. //
  1371. // Return status to the caller.
  1372. //
  1373. *ProtocolStatus = Status;
  1374. return STATUS_SUCCESS;
  1375. }
  1376. NTSTATUS
  1377. MspLm20EnumUsers (
  1378. IN PLSA_CLIENT_REQUEST pClientRequest,
  1379. IN PVOID pProtocolSubmitBuffer,
  1380. IN PVOID pClientBufferBase,
  1381. IN ULONG SubmitBufferSize,
  1382. OUT PVOID* ppProtocolReturnBuffer,
  1383. OUT PULONG pReturnBufferSize,
  1384. OUT PNTSTATUS pProtocolStatus
  1385. )
  1386. /*++
  1387. Routine Description:
  1388. This routine is the dispatch routine for LsaCallAuthenticationPackage()
  1389. with a message type of MsV1_0Lm20EnumerateUsers. This routine
  1390. enumerates all of the interactive, service, and batch logons to the MSV1_0
  1391. authentication package.
  1392. Arguments:
  1393. The arguments to this routine are identical to those of LsaApCallPackage.
  1394. Only the special attributes of these parameters as they apply to
  1395. this routine are mentioned here.
  1396. Return Value:
  1397. STATUS_SUCCESS - Indicates the service completed successfully.
  1398. --*/
  1399. {
  1400. NTSTATUS Status;
  1401. PMSV1_0_ENUMUSERS_REQUEST pEnumRequest = NULL;
  1402. PMSV1_0_ENUMUSERS_RESPONSE pEnumResponse = NULL;
  1403. CLIENT_BUFFER_DESC ClientBufferDesc;
  1404. ULONG LogonCount = 0;
  1405. BOOLEAN bActiveLogonsAreLocked = FALSE;
  1406. PUCHAR pWhere;
  1407. LIST_ENTRY* pScan = NULL;
  1408. ACTIVE_LOGON* pActiveLogon = NULL;
  1409. //
  1410. // Ensure the specified Submit Buffer is of reasonable size and
  1411. // relocate all of the pointers to be relative to the LSA allocated
  1412. // buffer.
  1413. //
  1414. NlpInitClientBuffer( &ClientBufferDesc, pClientRequest );
  1415. UNREFERENCED_PARAMETER( pClientBufferBase );
  1416. if ( SubmitBufferSize < sizeof(MSV1_0_ENUMUSERS_REQUEST) )
  1417. {
  1418. Status = STATUS_INVALID_PARAMETER;
  1419. goto Cleanup;
  1420. }
  1421. pEnumRequest = (PMSV1_0_ENUMUSERS_REQUEST) pProtocolSubmitBuffer;
  1422. ASSERT( pEnumRequest->MessageType == MsV1_0EnumerateUsers );
  1423. //
  1424. // Count the current number of active logons
  1425. //
  1426. NlpLockActiveLogonsRead();
  1427. bActiveLogonsAreLocked = TRUE;
  1428. for ( pScan = NlpActiveLogonListAnchor.Flink;
  1429. pScan != &NlpActiveLogonListAnchor;
  1430. pScan = pScan->Flink )
  1431. {
  1432. pActiveLogon = CONTAINING_RECORD(pScan, ACTIVE_LOGON, ListEntry);
  1433. //
  1434. // don't count the machine account logon.
  1435. //
  1436. if ( RtlEqualLuid(&NtLmGlobalLuidMachineLogon, &pActiveLogon->LogonId) )
  1437. {
  1438. continue;
  1439. }
  1440. LogonCount ++;
  1441. }
  1442. //
  1443. // Allocate a buffer to return to the caller.
  1444. //
  1445. *pReturnBufferSize = sizeof(MSV1_0_ENUMUSERS_RESPONSE) +
  1446. LogonCount * (sizeof(LUID) + sizeof(ULONG));
  1447. Status = NlpAllocateClientBuffer( &ClientBufferDesc,
  1448. sizeof(MSV1_0_ENUMUSERS_RESPONSE),
  1449. *pReturnBufferSize );
  1450. if ( !NT_SUCCESS( Status ) )
  1451. {
  1452. goto Cleanup;
  1453. }
  1454. pEnumResponse = (PMSV1_0_ENUMUSERS_RESPONSE) ClientBufferDesc.MsvBuffer;
  1455. //
  1456. // Fill in the return buffer.
  1457. //
  1458. pEnumResponse->MessageType = MsV1_0EnumerateUsers;
  1459. pEnumResponse->NumberOfLoggedOnUsers = LogonCount;
  1460. pWhere = (PUCHAR)(pEnumResponse + 1);
  1461. //
  1462. // Loop through the Active Logon Table copying the LogonId of each session.
  1463. //
  1464. pEnumResponse->LogonIds = (PLUID) (ClientBufferDesc.UserBuffer +
  1465. (pWhere - ClientBufferDesc.MsvBuffer));
  1466. for ( pScan = NlpActiveLogonListAnchor.Flink;
  1467. pScan != &NlpActiveLogonListAnchor;
  1468. pScan = pScan->Flink )
  1469. {
  1470. pActiveLogon = CONTAINING_RECORD(pScan, ACTIVE_LOGON, ListEntry);
  1471. //
  1472. // don't count the machine account logon.
  1473. //
  1474. if ( RtlEqualLuid(&NtLmGlobalLuidMachineLogon, &pActiveLogon->LogonId) )
  1475. {
  1476. continue;
  1477. }
  1478. *((PLUID)pWhere) = pActiveLogon->LogonId,
  1479. pWhere += sizeof(LUID);
  1480. }
  1481. //
  1482. // Loop through the Active Logon Table copying the EnumHandle of
  1483. // each session.
  1484. //
  1485. pEnumResponse->EnumHandles = (PULONG)(ClientBufferDesc.UserBuffer +
  1486. (pWhere - ClientBufferDesc.MsvBuffer));
  1487. for ( pScan = NlpActiveLogonListAnchor.Flink;
  1488. pScan != &NlpActiveLogonListAnchor;
  1489. pScan = pScan->Flink )
  1490. {
  1491. pActiveLogon = CONTAINING_RECORD(pScan, ACTIVE_LOGON, ListEntry);
  1492. //
  1493. // don't count the machine account logon.
  1494. //
  1495. if ( RtlEqualLuid(&NtLmGlobalLuidMachineLogon, &pActiveLogon->LogonId) )
  1496. {
  1497. continue;
  1498. }
  1499. *((PULONG)pWhere) = pActiveLogon->EnumHandle,
  1500. pWhere += sizeof(ULONG);
  1501. }
  1502. //
  1503. // Flush the buffer to the client's address space.
  1504. //
  1505. Status = NlpFlushClientBuffer( &ClientBufferDesc,
  1506. ppProtocolReturnBuffer );
  1507. Cleanup:
  1508. //
  1509. // Be sure to unlock the lock on the Active logon list.
  1510. //
  1511. if ( bActiveLogonsAreLocked )
  1512. {
  1513. NlpUnlockActiveLogons();
  1514. }
  1515. //
  1516. // If we weren't successful, free the buffer in the clients address space.
  1517. //
  1518. if ( !NT_SUCCESS(Status) )
  1519. {
  1520. NlpFreeClientBuffer( &ClientBufferDesc );
  1521. }
  1522. //
  1523. // Return status to the caller.
  1524. //
  1525. *pProtocolStatus = Status;
  1526. return STATUS_SUCCESS;
  1527. }
  1528. NTSTATUS
  1529. MspLm20GetUserInfo (
  1530. IN PLSA_CLIENT_REQUEST ClientRequest,
  1531. IN PVOID ProtocolSubmitBuffer,
  1532. IN PVOID ClientBufferBase,
  1533. IN ULONG SubmitBufferSize,
  1534. OUT PVOID *ProtocolReturnBuffer,
  1535. OUT PULONG ReturnBufferSize,
  1536. OUT PNTSTATUS ProtocolStatus
  1537. )
  1538. /*++
  1539. Routine Description:
  1540. This routine is the dispatch routine for LsaCallAuthenticationPackage()
  1541. with a message type of MsV1_0GetUserInfo. This routine
  1542. returns information describing a particular Logon Id.
  1543. Arguments:
  1544. The arguments to this routine are identical to those of LsaApCallPackage.
  1545. Only the special attributes of these parameters as they apply to
  1546. this routine are mentioned here.
  1547. Return Value:
  1548. STATUS_SUCCESS - Indicates the service completed successfully.
  1549. STATUS_QUOTA_EXCEEDED - This error indicates that the logon
  1550. could not be completed because the client does not have
  1551. sufficient quota to allocate the return buffer.
  1552. --*/
  1553. {
  1554. NTSTATUS Status;
  1555. PMSV1_0_GETUSERINFO_REQUEST pGetInfoRequest;
  1556. PMSV1_0_GETUSERINFO_RESPONSE pGetInfoResponse = NULL;
  1557. CLIENT_BUFFER_DESC ClientBufferDesc;
  1558. BOOLEAN bActiveLogonsAreLocked = FALSE;
  1559. PACTIVE_LOGON pActiveLogon = NULL;
  1560. ULONG SidLength;
  1561. //
  1562. // Ensure the specified Submit Buffer is of reasonable size and
  1563. // relocate all of the pointers to be relative to the LSA allocated
  1564. // buffer.
  1565. //
  1566. NlpInitClientBuffer( &ClientBufferDesc, ClientRequest );
  1567. UNREFERENCED_PARAMETER( ClientBufferBase );
  1568. if ( SubmitBufferSize < sizeof(MSV1_0_GETUSERINFO_REQUEST) )
  1569. {
  1570. Status = STATUS_INVALID_PARAMETER;
  1571. goto Cleanup;
  1572. }
  1573. pGetInfoRequest = (PMSV1_0_GETUSERINFO_REQUEST) ProtocolSubmitBuffer;
  1574. ASSERT( pGetInfoRequest->MessageType == MsV1_0GetUserInfo );
  1575. //
  1576. // Find the Active logon entry for this particular Logon Id.
  1577. //
  1578. NlpLockActiveLogonsRead();
  1579. bActiveLogonsAreLocked = TRUE;
  1580. pActiveLogon = NlpFindActiveLogon( &pGetInfoRequest->LogonId );
  1581. if (!pActiveLogon)
  1582. {
  1583. Status = STATUS_NO_SUCH_LOGON_SESSION;
  1584. goto Cleanup;
  1585. }
  1586. //
  1587. // Allocate a buffer to return to the caller.
  1588. //
  1589. SidLength = RtlLengthSid( pActiveLogon->UserSid );
  1590. *ReturnBufferSize = sizeof(MSV1_0_GETUSERINFO_RESPONSE) +
  1591. pActiveLogon->UserName.Length + sizeof(WCHAR) +
  1592. pActiveLogon->LogonDomainName.Length + sizeof(WCHAR) +
  1593. pActiveLogon->LogonServer.Length + sizeof(WCHAR) +
  1594. SidLength;
  1595. Status = NlpAllocateClientBuffer( &ClientBufferDesc,
  1596. sizeof(MSV1_0_GETUSERINFO_RESPONSE),
  1597. *ReturnBufferSize );
  1598. if ( !NT_SUCCESS( Status ) )
  1599. {
  1600. goto Cleanup;
  1601. }
  1602. pGetInfoResponse = (PMSV1_0_GETUSERINFO_RESPONSE) ClientBufferDesc.MsvBuffer;
  1603. //
  1604. // Fill in the return buffer.
  1605. //
  1606. pGetInfoResponse->MessageType = MsV1_0GetUserInfo;
  1607. pGetInfoResponse->LogonType = pActiveLogon->LogonType;
  1608. //
  1609. // Copy ULONG aligned data first
  1610. //
  1611. pGetInfoResponse->UserSid = ClientBufferDesc.UserBuffer +
  1612. ClientBufferDesc.StringOffset;
  1613. RtlCopyMemory( ClientBufferDesc.MsvBuffer + ClientBufferDesc.StringOffset,
  1614. pActiveLogon->UserSid,
  1615. SidLength );
  1616. ClientBufferDesc.StringOffset += SidLength;
  1617. //
  1618. // Copy WCHAR aligned data
  1619. //
  1620. NlpPutClientString( &ClientBufferDesc,
  1621. &pGetInfoResponse->UserName,
  1622. &pActiveLogon->UserName );
  1623. NlpPutClientString( &ClientBufferDesc,
  1624. &pGetInfoResponse->LogonDomainName,
  1625. &pActiveLogon->LogonDomainName );
  1626. NlpPutClientString( &ClientBufferDesc,
  1627. &pGetInfoResponse->LogonServer,
  1628. &pActiveLogon->LogonServer );
  1629. //
  1630. // Flush the buffer to the client's address space.
  1631. //
  1632. Status = NlpFlushClientBuffer( &ClientBufferDesc,
  1633. ProtocolReturnBuffer );
  1634. Cleanup:
  1635. //
  1636. // Be sure to unlock the lock on the Active logon list.
  1637. //
  1638. if ( bActiveLogonsAreLocked )
  1639. {
  1640. NlpUnlockActiveLogons();
  1641. }
  1642. //
  1643. // If we weren't successful, free the buffer in the clients address space.
  1644. //
  1645. if ( !NT_SUCCESS(Status))
  1646. {
  1647. NlpFreeClientBuffer( &ClientBufferDesc );
  1648. }
  1649. //
  1650. // Return status to the caller.
  1651. //
  1652. *ProtocolStatus = Status;
  1653. return STATUS_SUCCESS;
  1654. }
  1655. NTSTATUS
  1656. MspLm20ReLogonUsers (
  1657. IN PLSA_CLIENT_REQUEST ClientRequest,
  1658. IN PVOID ProtocolSubmitBuffer,
  1659. IN PVOID ClientBufferBase,
  1660. IN ULONG SubmitBufferSize,
  1661. OUT PVOID *ProtocolReturnBuffer,
  1662. OUT PULONG ReturnBufferSize,
  1663. OUT PNTSTATUS ProtocolStatus
  1664. )
  1665. /*++
  1666. Routine Description:
  1667. This routine is the dispatch routine for LsaCallAuthenticationPackage()
  1668. with a message type of MsV1_0RelogonUsers. For each logon session
  1669. which was validated by the specified domain controller, the logon session
  1670. is re-established with that same domain controller.
  1671. Arguments:
  1672. The arguments to this routine are identical to those of LsaApCallPackage.
  1673. Only the special attributes of these parameters as they apply to
  1674. this routine are mentioned here.
  1675. Return Value:
  1676. STATUS_SUCCESS - Indicates the service completed successfully.
  1677. --*/
  1678. {
  1679. UNREFERENCED_PARAMETER( ClientRequest );
  1680. UNREFERENCED_PARAMETER( ProtocolSubmitBuffer);
  1681. UNREFERENCED_PARAMETER( ClientBufferBase);
  1682. UNREFERENCED_PARAMETER( SubmitBufferSize);
  1683. UNREFERENCED_PARAMETER( ReturnBufferSize);
  1684. *ProtocolReturnBuffer = NULL;
  1685. *ProtocolStatus = STATUS_NOT_IMPLEMENTED;
  1686. return STATUS_SUCCESS;
  1687. }
  1688. NTSTATUS
  1689. MspLm20GenericPassthrough (
  1690. IN PLSA_CLIENT_REQUEST ClientRequest,
  1691. IN PVOID ProtocolSubmitBuffer,
  1692. IN PVOID ClientBufferBase,
  1693. IN ULONG SubmitBufferSize,
  1694. OUT PVOID *ProtocolReturnBuffer,
  1695. OUT PULONG ReturnBufferSize,
  1696. OUT PNTSTATUS ProtocolStatus
  1697. )
  1698. /*++
  1699. Routine Description:
  1700. This routine is the dispatch routine for LsaCallAuthenticationPackage()
  1701. with a message type of MsV1_0Lm20GenericPassthrough. It is called by
  1702. a client wishing to make a CallAuthenticationPackage call against
  1703. a domain controller.
  1704. Arguments:
  1705. The arguments to this routine are identical to those of LsaApCallPackage.
  1706. Only the special attributes of these parameters as they apply to
  1707. this routine are mentioned here.
  1708. Return Value:
  1709. STATUS_SUCCESS - Indicates the service completed successfully.
  1710. STATUS_QUOTA_EXCEEDED - This error indicates that the logon
  1711. could not be completed because the client does not have
  1712. sufficient quota to allocate the return buffer.
  1713. --*/
  1714. {
  1715. NTSTATUS Status = STATUS_SUCCESS;
  1716. PMSV1_0_PASSTHROUGH_REQUEST PassthroughRequest;
  1717. PMSV1_0_PASSTHROUGH_RESPONSE PassthroughResponse;
  1718. CLIENT_BUFFER_DESC ClientBufferDesc;
  1719. BOOLEAN Authoritative;
  1720. PNETLOGON_VALIDATION_GENERIC_INFO ValidationGeneric = NULL;
  1721. NETLOGON_GENERIC_INFO LogonGeneric;
  1722. PNETLOGON_LOGON_IDENTITY_INFO LogonInformation;
  1723. //
  1724. // WMI tracing helper struct
  1725. //
  1726. NTLM_TRACE_INFO TraceInfo = {0};
  1727. //
  1728. // Begin tracing a logon user
  1729. //
  1730. if (NtlmGlobalEventTraceFlag) {
  1731. //
  1732. // Trace header goo
  1733. //
  1734. SET_TRACE_HEADER(TraceInfo,
  1735. NtlmGenericPassthroughGuid,
  1736. EVENT_TRACE_TYPE_START,
  1737. WNODE_FLAG_TRACED_GUID,
  1738. 0);
  1739. TraceEvent(NtlmGlobalTraceLoggerHandle,
  1740. (PEVENT_TRACE_HEADER)&TraceInfo);
  1741. }
  1742. NlpInitClientBuffer( &ClientBufferDesc, ClientRequest );
  1743. *ProtocolStatus = STATUS_SUCCESS;
  1744. //
  1745. // Ensure the specified Submit Buffer is of reasonable size and
  1746. // relocate all of the pointers to be relative to the LSA allocated
  1747. // buffer.
  1748. //
  1749. if ( SubmitBufferSize < sizeof(MSV1_0_PASSTHROUGH_REQUEST) ) {
  1750. Status = STATUS_INVALID_PARAMETER;
  1751. goto Cleanup;
  1752. }
  1753. PassthroughRequest = (PMSV1_0_PASSTHROUGH_REQUEST) ProtocolSubmitBuffer;
  1754. RELOCATE_ONE( &PassthroughRequest->DomainName );
  1755. RELOCATE_ONE( &PassthroughRequest->PackageName );
  1756. //
  1757. // Make sure the buffer fits in the supplied size
  1758. //
  1759. if (PassthroughRequest->LogonData != NULL) {
  1760. if (PassthroughRequest->LogonData + PassthroughRequest->DataLength <
  1761. PassthroughRequest->LogonData ) {
  1762. Status = STATUS_INVALID_PARAMETER;
  1763. goto Cleanup;
  1764. }
  1765. if ((ULONG_PTR)ClientBufferBase + SubmitBufferSize < (ULONG_PTR)ClientBufferBase ) {
  1766. Status = STATUS_INVALID_PARAMETER;
  1767. goto Cleanup;
  1768. }
  1769. if (PassthroughRequest->LogonData + PassthroughRequest->DataLength >
  1770. (PUCHAR) ClientBufferBase + SubmitBufferSize) {
  1771. Status = STATUS_INVALID_PARAMETER;
  1772. goto Cleanup;
  1773. }
  1774. //
  1775. // Reset the pointers for the validation data
  1776. //
  1777. PassthroughRequest->LogonData =
  1778. (PUCHAR) PassthroughRequest -
  1779. (ULONG_PTR) ClientBufferBase +
  1780. (ULONG_PTR) PassthroughRequest->LogonData;
  1781. }
  1782. //
  1783. // Build the structure to pass to Netlogon
  1784. //
  1785. RtlZeroMemory(
  1786. &LogonGeneric,
  1787. sizeof(LogonGeneric)
  1788. );
  1789. LogonGeneric.Identity.LogonDomainName = PassthroughRequest->DomainName;
  1790. LogonGeneric.PackageName = PassthroughRequest->PackageName;
  1791. LogonGeneric.LogonData = PassthroughRequest->LogonData;
  1792. LogonGeneric.DataLength = PassthroughRequest->DataLength;
  1793. LogonInformation =
  1794. (PNETLOGON_LOGON_IDENTITY_INFO) &LogonGeneric;
  1795. //
  1796. // Call Netlogon to remote the request
  1797. //
  1798. //
  1799. // Wait for NETLOGON to finish initialization.
  1800. //
  1801. if ( !NlpNetlogonInitialized ) {
  1802. Status = NlWaitForNetlogon( NETLOGON_STARTUP_TIME );
  1803. if ( !NT_SUCCESS(Status) ) {
  1804. if ( Status != STATUS_NETLOGON_NOT_STARTED ) {
  1805. goto Cleanup;
  1806. }
  1807. } else {
  1808. NlpNetlogonInitialized = TRUE;
  1809. }
  1810. }
  1811. if ( NlpNetlogonInitialized ) {
  1812. //
  1813. // Trace the domain name and package name
  1814. //
  1815. if (NtlmGlobalEventTraceFlag){
  1816. //Header goo
  1817. SET_TRACE_HEADER(TraceInfo,
  1818. NtlmGenericPassthroughGuid,
  1819. EVENT_TRACE_TYPE_INFO,
  1820. WNODE_FLAG_TRACED_GUID|WNODE_FLAG_USE_MOF_PTR,
  1821. 4);
  1822. SET_TRACE_USTRING(TraceInfo,
  1823. TRACE_PASSTHROUGH_DOMAIN,
  1824. LogonGeneric.Identity.LogonDomainName);
  1825. SET_TRACE_USTRING(TraceInfo,
  1826. TRACE_PASSTHROUGH_PACKAGE,
  1827. LogonGeneric.PackageName);
  1828. TraceEvent(
  1829. NtlmGlobalTraceLoggerHandle,
  1830. (PEVENT_TRACE_HEADER)&TraceInfo
  1831. );
  1832. }
  1833. Status = (*NlpNetLogonSamLogon)(
  1834. NULL, // Server name
  1835. NULL, // Computer name
  1836. NULL, // Authenticator
  1837. NULL, // ReturnAuthenticator
  1838. NetlogonGenericInformation,
  1839. (LPBYTE) &LogonInformation,
  1840. NetlogonValidationGenericInfo2,
  1841. (LPBYTE *) &ValidationGeneric,
  1842. &Authoritative );
  1843. //
  1844. // Reset Netlogon initialized flag if local netlogon cannot be
  1845. // reached.
  1846. // (Use a more explicit status code)
  1847. //
  1848. if ( Status == RPC_NT_SERVER_UNAVAILABLE ||
  1849. Status == RPC_NT_UNKNOWN_IF ||
  1850. Status == STATUS_NETLOGON_NOT_STARTED ) {
  1851. Status = STATUS_NETLOGON_NOT_STARTED;
  1852. NlpNetlogonInitialized = FALSE;
  1853. }
  1854. } else {
  1855. //
  1856. // no netlogon: see if the request is destined for the local domain,
  1857. // to allow WORKGROUP support.
  1858. //
  1859. if ( LogonInformation->LogonDomainName.Length == 0 ||
  1860. (LogonInformation->LogonDomainName.Length != 0 &&
  1861. RtlEqualDomainName( &NlpSamDomainName,
  1862. &LogonInformation->LogonDomainName ) )
  1863. ) {
  1864. PNETLOGON_GENERIC_INFO GenericInfo;
  1865. NETLOGON_VALIDATION_GENERIC_INFO GenericValidation;
  1866. NTSTATUS ProtocolStatus;
  1867. GenericInfo = (PNETLOGON_GENERIC_INFO) LogonInformation;
  1868. GenericValidation.ValidationData = NULL;
  1869. GenericValidation.DataLength = 0;
  1870. //
  1871. // unwrap passthrough message and pass it off to dispatch.
  1872. //
  1873. Status = LsaICallPackagePassthrough(
  1874. &GenericInfo->PackageName,
  1875. 0, // Indicate pointers are relative.
  1876. GenericInfo->LogonData,
  1877. GenericInfo->DataLength,
  1878. (PVOID *) &GenericValidation.ValidationData,
  1879. &GenericValidation.DataLength,
  1880. &ProtocolStatus
  1881. );
  1882. if (NT_SUCCESS( Status ) )
  1883. Status = ProtocolStatus;
  1884. //
  1885. // If the call succeeded, allocate the return message.
  1886. //
  1887. if (NT_SUCCESS(Status)) {
  1888. PNETLOGON_VALIDATION_GENERIC_INFO ReturnInfo;
  1889. ULONG ValidationLength;
  1890. ValidationLength = sizeof(*ReturnInfo) + GenericValidation.DataLength;
  1891. ReturnInfo = (PNETLOGON_VALIDATION_GENERIC_INFO) MIDL_user_allocate(
  1892. ValidationLength
  1893. );
  1894. if (ReturnInfo != NULL) {
  1895. if ( GenericValidation.DataLength == 0 ||
  1896. GenericValidation.ValidationData == NULL ) {
  1897. ReturnInfo->DataLength = 0;
  1898. ReturnInfo->ValidationData = NULL;
  1899. } else {
  1900. ReturnInfo->DataLength = GenericValidation.DataLength;
  1901. ReturnInfo->ValidationData = (PUCHAR) (ReturnInfo + 1);
  1902. RtlCopyMemory(
  1903. ReturnInfo->ValidationData,
  1904. GenericValidation.ValidationData,
  1905. ReturnInfo->DataLength );
  1906. }
  1907. ValidationGeneric = ReturnInfo;
  1908. } else {
  1909. Status = STATUS_INSUFFICIENT_RESOURCES;
  1910. }
  1911. if (GenericValidation.ValidationData != NULL) {
  1912. LsaIFreeReturnBuffer(GenericValidation.ValidationData);
  1913. }
  1914. }
  1915. } else {
  1916. Status = STATUS_NETLOGON_NOT_STARTED;
  1917. }
  1918. }
  1919. if (!NT_SUCCESS(Status)) {
  1920. goto Cleanup;
  1921. }
  1922. //
  1923. // Allocate a buffer to return to the caller.
  1924. //
  1925. *ReturnBufferSize = sizeof(MSV1_0_PASSTHROUGH_RESPONSE) +
  1926. ValidationGeneric->DataLength;
  1927. Status = NlpAllocateClientBuffer( &ClientBufferDesc,
  1928. sizeof(MSV1_0_PASSTHROUGH_RESPONSE),
  1929. *ReturnBufferSize );
  1930. if ( !NT_SUCCESS( Status ) ) {
  1931. goto Cleanup;
  1932. }
  1933. PassthroughResponse = (PMSV1_0_PASSTHROUGH_RESPONSE) ClientBufferDesc.MsvBuffer;
  1934. //
  1935. // Fill in the return buffer.
  1936. //
  1937. PassthroughResponse->MessageType = MsV1_0GenericPassthrough;
  1938. PassthroughResponse->DataLength = ValidationGeneric->DataLength;
  1939. PassthroughResponse->ValidationData = ClientBufferDesc.UserBuffer + sizeof(MSV1_0_PASSTHROUGH_RESPONSE);
  1940. RtlCopyMemory(
  1941. PassthroughResponse + 1,
  1942. ValidationGeneric->ValidationData,
  1943. ValidationGeneric->DataLength
  1944. );
  1945. //
  1946. // Flush the buffer to the client's address space.
  1947. //
  1948. Status = NlpFlushClientBuffer( &ClientBufferDesc,
  1949. ProtocolReturnBuffer );
  1950. Cleanup:
  1951. if (ValidationGeneric != NULL) {
  1952. MIDL_user_free(ValidationGeneric);
  1953. }
  1954. if ( !NT_SUCCESS(Status)) {
  1955. NlpFreeClientBuffer( &ClientBufferDesc );
  1956. }
  1957. if (NtlmGlobalEventTraceFlag){
  1958. //
  1959. // Trace header goo
  1960. //
  1961. SET_TRACE_HEADER(TraceInfo,
  1962. NtlmGenericPassthroughGuid,
  1963. EVENT_TRACE_TYPE_END,
  1964. WNODE_FLAG_TRACED_GUID,
  1965. 0);
  1966. TraceEvent(NtlmGlobalTraceLoggerHandle,
  1967. (PEVENT_TRACE_HEADER)&TraceInfo);
  1968. }
  1969. *ProtocolStatus = Status;
  1970. return(STATUS_SUCCESS);
  1971. }
  1972. NTSTATUS
  1973. MspLm20CacheLogon (
  1974. IN PLSA_CLIENT_REQUEST ClientRequest,
  1975. IN PVOID ProtocolSubmitBuffer,
  1976. IN PVOID ClientBufferBase,
  1977. IN ULONG SubmitBufferSize,
  1978. OUT PVOID *ProtocolReturnBuffer,
  1979. OUT PULONG ReturnBufferSize,
  1980. OUT PNTSTATUS ProtocolStatus
  1981. )
  1982. /*++
  1983. Routine Description:
  1984. This routine is the dispatch routine for LsaCallAuthenticationPackage()
  1985. with a message type of MsV1_0Lm20CacheLogon. It is called by
  1986. a client wishing to cache logon information in the logon cache
  1987. Arguments:
  1988. The arguments to this routine are identical to those of LsaApCallPackage.
  1989. Only the special attributes of these parameters as they apply to
  1990. this routine are mentioned here.
  1991. Return Value:
  1992. STATUS_SUCCESS - Indicates the service completed successfully.
  1993. STATUS_QUOTA_EXCEEDED - This error indicates that the logon
  1994. could not be completed because the client does not have
  1995. sufficient quota to allocate the return buffer.
  1996. --*/
  1997. {
  1998. NTSTATUS Status = STATUS_SUCCESS;
  1999. PMSV1_0_CACHE_LOGON_REQUEST CacheRequest;
  2000. PNETLOGON_INTERACTIVE_INFO LogonInfo;
  2001. NETLOGON_VALIDATION_SAM_INFO4 ValidationInfo;
  2002. PVOID SupplementalCacheData = NULL;
  2003. ULONG SupplementalCacheDataLength = 0;
  2004. ULONG CacheRequestFlags = 0;
  2005. //
  2006. // NOTE: this entry point only allows callers within the LSA process
  2007. //
  2008. if (ClientRequest != NULL) {
  2009. *ProtocolStatus = STATUS_ACCESS_DENIED;
  2010. return(STATUS_SUCCESS);
  2011. }
  2012. CacheRequest = (PMSV1_0_CACHE_LOGON_REQUEST) ProtocolSubmitBuffer;
  2013. if ( SubmitBufferSize <= sizeof( MSV1_0_CACHE_LOGON_REQUEST_OLD ) ||
  2014. SubmitBufferSize > sizeof( MSV1_0_CACHE_LOGON_REQUEST ))
  2015. {
  2016. Status = STATUS_INVALID_PARAMETER;
  2017. goto Cleanup;
  2018. }
  2019. if ( SubmitBufferSize >= sizeof( MSV1_0_CACHE_LOGON_REQUEST_W2K ))
  2020. {
  2021. SupplementalCacheData = CacheRequest->SupplementalCacheData;
  2022. SupplementalCacheDataLength = CacheRequest->SupplementalCacheDataLength;
  2023. if ( SubmitBufferSize == sizeof( MSV1_0_CACHE_LOGON_REQUEST ))
  2024. {
  2025. CacheRequestFlags = CacheRequest->RequestFlags;
  2026. }
  2027. }
  2028. LogonInfo = (PNETLOGON_INTERACTIVE_INFO) CacheRequest->LogonInformation;
  2029. if( (CacheRequestFlags & MSV1_0_CACHE_LOGON_REQUEST_INFO4) == 0 )
  2030. {
  2031. RtlZeroMemory( &ValidationInfo, sizeof(ValidationInfo));
  2032. RtlCopyMemory( &ValidationInfo,
  2033. CacheRequest->ValidationInformation,
  2034. sizeof(NETLOGON_VALIDATION_SAM_INFO2) );
  2035. } else {
  2036. RtlCopyMemory( &ValidationInfo,
  2037. CacheRequest->ValidationInformation,
  2038. sizeof(NETLOGON_VALIDATION_SAM_INFO4) );
  2039. }
  2040. *ProtocolStatus = STATUS_SUCCESS;
  2041. if (( CacheRequestFlags & MSV1_0_CACHE_LOGON_DELETE_ENTRY) != 0 )
  2042. {
  2043. *ProtocolStatus = NlpDeleteCacheEntry(
  2044. 0, // no reason supplied, most likely this is STATUS_ACCOUNT_DISABLED
  2045. (USHORT) -1, // authoritative and indicates this is from CallAuthPackage
  2046. 0, // no logon type supplied
  2047. FALSE, // not invalidated by NTLM
  2048. LogonInfo
  2049. );
  2050. }
  2051. else
  2052. //
  2053. // Actually add the cache entry
  2054. //
  2055. {
  2056. *ProtocolStatus = NlpAddCacheEntry(
  2057. LogonInfo,
  2058. &ValidationInfo,
  2059. SupplementalCacheData,
  2060. SupplementalCacheDataLength,
  2061. CacheRequestFlags
  2062. );
  2063. }
  2064. Cleanup:
  2065. return (STATUS_SUCCESS);
  2066. UNREFERENCED_PARAMETER( ClientRequest);
  2067. UNREFERENCED_PARAMETER( ProtocolReturnBuffer);
  2068. UNREFERENCED_PARAMETER( ClientBufferBase);
  2069. UNREFERENCED_PARAMETER( ReturnBufferSize);
  2070. }
  2071. NTSTATUS
  2072. MspLm20CacheLookup (
  2073. IN PLSA_CLIENT_REQUEST ClientRequest,
  2074. IN PVOID ProtocolSubmitBuffer,
  2075. IN PVOID ClientBufferBase,
  2076. IN ULONG SubmitBufferSize,
  2077. OUT PVOID *ProtocolReturnBuffer,
  2078. OUT PULONG ReturnBufferSize,
  2079. OUT PNTSTATUS ProtocolStatus
  2080. )
  2081. /*++
  2082. Routine Description:
  2083. This routine is the dispatch routine for LsaCallAuthenticationPackage()
  2084. with a message type of MsV1_0Lm20CacheLookup. It is called by
  2085. a client wishing to extract cache logon information and optionally
  2086. verify the credential.
  2087. Arguments:
  2088. The arguments to this routine are identical to those of LsaApCallPackage.
  2089. Only the special attributes of these parameters as they apply to
  2090. this routine are mentioned here.
  2091. Return Value:
  2092. STATUS_SUCCESS - Indicates the service completed successfully.
  2093. STATUS_QUOTA_EXCEEDED - This error indicates that the logon
  2094. could not be completed because the client does not have
  2095. sufficient quota to allocate the return buffer.
  2096. --*/
  2097. {
  2098. NTSTATUS Status = STATUS_SUCCESS;
  2099. PMSV1_0_CACHE_LOOKUP_REQUEST CacheRequest;
  2100. PMSV1_0_CACHE_LOOKUP_RESPONSE CacheResponse;
  2101. NETLOGON_LOGON_IDENTITY_INFO LogonInfo;
  2102. PNETLOGON_VALIDATION_SAM_INFO4 ValidationInfo = NULL;
  2103. CACHE_PASSWORDS cachePasswords;
  2104. CLIENT_BUFFER_DESC ClientBufferDesc;
  2105. PNT_OWF_PASSWORD pNtOwfPassword = NULL;
  2106. NT_OWF_PASSWORD ComputedNtOwfPassword;
  2107. PVOID SupplementalCacheData = NULL;
  2108. ULONG SupplementalCacheDataLength;
  2109. //
  2110. // Ensure the client is from the LSA process
  2111. //
  2112. if (ClientRequest != NULL) {
  2113. *ProtocolStatus = STATUS_ACCESS_DENIED;
  2114. return(STATUS_SUCCESS);
  2115. }
  2116. NlpInitClientBuffer( &ClientBufferDesc, ClientRequest );
  2117. *ProtocolStatus = STATUS_SUCCESS;
  2118. //
  2119. // Ensure the specified Submit Buffer is of reasonable size and
  2120. // relocate all of the pointers to be relative to the LSA allocated
  2121. // buffer.
  2122. //
  2123. if ( SubmitBufferSize < sizeof(MSV1_0_CACHE_LOOKUP_REQUEST) ) {
  2124. Status = STATUS_INVALID_PARAMETER;
  2125. goto Cleanup;
  2126. }
  2127. CacheRequest = (PMSV1_0_CACHE_LOOKUP_REQUEST) ProtocolSubmitBuffer;
  2128. RtlZeroMemory(
  2129. &LogonInfo,
  2130. sizeof(LogonInfo)
  2131. );
  2132. //
  2133. // NOTE: this submit call only supports in-process calls within the LSA
  2134. // so buffers within the submit buffer are assumed to be valid and
  2135. // hence not validated in the same way that out-proc calls are.
  2136. //
  2137. LogonInfo.LogonDomainName = CacheRequest->DomainName;
  2138. LogonInfo.UserName = CacheRequest->UserName;
  2139. if( CacheRequest->CredentialType != MSV1_0_CACHE_LOOKUP_CREDTYPE_NONE &&
  2140. CacheRequest->CredentialType != MSV1_0_CACHE_LOOKUP_CREDTYPE_RAW &&
  2141. CacheRequest->CredentialType != MSV1_0_CACHE_LOOKUP_CREDTYPE_NTOWF ) {
  2142. Status = STATUS_INVALID_PARAMETER;
  2143. goto Cleanup;
  2144. }
  2145. //
  2146. // get the cache entry
  2147. //
  2148. *ProtocolStatus = NlpGetCacheEntry(
  2149. &LogonInfo,
  2150. MSV1_0_CACHE_LOGON_REQUEST_SMARTCARD_ONLY, // allow smartcard only cache entry
  2151. NULL, // no need for credentials domain name
  2152. NULL, // no need for credentials user name
  2153. &ValidationInfo,
  2154. &cachePasswords,
  2155. &SupplementalCacheData,
  2156. &SupplementalCacheDataLength
  2157. );
  2158. if (!NT_SUCCESS(*ProtocolStatus)) {
  2159. goto Cleanup;
  2160. }
  2161. if( CacheRequest->CredentialType == MSV1_0_CACHE_LOOKUP_CREDTYPE_NONE ) {
  2162. if( CacheRequest->CredentialInfoLength != 0 ) {
  2163. Status = STATUS_INVALID_PARAMETER;
  2164. goto Cleanup;
  2165. }
  2166. }
  2167. //
  2168. // verify the password, if necessary.
  2169. //
  2170. if ( CacheRequest->CredentialType == MSV1_0_CACHE_LOOKUP_CREDTYPE_RAW ) {
  2171. //
  2172. // convert RAW to NTOWF.
  2173. //
  2174. UNICODE_STRING TempPassword;
  2175. if ( CacheRequest->CredentialInfoLength > 0xFFFF ) {
  2176. Status = STATUS_INVALID_PARAMETER;
  2177. goto Cleanup;
  2178. }
  2179. TempPassword.Buffer = (PWSTR)&CacheRequest->CredentialSubmitBuffer;
  2180. TempPassword.Length = (USHORT)CacheRequest->CredentialInfoLength;
  2181. TempPassword.MaximumLength = TempPassword.Length;
  2182. pNtOwfPassword = &ComputedNtOwfPassword;
  2183. Status = RtlCalculateNtOwfPassword( &TempPassword, pNtOwfPassword );
  2184. if( !NT_SUCCESS( Status ) ) {
  2185. goto Cleanup;
  2186. }
  2187. //
  2188. // now, convert the request to NT_OWF style.
  2189. //
  2190. CacheRequest->CredentialType = MSV1_0_CACHE_LOOKUP_CREDTYPE_NTOWF;
  2191. CacheRequest->CredentialInfoLength = sizeof( NT_OWF_PASSWORD );
  2192. }
  2193. if ( CacheRequest->CredentialType == MSV1_0_CACHE_LOOKUP_CREDTYPE_NTOWF ) {
  2194. if( CacheRequest->CredentialInfoLength != sizeof( NT_OWF_PASSWORD ) ) {
  2195. Status = STATUS_INVALID_PARAMETER;
  2196. goto Cleanup;
  2197. }
  2198. if( !cachePasswords.SecretPasswords.NtPasswordPresent ) {
  2199. Status = STATUS_LOGON_FAILURE;
  2200. goto Cleanup;
  2201. }
  2202. if( pNtOwfPassword == NULL ) {
  2203. pNtOwfPassword = (PNT_OWF_PASSWORD)&CacheRequest->CredentialSubmitBuffer;
  2204. }
  2205. Status = NlpComputeSaltedHashedPassword(
  2206. pNtOwfPassword,
  2207. pNtOwfPassword,
  2208. &ValidationInfo->EffectiveName
  2209. );
  2210. if (!NT_SUCCESS( Status )) {
  2211. goto Cleanup;
  2212. }
  2213. if (RtlCompareMemory(
  2214. pNtOwfPassword,
  2215. &cachePasswords.SecretPasswords.NtOwfPassword,
  2216. sizeof( NT_OWF_PASSWORD )
  2217. ) != sizeof(NT_OWF_PASSWORD) )
  2218. {
  2219. Status = STATUS_LOGON_FAILURE;
  2220. goto Cleanup;
  2221. }
  2222. }
  2223. //
  2224. // Return the validation info here.
  2225. //
  2226. *ReturnBufferSize = sizeof(MSV1_0_CACHE_LOOKUP_RESPONSE);
  2227. Status = NlpAllocateClientBuffer( &ClientBufferDesc,
  2228. sizeof(MSV1_0_CACHE_LOOKUP_RESPONSE),
  2229. *ReturnBufferSize );
  2230. if ( !NT_SUCCESS( Status ) ) {
  2231. goto Cleanup;
  2232. }
  2233. CacheResponse = (PMSV1_0_CACHE_LOOKUP_RESPONSE) ClientBufferDesc.MsvBuffer;
  2234. //
  2235. // Fill in the return buffer.
  2236. //
  2237. CacheResponse->MessageType = MsV1_0CacheLookup;
  2238. CacheResponse->ValidationInformation = ValidationInfo;
  2239. CacheResponse->SupplementalCacheData = SupplementalCacheData;
  2240. CacheResponse->SupplementalCacheDataLength = SupplementalCacheDataLength;
  2241. //
  2242. // Flush the buffer to the client's address space.
  2243. //
  2244. Status = NlpFlushClientBuffer( &ClientBufferDesc,
  2245. ProtocolReturnBuffer );
  2246. Cleanup:
  2247. if ( !NT_SUCCESS(Status)) {
  2248. NlpFreeClientBuffer( &ClientBufferDesc );
  2249. if (ValidationInfo != NULL) {
  2250. MIDL_user_free( ValidationInfo );
  2251. }
  2252. if (SupplementalCacheData != NULL) {
  2253. MIDL_user_free( SupplementalCacheData );
  2254. }
  2255. }
  2256. RtlSecureZeroMemory( &ComputedNtOwfPassword, sizeof( ComputedNtOwfPassword ) );
  2257. RtlSecureZeroMemory( &cachePasswords, sizeof(cachePasswords) );
  2258. return(STATUS_SUCCESS);
  2259. UNREFERENCED_PARAMETER( ClientBufferBase);
  2260. }
  2261. NTSTATUS
  2262. MspSetProcessOption(
  2263. IN PLSA_CLIENT_REQUEST ClientRequest,
  2264. IN PVOID ProtocolSubmitBuffer,
  2265. IN PVOID ClientBufferBase,
  2266. IN ULONG SubmitBufferSize,
  2267. OUT PVOID *ProtocolReturnBuffer,
  2268. OUT PULONG ReturnBufferSize,
  2269. OUT PNTSTATUS ProtocolStatus
  2270. )
  2271. /*++
  2272. Routine Description:
  2273. This routine is the dispatch routine for LsaCallAuthenticationPackage()
  2274. with a message type of MsV1_0SetProcessOption.
  2275. Arguments:
  2276. The arguments to this routine are identical to those of LsaApCallPackage.
  2277. Only the special attributes of these parameters as they apply to
  2278. this routine are mentioned here.
  2279. Return Value:
  2280. STATUS_SUCCESS - Indicates the service completed successfully.
  2281. STATUS_QUOTA_EXCEEDED - This error indicates that the logon
  2282. could not be completed because the client does not have
  2283. sufficient quota to allocate the return buffer.
  2284. --*/
  2285. {
  2286. NTSTATUS Status = STATUS_SUCCESS;
  2287. PMSV1_0_SETPROCESSOPTION_REQUEST SetProcessOptionRequest;
  2288. *ProtocolStatus = STATUS_UNSUCCESSFUL;
  2289. UNREFERENCED_PARAMETER(ClientBufferBase);
  2290. UNREFERENCED_PARAMETER(ReturnBufferSize);
  2291. UNREFERENCED_PARAMETER(ProtocolReturnBuffer);
  2292. UNREFERENCED_PARAMETER(ClientRequest);
  2293. //
  2294. // Ensure the specified Submit Buffer is of reasonable size and
  2295. // relocate all of the pointers to be relative to the LSA allocated
  2296. // buffer.
  2297. //
  2298. if ( SubmitBufferSize < sizeof(MSV1_0_SETPROCESSOPTION_REQUEST) ) {
  2299. Status = STATUS_INVALID_PARAMETER;
  2300. goto Cleanup;
  2301. }
  2302. SetProcessOptionRequest = (PMSV1_0_SETPROCESSOPTION_REQUEST) ProtocolSubmitBuffer;
  2303. if( NtLmSetProcessOption(
  2304. SetProcessOptionRequest->ProcessOptions,
  2305. SetProcessOptionRequest->DisableOptions
  2306. ) )
  2307. {
  2308. *ProtocolStatus = STATUS_SUCCESS;
  2309. }
  2310. Status = STATUS_SUCCESS;
  2311. Cleanup:
  2312. return(Status);
  2313. }
  2314. NTSTATUS
  2315. LsaApLogonUserEx2 (
  2316. IN PLSA_CLIENT_REQUEST ClientRequest,
  2317. IN SECURITY_LOGON_TYPE LogonType,
  2318. IN PVOID ProtocolSubmitBuffer,
  2319. IN PVOID ClientBufferBase,
  2320. IN ULONG SubmitBufferSize,
  2321. OUT PVOID *ProfileBuffer,
  2322. OUT PULONG ProfileBufferSize,
  2323. OUT PLUID LogonId,
  2324. OUT PNTSTATUS SubStatus,
  2325. OUT PLSA_TOKEN_INFORMATION_TYPE TokenInformationType,
  2326. OUT PVOID *TokenInformation,
  2327. OUT PUNICODE_STRING *AccountName,
  2328. OUT PUNICODE_STRING *AuthenticatingAuthority,
  2329. OUT PUNICODE_STRING *MachineName,
  2330. OUT PSECPKG_PRIMARY_CRED PrimaryCredentials,
  2331. OUT PSECPKG_SUPPLEMENTAL_CRED_ARRAY * SupplementalCredentials
  2332. )
  2333. /*++
  2334. Routine Description:
  2335. This routine is used to authenticate a user logon attempt. This is
  2336. the user's initial logon. A new LSA logon session will be established
  2337. for the user and validation information for the user will be returned.
  2338. Arguments:
  2339. ClientRequest - Is a pointer to an opaque data structure
  2340. representing the client's request.
  2341. LogonType - Identifies the type of logon being attempted.
  2342. ProtocolSubmitBuffer - Supplies the authentication
  2343. information specific to the authentication package.
  2344. ClientBufferBase - Provides the address within the client
  2345. process at which the authentication information was resident.
  2346. This may be necessary to fix-up any pointers within the
  2347. authentication information buffer.
  2348. SubmitBufferSize - Indicates the Size, in bytes,
  2349. of the authentication information buffer.
  2350. ProfileBuffer - Is used to return the address of the profile
  2351. buffer in the client process. The authentication package is
  2352. responsible for allocating and returning the profile buffer
  2353. within the client process. However, if the LSA subsequently
  2354. encounters an error which prevents a successful logon, then
  2355. the LSA will take care of deallocating that buffer. This
  2356. buffer is expected to have been allocated with the
  2357. AllocateClientBuffer() service.
  2358. The format and semantics of this buffer are specific to the
  2359. authentication package.
  2360. ProfileBufferSize - Receives the Size (in bytes) of the
  2361. returned profile buffer.
  2362. SubStatus - If the logon failed due to account restrictions, the
  2363. reason for the failure should be returned via this parameter.
  2364. The reason is authentication-package specific. The substatus
  2365. values for authentication package "MSV1.0" are:
  2366. STATUS_INVALID_LOGON_HOURS
  2367. STATUS_INVALID_WORKSTATION
  2368. STATUS_PASSWORD_EXPIRED
  2369. STATUS_ACCOUNT_DISABLED
  2370. TokenInformationLevel - If the logon is successful, this field is
  2371. used to indicate what level of information is being returned
  2372. for inclusion in the Token to be created. This information
  2373. is returned via the TokenInformation parameter.
  2374. TokenInformation - If the logon is successful, this parameter is
  2375. used by the authentication package to return information to
  2376. be included in the token. The format and content of the
  2377. buffer returned is indicated by the TokenInformationLevel
  2378. return value.
  2379. AccountName - A Unicode string describing the account name
  2380. being logged on to. This parameter must always be returned
  2381. regardless of the success or failure of the operation.
  2382. AuthenticatingAuthority - A Unicode string describing the Authenticating
  2383. Authority for the logon. This string may optionally be omitted.
  2384. PrimaryCredentials - Returns primary credentials for handing to other
  2385. packages.
  2386. SupplementalCredentials - Array of supplemental credential blobs for
  2387. other packages.
  2388. Return Value:
  2389. STATUS_SUCCESS - Indicates the service completed successfully.
  2390. STATUS_QUOTA_EXCEEDED - This error indicates that the logon
  2391. could not be completed because the client does not have
  2392. sufficient quota to allocate the return buffer.
  2393. STATUS_NO_LOGON_SERVERS - Indicates that no domain controllers
  2394. are currently able to service the authentication request.
  2395. STATUS_LOGON_FAILURE - Indicates the logon attempt failed. No
  2396. indication as to the reason for failure is given, but typical
  2397. reasons include mispelled usernames, mispelled passwords.
  2398. STATUS_ACCOUNT_RESTRICTION - Indicates the user account and
  2399. password were legitimate, but that the user account has some
  2400. restriction preventing successful logon at this time.
  2401. STATUS_BAD_VALIDATION_CLASS - The authentication information
  2402. provided is not a validation class known to the specified
  2403. authentication package.
  2404. STATUS_INVALID_LOGON_CLASS - LogonType was invalid.
  2405. STATUS_LOGON_SESSION_COLLISION- Internal Error: A LogonId was selected for
  2406. this logon session. The selected LogonId already exists.
  2407. STATUS_NETLOGON_NOT_STARTED - The Sam Server or Netlogon service was
  2408. required to perform this function. The required server was not running.
  2409. STATUS_NO_MEMORY - Insufficient virtual memory or pagefile quota exists.
  2410. --*/
  2411. {
  2412. NTSTATUS Status = STATUS_SUCCESS;
  2413. LSA_TOKEN_INFORMATION_TYPE LsaTokenInformationType = LsaTokenInformationV2;
  2414. PNETLOGON_VALIDATION_SAM_INFO4 NlpUser = NULL;
  2415. ULONG ActiveLogonEntrySize;
  2416. PACTIVE_LOGON pActiveLogonEntry = NULL;
  2417. BOOLEAN bLogonEntryLinked = FALSE;
  2418. BOOLEAN LogonSessionCreated = FALSE;
  2419. BOOLEAN LogonCredentialAdded = FALSE;
  2420. ULONG Flags = 0;
  2421. BOOLEAN Authoritative = FALSE;
  2422. BOOLEAN BadPasswordCountZeroed;
  2423. BOOLEAN StandaloneWorkstation = FALSE;
  2424. PSID UserSid = NULL;
  2425. PMSV1_0_PRIMARY_CREDENTIAL Credential = NULL;
  2426. ULONG CredentialSize = 0;
  2427. PSECURITY_SEED_AND_LENGTH SeedAndLength;
  2428. UCHAR Seed;
  2429. PUNICODE_STRING WorkStationName = NULL;
  2430. // Need to figure out whether to delete the profile buffer
  2431. BOOLEAN fSubAuthEx = FALSE;
  2432. //
  2433. // deferred NTLM3 checks.
  2434. //
  2435. BOOLEAN fNtLm3 = FALSE;
  2436. //
  2437. // Whether to wait for network & netlogon. If we are attempting
  2438. // forced cached credentials logon, we will avoid doing so.
  2439. //
  2440. BOOLEAN fWaitForNetwork = TRUE;
  2441. BOOLEAN CacheTried = FALSE;
  2442. //
  2443. // Temporary storage while we try to figure
  2444. // out what our username and authenticating
  2445. // authority is.
  2446. //
  2447. UNICODE_STRING TmpName = { 0, 0, NULL };
  2448. WCHAR TmpNameBuffer[UNLEN];
  2449. UNICODE_STRING TmpAuthority = { 0, 0, NULL };
  2450. WCHAR TmpAuthorityBuffer[DNS_MAX_NAME_LENGTH];
  2451. //
  2452. // Logon Information.
  2453. //
  2454. NETLOGON_LOGON_INFO_CLASS LogonLevel = 0;
  2455. NETLOGON_INTERACTIVE_INFO LogonInteractive;
  2456. NETLOGON_NETWORK_INFO LogonNetwork = {0};
  2457. PNETLOGON_LOGON_IDENTITY_INFO LogonInformation = NULL;
  2458. PMSV1_0_LM20_LOGON NetworkAuthentication = NULL;
  2459. //
  2460. // Secret information, if we are doing a service logon
  2461. //
  2462. LSAPR_HANDLE SecretHandle;
  2463. PLSAPR_CR_CIPHER_VALUE SecretCurrent = NULL;
  2464. UNICODE_STRING Prefix, SavedPassword = {0};
  2465. BOOLEAN ServiceSecretLogon = FALSE;
  2466. PMSV1_0_INTERACTIVE_LOGON Authentication = NULL;
  2467. //
  2468. // Credential manager stored credentials.
  2469. //
  2470. UNICODE_STRING CredmanUserName = {0, 0, NULL };
  2471. UNICODE_STRING CredmanDomainName = {0, 0, NULL };
  2472. UNICODE_STRING CredmanPassword = {0, 0, NULL };
  2473. BOOLEAN TryCacheFirst = FALSE;
  2474. NTSTATUS NetlogonStatus = STATUS_SUCCESS;
  2475. LM_RESPONSE LmResponse = {0};
  2476. NT_RESPONSE NtResponse = {0};
  2477. //
  2478. // interactive cached logon users can be mapped, therefore accountname
  2479. // can differ from credentails username. used to build primary credentials
  2480. //
  2481. UNICODE_STRING CredentialUserName = {0};
  2482. UNICODE_STRING CredentialDomainName = {0};
  2483. PUNICODE_STRING CredentialUserToUse = NULL;
  2484. PUNICODE_STRING CredentialDomainToUse = NULL;
  2485. //
  2486. // WMI tracing helper struct
  2487. //
  2488. NTLM_TRACE_INFO TraceInfo = {0};
  2489. #if _WIN64
  2490. PVOID pTempSubmitBuffer = ProtocolSubmitBuffer;
  2491. SECPKG_CALL_INFO CallInfo;
  2492. BOOL fAllocatedSubmitBuffer = FALSE;
  2493. if ( ClientRequest == (PLSA_CLIENT_REQUEST)( -1 ) )
  2494. {
  2495. //
  2496. // if the call originated inproc, the buffers have already been
  2497. // marshalled/etc.
  2498. //
  2499. ZeroMemory( &CallInfo, sizeof(CallInfo) );
  2500. } else {
  2501. if (!LsaFunctions->GetCallInfo(&CallInfo))
  2502. {
  2503. Status = STATUS_INTERNAL_ERROR;
  2504. goto Cleanup;
  2505. }
  2506. }
  2507. #endif
  2508. //
  2509. // CachedInteractive logons are treated same as Interactive except
  2510. // that we avoid hitting the network.
  2511. //
  2512. if (LogonType == CachedInteractive) {
  2513. fWaitForNetwork = FALSE;
  2514. LogonType = Interactive;
  2515. }
  2516. //
  2517. // Begin tracing a logon user
  2518. //
  2519. if (NtlmGlobalEventTraceFlag){
  2520. //
  2521. // Trace header goo
  2522. //
  2523. SET_TRACE_HEADER(TraceInfo,
  2524. NtlmLogonGuid,
  2525. EVENT_TRACE_TYPE_START,
  2526. WNODE_FLAG_TRACED_GUID,
  2527. 0);
  2528. TraceEvent(NtlmGlobalTraceLoggerHandle,
  2529. (PEVENT_TRACE_HEADER)&TraceInfo);
  2530. }
  2531. //
  2532. // Initialize
  2533. //
  2534. *ProfileBuffer = NULL;
  2535. *SubStatus = STATUS_SUCCESS;
  2536. *AuthenticatingAuthority = NULL;
  2537. *AccountName = NULL;
  2538. TmpName.Buffer = TmpNameBuffer;
  2539. TmpName.MaximumLength = UNLEN * sizeof( WCHAR );
  2540. TmpName.Length = 0;
  2541. TmpAuthority.Buffer = TmpAuthorityBuffer;
  2542. TmpAuthority.MaximumLength = DNS_MAX_NAME_LENGTH * sizeof( WCHAR );
  2543. TmpAuthority.Length = 0;
  2544. CredmanUserName.Buffer = NULL;
  2545. CredmanDomainName.Buffer = NULL;
  2546. CredmanPassword.Buffer = NULL;
  2547. *SupplementalCredentials = 0;
  2548. RtlZeroMemory(
  2549. PrimaryCredentials,
  2550. sizeof(SECPKG_PRIMARY_CRED)
  2551. );
  2552. //
  2553. // Check the Authentication information and build a LogonInformation
  2554. // structure to pass to SAM or Netlogon.
  2555. //
  2556. // NOTE: Netlogon treats Service and Batch logons as if they are
  2557. // Interactive.
  2558. //
  2559. switch ( LogonType ) {
  2560. case Service:
  2561. case Interactive:
  2562. case Batch:
  2563. case NetworkCleartext:
  2564. case RemoteInteractive:
  2565. {
  2566. MSV1_0_PRIMARY_CREDENTIAL BuiltCredential;
  2567. #if _WIN64
  2568. //
  2569. // Expand the ProtocolSubmitBuffer to 64-bit pointers if this
  2570. // call came from a WOW client.
  2571. //
  2572. if (CallInfo.Attributes & SECPKG_CALL_WOWCLIENT)
  2573. {
  2574. Authentication =
  2575. (PMSV1_0_INTERACTIVE_LOGON) ProtocolSubmitBuffer;
  2576. Status = MsvConvertWOWInteractiveLogonBuffer(
  2577. ProtocolSubmitBuffer,
  2578. ClientBufferBase,
  2579. &SubmitBufferSize,
  2580. &pTempSubmitBuffer
  2581. );
  2582. if (!NT_SUCCESS(Status))
  2583. {
  2584. goto Cleanup;
  2585. }
  2586. fAllocatedSubmitBuffer = TRUE;
  2587. //
  2588. // Some macros below expand out to use ProtocolSubmitBuffer directly.
  2589. // We've secretly replaced their usual ProtocolSubmitBuffer with
  2590. // pTempSubmitBuffer -- let's see if they can tell the difference.
  2591. //
  2592. ProtocolSubmitBuffer = pTempSubmitBuffer;
  2593. }
  2594. #endif // _WIN64
  2595. WorkStationName = &NlpComputerName;
  2596. //
  2597. // Ensure this is really an interactive logon.
  2598. //
  2599. Authentication =
  2600. (PMSV1_0_INTERACTIVE_LOGON) ProtocolSubmitBuffer;
  2601. if (SubmitBufferSize < sizeof(MSV1_0_INTERACTIVE_LOGON)) {
  2602. SspPrint((SSP_CRITICAL, "LsaApLogonUser: bogus interactive logon info size %#x\n", SubmitBufferSize));
  2603. Status = STATUS_INVALID_PARAMETER;
  2604. goto Cleanup;
  2605. }
  2606. if ( (Authentication->MessageType != MsV1_0InteractiveLogon ) &&
  2607. (Authentication->MessageType != MsV1_0WorkstationUnlockLogon) ) {
  2608. SspPrint((SSP_CRITICAL, "LsaApLogonUser: Bad Validation Class %d\n", Authentication->MessageType));
  2609. Status = STATUS_BAD_VALIDATION_CLASS;
  2610. goto Cleanup;
  2611. }
  2612. //
  2613. // If the password length is greater than 255 (i.e., the
  2614. // upper byte of the length is non-zero) then the password
  2615. // has been run-encoded for privacy reasons. Get the
  2616. // run-encode seed out of the upper-byte of the length
  2617. // for later use.
  2618. //
  2619. //
  2620. SeedAndLength = (PSECURITY_SEED_AND_LENGTH)
  2621. &Authentication->Password.Length;
  2622. Seed = SeedAndLength->Seed;
  2623. SeedAndLength->Seed = 0;
  2624. //
  2625. // Enforce length restrictions on username and password.
  2626. //
  2627. if ( Authentication->UserName.Length > (UNLEN*sizeof(WCHAR)) ||
  2628. Authentication->Password.Length > (PWLEN*sizeof(WCHAR)) )
  2629. {
  2630. SspPrint((SSP_CRITICAL, "LsaApLogonUser: Name or password too long\n"));
  2631. Status = STATUS_NAME_TOO_LONG;
  2632. goto Cleanup;
  2633. }
  2634. //
  2635. // Relocate any pointers to be relative to 'Authentication'
  2636. //
  2637. NULL_RELOCATE_ONE( &Authentication->LogonDomainName );
  2638. RELOCATE_ONE( &Authentication->UserName );
  2639. NULL_RELOCATE_ONE( &Authentication->Password );
  2640. if ( (Authentication->LogonDomainName.Length <= sizeof(WCHAR)) &&
  2641. (Authentication->Password.Length <= sizeof(WCHAR))
  2642. )
  2643. {
  2644. Status = CredpProcessUserNameCredential(
  2645. &Authentication->UserName,
  2646. &CredmanUserName,
  2647. &CredmanDomainName,
  2648. &CredmanPassword
  2649. );
  2650. if ( NT_SUCCESS(Status) )
  2651. {
  2652. Authentication->UserName = CredmanUserName;
  2653. Authentication->LogonDomainName = CredmanDomainName;
  2654. Authentication->Password = CredmanPassword;
  2655. } else if (Status == STATUS_NOT_SUPPORTED)
  2656. {
  2657. SspPrint((SSP_CRITICAL, "LsaApLogonUser: unsupported marshalled cred\n"));
  2658. goto Cleanup;
  2659. }
  2660. Status = STATUS_SUCCESS;
  2661. }
  2662. #if 0
  2663. //
  2664. // Handle UPN and composite NETBIOS syntax
  2665. //
  2666. {
  2667. UNICODE_STRING User = Authentication->UserName;
  2668. UNICODE_STRING Domain = Authentication->LogonDomainName;
  2669. Status =
  2670. NtLmParseName(
  2671. &User,
  2672. &Domain,
  2673. FALSE
  2674. );
  2675. if(NT_SUCCESS(Status)){
  2676. Authentication->UserName = User;
  2677. Authentication->LogonDomainName = Domain;
  2678. }
  2679. }
  2680. #endif
  2681. if ( LogonType == Service )
  2682. {
  2683. SECPKG_CALL_INFO CallInfo;
  2684. if ( LsaFunctions->GetCallInfo(&CallInfo) &&
  2685. (CallInfo.Attributes & SECPKG_CALL_IS_TCB) )
  2686. {
  2687. //
  2688. // If we have a service logon, the password we got is likely the name of the secret
  2689. // that is holding the account password. Make sure to read that secret here
  2690. //
  2691. RtlInitUnicodeString( &Prefix, L"_SC_" );
  2692. if ( RtlPrefixUnicodeString( &Prefix, &Authentication->Password, TRUE ) )
  2693. {
  2694. Status = LsarOpenSecret( NtLmGlobalPolicyHandle,
  2695. ( PLSAPR_UNICODE_STRING )&Authentication->Password,
  2696. SECRET_QUERY_VALUE,
  2697. &SecretHandle );
  2698. if ( NT_SUCCESS( Status ) )
  2699. {
  2700. Status = LsarQuerySecret( SecretHandle,
  2701. &SecretCurrent,
  2702. NULL,
  2703. NULL,
  2704. NULL );
  2705. if ( NT_SUCCESS( Status ) && (SecretCurrent != NULL) )
  2706. {
  2707. RtlCopyMemory( &SavedPassword,
  2708. &Authentication->Password,
  2709. sizeof( UNICODE_STRING ) );
  2710. Authentication->Password.Length = ( USHORT )SecretCurrent->Length;
  2711. Authentication->Password.MaximumLength =
  2712. ( USHORT )SecretCurrent->MaximumLength;
  2713. Authentication->Password.Buffer = ( USHORT * )SecretCurrent->Buffer;
  2714. ServiceSecretLogon = TRUE;
  2715. Seed = 0; // do not run RtlRunDecodeUnicodeString for this password
  2716. }
  2717. LsarClose( &SecretHandle );
  2718. }
  2719. }
  2720. }
  2721. if ( !NT_SUCCESS( Status ) ) {
  2722. SspPrint((SSP_CRITICAL, "LsaApLogonUser: failed to querying service password\n"));
  2723. goto Cleanup;
  2724. }
  2725. }
  2726. //
  2727. // Now decode the password, if necessary
  2728. //
  2729. if (Seed != 0) {
  2730. try {
  2731. RtlRunDecodeUnicodeString( Seed, &Authentication->Password);
  2732. } except(EXCEPTION_EXECUTE_HANDLER) {
  2733. SspPrint((SSP_CRITICAL, "LsaApLogonUser: failed to decode password\n"));
  2734. Status = STATUS_ILL_FORMED_PASSWORD;
  2735. goto Cleanup;
  2736. }
  2737. }
  2738. //
  2739. // Copy out the user name and Authenticating Authority so we can audit them.
  2740. //
  2741. RtlCopyUnicodeString( &TmpName, &Authentication->UserName );
  2742. if ( Authentication->LogonDomainName.Buffer != NULL ) {
  2743. RtlCopyUnicodeString( &TmpAuthority, &Authentication->LogonDomainName );
  2744. }
  2745. //
  2746. // Put the password in the PrimaryCredential to pass to the sundry security packages.
  2747. //
  2748. PrimaryCredentials->Password.Length = PrimaryCredentials->Password.MaximumLength =
  2749. Authentication->Password.Length;
  2750. PrimaryCredentials->Password.Buffer = (*Lsa.AllocateLsaHeap)(Authentication->Password.Length);
  2751. if (PrimaryCredentials->Password.Buffer == NULL) {
  2752. Status = STATUS_INSUFFICIENT_RESOURCES;
  2753. goto Cleanup;
  2754. }
  2755. RtlCopyMemory(
  2756. PrimaryCredentials->Password.Buffer,
  2757. Authentication->Password.Buffer,
  2758. Authentication->Password.Length
  2759. );
  2760. PrimaryCredentials->Flags = PRIMARY_CRED_CLEAR_PASSWORD;
  2761. //
  2762. // We're all done with the cleartext password
  2763. // Don't let it get to the pagefile.
  2764. //
  2765. try {
  2766. if ( Authentication->Password.Buffer != NULL ) {
  2767. RtlEraseUnicodeString( &Authentication->Password );
  2768. }
  2769. } except(EXCEPTION_EXECUTE_HANDLER) {
  2770. SspPrint((SSP_CRITICAL, "LsaApLogonUser: failed to decode password\n"));
  2771. Status = STATUS_ILL_FORMED_PASSWORD;
  2772. goto Cleanup;
  2773. }
  2774. //
  2775. // Compute the OWF of the password.
  2776. //
  2777. NlpPutOwfsInPrimaryCredential( &PrimaryCredentials->Password,
  2778. (BOOLEAN) (PrimaryCredentials->Flags & PRIMARY_CRED_OWF_PASSWORD),
  2779. &BuiltCredential );
  2780. //
  2781. // Define the description of the user to log on.
  2782. //
  2783. LogonLevel = NetlogonInteractiveInformation;
  2784. LogonInformation =
  2785. (PNETLOGON_LOGON_IDENTITY_INFO) &LogonInteractive;
  2786. LogonInteractive.Identity.LogonDomainName =
  2787. Authentication->LogonDomainName;
  2788. LogonInteractive.Identity.ParameterControl = 0;
  2789. LogonInteractive.Identity.UserName = Authentication->UserName;
  2790. LogonInteractive.Identity.Workstation = NlpComputerName;
  2791. LogonInteractive.LmOwfPassword = BuiltCredential.LmOwfPassword;
  2792. LogonInteractive.NtOwfPassword = BuiltCredential.NtOwfPassword;
  2793. RtlSecureZeroMemory(&BuiltCredential, sizeof(BuiltCredential));
  2794. }
  2795. break;
  2796. case Network:
  2797. {
  2798. PMSV1_0_LM20_LOGON Authentication;
  2799. BOOLEAN EnforceTcb = FALSE;
  2800. //
  2801. // Expand the ProtocolSubmitBuffer to 64-bit pointers if this
  2802. // call came from a WOW client.
  2803. //
  2804. #if _WIN64
  2805. if (CallInfo.Attributes & SECPKG_CALL_WOWCLIENT)
  2806. {
  2807. Authentication =
  2808. (PMSV1_0_LM20_LOGON) ProtocolSubmitBuffer;
  2809. Status = MsvConvertWOWNetworkLogonBuffer(
  2810. ProtocolSubmitBuffer,
  2811. ClientBufferBase,
  2812. &SubmitBufferSize,
  2813. &pTempSubmitBuffer
  2814. );
  2815. if (!NT_SUCCESS(Status))
  2816. {
  2817. goto Cleanup;
  2818. }
  2819. fAllocatedSubmitBuffer = TRUE;
  2820. //
  2821. // Some macros below expand out to use ProtocolSubmitBuffer directly.
  2822. // We've secretly replaced their usual ProtocolSubmitBuffer with
  2823. // pTempSubmitBuffer -- let's see if they can tell the difference.
  2824. //
  2825. ProtocolSubmitBuffer = pTempSubmitBuffer;
  2826. }
  2827. #endif
  2828. //
  2829. // Ensure this is really a network logon request.
  2830. //
  2831. Authentication =
  2832. (PMSV1_0_LM20_LOGON) ProtocolSubmitBuffer;
  2833. NetworkAuthentication = Authentication;
  2834. if (SubmitBufferSize < sizeof(MSV1_0_LM20_LOGON)) {
  2835. SspPrint((SSP_CRITICAL, "LsaApLogonUser: bogus network logon info size %#x\n", SubmitBufferSize));
  2836. Status = STATUS_INVALID_PARAMETER;
  2837. goto Cleanup;
  2838. }
  2839. if ( Authentication->MessageType != MsV1_0Lm20Logon &&
  2840. Authentication->MessageType != MsV1_0SubAuthLogon &&
  2841. Authentication->MessageType != MsV1_0NetworkLogon )
  2842. {
  2843. SspPrint((SSP_CRITICAL, "LsaApLogonUser: Bad Validation Class\n"));
  2844. Status = STATUS_BAD_VALIDATION_CLASS;
  2845. goto Cleanup;
  2846. }
  2847. //
  2848. // Relocate any pointers to be relative to 'Authentication'
  2849. //
  2850. NULL_RELOCATE_ONE( &Authentication->LogonDomainName );
  2851. NULL_RELOCATE_ONE( &Authentication->UserName );
  2852. RELOCATE_ONE( &Authentication->Workstation );
  2853. #if 0
  2854. //
  2855. // Handle UPN and composite NETBIOS syntax
  2856. //
  2857. {
  2858. UNICODE_STRING User = Authentication->UserName;
  2859. UNICODE_STRING Domain = Authentication->LogonDomainName;
  2860. Status =
  2861. NtLmParseName(
  2862. &User,
  2863. &Domain,
  2864. FALSE
  2865. );
  2866. if(NT_SUCCESS(Status)){
  2867. Authentication->UserName = User;
  2868. Authentication->LogonDomainName = Domain;
  2869. }
  2870. }
  2871. #endif
  2872. //
  2873. // Copy out the user name and Authenticating Authority so we can audit them.
  2874. //
  2875. if ( Authentication->UserName.Buffer != NULL ) {
  2876. RtlCopyUnicodeString( &TmpName, &Authentication->UserName );
  2877. }
  2878. if ( Authentication->LogonDomainName.Buffer != NULL ) {
  2879. RtlCopyUnicodeString( &TmpAuthority, &Authentication->LogonDomainName );
  2880. }
  2881. NULL_RELOCATE_ONE((PUNICODE_STRING)&Authentication->CaseSensitiveChallengeResponse );
  2882. NULL_RELOCATE_ONE((PUNICODE_STRING)&Authentication->CaseInsensitiveChallengeResponse );
  2883. //
  2884. // Define the description of the user to log on.
  2885. //
  2886. LogonLevel = NetlogonNetworkInformation;
  2887. LogonInformation =
  2888. (PNETLOGON_LOGON_IDENTITY_INFO) &LogonNetwork;
  2889. LogonNetwork.Identity.LogonDomainName =
  2890. Authentication->LogonDomainName;
  2891. if ( Authentication->ParameterControl & MSV1_0_CLEARTEXT_PASSWORD_SUPPLIED )
  2892. {
  2893. NT_OWF_PASSWORD NtOwfPassword;
  2894. LM_OWF_PASSWORD LmOwfPassword;
  2895. CHAR LmPassword[LM20_PWLEN + 1] = {0};
  2896. UCHAR Challenge[MSV1_0_CHALLENGE_LENGTH] = {0};
  2897. ULONG LmProtocolSupported = NtLmGlobalLmProtocolSupported;
  2898. //
  2899. // trash the challenge, to avoid allowing a challenge/response
  2900. // replay through this (untrusted) interface.
  2901. //
  2902. Status = SspGenerateRandomBits(Authentication->ChallengeToClient, sizeof(Authentication->ChallengeToClient));
  2903. if (NT_SUCCESS(Status))
  2904. {
  2905. SspPrint((SSP_WARNING, "LsaApLogonUserEx2: ClearText password supplied, ChallengeToClient trashed\n"));
  2906. Authentication->ParameterControl &= ~MSV1_0_USE_CLIENT_CHALLENGE;
  2907. RtlCopyMemory(
  2908. LmPassword,
  2909. Authentication->CaseInsensitiveChallengeResponse.Buffer,
  2910. min(LM20_PWLEN, Authentication->CaseInsensitiveChallengeResponse.Length)
  2911. );
  2912. Status = RtlCalculateLmOwfPassword(LmPassword, &LmOwfPassword);
  2913. }
  2914. if (NT_SUCCESS(Status))
  2915. {
  2916. Status = RtlCalculateNtOwfPassword(
  2917. (UNICODE_STRING*) &Authentication->CaseSensitiveChallengeResponse,
  2918. &NtOwfPassword
  2919. );
  2920. }
  2921. if (NT_SUCCESS(Status))
  2922. {
  2923. if (LmProtocolSupported < NoLm)
  2924. {
  2925. Status = RtlCalculateLmResponse(
  2926. (PLM_CHALLENGE) Authentication->ChallengeToClient,
  2927. &LmOwfPassword,
  2928. &LmResponse
  2929. );
  2930. RtlCopyMemory(Challenge, Authentication->ChallengeToClient, sizeof(Challenge));
  2931. }
  2932. else if (LmProtocolSupported == NoLm)
  2933. {
  2934. Authentication->ParameterControl |= MSV1_0_USE_CLIENT_CHALLENGE;
  2935. Status = SspGenerateRandomBits(&LmResponse, MSV1_0_CHALLENGE_LENGTH);
  2936. if (NT_SUCCESS(Status))
  2937. {
  2938. MsvpCalculateNtlm2Challenge(
  2939. Authentication->ChallengeToClient,
  2940. (UCHAR*) &LmResponse,
  2941. Challenge
  2942. );
  2943. }
  2944. }
  2945. else if (LmProtocolSupported >= UseNtlm3)
  2946. {
  2947. MsvpLm3Response(
  2948. &NtOwfPassword,
  2949. &Authentication->UserName,
  2950. &Authentication->LogonDomainName,
  2951. Authentication->ChallengeToClient,
  2952. (MSV1_0_LM3_RESPONSE*) &LmResponse,
  2953. (UCHAR*) &LmResponse,
  2954. NULL,
  2955. NULL
  2956. );
  2957. }
  2958. }
  2959. if (NT_SUCCESS(Status))
  2960. {
  2961. Authentication->ParameterControl &= ~(MSV1_0_CLEARTEXT_PASSWORD_SUPPLIED | MSV1_0_CLEARTEXT_PASSWORD_ALLOWED);
  2962. Authentication->CaseInsensitiveChallengeResponse.MaximumLength =
  2963. Authentication->CaseInsensitiveChallengeResponse.Length = sizeof(LmResponse);
  2964. Authentication->CaseInsensitiveChallengeResponse.Buffer = (CHAR*) &LmResponse;
  2965. if (LmProtocolSupported < UseNtlm3)
  2966. {
  2967. Status = RtlCalculateNtResponse(
  2968. (PNT_CHALLENGE) Challenge,
  2969. &NtOwfPassword,
  2970. &NtResponse
  2971. );
  2972. if (NT_SUCCESS(Status))
  2973. {
  2974. Authentication->CaseSensitiveChallengeResponse.MaximumLength =
  2975. Authentication->CaseSensitiveChallengeResponse.Length = sizeof(NtResponse);
  2976. Authentication->CaseSensitiveChallengeResponse.Buffer = (CHAR*) &NtResponse;
  2977. }
  2978. }
  2979. else
  2980. {
  2981. Authentication->CaseSensitiveChallengeResponse.MaximumLength =
  2982. Authentication->CaseSensitiveChallengeResponse.Length = 0;
  2983. }
  2984. }
  2985. RtlSecureZeroMemory(&NtOwfPassword, sizeof(NtOwfPassword));
  2986. RtlSecureZeroMemory(&LmOwfPassword, sizeof(LmOwfPassword));
  2987. RtlSecureZeroMemory(LmPassword, sizeof(LmPassword));
  2988. if (!NT_SUCCESS(Status))
  2989. {
  2990. SspPrint((SSP_CRITICAL, "LsaApLogonUserEx2 failed cleartext logon\n"));
  2991. goto Cleanup;
  2992. }
  2993. } else {
  2994. //
  2995. // if cleartext was not supplied, caller must be trusted for inproc calls.
  2996. //
  2997. if ( ClientRequest != (PLSA_CLIENT_REQUEST)( -1 ) )
  2998. {
  2999. EnforceTcb = TRUE;
  3000. }
  3001. }
  3002. if ( Authentication->MessageType == MsV1_0Lm20Logon ) {
  3003. LogonNetwork.Identity.ParameterControl = MSV1_0_CLEARTEXT_PASSWORD_ALLOWED;
  3004. } else {
  3005. ASSERT( CLEARTEXT_PASSWORD_ALLOWED == MSV1_0_CLEARTEXT_PASSWORD_ALLOWED );
  3006. LogonNetwork.Identity.ParameterControl =
  3007. Authentication->ParameterControl;
  3008. // For NT 5.0 SubAuth Packages, there is a SubAuthPackageId. Stuff
  3009. // that into ParameterControl so pre 5.0 MsvSamValidate won't choke.
  3010. if ( Authentication->MessageType == MsV1_0SubAuthLogon )
  3011. {
  3012. PMSV1_0_SUBAUTH_LOGON SubAuthentication =
  3013. (PMSV1_0_SUBAUTH_LOGON) ProtocolSubmitBuffer;
  3014. // Need to not delete return buffers even in case of error
  3015. // for MsV1_0SubAuthLogon (includes arap).
  3016. fSubAuthEx = TRUE;
  3017. LogonNetwork.Identity.ParameterControl |=
  3018. (SubAuthentication->SubAuthPackageId << MSV1_0_SUBAUTHENTICATION_DLL_SHIFT) | MSV1_0_SUBAUTHENTICATION_DLL_EX;
  3019. EnforceTcb = TRUE ;
  3020. } else {
  3021. if ( Authentication->ParameterControl & MSV1_0_SUBAUTHENTICATION_DLL )
  3022. {
  3023. EnforceTcb = TRUE;
  3024. }
  3025. }
  3026. }
  3027. if ( EnforceTcb )
  3028. {
  3029. SECPKG_CALL_INFO CallInfo;
  3030. if (!LsaFunctions->GetCallInfo(&CallInfo) ||
  3031. (CallInfo.Attributes & SECPKG_CALL_IS_TCB) == 0)
  3032. {
  3033. SspPrint((SSP_CRITICAL, "LsaApLogonUser: subauth/chalresp caller isn't privileged\n"));
  3034. Status = STATUS_ACCESS_DENIED;
  3035. goto Cleanup;
  3036. }
  3037. }
  3038. LogonNetwork.Identity.UserName = Authentication->UserName;
  3039. LogonNetwork.Identity.Workstation = Authentication->Workstation;
  3040. WorkStationName = &Authentication->Workstation;
  3041. LogonNetwork.NtChallengeResponse =
  3042. Authentication->CaseSensitiveChallengeResponse;
  3043. LogonNetwork.LmChallengeResponse =
  3044. Authentication->CaseInsensitiveChallengeResponse;
  3045. ASSERT( LM_CHALLENGE_LENGTH ==
  3046. sizeof(Authentication->ChallengeToClient) );
  3047. //
  3048. // If using client challenge, then mix it with the server's challenge
  3049. // to get the challenge we pass on. It would make more sense to do this
  3050. // in MsvpPasswordValidate, except that would require the DCs to be upgraded.
  3051. // Doing it here only requires agreement between the client and server, because
  3052. // the modified challenge will be passed on to the DCs.
  3053. //
  3054. if ((Authentication->ParameterControl & MSV1_0_USE_CLIENT_CHALLENGE) &&
  3055. (Authentication->CaseSensitiveChallengeResponse.Length == NT_RESPONSE_LENGTH) &&
  3056. (Authentication->CaseInsensitiveChallengeResponse.Length >= MSV1_0_CHALLENGE_LENGTH))
  3057. {
  3058. MsvpCalculateNtlm2Challenge (
  3059. Authentication->ChallengeToClient,
  3060. (PUCHAR) Authentication->CaseInsensitiveChallengeResponse.Buffer,
  3061. (PUCHAR) &LogonNetwork.LmChallenge
  3062. );
  3063. } else {
  3064. RtlCopyMemory(
  3065. &LogonNetwork.LmChallenge,
  3066. Authentication->ChallengeToClient,
  3067. LM_CHALLENGE_LENGTH );
  3068. }
  3069. //
  3070. // if using NTLM3, then check that the target info is for this machine.
  3071. //
  3072. if ((Authentication->ParameterControl & MSV1_0_USE_CLIENT_CHALLENGE) &&
  3073. (Authentication->CaseSensitiveChallengeResponse.Length >= sizeof(MSV1_0_NTLM3_RESPONSE)))
  3074. {
  3075. fNtLm3 = TRUE;
  3076. //
  3077. // defer NTLM3 checks until later on when SAM initialized.
  3078. //
  3079. }
  3080. //
  3081. // Enforce length restrictions on username
  3082. //
  3083. if ( Authentication->UserName.Length > (UNLEN*sizeof(WCHAR)) )
  3084. {
  3085. SspPrint((SSP_CRITICAL, "LsaApLogonUser: Name too long\n"));
  3086. Status = STATUS_NAME_TOO_LONG;
  3087. goto Cleanup;
  3088. }
  3089. //
  3090. // If this is a null session logon,
  3091. // just build a NULL token.
  3092. //
  3093. if ( Authentication->UserName.Length == 0 &&
  3094. Authentication->CaseSensitiveChallengeResponse.Length == 0 &&
  3095. (Authentication->CaseInsensitiveChallengeResponse.Length == 0 ||
  3096. (Authentication->CaseInsensitiveChallengeResponse.Length == 1 &&
  3097. *Authentication->CaseInsensitiveChallengeResponse.Buffer == '\0') ) ) {
  3098. LsaTokenInformationType = LsaTokenInformationNull;
  3099. }
  3100. }
  3101. break;
  3102. default:
  3103. Status = STATUS_INVALID_LOGON_TYPE;
  3104. goto CleanupShort;
  3105. }
  3106. //
  3107. // Allocate a LogonId for this logon session.
  3108. //
  3109. Status = NtAllocateLocallyUniqueId( LogonId );
  3110. if ( !NT_SUCCESS( Status ) ) {
  3111. goto Cleanup;
  3112. }
  3113. NEW_TO_OLD_LARGE_INTEGER( (*LogonId), LogonInformation->LogonId );
  3114. PrimaryCredentials->LogonId = *LogonId;
  3115. PrimaryCredentials->Flags |= (RPC_C_AUTHN_WINNT << PRIMARY_CRED_LOGON_PACKAGE_SHIFT);
  3116. //
  3117. // Create a new logon session
  3118. //
  3119. Status = (*Lsa.CreateLogonSession)( LogonId );
  3120. if ( !NT_SUCCESS(Status) ) {
  3121. SspPrint((SSP_CRITICAL, "LsaApLogonUser: Collision from CreateLogonSession %x\n", Status));
  3122. goto Cleanup;
  3123. }
  3124. LogonSessionCreated = TRUE;
  3125. //
  3126. // Don't worry about SAM or the LSA if this is a Null Session logon.
  3127. //
  3128. // The server does a Null Session logon during initialization.
  3129. // It shouldn't have to wait for SAM to initialize.
  3130. //
  3131. if ( LsaTokenInformationType != LsaTokenInformationNull ) {
  3132. //
  3133. // If Sam is not yet initialized,
  3134. // do it now.
  3135. //
  3136. if ( !NlpSamInitialized ) {
  3137. Status = NlSamInitialize( SAM_STARTUP_TIME );
  3138. if ( !NT_SUCCESS(Status) ) {
  3139. goto Cleanup;
  3140. }
  3141. }
  3142. //
  3143. // If this is a workstation,
  3144. // differentiate between a standalone workstation and a member
  3145. // workstation.
  3146. //
  3147. // (This is is done on every logon, rather than during initialization,
  3148. // to allow the value to be changed via the UI).
  3149. //
  3150. if ( NlpWorkstation ) {
  3151. RtlAcquireResourceShared(&NtLmGlobalCritSect, TRUE);
  3152. StandaloneWorkstation = (BOOLEAN) (NtLmGlobalTargetFlags == NTLMSSP_TARGET_TYPE_SERVER);
  3153. RtlReleaseResource(&NtLmGlobalCritSect);
  3154. } else {
  3155. StandaloneWorkstation = FALSE;
  3156. }
  3157. }
  3158. //
  3159. // Try again to load netlogon.dll
  3160. //
  3161. if ( NlpNetlogonDllHandle == NULL ) {
  3162. NlpLoadNetlogonDll();
  3163. }
  3164. //
  3165. // do NTLM3 processing that was deferred until now due to initialization
  3166. // requirements.
  3167. //
  3168. if ( fNtLm3 )
  3169. {
  3170. PMSV1_0_AV_PAIR pAV;
  3171. PMSV1_0_NTLM3_RESPONSE pResp;
  3172. LONG iRespLen;
  3173. ULONG NtLmProtocolSupported = NtLmGlobalLmProtocolSupported;
  3174. //
  3175. // get the computer name from the response
  3176. //
  3177. pResp = (PMSV1_0_NTLM3_RESPONSE)
  3178. NetworkAuthentication->CaseSensitiveChallengeResponse.Buffer;
  3179. iRespLen = NetworkAuthentication->CaseSensitiveChallengeResponse.Length -
  3180. sizeof(MSV1_0_NTLM3_RESPONSE);
  3181. pAV = MsvpAvlGet((PMSV1_0_AV_PAIR)pResp->Buffer, MsvAvNbComputerName, iRespLen);
  3182. //
  3183. // if there is one (OK to be missing), see that it is us
  3184. // REVIEW -- only allow it to be missing if registry says OK?
  3185. //
  3186. if (pAV) {
  3187. UNICODE_STRING Candidate;
  3188. Candidate.Buffer = (PWSTR)(pAV+1);
  3189. Candidate.Length = (USHORT)(pAV->AvLen);
  3190. Candidate.MaximumLength = Candidate.Length;
  3191. if(!RtlEqualUnicodeString( &NlpComputerName, &Candidate, TRUE ))
  3192. {
  3193. SspPrint((SSP_WARNING, "LsaApLogonUserEx2 failed NbComputerName compare\n"));
  3194. Status = STATUS_LOGON_FAILURE;
  3195. goto Cleanup;
  3196. }
  3197. } else if (NtLmProtocolSupported >= RefuseNtlm3NoTarget) {
  3198. SspPrint((SSP_WARNING, "LsaApLogonUserEx2 no target supplied\n"));
  3199. Status = STATUS_LOGON_FAILURE;
  3200. goto Cleanup;
  3201. }
  3202. //
  3203. // get the domain name from the response
  3204. //
  3205. pAV = MsvpAvlGet((PMSV1_0_AV_PAIR)pResp->Buffer, MsvAvNbDomainName, iRespLen);
  3206. //
  3207. // must exist and must be us.
  3208. //
  3209. if (pAV) {
  3210. UNICODE_STRING Candidate;
  3211. Candidate.Buffer = (PWSTR)(pAV+1);
  3212. Candidate.Length = pAV->AvLen;
  3213. Candidate.MaximumLength = pAV->AvLen;
  3214. if( StandaloneWorkstation ) {
  3215. if( !RtlEqualDomainName(&NlpComputerName, &Candidate) ) {
  3216. SspPrint((SSP_WARNING, "LsaApLogonUserEx2 failed NbDomainName compare\n"));
  3217. Status = STATUS_LOGON_FAILURE;
  3218. goto Cleanup;
  3219. }
  3220. } else {
  3221. if( !RtlEqualDomainName(&NlpPrimaryDomainName, &Candidate) ) {
  3222. SspPrint((SSP_WARNING, "LsaApLogonUserEx2 failed PrimaryDomainName compare\n"));
  3223. Status = STATUS_LOGON_FAILURE;
  3224. goto Cleanup;
  3225. }
  3226. }
  3227. } else {
  3228. SspPrint((SSP_WARNING, "LsaApLogonUserEx2 domain name not supplied\n"));
  3229. Status = STATUS_LOGON_FAILURE;
  3230. goto Cleanup;
  3231. }
  3232. }
  3233. //
  3234. // Do the actual logon now.
  3235. //
  3236. //
  3237. // If a null token is being built,
  3238. // don't authenticate at all.
  3239. //
  3240. if ( LsaTokenInformationType == LsaTokenInformationNull ) {
  3241. /* Nothing to do here. */
  3242. //
  3243. // Call Sam directly to get the validation information when:
  3244. //
  3245. // The network is not installed, OR
  3246. // This is a standalone workstation (not a member of a domain).
  3247. // This is a workstation and we're logging onto an account on the
  3248. // workstation.
  3249. //
  3250. } else if ( NlpNetlogonDllHandle == NULL || !NlpLanmanInstalled ||
  3251. //
  3252. // StandaloneWorkstation and NtLmGlobalUnicodeDnsDomainNameString.Length != 0 means
  3253. // the machine is joined to MIT realms
  3254. //
  3255. (StandaloneWorkstation && (NtLmGlobalUnicodeDnsDomainNameString.Length == 0))
  3256. || ( NlpWorkstation
  3257. && (LogonInformation->LogonDomainName.Length != 0)
  3258. && RtlEqualDomainName( &NlpSamDomainName,
  3259. &LogonInformation->LogonDomainName )) ) {
  3260. // Allow guest logons only
  3261. DWORD AccountsToTry = MSVSAM_SPECIFIED | MSVSAM_GUEST;
  3262. if ((LogonType == Network) &&
  3263. (LogonNetwork.Identity.ParameterControl & MSV1_0_TRY_GUEST_ACCOUNT_ONLY))
  3264. {
  3265. AccountsToTry = MSVSAM_GUEST;
  3266. }
  3267. //
  3268. // for local logons, CachedInteractive is not supported.
  3269. //
  3270. if ( !fWaitForNetwork )
  3271. {
  3272. Status = STATUS_NOT_SUPPORTED;
  3273. goto Cleanup;
  3274. }
  3275. TryCacheFirst = FALSE;
  3276. Authoritative = FALSE;
  3277. //
  3278. // Get the Validation information from the local SAM database
  3279. //
  3280. Status = MsvSamValidate(
  3281. NlpSamDomainHandle,
  3282. NlpUasCompatibilityRequired,
  3283. MsvApSecureChannel,
  3284. &NlpComputerName, // Logon Server is this machine
  3285. &NlpSamDomainName,
  3286. NlpSamDomainId,
  3287. LogonLevel,
  3288. LogonInformation,
  3289. NetlogonValidationSamInfo4,
  3290. (PVOID *) &NlpUser,
  3291. &Authoritative,
  3292. &BadPasswordCountZeroed,
  3293. AccountsToTry);
  3294. if ( !NT_SUCCESS( Status ) ) {
  3295. goto Cleanup;
  3296. }
  3297. // So we don't get a LOGON COLLISION from the old msv package
  3298. Flags |= LOGON_BY_LOCAL;
  3299. //
  3300. // If we couldn't validate via one of the above mechanisms,
  3301. // call the local Netlogon service to get the validation information.
  3302. //
  3303. } else {
  3304. //
  3305. // machines joined to MIT realms
  3306. //
  3307. if (StandaloneWorkstation) // NtLmGlobalUnicodeDnsDomainNameString.Length != 0
  3308. {
  3309. fWaitForNetwork = FALSE; // no netlogon service
  3310. TryCacheFirst = TRUE; // need local fallback when cachedlogon fails
  3311. Status = STATUS_NO_LOGON_SERVERS; // fake it
  3312. }
  3313. if ( fWaitForNetwork )
  3314. {
  3315. if ( (NtLmCheckProcessOption( MSV1_0_OPTION_TRY_CACHE_FIRST ) & MSV1_0_OPTION_TRY_CACHE_FIRST) )
  3316. {
  3317. TryCacheFirst = TRUE;
  3318. }
  3319. }
  3320. //
  3321. // If we are attempting cached credentials logon avoid getting stuck
  3322. // on netlogon or the network.
  3323. //
  3324. RetryNonCached:
  3325. if (fWaitForNetwork && !TryCacheFirst) {
  3326. //
  3327. // Wait for NETLOGON to finish initialization.
  3328. //
  3329. if ( !NlpNetlogonInitialized ) {
  3330. Status = NlWaitForNetlogon( NETLOGON_STARTUP_TIME );
  3331. if ( !NT_SUCCESS(Status) ) {
  3332. if ( Status != STATUS_NETLOGON_NOT_STARTED ) {
  3333. goto Cleanup;
  3334. }
  3335. } else {
  3336. NlpNetlogonInitialized = TRUE;
  3337. }
  3338. }
  3339. //
  3340. // Actually call the netlogon service.
  3341. //
  3342. if ( NlpNetlogonInitialized ) {
  3343. Authoritative = FALSE;
  3344. Status = (*NlpNetLogonSamLogon)(
  3345. NULL, // Server name
  3346. NULL, // Computer name
  3347. NULL, // Authenticator
  3348. NULL, // ReturnAuthenticator
  3349. LogonLevel,
  3350. (LPBYTE) &LogonInformation,
  3351. NetlogonValidationSamInfo4,
  3352. (LPBYTE *) &NlpUser,
  3353. &Authoritative );
  3354. //
  3355. // save the result from netlogon.
  3356. //
  3357. NetlogonStatus = Status;
  3358. //
  3359. // Reset Netlogon initialized flag if local netlogon cannot be
  3360. // reached.
  3361. // (Use a more explicit status code)
  3362. //
  3363. if ( !NT_SUCCESS(Status) )
  3364. {
  3365. switch (Status)
  3366. {
  3367. //
  3368. // for documented errors that netlogon can return
  3369. // for authoritative failures, leave the status code as-is.
  3370. //
  3371. case STATUS_NO_TRUST_LSA_SECRET:
  3372. case STATUS_TRUSTED_DOMAIN_FAILURE:
  3373. case STATUS_INVALID_INFO_CLASS:
  3374. case STATUS_TRUSTED_RELATIONSHIP_FAILURE:
  3375. case STATUS_ACCESS_DENIED:
  3376. case STATUS_NO_SUCH_USER:
  3377. case STATUS_WRONG_PASSWORD:
  3378. case STATUS_INVALID_LOGON_HOURS:
  3379. case STATUS_PASSWORD_EXPIRED:
  3380. case STATUS_ACCOUNT_DISABLED:
  3381. case STATUS_INVALID_PARAMETER:
  3382. case STATUS_PASSWORD_MUST_CHANGE:
  3383. case STATUS_ACCOUNT_EXPIRED:
  3384. case STATUS_ACCOUNT_LOCKED_OUT:
  3385. case STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT:
  3386. case STATUS_NOLOGON_SERVER_TRUST_ACCOUNT:
  3387. case STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT:
  3388. case STATUS_INVALID_WORKSTATION:
  3389. case STATUS_DLL_NOT_FOUND: // subauth dll not found
  3390. case STATUS_PROCEDURE_NOT_FOUND: // returned when subauth registry is not found or procedure is not found
  3391. case STATUS_ACCOUNT_RESTRICTION: // Other Org check failed
  3392. case STATUS_AUTHENTICATION_FIREWALL_FAILED: // Other Org check failed
  3393. {
  3394. break;
  3395. }
  3396. //
  3397. // for errors that are known to occur during unexpected
  3398. // conditions, over-ride status to allow cache lookup.
  3399. //
  3400. case RPC_NT_SERVER_UNAVAILABLE:
  3401. case RPC_NT_UNKNOWN_IF:
  3402. case RPC_NT_CALL_CANCELLED:
  3403. {
  3404. NetlogonStatus = STATUS_NO_LOGON_SERVERS;
  3405. Status = NetlogonStatus;
  3406. NlpNetlogonInitialized = FALSE;
  3407. break;
  3408. }
  3409. // default will catch a host of RPC related errors.
  3410. // some mentioned below.
  3411. //case EPT_NT_NOT_REGISTERED:
  3412. //case RPC_NT_CALL_FAILED_DNE:
  3413. //case RPC_NT_SERVER_TOO_BUSY:
  3414. //case RPC_NT_CALL_FAILED:
  3415. // case STATUS_NETLOGON_NOT_STARTED:
  3416. default:
  3417. {
  3418. Status = STATUS_NETLOGON_NOT_STARTED;
  3419. NlpNetlogonInitialized = FALSE;
  3420. break;
  3421. }
  3422. } // switch
  3423. } // if
  3424. }
  3425. else
  3426. {
  3427. NetlogonStatus = STATUS_NETLOGON_NOT_STARTED;
  3428. Status = NetlogonStatus;
  3429. }
  3430. } else {
  3431. //
  3432. // We want to force cached credentials path by behaving as if no
  3433. // network logon servers were available.
  3434. //
  3435. NetlogonStatus = STATUS_NO_LOGON_SERVERS;
  3436. Status = NetlogonStatus;
  3437. }
  3438. //
  3439. // If this is the requested domain,
  3440. // go directly to SAM if the netlogon service isn't available.
  3441. //
  3442. // We want to go to the netlogon service if it is available since it
  3443. // does special handling of bad passwords and account lockout. However,
  3444. // if the netlogon service is down, the local SAM database makes a
  3445. // better cache than any other mechanism.
  3446. //
  3447. if ( (!NlpNetlogonInitialized
  3448. && (LogonInformation->LogonDomainName.Length != 0)
  3449. && RtlEqualDomainName( &NlpSamDomainName,
  3450. &LogonInformation->LogonDomainName ))
  3451. || (StandaloneWorkstation && !TryCacheFirst) // fallback case for machines joined to MIT realms
  3452. ) {
  3453. // Allow guest logons only
  3454. DWORD AccountsToTry = MSVSAM_SPECIFIED | MSVSAM_GUEST;
  3455. if ((LogonType == Network) &&
  3456. (LogonNetwork.Identity.ParameterControl & MSV1_0_TRY_GUEST_ACCOUNT_ONLY))
  3457. {
  3458. AccountsToTry = MSVSAM_GUEST;
  3459. }
  3460. //
  3461. // we aren't trying to satisfy from cache.
  3462. //
  3463. TryCacheFirst = FALSE;
  3464. Authoritative = FALSE;
  3465. //
  3466. // Get the Validation information from the local SAM database
  3467. //
  3468. Status = MsvSamValidate(
  3469. NlpSamDomainHandle,
  3470. NlpUasCompatibilityRequired,
  3471. MsvApSecureChannel,
  3472. &NlpComputerName, // Logon Server is this machine
  3473. &NlpSamDomainName,
  3474. NlpSamDomainId,
  3475. LogonLevel,
  3476. LogonInformation,
  3477. NetlogonValidationSamInfo4,
  3478. (PVOID *) &NlpUser,
  3479. &Authoritative,
  3480. &BadPasswordCountZeroed,
  3481. AccountsToTry);
  3482. if ( !NT_SUCCESS( Status ) ) {
  3483. goto Cleanup;
  3484. }
  3485. // So we don't get a LOGON COLLISION from the old msv package
  3486. Flags |= LOGON_BY_LOCAL;
  3487. //
  3488. // If Netlogon was successful,
  3489. // add this user to the logon cache.
  3490. //
  3491. } else if ( NT_SUCCESS( Status ) ) {
  3492. //
  3493. // Indicate this session was validated by the Netlogon
  3494. // service.
  3495. //
  3496. Flags |= LOGON_BY_NETLOGON;
  3497. //
  3498. // Cache interactive logon information.
  3499. //
  3500. // NOTE: Batch and Service logons are treated
  3501. // the same as Interactive here.
  3502. //
  3503. if (LogonType == Interactive ||
  3504. LogonType == Service ||
  3505. LogonType == Batch ||
  3506. LogonType == RemoteInteractive) {
  3507. NTSTATUS ntStatus;
  3508. LogonInteractive.Identity.ParameterControl = RPC_C_AUTHN_WINNT;
  3509. ntStatus = NlpAddCacheEntry(
  3510. &LogonInteractive,
  3511. NlpUser,
  3512. NULL,
  3513. 0,
  3514. MSV1_0_CACHE_LOGON_REQUEST_INFO4
  3515. );
  3516. }
  3517. //
  3518. // If Netlogon is simply not available at this time,
  3519. // try to logon through the cache.
  3520. //
  3521. // STATUS_NO_LOGON_SERVERS indicates the netlogon service couldn't
  3522. // contact a DC to handle this request.
  3523. //
  3524. // STATUS_NETLOGON_NOT_STARTED indicates the local netlogon service
  3525. // isn't running.
  3526. //
  3527. //
  3528. // We use the cache for ANY logon type. This not only allows a
  3529. // user to logon interactively, but it allows that same user to
  3530. // connect from another machine while the DC is down.
  3531. //
  3532. } else if ( Status == STATUS_NO_LOGON_SERVERS ||
  3533. Status == STATUS_NETLOGON_NOT_STARTED ) {
  3534. NTSTATUS ntStatus;
  3535. CACHE_PASSWORDS cachePasswords;
  3536. ULONG LocalFlags = 0;
  3537. CacheTried = TRUE;
  3538. //
  3539. // reset Status to NetlogonStatus if an error was encountered.
  3540. //
  3541. if (!NT_SUCCESS( NetlogonStatus ))
  3542. {
  3543. Status = NetlogonStatus;
  3544. }
  3545. //
  3546. // Try to logon via the cache.
  3547. //
  3548. //
  3549. ntStatus = NlpGetCacheEntry(
  3550. LogonInformation,
  3551. 0, // no lookup flags
  3552. &CredentialDomainName,
  3553. &CredentialUserName,
  3554. &NlpUser,
  3555. &cachePasswords,
  3556. NULL, // SupplementalCacheData
  3557. NULL // SupplementalCacheDataLength
  3558. );
  3559. if (!NT_SUCCESS(ntStatus)) {
  3560. //
  3561. // The original status code is more interesting than
  3562. // the fact that the cache didn't work.
  3563. //
  3564. NlpUser = NULL; // NlpGetCacheEntry dirties this
  3565. goto Cleanup;
  3566. }
  3567. if ( LogonType != Network )
  3568. {
  3569. //
  3570. // The cache information contains salted hashed passwords,
  3571. // so modify the logon information similarly.
  3572. //
  3573. ntStatus = NlpComputeSaltedHashedPassword(
  3574. &LogonInteractive.NtOwfPassword,
  3575. &LogonInteractive.NtOwfPassword,
  3576. &NlpUser->EffectiveName
  3577. );
  3578. if (!NT_SUCCESS(ntStatus)) {
  3579. goto Cleanup;
  3580. }
  3581. ntStatus = NlpComputeSaltedHashedPassword(
  3582. &LogonInteractive.LmOwfPassword,
  3583. &LogonInteractive.LmOwfPassword,
  3584. &NlpUser->EffectiveName
  3585. );
  3586. if (!NT_SUCCESS(ntStatus)) {
  3587. goto Cleanup;
  3588. }
  3589. } else {
  3590. PMSV1_0_PRIMARY_CREDENTIAL TempPrimaryCredential;
  3591. ULONG PrimaryCredentialSize;
  3592. if (!UserSid)
  3593. {
  3594. UserSid = NlpMakeDomainRelativeSid(NlpUser->LogonDomainId, NlpUser->UserId);
  3595. if (UserSid == NULL)
  3596. {
  3597. Status = STATUS_NO_MEMORY;
  3598. SspPrint((SSP_CRITICAL, "LsaApLogonUser: NlpMakeDomainRelativeSid no memory\n"));
  3599. goto Cleanup;
  3600. }
  3601. }
  3602. //
  3603. // because the cache no longer stores OWFs, the cached salted OWF
  3604. // is not useful for validation for network logon.
  3605. // The only place we can get a OWF to match is the active logon
  3606. // cache
  3607. //
  3608. ntStatus = NlpGetPrimaryCredentialByUserSid(
  3609. UserSid,
  3610. &TempPrimaryCredential,
  3611. &PrimaryCredentialSize
  3612. );
  3613. if (!NT_SUCCESS(ntStatus))
  3614. {
  3615. Status = STATUS_WRONG_PASSWORD;
  3616. goto Cleanup;
  3617. }
  3618. //
  3619. // copy out the OWFs, then free the allocated buffer.
  3620. //
  3621. if (TempPrimaryCredential->NtPasswordPresent)
  3622. {
  3623. RtlCopyMemory(&cachePasswords.SecretPasswords.NtOwfPassword, &TempPrimaryCredential->NtOwfPassword, sizeof(NT_OWF_PASSWORD));
  3624. cachePasswords.SecretPasswords.NtPasswordPresent = TRUE;
  3625. }
  3626. else
  3627. {
  3628. cachePasswords.SecretPasswords.NtPasswordPresent = FALSE;
  3629. }
  3630. if (TempPrimaryCredential->LmPasswordPresent)
  3631. {
  3632. RtlCopyMemory(&cachePasswords.SecretPasswords.LmOwfPassword, &TempPrimaryCredential->LmOwfPassword, sizeof(LM_OWF_PASSWORD));
  3633. cachePasswords.SecretPasswords.LmPasswordPresent = TRUE;
  3634. }
  3635. else
  3636. {
  3637. cachePasswords.SecretPasswords.LmPasswordPresent = FALSE;
  3638. }
  3639. RtlZeroMemory(TempPrimaryCredential, PrimaryCredentialSize);
  3640. (*Lsa.FreeLsaHeap)(TempPrimaryCredential);
  3641. }
  3642. //
  3643. // Now we have the information from the cache, validate the
  3644. // user's password
  3645. //
  3646. if (!MsvpPasswordValidate(
  3647. NlpUasCompatibilityRequired,
  3648. LogonLevel,
  3649. (PVOID)LogonInformation,
  3650. &cachePasswords.SecretPasswords,
  3651. &LocalFlags,
  3652. &NlpUser->UserSessionKey,
  3653. (PLM_SESSION_KEY)
  3654. &NlpUser->ExpansionRoom[SAMINFO_LM_SESSION_KEY]
  3655. )) {
  3656. Status = STATUS_WRONG_PASSWORD;
  3657. goto Cleanup;
  3658. }
  3659. Status = STATUS_SUCCESS;
  3660. //
  3661. // successful logon from cache. Don't retry if a failure occurs below.
  3662. //
  3663. TryCacheFirst = FALSE;
  3664. //
  3665. // The cache always returns a NETLOGONV_VALIDATION_SAM_INFO2
  3666. // structure so set the LOGON_EXTRA_SIDS flag, whether or not
  3667. // there are extra sids. Also, if there was a package ID indicated
  3668. // put it in the PrimaryCredentials and remove it from the
  3669. // NlpUser structure so it doesn't confuse anyone else.
  3670. //
  3671. PrimaryCredentials->Flags &= ~PRIMARY_CRED_PACKAGE_MASK;
  3672. PrimaryCredentials->Flags |= NlpUser->UserFlags & PRIMARY_CRED_PACKAGE_MASK;
  3673. PrimaryCredentials->Flags |= PRIMARY_CRED_CACHED_LOGON;
  3674. NlpUser->UserFlags &= ~PRIMARY_CRED_PACKAGE_MASK;
  3675. NlpUser->UserFlags |= LOGON_CACHED_ACCOUNT | LOGON_EXTRA_SIDS | LocalFlags;
  3676. Flags |= LOGON_BY_CACHE;
  3677. //
  3678. // If the account is permanently dead on the domain controller,
  3679. // Flush this entry from the cache.
  3680. //
  3681. // Notice that STATUS_INVALID_LOGON_HOURS is not in the list below.
  3682. // This ensures a user will be able to remove his portable machine
  3683. // from the net and use it after hours.
  3684. //
  3685. // Notice the STATUS_WRONG_PASSWORD is not in the list below.
  3686. // We're as likely to flush the cache for typo'd passwords as anything
  3687. // else. What we'd really like to do is flush the cache if the
  3688. // password on the DC is different than the one in cache; but that's
  3689. // impossible to detect.
  3690. //
  3691. // ONLY DO THIS FOR INTERACTIVE LOGONS
  3692. // (not Service or Batch).
  3693. //
  3694. } else if ( ((LogonType == Interactive) || (LogonType == RemoteInteractive)) &&
  3695. (Status == STATUS_NO_SUCH_USER ||
  3696. Status == STATUS_INVALID_WORKSTATION ||
  3697. Status == STATUS_PASSWORD_EXPIRED ||
  3698. Status == STATUS_ACCOUNT_DISABLED ||
  3699. Status == STATUS_ACCOUNT_RESTRICTION || // Other Org check failed
  3700. Status == STATUS_AUTHENTICATION_FIREWALL_FAILED) ) { // Other Org check failed
  3701. //
  3702. // Delete the cache entry
  3703. NTSTATUS ntStatus;
  3704. ntStatus = NlpDeleteCacheEntry(
  3705. Status, // reason
  3706. Authoritative ? 1 : 0,
  3707. LogonType, // logon type
  3708. TRUE, // invalidated by NTLM
  3709. &LogonInteractive
  3710. );
  3711. if (!NT_SUCCESS(ntStatus))
  3712. {
  3713. SspPrint((SSP_CRITICAL, "LsaApLogonUser: NlpDeleteCacheEntry returns %x\n", ntStatus));
  3714. //
  3715. // netlogon returns non-authoritative no_such_user error and
  3716. // there is a matching MIT cache entry, try cached logon
  3717. //
  3718. if (fWaitForNetwork && (Status == STATUS_NO_SUCH_USER) && (!Authoritative)
  3719. && (ntStatus == STATUS_NO_LOGON_SERVERS))
  3720. {
  3721. fWaitForNetwork = FALSE; // fake it
  3722. Status = STATUS_NO_LOGON_SERVERS;
  3723. goto RetryNonCached;
  3724. }
  3725. }
  3726. goto Cleanup;
  3727. } else {
  3728. goto Cleanup;
  3729. }
  3730. }
  3731. //
  3732. // if this is PersonalSKU, only allow DOMAIN_USER_RID_ADMIN to logon
  3733. // if doing safe-mode boot (NtLmGlobalSafeBoot == TRUE)
  3734. //
  3735. if ( NlpUser &&
  3736. NlpUser->UserId == DOMAIN_USER_RID_ADMIN &&
  3737. !NtLmGlobalSafeBoot &&
  3738. NtLmGlobalPersonalSKU &&
  3739. NlpSamDomainId &&
  3740. RtlEqualSid( NlpUser->LogonDomainId, NlpSamDomainId )
  3741. )
  3742. {
  3743. Status = STATUS_ACCOUNT_RESTRICTION;
  3744. SspPrint((SSP_CRITICAL,
  3745. "LsaApLogonUser: For Personal SKU Administrator cannot log on except during safe mode boot\n"));
  3746. goto Cleanup;
  3747. }
  3748. //
  3749. // For everything except network logons,
  3750. // save the credentials in the LSA,
  3751. // create active logon table entry,
  3752. // return the interactive profile buffer.
  3753. //
  3754. if ( LogonType == Interactive ||
  3755. LogonType == Service ||
  3756. LogonType == Batch ||
  3757. LogonType == NetworkCleartext ||
  3758. LogonType == RemoteInteractive
  3759. )
  3760. {
  3761. PUCHAR pWhere;
  3762. USHORT LogonCount;
  3763. ULONG UserSidSize;
  3764. UNICODE_STRING SamAccountName;
  3765. UNICODE_STRING NetbiosDomainName;
  3766. UNICODE_STRING DnsDomainName;
  3767. UNICODE_STRING Upn;
  3768. UNICODE_STRING LogonServer;
  3769. //
  3770. // Grab the various forms of the account name
  3771. //
  3772. NlpGetAccountNames( LogonInformation,
  3773. NlpUser,
  3774. &SamAccountName,
  3775. &NetbiosDomainName,
  3776. &DnsDomainName,
  3777. &Upn );
  3778. if (CredentialUserName.Length == 0)
  3779. {
  3780. CredentialUserToUse = &SamAccountName;
  3781. }
  3782. else
  3783. {
  3784. CredentialUserToUse = &CredentialUserName;
  3785. }
  3786. if (CredentialDomainName.Length == 0)
  3787. {
  3788. CredentialDomainToUse = &NetbiosDomainName;
  3789. }
  3790. else
  3791. {
  3792. CredentialDomainToUse = &CredentialDomainName;
  3793. }
  3794. //
  3795. // Build the NTLM primary credential using NetbiosDomainName\SamAccountName
  3796. //
  3797. //
  3798. #ifdef MAP_DOMAIN_NAMES_AT_LOGON
  3799. {
  3800. UNICODE_STRING MappedDomain;
  3801. RtlInitUnicodeString(
  3802. &MappedDomain,
  3803. NULL
  3804. );
  3805. Status = NlpMapLogonDomain(
  3806. &MappedDomain,
  3807. &NetbiosDomainName );
  3808. if (!NT_SUCCESS(Status)) {
  3809. goto Cleanup;
  3810. }
  3811. Status = NlpMakePrimaryCredential( &MappedDomain,
  3812. &SamAccountName,
  3813. &PrimaryCredentials->Password,
  3814. &Credential,
  3815. &CredentialSize );
  3816. if (MappedDomain.Buffer != NULL) {
  3817. NtLmFree(MappedDomain.Buffer);
  3818. }
  3819. if ( !NT_SUCCESS( Status ) ) {
  3820. goto Cleanup;
  3821. }
  3822. }
  3823. #else
  3824. Status = NlpMakePrimaryCredential(&NetbiosDomainName,
  3825. &SamAccountName,
  3826. &PrimaryCredentials->Password,
  3827. &Credential,
  3828. &CredentialSize);
  3829. if ( !NT_SUCCESS( Status ) ) {
  3830. goto Cleanup;
  3831. }
  3832. #endif
  3833. //
  3834. // Add additional names to the logon session name map. Ignore failure
  3835. // as that just means GetUserNameEx calls for these name formats later
  3836. // on will be satisfied by hitting the wire.
  3837. //
  3838. if (NlpUser->FullName.Length != 0)
  3839. {
  3840. I_LsaIAddNameToLogonSession(LogonId, NameDisplay, &NlpUser->FullName);
  3841. }
  3842. if (Upn.Length != 0)
  3843. {
  3844. I_LsaIAddNameToLogonSession(LogonId, NameUserPrincipal, &Upn);
  3845. }
  3846. if (DnsDomainName.Length != 0)
  3847. {
  3848. I_LsaIAddNameToLogonSession(LogonId, NameDnsDomain, &DnsDomainName);
  3849. }
  3850. //
  3851. // Fill the username and domain name into the primary credential
  3852. // that's passed to the other security packages.
  3853. //
  3854. // The names filled in are the effective names after authentication.
  3855. // For instance, it isn't the UPN passed to this function.
  3856. //
  3857. PrimaryCredentials->DownlevelName.Length = CredentialUserToUse->Length;
  3858. PrimaryCredentials->DownlevelName.MaximumLength = PrimaryCredentials->DownlevelName.Length; // + sizeof(WCHAR);
  3859. PrimaryCredentials->DownlevelName.Buffer = (*Lsa.AllocateLsaHeap)(PrimaryCredentials->DownlevelName.MaximumLength);
  3860. if (PrimaryCredentials->DownlevelName.Buffer == NULL) {
  3861. Status = STATUS_INSUFFICIENT_RESOURCES;
  3862. goto Cleanup;
  3863. }
  3864. RtlCopyMemory(
  3865. PrimaryCredentials->DownlevelName.Buffer,
  3866. CredentialUserToUse->Buffer,
  3867. CredentialUserToUse->Length
  3868. );
  3869. PrimaryCredentials->DomainName.Length = CredentialDomainToUse->Length;
  3870. PrimaryCredentials->DomainName.MaximumLength = PrimaryCredentials->DomainName.Length; // + sizeof(WCHAR);
  3871. PrimaryCredentials->DomainName.Buffer = (*Lsa.AllocateLsaHeap)(PrimaryCredentials->DomainName.Length);
  3872. if (PrimaryCredentials->DomainName.Buffer == NULL) {
  3873. Status = STATUS_INSUFFICIENT_RESOURCES;
  3874. goto Cleanup;
  3875. }
  3876. RtlCopyMemory(
  3877. PrimaryCredentials->DomainName.Buffer,
  3878. CredentialDomainToUse->Buffer,
  3879. CredentialDomainToUse->Length
  3880. );
  3881. RtlCopyMemory(&LogonServer, &NlpUser->LogonServer, sizeof(UNICODE_STRING));
  3882. if ( LogonServer.Length != 0 ) {
  3883. PrimaryCredentials->LogonServer.Length = PrimaryCredentials->LogonServer.MaximumLength =
  3884. LogonServer.Length;
  3885. PrimaryCredentials->LogonServer.Buffer = (*Lsa.AllocateLsaHeap)(LogonServer.Length);
  3886. if (PrimaryCredentials->LogonServer.Buffer == NULL) {
  3887. Status = STATUS_INSUFFICIENT_RESOURCES;
  3888. goto Cleanup;
  3889. }
  3890. RtlCopyMemory(
  3891. PrimaryCredentials->LogonServer.Buffer,
  3892. LogonServer.Buffer,
  3893. LogonServer.Length
  3894. );
  3895. }
  3896. //
  3897. // Save the credential in the LSA.
  3898. //
  3899. Status = NlpAddPrimaryCredential( LogonId,
  3900. Credential,
  3901. CredentialSize );
  3902. if ( !NT_SUCCESS( Status ) ) {
  3903. SspPrint((SSP_CRITICAL, "LsaApLogonUser: error from AddCredential %lX\n",
  3904. Status));
  3905. goto Cleanup;
  3906. }
  3907. LogonCredentialAdded = TRUE;
  3908. //
  3909. // Build a Sid for this user.
  3910. //
  3911. if (!UserSid)
  3912. {
  3913. UserSid = NlpMakeDomainRelativeSid(NlpUser->LogonDomainId,
  3914. NlpUser->UserId);
  3915. if (UserSid == NULL)
  3916. {
  3917. Status = STATUS_NO_MEMORY;
  3918. SspPrint((SSP_CRITICAL, "LsaApLogonUser: No memory\n"));
  3919. goto Cleanup;
  3920. }
  3921. }
  3922. PrimaryCredentials->UserSid = UserSid;
  3923. UserSid = NULL;
  3924. UserSidSize = RtlLengthSid( PrimaryCredentials->UserSid );
  3925. //
  3926. // Allocate an entry for the active logon table.
  3927. //
  3928. ActiveLogonEntrySize = ROUND_UP_COUNT(sizeof(ACTIVE_LOGON), ALIGN_DWORD) +
  3929. ROUND_UP_COUNT(UserSidSize, sizeof(WCHAR)) +
  3930. SamAccountName.Length + sizeof(WCHAR) +
  3931. NetbiosDomainName.Length + sizeof(WCHAR) +
  3932. NlpUser->LogonServer.Length + sizeof(WCHAR);
  3933. pActiveLogonEntry = I_NtLmAllocate( ActiveLogonEntrySize );
  3934. if ( pActiveLogonEntry == NULL )
  3935. {
  3936. Status = STATUS_NO_MEMORY;
  3937. SspPrint((SSP_CRITICAL, "LsaApLogonUser: No memory %ld\n", ActiveLogonEntrySize));
  3938. goto Cleanup;
  3939. }
  3940. //
  3941. // Fill in the logon table entry.
  3942. //
  3943. pWhere = (PUCHAR)(pActiveLogonEntry + 1);
  3944. pActiveLogonEntry->Signature = NTLM_ACTIVE_LOGON_MAGIC_SIGNATURE;
  3945. OLD_TO_NEW_LARGE_INTEGER(
  3946. LogonInformation->LogonId,
  3947. pActiveLogonEntry->LogonId );
  3948. pActiveLogonEntry->Flags = Flags;
  3949. pActiveLogonEntry->LogonType = LogonType;
  3950. //
  3951. // Copy DWORD aligned fields first.
  3952. //
  3953. pWhere = ROUND_UP_POINTER( pWhere, ALIGN_DWORD );
  3954. Status = RtlCopySid(UserSidSize, (PSID)pWhere, PrimaryCredentials->UserSid);
  3955. if ( !NT_SUCCESS(Status) )
  3956. {
  3957. goto Cleanup;
  3958. }
  3959. pActiveLogonEntry->UserSid = (PSID) pWhere;
  3960. pWhere += UserSidSize;
  3961. //
  3962. // Copy WCHAR aligned fields
  3963. //
  3964. pWhere = ROUND_UP_POINTER( pWhere, ALIGN_WCHAR );
  3965. NlpPutString( &pActiveLogonEntry->UserName,
  3966. &SamAccountName,
  3967. &pWhere );
  3968. NlpPutString( &pActiveLogonEntry->LogonDomainName,
  3969. &NetbiosDomainName,
  3970. &pWhere );
  3971. NlpPutString( &pActiveLogonEntry->LogonServer,
  3972. &NlpUser->LogonServer,
  3973. &pWhere );
  3974. //
  3975. // Get the next enumeration handle for this session.
  3976. //
  3977. pActiveLogonEntry->EnumHandle = (ULONG)InterlockedIncrement((PLONG)&NlpEnumerationHandle);
  3978. NlpLockActiveLogonsRead();
  3979. //
  3980. // Insert this entry into the active logon table.
  3981. //
  3982. if (NlpFindActiveLogon( LogonId ))
  3983. {
  3984. //
  3985. // This Logon ID is already in use.
  3986. //
  3987. NlpUnlockActiveLogons();
  3988. Status = STATUS_LOGON_SESSION_COLLISION;
  3989. SspPrint((SSP_CRITICAL,
  3990. "LsaApLogonUserEx2: Collision from NlpFindActiveLogon for %#x:%#x\n",
  3991. LogonId->HighPart, LogonId->LowPart));
  3992. goto Cleanup;
  3993. }
  3994. //
  3995. // LogonId is unique, the same LogonId wouldn't be added twice
  3996. //
  3997. NlpLockActiveLogonsReadToWrite();
  3998. InsertTailList(&NlpActiveLogonListAnchor, &pActiveLogonEntry->ListEntry);
  3999. NlpUnlockActiveLogons();
  4000. SspPrint((SSP_LOGON_SESS, "LsaApLogonUserEx2 inserted %#x:%#x\n",
  4001. pActiveLogonEntry->LogonId.HighPart, pActiveLogonEntry->LogonId.LowPart));
  4002. bLogonEntryLinked = TRUE;
  4003. //
  4004. // Ensure the LogonCount is at least as big as it is for this
  4005. // machine.
  4006. //
  4007. LogonCount = (USHORT) NlpCountActiveLogon( &NetbiosDomainName,
  4008. &SamAccountName );
  4009. if ( NlpUser->LogonCount < LogonCount )
  4010. {
  4011. NlpUser->LogonCount = LogonCount;
  4012. }
  4013. //
  4014. // Alocate the profile buffer to return to the client
  4015. //
  4016. Status = NlpAllocateInteractiveProfile(
  4017. ClientRequest,
  4018. (PMSV1_0_INTERACTIVE_PROFILE *) ProfileBuffer,
  4019. ProfileBufferSize,
  4020. NlpUser );
  4021. if ( !NT_SUCCESS( Status ) )
  4022. {
  4023. SspPrint((SSP_CRITICAL,
  4024. "LsaApLogonUser: Allocate Profile Failed: %lx\n", Status));
  4025. goto Cleanup;
  4026. }
  4027. } else if ( LogonType == Network ) {
  4028. //
  4029. // if doing client challenge, and it's a vanilla NTLM response,
  4030. // and it's not a null session, compute unique per-session session keys
  4031. // N.B: not needed if it's NTLM++, not possible if LM
  4032. //
  4033. if ((NetworkAuthentication->ParameterControl & MSV1_0_USE_CLIENT_CHALLENGE) &&
  4034. (NetworkAuthentication->CaseSensitiveChallengeResponse.Length == NT_RESPONSE_LENGTH ) && // vanilla NTLM response
  4035. (NetworkAuthentication->CaseInsensitiveChallengeResponse.Length >= MSV1_0_CHALLENGE_LENGTH ) &&
  4036. (NlpUser != NULL)) // NULL session iff NlpUser == NULL
  4037. {
  4038. MsvpCalculateNtlm2SessionKeys(
  4039. &NlpUser->UserSessionKey,
  4040. NetworkAuthentication->ChallengeToClient,
  4041. (PUCHAR) NetworkAuthentication->CaseInsensitiveChallengeResponse.Buffer,
  4042. (PUSER_SESSION_KEY) &NlpUser->UserSessionKey,
  4043. (PLM_SESSION_KEY)&NlpUser->ExpansionRoom[SAMINFO_LM_SESSION_KEY]
  4044. );
  4045. }
  4046. //
  4047. // Alocate the profile buffer to return to the client
  4048. //
  4049. Status = NlpAllocateNetworkProfile(
  4050. ClientRequest,
  4051. (PMSV1_0_LM20_LOGON_PROFILE *) ProfileBuffer,
  4052. ProfileBufferSize,
  4053. NlpUser,
  4054. LogonNetwork.Identity.ParameterControl );
  4055. if ( !NT_SUCCESS( Status ) ) {
  4056. SspPrint((SSP_CRITICAL,
  4057. "LsaApLogonUser: Allocate Profile Failed: %lx. This could also be a status for a subauth logon.\n", Status));
  4058. goto Cleanup;
  4059. }
  4060. if ( NlpUser != NULL )
  4061. {
  4062. UNICODE_STRING SamAccountName;
  4063. UNICODE_STRING NetbiosDomainName;
  4064. UNICODE_STRING DnsDomainName;
  4065. UNICODE_STRING Upn;
  4066. UNICODE_STRING LogonServer;
  4067. //
  4068. // Grab the various forms of the account name
  4069. //
  4070. NlpGetAccountNames( LogonInformation,
  4071. NlpUser,
  4072. &SamAccountName,
  4073. &NetbiosDomainName,
  4074. &DnsDomainName,
  4075. &Upn );
  4076. //
  4077. // Add additional names to the logon session name map. Ignore failure
  4078. // as that just means GetUserNameEx calls for these name formats later
  4079. // on will be satisfied by hitting the wire.
  4080. //
  4081. if (NlpUser->FullName.Length != 0)
  4082. {
  4083. I_LsaIAddNameToLogonSession(LogonId, NameDisplay, &NlpUser->FullName);
  4084. }
  4085. if (Upn.Length != 0)
  4086. {
  4087. I_LsaIAddNameToLogonSession(LogonId, NameUserPrincipal, &Upn);
  4088. }
  4089. if (DnsDomainName.Length != 0)
  4090. {
  4091. I_LsaIAddNameToLogonSession(LogonId, NameDnsDomain, &DnsDomainName);
  4092. }
  4093. //
  4094. // Fill the username and domain name into the primary credential
  4095. // that's passed to the other security packages.
  4096. //
  4097. // The names filled in are the effective names after authentication.
  4098. // For instance, it isn't the UPN passed to this function.
  4099. //
  4100. if ( SamAccountName.Length == 0 )
  4101. {
  4102. SamAccountName = TmpName;
  4103. }
  4104. PrimaryCredentials->DownlevelName.Length = PrimaryCredentials->DownlevelName.MaximumLength =
  4105. SamAccountName.Length;
  4106. PrimaryCredentials->DownlevelName.Buffer = (*Lsa.AllocateLsaHeap)(SamAccountName.Length);
  4107. if (PrimaryCredentials->DownlevelName.Buffer == NULL) {
  4108. Status = STATUS_INSUFFICIENT_RESOURCES;
  4109. goto Cleanup;
  4110. }
  4111. RtlCopyMemory(
  4112. PrimaryCredentials->DownlevelName.Buffer,
  4113. SamAccountName.Buffer,
  4114. SamAccountName.Length
  4115. );
  4116. PrimaryCredentials->DomainName.Length = PrimaryCredentials->DomainName.MaximumLength =
  4117. NetbiosDomainName.Length;
  4118. PrimaryCredentials->DomainName.Buffer = (*Lsa.AllocateLsaHeap)(NetbiosDomainName.Length);
  4119. if (PrimaryCredentials->DomainName.Buffer == NULL) {
  4120. Status = STATUS_INSUFFICIENT_RESOURCES;
  4121. goto Cleanup;
  4122. }
  4123. RtlCopyMemory(
  4124. PrimaryCredentials->DomainName.Buffer,
  4125. NetbiosDomainName.Buffer,
  4126. NetbiosDomainName.Length
  4127. );
  4128. RtlCopyMemory(&LogonServer, &NlpUser->LogonServer, sizeof(UNICODE_STRING));
  4129. if ( LogonServer.Length != 0 ) {
  4130. PrimaryCredentials->LogonServer.Length = PrimaryCredentials->LogonServer.MaximumLength =
  4131. LogonServer.Length;
  4132. PrimaryCredentials->LogonServer.Buffer = (*Lsa.AllocateLsaHeap)(LogonServer.Length);
  4133. if (PrimaryCredentials->LogonServer.Buffer == NULL) {
  4134. Status = STATUS_INSUFFICIENT_RESOURCES;
  4135. goto Cleanup;
  4136. }
  4137. RtlCopyMemory(
  4138. PrimaryCredentials->LogonServer.Buffer,
  4139. LogonServer.Buffer,
  4140. LogonServer.Length
  4141. );
  4142. }
  4143. //
  4144. // Build a Sid for this user.
  4145. //
  4146. UserSid = NlpMakeDomainRelativeSid( NlpUser->LogonDomainId,
  4147. NlpUser->UserId );
  4148. if ( UserSid == NULL ) {
  4149. Status = STATUS_NO_MEMORY;
  4150. SspPrint((SSP_CRITICAL, "LsaApLogonUser: No memory\n"));
  4151. goto Cleanup;
  4152. }
  4153. PrimaryCredentials->UserSid = UserSid;
  4154. UserSid = NULL;
  4155. }
  4156. }
  4157. //
  4158. // Build the token information to return to the LSA
  4159. //
  4160. switch (LsaTokenInformationType) {
  4161. case LsaTokenInformationV2:
  4162. Status = NlpMakeTokenInformationV2(
  4163. NlpUser,
  4164. (PLSA_TOKEN_INFORMATION_V2 *)TokenInformation );
  4165. if ( !NT_SUCCESS( Status ) ) {
  4166. SspPrint((SSP_CRITICAL,
  4167. "LsaApLogonUser: MakeTokenInformationV2 Failed: %lx\n", Status));
  4168. goto Cleanup;
  4169. }
  4170. break;
  4171. case LsaTokenInformationNull:
  4172. {
  4173. PLSA_TOKEN_INFORMATION_NULL VNull;
  4174. VNull = (*Lsa.AllocateLsaHeap)(sizeof(LSA_TOKEN_INFORMATION_NULL) );
  4175. if ( VNull == NULL ) {
  4176. Status = STATUS_NO_MEMORY;
  4177. goto Cleanup;
  4178. }
  4179. VNull->Groups = NULL;
  4180. VNull->ExpirationTime.HighPart = 0x7FFFFFFF;
  4181. VNull->ExpirationTime.LowPart = 0xFFFFFFFF;
  4182. *TokenInformation = VNull;
  4183. }
  4184. }
  4185. *TokenInformationType = LsaTokenInformationType;
  4186. Status = STATUS_SUCCESS;
  4187. Cleanup:
  4188. //
  4189. // if we tried to logon from cache, try again if it failed.
  4190. //
  4191. if ( fWaitForNetwork && TryCacheFirst && CacheTried && !NT_SUCCESS(Status) )
  4192. {
  4193. if (CredentialUserName.Buffer)
  4194. {
  4195. NtLmFreePrivateHeap(CredentialUserName.Buffer);
  4196. RtlZeroMemory(&CredentialUserName, sizeof(CredentialUserName));
  4197. }
  4198. if (CredentialDomainName.Buffer)
  4199. {
  4200. NtLmFreePrivateHeap(CredentialDomainName.Buffer);
  4201. RtlZeroMemory(&CredentialDomainName, sizeof(CredentialDomainName));
  4202. }
  4203. if ( NlpUser != NULL )
  4204. {
  4205. MIDL_user_free( NlpUser );
  4206. NlpUser = NULL;
  4207. }
  4208. TryCacheFirst = FALSE;
  4209. goto RetryNonCached;
  4210. }
  4211. NtLmFreePrivateHeap( CredmanUserName.Buffer );
  4212. NtLmFreePrivateHeap( CredmanDomainName.Buffer );
  4213. NtLmFreePrivateHeap( CredmanPassword.Buffer );
  4214. //
  4215. // Restore the saved password
  4216. //
  4217. if ( ServiceSecretLogon ) {
  4218. RtlCopyMemory( &Authentication->Password,
  4219. &SavedPassword,
  4220. sizeof( UNICODE_STRING ) );
  4221. //
  4222. // Free the secret value we read...
  4223. //
  4224. LsaIFree_LSAPR_CR_CIPHER_VALUE( SecretCurrent );
  4225. }
  4226. //
  4227. // If the logon wasn't successful,
  4228. // cleanup resources we would have returned to the caller.
  4229. //
  4230. if ( !NT_SUCCESS(Status) ) {
  4231. if ( LogonSessionCreated ) {
  4232. (VOID)(*Lsa.DeleteLogonSession)( LogonId );
  4233. }
  4234. if ( pActiveLogonEntry != NULL ) {
  4235. if ( bLogonEntryLinked ) {
  4236. LsaApLogonTerminated( LogonId );
  4237. } else {
  4238. if ( LogonCredentialAdded ) {
  4239. (VOID) NlpDeletePrimaryCredential(
  4240. LogonId );
  4241. }
  4242. I_NtLmFree( pActiveLogonEntry );
  4243. }
  4244. }
  4245. // Special case for MsV1_0SubAuthLogon (includes arap).
  4246. // (Don't free ProfileBuffer during error conditions which may not be fatal)
  4247. if (!fSubAuthEx)
  4248. {
  4249. if ( *ProfileBuffer != NULL ) {
  4250. if (ClientRequest != (PLSA_CLIENT_REQUEST) (-1))
  4251. (VOID)(*Lsa.FreeClientBuffer)( ClientRequest, *ProfileBuffer );
  4252. else
  4253. (VOID)(*Lsa.FreeLsaHeap)( *ProfileBuffer );
  4254. *ProfileBuffer = NULL;
  4255. }
  4256. }
  4257. if (PrimaryCredentials->DownlevelName.Buffer != NULL) {
  4258. (*Lsa.FreeLsaHeap)(PrimaryCredentials->DownlevelName.Buffer);
  4259. }
  4260. if (PrimaryCredentials->DomainName.Buffer != NULL) {
  4261. (*Lsa.FreeLsaHeap)(PrimaryCredentials->DomainName.Buffer);
  4262. }
  4263. if (PrimaryCredentials->Password.Buffer != NULL) {
  4264. RtlZeroMemory(
  4265. PrimaryCredentials->Password.Buffer,
  4266. PrimaryCredentials->Password.Length
  4267. );
  4268. (*Lsa.FreeLsaHeap)(PrimaryCredentials->Password.Buffer);
  4269. }
  4270. if (PrimaryCredentials->LogonServer.Buffer != NULL) {
  4271. (*Lsa.FreeLsaHeap)(PrimaryCredentials->LogonServer.Buffer);
  4272. }
  4273. RtlZeroMemory(
  4274. PrimaryCredentials,
  4275. sizeof(SECPKG_PRIMARY_CRED)
  4276. );
  4277. }
  4278. //
  4279. // Copy out Authenticating authority and user name.
  4280. //
  4281. if ( NT_SUCCESS(Status) && LsaTokenInformationType != LsaTokenInformationNull ) {
  4282. //
  4283. // Use the information from the NlpUser structure, since it gives
  4284. // us accurate information about what account we're logging on to,
  4285. // rather than who we were.
  4286. //
  4287. if ( LogonType != Network )
  4288. {
  4289. TmpName = NlpUser->EffectiveName;
  4290. }
  4291. else
  4292. {
  4293. //
  4294. // older servers may not return the effectivename for non-guest network logon.
  4295. //
  4296. if( NlpUser->EffectiveName.Length != 0 )
  4297. {
  4298. TmpName = NlpUser->EffectiveName;
  4299. }
  4300. }
  4301. TmpAuthority = NlpUser->LogonDomainName;
  4302. }
  4303. *AccountName = (*Lsa.AllocateLsaHeap)( sizeof( UNICODE_STRING ) );
  4304. if ( *AccountName != NULL ) {
  4305. (*AccountName)->Buffer = (*Lsa.AllocateLsaHeap)(TmpName.Length + sizeof( UNICODE_NULL) );
  4306. if ( (*AccountName)->Buffer != NULL ) {
  4307. (*AccountName)->MaximumLength = TmpName.Length + sizeof( UNICODE_NULL );
  4308. RtlCopyUnicodeString( *AccountName, &TmpName );
  4309. } else if (NT_SUCCESS(Status)) {
  4310. Status = STATUS_NO_MEMORY;
  4311. } else {
  4312. RtlInitUnicodeString( *AccountName, NULL );
  4313. }
  4314. } else if (NT_SUCCESS(Status)) {
  4315. Status = STATUS_NO_MEMORY;
  4316. }
  4317. *AuthenticatingAuthority = (*Lsa.AllocateLsaHeap)( sizeof( UNICODE_STRING ) );
  4318. if ( *AuthenticatingAuthority != NULL ) {
  4319. (*AuthenticatingAuthority)->Buffer = (*Lsa.AllocateLsaHeap)( TmpAuthority.Length + sizeof( UNICODE_NULL ) );
  4320. if ( (*AuthenticatingAuthority)->Buffer != NULL ) {
  4321. (*AuthenticatingAuthority)->MaximumLength = (USHORT)(TmpAuthority.Length + sizeof( UNICODE_NULL ));
  4322. RtlCopyUnicodeString( *AuthenticatingAuthority, &TmpAuthority );
  4323. } else if (NT_SUCCESS(Status)) {
  4324. Status = STATUS_NO_MEMORY;
  4325. } else {
  4326. RtlInitUnicodeString( *AuthenticatingAuthority, NULL );
  4327. }
  4328. } else if (NT_SUCCESS(Status)) {
  4329. Status = STATUS_NO_MEMORY;
  4330. }
  4331. *MachineName = NULL;
  4332. if (WorkStationName != NULL) {
  4333. *MachineName = (*Lsa.AllocateLsaHeap)( sizeof( UNICODE_STRING ) );
  4334. if ( *MachineName != NULL ) {
  4335. (*MachineName)->Buffer = (*Lsa.AllocateLsaHeap)( WorkStationName->Length + sizeof( UNICODE_NULL ) );
  4336. if ( (*MachineName)->Buffer != NULL ) {
  4337. (*MachineName)->MaximumLength = (USHORT)(WorkStationName->Length + sizeof( UNICODE_NULL ));
  4338. RtlCopyUnicodeString( *MachineName, WorkStationName );
  4339. } else if (NT_SUCCESS(Status)) {
  4340. Status = STATUS_NO_MEMORY;
  4341. } else {
  4342. RtlInitUnicodeString( *MachineName, NULL );
  4343. }
  4344. } else if (NT_SUCCESS(Status)) {
  4345. Status = STATUS_NO_MEMORY;
  4346. }
  4347. }
  4348. //
  4349. // Map status codes to prevent specific information from being
  4350. // released about this user.
  4351. //
  4352. switch (Status) {
  4353. case STATUS_WRONG_PASSWORD:
  4354. case STATUS_NO_SUCH_USER:
  4355. case STATUS_DOMAIN_TRUST_INCONSISTENT:
  4356. //
  4357. // sleep 3 seconds to "discourage" dictionary attacks.
  4358. // Don't worry about interactive logon dictionary attacks.
  4359. // They will be slow anyway.
  4360. //
  4361. // per bug 171041, SField, RichardW, CliffV all decided this
  4362. // delay has almost zero value for Win2000. Offline attacks at
  4363. // sniffed wire traffic are more efficient and viable. Further,
  4364. // opimizations in logon code path make failed interactive logons
  4365. // very fast.
  4366. //
  4367. // if (LogonType != Interactive) {
  4368. // Sleep( 3000 );
  4369. // }
  4370. //
  4371. // This is for auditing. Make sure to clear it out before
  4372. // passing it out of LSA to the caller.
  4373. //
  4374. *SubStatus = Status;
  4375. Status = STATUS_LOGON_FAILURE;
  4376. break;
  4377. case STATUS_INVALID_LOGON_HOURS:
  4378. case STATUS_INVALID_WORKSTATION:
  4379. case STATUS_PASSWORD_EXPIRED:
  4380. case STATUS_ACCOUNT_DISABLED:
  4381. *SubStatus = Status;
  4382. Status = STATUS_ACCOUNT_RESTRICTION;
  4383. break;
  4384. //
  4385. // This means that the Other Organization check failed.
  4386. // It would probably be better to set the substatus
  4387. // to a more descriptive error but that could potentially
  4388. // create compatibility problems.
  4389. //
  4390. case STATUS_ACCOUNT_RESTRICTION:
  4391. *SubStatus = STATUS_ACCOUNT_RESTRICTION;
  4392. break;
  4393. default:
  4394. break;
  4395. }
  4396. //
  4397. // Cleanup locally used resources
  4398. //
  4399. if ( Credential != NULL ) {
  4400. RtlZeroMemory(Credential, CredentialSize);
  4401. (*Lsa.FreeLsaHeap)( Credential );
  4402. }
  4403. if ( NlpUser != NULL ) {
  4404. MIDL_user_free( NlpUser );
  4405. }
  4406. if ( UserSid != NULL ) {
  4407. (*Lsa.FreeLsaHeap)( UserSid );
  4408. }
  4409. //
  4410. // Cleanup short was added to avoid returning from the middle of the function.
  4411. //
  4412. CleanupShort:
  4413. //
  4414. // End tracing a logon user
  4415. //
  4416. if (NtlmGlobalEventTraceFlag) {
  4417. UNICODE_STRING strTempDomain = {0};
  4418. //
  4419. // Trace header goo
  4420. //
  4421. SET_TRACE_HEADER(TraceInfo,
  4422. NtlmLogonGuid,
  4423. EVENT_TRACE_TYPE_END,
  4424. WNODE_FLAG_TRACED_GUID|WNODE_FLAG_USE_MOF_PTR,
  4425. 6);
  4426. SET_TRACE_DATA(TraceInfo,
  4427. TRACE_LOGON_STATUS,
  4428. Status);
  4429. SET_TRACE_DATA(TraceInfo,
  4430. TRACE_LOGON_TYPE,
  4431. LogonType);
  4432. SET_TRACE_USTRING(TraceInfo,
  4433. TRACE_LOGON_USERNAME,
  4434. (**AccountName));
  4435. if (AuthenticatingAuthority)
  4436. strTempDomain = **AuthenticatingAuthority;
  4437. SET_TRACE_USTRING(TraceInfo,
  4438. TRACE_LOGON_DOMAINNAME,
  4439. strTempDomain);
  4440. TraceEvent(NtlmGlobalTraceLoggerHandle,
  4441. (PEVENT_TRACE_HEADER)&TraceInfo);
  4442. }
  4443. #if _WIN64
  4444. //
  4445. // Do this last since some of the cleanup code above may refer to addresses
  4446. // inside the pTempSubmitBuffer/ProtocolSubmitBuffer (e.g., copying out the
  4447. // Workstation name, etc).
  4448. //
  4449. if (fAllocatedSubmitBuffer)
  4450. {
  4451. NtLmFreePrivateHeap( pTempSubmitBuffer );
  4452. }
  4453. #endif // _WIN64
  4454. if (CredentialUserName.Buffer)
  4455. {
  4456. NtLmFreePrivateHeap(CredentialUserName.Buffer);
  4457. }
  4458. if (CredentialDomainName.Buffer)
  4459. {
  4460. NtLmFreePrivateHeap(CredentialDomainName.Buffer);
  4461. }
  4462. //
  4463. // Return status to the caller
  4464. //
  4465. return Status;
  4466. }
  4467. VOID
  4468. LsaApLogonTerminated (
  4469. IN PLUID pLogonId
  4470. )
  4471. /*++
  4472. Routine Description:
  4473. This routine is used to notify each authentication package when a logon
  4474. session terminates. A logon session terminates when the last token
  4475. referencing the logon session is deleted.
  4476. Arguments:
  4477. pLogonId - Is the logon ID that just logged off.
  4478. Return Status:
  4479. None.
  4480. --*/
  4481. {
  4482. PACTIVE_LOGON pActiveLogon = NULL;
  4483. //
  4484. // Find the entry and de-link it from the active logon table.
  4485. //
  4486. // this scheme assumes we won't be called concurrently, multiple times,
  4487. // for the same LogonId. (would need to take write lock up front to support that).
  4488. // (note that it's quite possible to frequently attempt deleting logon sessions
  4489. // associated with other packages, which would not exist in the NTLM universe).
  4490. //
  4491. NlpLockActiveLogonsRead();
  4492. if ( NULL == (pActiveLogon = NlpFindActiveLogon( pLogonId )) )
  4493. {
  4494. NlpUnlockActiveLogons();
  4495. return;
  4496. }
  4497. NlpLockActiveLogonsReadToWrite();
  4498. //
  4499. // comment out the following assuming the same logon session can not be
  4500. // deleted twice
  4501. //
  4502. #if 0
  4503. if ( NULL == (pActiveLogon = NlpFindActiveLogon( pLogonId )) )
  4504. {
  4505. NlpUnlockActiveLogons();
  4506. return;
  4507. }
  4508. #endif
  4509. RemoveEntryList(&pActiveLogon->ListEntry);
  4510. NlpUnlockActiveLogons();
  4511. //
  4512. // Delete the credential.
  4513. //
  4514. // (Currently the LSA deletes all of the credentials before calling
  4515. // the authentication package. This line is added to be compatible
  4516. // with a more reasonable LSA.)
  4517. //
  4518. (VOID) NlpDeletePrimaryCredential( &pActiveLogon->LogonId );
  4519. //
  4520. // Deallocate the now orphaned entry.
  4521. //
  4522. I_NtLmFree( pActiveLogon );
  4523. //
  4524. // NB: We don't delete the logon session or credentials.
  4525. // That will be done by the LSA itself after we return.
  4526. //
  4527. return;
  4528. }
  4529. //+-------------------------------------------------------------------------
  4530. //
  4531. // Function: SspAcceptCredentials
  4532. //
  4533. // Synopsis: This routine is called after another package has logged
  4534. // a user on. The other package provides a user name and
  4535. // password and the Kerberos package will create a logon
  4536. // session for this user.
  4537. //
  4538. // Effects: Creates a logon session
  4539. //
  4540. // Arguments: LogonType - Type of logon, such as network or interactive
  4541. // PrimaryCredentials - Primary credentials for the account,
  4542. // containing a domain name, password, SID, etc.
  4543. // SupplementalCredentials - If present, contains credentials
  4544. // from the account itself.
  4545. //
  4546. // Requires:
  4547. //
  4548. // Returns:
  4549. //
  4550. // Notes:
  4551. //
  4552. //
  4553. //--------------------------------------------------------------------------
  4554. NTSTATUS
  4555. SspAcceptCredentials(
  4556. IN SECURITY_LOGON_TYPE LogonType,
  4557. IN PSECPKG_PRIMARY_CRED pPrimaryCredentials,
  4558. IN PSECPKG_SUPPLEMENTAL_CRED pSupplementalCredentials
  4559. )
  4560. {
  4561. NTSTATUS Status = STATUS_SUCCESS;
  4562. PMSV1_0_PRIMARY_CREDENTIAL pCredential = NULL;
  4563. ULONG CredentialSize = 0;
  4564. LUID SystemLuid = SYSTEM_LUID;
  4565. LUID NetworkServiceLuid = NETWORKSERVICE_LUID;
  4566. UNICODE_STRING DomainNameToUse;
  4567. UNICODE_STRING RealDomainName = {0};
  4568. PACTIVE_LOGON pActiveLogon = NULL;
  4569. PACTIVE_LOGON pActiveLogonEntry = NULL;
  4570. ULONG ActiveLogonEntrySize;
  4571. ULONG UserSidSize;
  4572. PUCHAR pWhere = NULL;
  4573. BOOLEAN bLogonEntryLinked = FALSE;
  4574. PMSV1_0_SUPPLEMENTAL_CREDENTIAL pMsvCredentials = NULL;
  4575. LUID CredentialLuid;
  4576. CredentialLuid = pPrimaryCredentials->LogonId;
  4577. //
  4578. // If there is no cleartext password, bail out here because we
  4579. // can't build a real credential.
  4580. //
  4581. if ((pPrimaryCredentials->Flags & PRIMARY_CRED_CLEAR_PASSWORD) == 0)
  4582. {
  4583. ASSERT((!(pPrimaryCredentials->Flags & PRIMARY_CRED_OWF_PASSWORD)) && "OWF password is not supported yet");
  4584. if (!ARGUMENT_PRESENT(pSupplementalCredentials))
  4585. {
  4586. Status = STATUS_SUCCESS;
  4587. goto Cleanup;
  4588. }
  4589. else
  4590. {
  4591. //
  4592. // Validate the MSV credentials
  4593. //
  4594. pMsvCredentials = (PMSV1_0_SUPPLEMENTAL_CREDENTIAL) pSupplementalCredentials->Credentials;
  4595. if (pSupplementalCredentials->CredentialSize < sizeof(MSV1_0_SUPPLEMENTAL_CREDENTIAL))
  4596. {
  4597. //
  4598. // LOGLOG: bad credentials - ignore them
  4599. //
  4600. Status = STATUS_SUCCESS;
  4601. goto Cleanup;
  4602. }
  4603. if (pMsvCredentials->Version != MSV1_0_CRED_VERSION)
  4604. {
  4605. Status = STATUS_SUCCESS;
  4606. goto Cleanup;
  4607. }
  4608. }
  4609. }
  4610. //
  4611. // stash the credential associated with SYSTEM under another logonID
  4612. // this is done so we can utilize that credential at a later time if
  4613. // requested by the caller.
  4614. //
  4615. if (RtlEqualLuid(
  4616. &CredentialLuid,
  4617. &SystemLuid
  4618. ))
  4619. {
  4620. CredentialLuid = NtLmGlobalLuidMachineLogon;
  4621. }
  4622. if ( NtLmLocklessGlobalPreferredDomainString.Buffer != NULL )
  4623. {
  4624. DomainNameToUse = NtLmLocklessGlobalPreferredDomainString;
  4625. }
  4626. else
  4627. {
  4628. //
  4629. // pick the correct names that are updated by policy callback
  4630. //
  4631. if (RtlEqualLuid(&pPrimaryCredentials->LogonId, &SystemLuid)
  4632. || RtlEqualLuid(&pPrimaryCredentials->LogonId, &NetworkServiceLuid))
  4633. {
  4634. RtlAcquireResourceShared(&NtLmGlobalCritSect, TRUE);
  4635. Status = NtLmDuplicateUnicodeString(
  4636. &RealDomainName,
  4637. &NtLmGlobalUnicodePrimaryDomainNameString
  4638. );
  4639. RtlReleaseResource(&NtLmGlobalCritSect);
  4640. if (!NT_SUCCESS(Status))
  4641. {
  4642. goto Cleanup;
  4643. }
  4644. DomainNameToUse = RealDomainName;
  4645. }
  4646. else
  4647. {
  4648. DomainNameToUse = pPrimaryCredentials->DomainName;
  4649. }
  4650. }
  4651. //
  4652. // Build the primary credential
  4653. //
  4654. if ((pPrimaryCredentials->Flags & PRIMARY_CRED_CLEAR_PASSWORD) != 0)
  4655. {
  4656. Status = NlpMakePrimaryCredential( &DomainNameToUse,
  4657. &pPrimaryCredentials->DownlevelName,
  4658. &pPrimaryCredentials->Password,
  4659. &pCredential,
  4660. &CredentialSize );
  4661. }
  4662. else
  4663. {
  4664. ASSERT(pMsvCredentials && "SspAcceptCredentials must have supplemental credentials");
  4665. Status = NlpMakePrimaryCredentialFromMsvCredential(
  4666. &DomainNameToUse,
  4667. &pPrimaryCredentials->DownlevelName,
  4668. pMsvCredentials,
  4669. &pCredential,
  4670. &CredentialSize );
  4671. }
  4672. if (!NT_SUCCESS(Status))
  4673. {
  4674. goto Cleanup;
  4675. }
  4676. //
  4677. // If this is an update, just change the password
  4678. //
  4679. if ((pPrimaryCredentials->Flags & PRIMARY_CRED_UPDATE) != 0)
  4680. {
  4681. Status = NlpChangePwdCredByLogonId(
  4682. &CredentialLuid,
  4683. pCredential,
  4684. 0 == (pPrimaryCredentials->Flags & PRIMARY_CRED_CLEAR_PASSWORD) // need notification only when there is no cleartext password
  4685. );
  4686. goto Cleanup;
  4687. }
  4688. //
  4689. // Now create an entry in the active logon list
  4690. //
  4691. UserSidSize = RtlLengthSid( pPrimaryCredentials->UserSid );
  4692. //
  4693. // Allocate an entry for the active logon table.
  4694. //
  4695. ActiveLogonEntrySize = ROUND_UP_COUNT(sizeof(ACTIVE_LOGON), ALIGN_DWORD) +
  4696. ROUND_UP_COUNT(UserSidSize, sizeof(WCHAR)) +
  4697. pPrimaryCredentials->DownlevelName.Length + sizeof(WCHAR) +
  4698. DomainNameToUse.Length + sizeof(WCHAR) +
  4699. pPrimaryCredentials->LogonServer.Length + sizeof(WCHAR);
  4700. pActiveLogonEntry = I_NtLmAllocate( ActiveLogonEntrySize );
  4701. if ( pActiveLogonEntry == NULL )
  4702. {
  4703. Status = STATUS_NO_MEMORY;
  4704. SspPrint((SSP_CRITICAL, "SpAcceptCredentials: no memory %ld\n", ActiveLogonEntrySize));
  4705. goto Cleanup;
  4706. }
  4707. pActiveLogonEntry->Signature = NTLM_ACTIVE_LOGON_MAGIC_SIGNATURE;
  4708. //
  4709. // Fill in the logon table entry.
  4710. //
  4711. pWhere = (PUCHAR) (pActiveLogonEntry + 1);
  4712. OLD_TO_NEW_LARGE_INTEGER(
  4713. CredentialLuid,
  4714. pActiveLogonEntry->LogonId
  4715. );
  4716. //
  4717. // Indicate that this was a logon by another package because we don't want to
  4718. // notify Netlogon of the logoff.
  4719. //
  4720. pActiveLogonEntry->Flags = LOGON_BY_OTHER_PACKAGE;
  4721. pActiveLogonEntry->LogonType = LogonType;
  4722. //
  4723. // Copy DWORD aligned fields first.
  4724. //
  4725. pWhere = ROUND_UP_POINTER( pWhere, ALIGN_DWORD );
  4726. Status = RtlCopySid(UserSidSize, (PSID)pWhere, pPrimaryCredentials->UserSid);
  4727. if ( !NT_SUCCESS(Status) )
  4728. {
  4729. goto Cleanup;
  4730. }
  4731. pActiveLogonEntry->UserSid = (PSID) pWhere;
  4732. pWhere += UserSidSize;
  4733. //
  4734. // Copy WCHAR aligned fields
  4735. //
  4736. pWhere = ROUND_UP_POINTER( pWhere, ALIGN_WCHAR );
  4737. NlpPutString( &pActiveLogonEntry->UserName,
  4738. &pPrimaryCredentials->DownlevelName,
  4739. &pWhere );
  4740. NlpPutString( &pActiveLogonEntry->LogonDomainName,
  4741. &DomainNameToUse,
  4742. &pWhere );
  4743. NlpPutString( &pActiveLogonEntry->LogonServer,
  4744. &pPrimaryCredentials->LogonServer,
  4745. &pWhere );
  4746. //
  4747. // Insert this entry into the active logon table.
  4748. //
  4749. // (sfield) LONGHORN: determine if/why there would ever be a collision.
  4750. // LSA should enforce no collision is possible via locally unique id..
  4751. //
  4752. NlpLockActiveLogonsRead();
  4753. pActiveLogon = NlpFindActiveLogon( &CredentialLuid );
  4754. if (pActiveLogon)
  4755. {
  4756. //
  4757. // This Logon ID is already in use.
  4758. //
  4759. //
  4760. // Check to see if this was someone we logged on
  4761. //
  4762. if ((pActiveLogon->Flags & (LOGON_BY_CACHE | LOGON_BY_NETLOGON | LOGON_BY_LOCAL)) != 0)
  4763. {
  4764. //
  4765. // Unlock early since we hold a write lock
  4766. //
  4767. NlpUnlockActiveLogons();
  4768. //
  4769. // We did the logon, so don't bother to add it again.
  4770. //
  4771. Status = STATUS_SUCCESS;
  4772. }
  4773. else
  4774. {
  4775. //
  4776. // Unlock early since we hold a write lock
  4777. //
  4778. NlpUnlockActiveLogons();
  4779. Status = STATUS_LOGON_SESSION_COLLISION;
  4780. SspPrint((SSP_CRITICAL,
  4781. "SpAcceptCredentials: Collision from NlpFindActiveLogon for %#x:%#x\n",
  4782. pActiveLogonEntry->LogonId.HighPart, pActiveLogonEntry->LogonId.LowPart));
  4783. }
  4784. goto Cleanup;
  4785. }
  4786. pActiveLogonEntry->EnumHandle = (ULONG)InterlockedIncrement( (PLONG)&NlpEnumerationHandle );
  4787. NlpLockActiveLogonsReadToWrite();
  4788. //
  4789. // if we worry about two SspAcceptCredentials accepting the same LogonId
  4790. // at the same time, first it is extremely unlikely, second, it is benign
  4791. //
  4792. // if (!NlpFindActiveLogon( &CredentialLuid )) // must sure this LogonId is not in the list
  4793. // {
  4794. // InsertTailList(&NlpActiveLogonListAnchor, &pActiveLogonEntry->ListEntry);
  4795. // }
  4796. //
  4797. InsertTailList(&NlpActiveLogonListAnchor, &pActiveLogonEntry->ListEntry);
  4798. NlpUnlockActiveLogons();
  4799. SspPrint((SSP_LOGON_SESS, "SpAcceptCredentials inserted %#x:%#x\n",
  4800. pActiveLogonEntry->LogonId.HighPart, pActiveLogonEntry->LogonId.LowPart));
  4801. bLogonEntryLinked = TRUE;
  4802. //
  4803. // Save the credential in the LSA.
  4804. //
  4805. Status = NlpAddPrimaryCredential(
  4806. &CredentialLuid,
  4807. pCredential,
  4808. CredentialSize
  4809. );
  4810. if ( !NT_SUCCESS( Status ) )
  4811. {
  4812. SspPrint((SSP_CRITICAL, "SpAcceptCredentials: error from AddCredential %lX\n",
  4813. Status));
  4814. goto Cleanup;
  4815. }
  4816. pActiveLogonEntry = NULL;
  4817. Cleanup:
  4818. if (pActiveLogonEntry)
  4819. {
  4820. if (bLogonEntryLinked)
  4821. {
  4822. LsaApLogonTerminated( &CredentialLuid );
  4823. }
  4824. else
  4825. {
  4826. I_NtLmFree( pActiveLogonEntry );
  4827. }
  4828. }
  4829. if ( pCredential )
  4830. {
  4831. RtlZeroMemory(pCredential, CredentialSize);
  4832. LsaFunctions->FreeLsaHeap( pCredential );
  4833. }
  4834. if (RealDomainName.Buffer)
  4835. {
  4836. NtLmFreePrivateHeap(RealDomainName.Buffer);
  4837. }
  4838. return (Status);
  4839. }
  4840. //+-------------------------------------------------------------------------
  4841. //
  4842. // Function: NlpMapLogonDomain
  4843. //
  4844. // Synopsis: This routine is called while MSV1_0 package is logging
  4845. // a user on. The logon domain name is mapped to another
  4846. // domain to be stored in the credential.
  4847. //
  4848. // Effects: Allocates output string
  4849. //
  4850. // Arguments: MappedDomain - Receives mapped domain name
  4851. // LogonDomain - Domain to which user is logging on
  4852. //
  4853. // Requires:
  4854. //
  4855. // Returns:
  4856. //
  4857. // Notes:
  4858. //
  4859. //
  4860. //--------------------------------------------------------------------------
  4861. NTSTATUS
  4862. NlpMapLogonDomain(
  4863. OUT PUNICODE_STRING MappedDomain,
  4864. IN PUNICODE_STRING LogonDomain
  4865. )
  4866. {
  4867. NTSTATUS Status = STATUS_SUCCESS;
  4868. if ( (NtLmLocklessGlobalMappedDomainString.Buffer == NULL) ||
  4869. !RtlEqualDomainName( LogonDomain, &NtLmLocklessGlobalMappedDomainString )
  4870. )
  4871. {
  4872. Status = NtLmDuplicateUnicodeString(
  4873. MappedDomain,
  4874. LogonDomain
  4875. );
  4876. goto Cleanup;
  4877. }
  4878. if ( NtLmLocklessGlobalPreferredDomainString.Buffer == NULL )
  4879. {
  4880. Status = NtLmDuplicateUnicodeString(
  4881. MappedDomain,
  4882. LogonDomain
  4883. );
  4884. } else {
  4885. Status = NtLmDuplicateUnicodeString(
  4886. MappedDomain,
  4887. &NtLmLocklessGlobalPreferredDomainString
  4888. );
  4889. }
  4890. Cleanup:
  4891. return(Status);
  4892. }
  4893. // calculate NTLM2 challenge from client and server challenges
  4894. VOID
  4895. MsvpCalculateNtlm2Challenge (
  4896. IN UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH],
  4897. IN UCHAR ChallengeFromClient[MSV1_0_CHALLENGE_LENGTH],
  4898. OUT UCHAR Challenge[MSV1_0_CHALLENGE_LENGTH]
  4899. )
  4900. {
  4901. MD5_CTX Md5Context;
  4902. SspPrint((SSP_NTLM_V2, "MsvpCalculateNtlm2Challenge mixing ChallengeFromClient and ChallengeToClient\n"));
  4903. MD5Init(
  4904. &Md5Context
  4905. );
  4906. MD5Update(
  4907. &Md5Context,
  4908. ChallengeToClient,
  4909. MSV1_0_CHALLENGE_LENGTH
  4910. );
  4911. MD5Update(
  4912. &Md5Context,
  4913. ChallengeFromClient,
  4914. MSV1_0_CHALLENGE_LENGTH
  4915. );
  4916. MD5Final(
  4917. &Md5Context
  4918. );
  4919. ASSERT(MD5DIGESTLEN >= MSV1_0_CHALLENGE_LENGTH);
  4920. RtlCopyMemory(
  4921. Challenge,
  4922. Md5Context.digest,
  4923. MSV1_0_CHALLENGE_LENGTH
  4924. );
  4925. }
  4926. // calculate NTLM2 session keys from User session key given
  4927. // to us by the system with the user's account
  4928. VOID
  4929. MsvpCalculateNtlm2SessionKeys (
  4930. IN PUSER_SESSION_KEY NtUserSessionKey,
  4931. IN UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH],
  4932. IN UCHAR ChallengeFromClient[MSV1_0_CHALLENGE_LENGTH],
  4933. OUT PUSER_SESSION_KEY LocalUserSessionKey,
  4934. OUT PLM_SESSION_KEY LocalLmSessionKey
  4935. )
  4936. {
  4937. // SESSKEY = HMAC(NtUserSessionKey, (ChallengeToClient, ChallengeFromClient))
  4938. // Lm session key is first 8 bytes of session key
  4939. HMACMD5_CTX HMACMD5Context;
  4940. HMACMD5Init(
  4941. &HMACMD5Context,
  4942. (PUCHAR)NtUserSessionKey,
  4943. sizeof(*NtUserSessionKey)
  4944. );
  4945. HMACMD5Update(
  4946. &HMACMD5Context,
  4947. ChallengeToClient,
  4948. MSV1_0_CHALLENGE_LENGTH
  4949. );
  4950. HMACMD5Update(
  4951. &HMACMD5Context,
  4952. ChallengeFromClient,
  4953. MSV1_0_CHALLENGE_LENGTH
  4954. );
  4955. HMACMD5Final(
  4956. &HMACMD5Context,
  4957. (PUCHAR)LocalUserSessionKey
  4958. );
  4959. RtlCopyMemory(
  4960. LocalLmSessionKey,
  4961. LocalUserSessionKey,
  4962. sizeof(*LocalLmSessionKey)
  4963. );
  4964. }
  4965. // calculate NTLM3 OWF from credentials
  4966. VOID
  4967. MsvpCalculateNtlm3Owf (
  4968. IN PNT_OWF_PASSWORD pNtOwfPassword,
  4969. IN PUNICODE_STRING pUserName,
  4970. IN PUNICODE_STRING pLogonDomainName,
  4971. OUT UCHAR Ntlm3Owf[MSV1_0_NTLM3_OWF_LENGTH]
  4972. )
  4973. {
  4974. HMACMD5_CTX HMACMD5Context;
  4975. WCHAR UCUserName[UNLEN+1];
  4976. UNICODE_STRING UCUserNameString;
  4977. UCUserNameString.Length = 0;
  4978. UCUserNameString.MaximumLength = UNLEN;
  4979. UCUserNameString.Buffer = UCUserName;
  4980. RtlUpcaseUnicodeString(
  4981. &UCUserNameString,
  4982. pUserName,
  4983. FALSE
  4984. );
  4985. // Calculate NTLM3 OWF -- HMAC(MD4(P), (UserName, LogonDomainName))
  4986. HMACMD5Init(
  4987. &HMACMD5Context,
  4988. (PUCHAR)pNtOwfPassword,
  4989. sizeof(*pNtOwfPassword)
  4990. );
  4991. HMACMD5Update(
  4992. &HMACMD5Context,
  4993. (PUCHAR)UCUserNameString.Buffer,
  4994. pUserName->Length
  4995. );
  4996. HMACMD5Update(
  4997. &HMACMD5Context,
  4998. (PUCHAR)pLogonDomainName->Buffer,
  4999. pLogonDomainName->Length
  5000. );
  5001. HMACMD5Final(
  5002. &HMACMD5Context,
  5003. Ntlm3Owf
  5004. );
  5005. }
  5006. // calculate LM3 response from credentials
  5007. VOID
  5008. MsvpLm3Response (
  5009. IN PNT_OWF_PASSWORD pNtOwfPassword,
  5010. IN PUNICODE_STRING pUserName,
  5011. IN PUNICODE_STRING pLogonDomainName,
  5012. IN UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH],
  5013. IN PMSV1_0_LM3_RESPONSE pLm3Response,
  5014. OUT UCHAR Response[MSV1_0_NTLM3_RESPONSE_LENGTH],
  5015. OUT PUSER_SESSION_KEY UserSessionKey,
  5016. OUT PLM_SESSION_KEY LmSessionKey
  5017. )
  5018. {
  5019. HMACMD5_CTX HMACMD5Context;
  5020. UCHAR Ntlm3Owf[MSV1_0_NTLM3_OWF_LENGTH];
  5021. SspPrint((SSP_NTLM_V2, "MsvpLm3Response: %wZ\\%wZ\n", pLogonDomainName, pUserName));
  5022. // get NTLM3 OWF
  5023. MsvpCalculateNtlm3Owf (
  5024. pNtOwfPassword,
  5025. pUserName,
  5026. pLogonDomainName,
  5027. Ntlm3Owf
  5028. );
  5029. // Calculate NTLM3 Response
  5030. // HMAC(Ntlm3Owf, (NS, V, HV, T, NC, S))
  5031. HMACMD5Init(
  5032. &HMACMD5Context,
  5033. Ntlm3Owf,
  5034. MSV1_0_NTLM3_OWF_LENGTH
  5035. );
  5036. HMACMD5Update(
  5037. &HMACMD5Context,
  5038. ChallengeToClient,
  5039. MSV1_0_CHALLENGE_LENGTH
  5040. );
  5041. HMACMD5Update(
  5042. &HMACMD5Context,
  5043. (PUCHAR)pLm3Response->ChallengeFromClient,
  5044. MSV1_0_CHALLENGE_LENGTH
  5045. );
  5046. ASSERT(MD5DIGESTLEN == MSV1_0_NTLM3_RESPONSE_LENGTH);
  5047. HMACMD5Final(
  5048. &HMACMD5Context,
  5049. Response
  5050. );
  5051. if( (UserSessionKey != NULL) && (LmSessionKey != NULL) )
  5052. {
  5053. // now compute the session keys
  5054. // HMAC(Kr, R)
  5055. HMACMD5Init(
  5056. &HMACMD5Context,
  5057. Ntlm3Owf,
  5058. MSV1_0_NTLM3_OWF_LENGTH
  5059. );
  5060. HMACMD5Update(
  5061. &HMACMD5Context,
  5062. Response,
  5063. MSV1_0_NTLM3_RESPONSE_LENGTH
  5064. );
  5065. ASSERT(MD5DIGESTLEN == MSV1_0_USER_SESSION_KEY_LENGTH);
  5066. HMACMD5Final(
  5067. &HMACMD5Context,
  5068. (PUCHAR)UserSessionKey
  5069. );
  5070. ASSERT(MSV1_0_LANMAN_SESSION_KEY_LENGTH <= MSV1_0_USER_SESSION_KEY_LENGTH);
  5071. RtlCopyMemory(
  5072. LmSessionKey,
  5073. UserSessionKey,
  5074. MSV1_0_LANMAN_SESSION_KEY_LENGTH);
  5075. }
  5076. return;
  5077. }
  5078. VOID
  5079. MsvpNtlm3Response (
  5080. IN PNT_OWF_PASSWORD pNtOwfPassword,
  5081. IN PUNICODE_STRING pUserName,
  5082. IN PUNICODE_STRING pLogonDomainName,
  5083. IN ULONG ServerNameLength,
  5084. IN UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH],
  5085. IN PMSV1_0_NTLM3_RESPONSE pNtlm3Response,
  5086. OUT UCHAR Response[MSV1_0_NTLM3_RESPONSE_LENGTH],
  5087. OUT PUSER_SESSION_KEY UserSessionKey,
  5088. OUT PLM_SESSION_KEY LmSessionKey
  5089. )
  5090. {
  5091. HMACMD5_CTX HMACMD5Context;
  5092. UCHAR Ntlm3Owf[MSV1_0_NTLM3_OWF_LENGTH];
  5093. SspPrint((SSP_NTLM_V2, "MsvpNtlm3Response: %wZ\\%wZ\n", pLogonDomainName, pUserName));
  5094. // get NTLM3 OWF
  5095. MsvpCalculateNtlm3Owf (
  5096. pNtOwfPassword,
  5097. pUserName,
  5098. pLogonDomainName,
  5099. Ntlm3Owf
  5100. );
  5101. // Calculate NTLM3 Response
  5102. // HMAC(Ntlm3Owf, (NS, V, HV, T, NC, S))
  5103. HMACMD5Init(
  5104. &HMACMD5Context,
  5105. Ntlm3Owf,
  5106. MSV1_0_NTLM3_OWF_LENGTH
  5107. );
  5108. HMACMD5Update(
  5109. &HMACMD5Context,
  5110. ChallengeToClient,
  5111. MSV1_0_CHALLENGE_LENGTH
  5112. );
  5113. HMACMD5Update(
  5114. &HMACMD5Context,
  5115. &pNtlm3Response->RespType,
  5116. (MSV1_0_NTLM3_INPUT_LENGTH + ServerNameLength)
  5117. );
  5118. ASSERT(MD5DIGESTLEN == MSV1_0_NTLM3_RESPONSE_LENGTH);
  5119. HMACMD5Final(
  5120. &HMACMD5Context,
  5121. Response
  5122. );
  5123. // now compute the session keys
  5124. // HMAC(Kr, R)
  5125. HMACMD5Init(
  5126. &HMACMD5Context,
  5127. Ntlm3Owf,
  5128. MSV1_0_NTLM3_OWF_LENGTH
  5129. );
  5130. HMACMD5Update(
  5131. &HMACMD5Context,
  5132. Response,
  5133. MSV1_0_NTLM3_RESPONSE_LENGTH
  5134. );
  5135. ASSERT(MD5DIGESTLEN == MSV1_0_USER_SESSION_KEY_LENGTH);
  5136. HMACMD5Final(
  5137. &HMACMD5Context,
  5138. (PUCHAR)UserSessionKey
  5139. );
  5140. ASSERT(MSV1_0_LANMAN_SESSION_KEY_LENGTH <= MSV1_0_USER_SESSION_KEY_LENGTH);
  5141. RtlCopyMemory(
  5142. LmSessionKey,
  5143. UserSessionKey,
  5144. MSV1_0_LANMAN_SESSION_KEY_LENGTH);
  5145. return;
  5146. }
  5147. NTSTATUS
  5148. MsvpLm20GetNtlm3ChallengeResponse (
  5149. IN PNT_OWF_PASSWORD pNtOwfPassword,
  5150. IN PUNICODE_STRING pUserName,
  5151. IN PUNICODE_STRING pLogonDomainName,
  5152. IN PUNICODE_STRING pServerName,
  5153. IN UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH],
  5154. OUT PMSV1_0_NTLM3_RESPONSE pNtlm3Response,
  5155. OUT PMSV1_0_LM3_RESPONSE pLm3Response,
  5156. OUT PUSER_SESSION_KEY UserSessionKey,
  5157. OUT PLM_SESSION_KEY LmSessionKey
  5158. )
  5159. /*++
  5160. Routine Description:
  5161. This routine calculates the NT and LM response for the NTLM3
  5162. authentication protocol
  5163. It generates the time stamp, version numbers, and
  5164. client challenge, and the NTLM3 and LM3 responses.
  5165. --*/
  5166. {
  5167. NTSTATUS Status;
  5168. // fill in version numbers, timestamp, and client's challenge
  5169. pNtlm3Response->RespType = 1;
  5170. pNtlm3Response->HiRespType = 1;
  5171. pNtlm3Response->Flags = 0;
  5172. pNtlm3Response->MsgWord = 0;
  5173. Status = NtQuerySystemTime ( (PLARGE_INTEGER)&pNtlm3Response->TimeStamp );
  5174. if (NT_SUCCESS(Status)) {
  5175. Status = SspGenerateRandomBits(
  5176. pNtlm3Response->ChallengeFromClient,
  5177. MSV1_0_CHALLENGE_LENGTH
  5178. );
  5179. }
  5180. if (!NT_SUCCESS(Status)) {
  5181. return Status;
  5182. }
  5183. #ifdef USE_CONSTANT_CHALLENGE
  5184. pNtlm3Response->TimeStamp = 0;
  5185. RtlZeroMemory(
  5186. pNtlm3Response->ChallengeFromClient,
  5187. MSV1_0_CHALLENGE_LENGTH
  5188. );
  5189. #endif
  5190. RtlCopyMemory(
  5191. pNtlm3Response->Buffer,
  5192. pServerName->Buffer,
  5193. pServerName->Length
  5194. );
  5195. // Calculate NTLM3 response, filling in response field
  5196. MsvpNtlm3Response (
  5197. pNtOwfPassword,
  5198. pUserName,
  5199. pLogonDomainName,
  5200. pServerName->Length,
  5201. ChallengeToClient,
  5202. pNtlm3Response,
  5203. pNtlm3Response->Response,
  5204. UserSessionKey,
  5205. LmSessionKey
  5206. );
  5207. // Use same challenge to compute the LM3 response
  5208. RtlCopyMemory(
  5209. pLm3Response->ChallengeFromClient,
  5210. pNtlm3Response->ChallengeFromClient,
  5211. MSV1_0_CHALLENGE_LENGTH
  5212. );
  5213. // Calculate LM3 response
  5214. MsvpLm3Response (
  5215. pNtOwfPassword,
  5216. pUserName,
  5217. pLogonDomainName,
  5218. ChallengeToClient,
  5219. pLm3Response,
  5220. pLm3Response->Response,
  5221. NULL,
  5222. NULL
  5223. );
  5224. return STATUS_SUCCESS;
  5225. }
  5226. // MsvAvInit -- function to initialize AV pair list
  5227. PMSV1_0_AV_PAIR
  5228. MsvpAvlInit(
  5229. IN void * pAvList
  5230. )
  5231. {
  5232. PMSV1_0_AV_PAIR pAvPair;
  5233. pAvPair = (PMSV1_0_AV_PAIR)pAvList;
  5234. pAvPair->AvId = MsvAvEOL;
  5235. pAvPair->AvLen = 0;
  5236. return pAvPair;
  5237. }
  5238. // MsvpAvGet -- function to find a particular AV pair by ID
  5239. PMSV1_0_AV_PAIR
  5240. MsvpAvlGet(
  5241. IN PMSV1_0_AV_PAIR pAvList, // first pair of AV pair list
  5242. IN MSV1_0_AVID AvId, // AV pair to find
  5243. IN LONG cAvList // size of AV list
  5244. )
  5245. {
  5246. MSV1_0_AV_PAIR AvPair = {0};
  5247. MSV1_0_AV_PAIR* pAvPair = NULL;
  5248. if (cAvList < sizeof(AvPair)) {
  5249. return NULL;
  5250. }
  5251. pAvPair = pAvList;
  5252. RtlCopyMemory(
  5253. &AvPair,
  5254. pAvPair,
  5255. sizeof(AvPair)
  5256. );
  5257. while (1) {
  5258. if ( (cAvList <= 0) || (((ULONG) cAvList < AvPair.AvLen + sizeof(MSV1_0_AV_PAIR))) ) {
  5259. return NULL;
  5260. }
  5261. if (AvPair.AvId == AvId)
  5262. return pAvPair;
  5263. if (AvPair.AvId == MsvAvEOL)
  5264. return NULL;
  5265. cAvList -= (AvPair.AvLen + sizeof(MSV1_0_AV_PAIR));
  5266. if (cAvList <= 0)
  5267. return NULL;
  5268. pAvPair = (PMSV1_0_AV_PAIR) ((PUCHAR) pAvPair + AvPair.AvLen + sizeof(MSV1_0_AV_PAIR));
  5269. RtlCopyMemory(
  5270. &AvPair,
  5271. pAvPair,
  5272. sizeof(AvPair)
  5273. );
  5274. }
  5275. }
  5276. // MsvpAvlLen -- function to find length of a AV list
  5277. ULONG
  5278. MsvpAvlLen(
  5279. IN PMSV1_0_AV_PAIR pAvList, // first pair of AV pair list
  5280. IN LONG cAvList // max size of AV list
  5281. )
  5282. {
  5283. PMSV1_0_AV_PAIR pCurPair;
  5284. // find the EOL
  5285. pCurPair = MsvpAvlGet(pAvList, MsvAvEOL, cAvList);
  5286. if( pCurPair == NULL )
  5287. return 0;
  5288. // compute length (not forgetting the EOL pair)
  5289. return (ULONG)(((PUCHAR)pCurPair - (PUCHAR)pAvList) + sizeof(MSV1_0_AV_PAIR));
  5290. }
  5291. // MsvpAvlAdd -- function to add an AV pair to a list
  5292. // assumes buffer is long enough!
  5293. // returns NULL on failure.
  5294. PMSV1_0_AV_PAIR
  5295. MsvpAvlAdd(
  5296. IN PMSV1_0_AV_PAIR pAvList, // first pair of AV pair list
  5297. IN MSV1_0_AVID AvId, // AV pair to add
  5298. IN PUNICODE_STRING pString, // value of pair
  5299. IN LONG cAvList // max size of AV list
  5300. )
  5301. {
  5302. PMSV1_0_AV_PAIR pCurPair;
  5303. // find the EOL
  5304. pCurPair = MsvpAvlGet(pAvList, MsvAvEOL, cAvList);
  5305. if( pCurPair == NULL )
  5306. return NULL;
  5307. //
  5308. // append the new AvPair (assume the buffer is long enough!)
  5309. //
  5310. pCurPair->AvId = (USHORT)AvId;
  5311. pCurPair->AvLen = (USHORT)pString->Length;
  5312. memcpy(pCurPair+1, pString->Buffer, pCurPair->AvLen);
  5313. // top it off with a new EOL
  5314. pCurPair = (PMSV1_0_AV_PAIR)((PUCHAR)pCurPair + sizeof(MSV1_0_AV_PAIR) + pCurPair->AvLen);
  5315. pCurPair->AvId = MsvAvEOL;
  5316. pCurPair->AvLen = 0;
  5317. return pCurPair;
  5318. }
  5319. // MsvpAvlSize -- fucntion to calculate length needed for an AV list
  5320. ULONG
  5321. MsvpAvlSize(
  5322. IN ULONG iPairs, // number of AV pairs response will include
  5323. IN ULONG iPairsLen // total size of values for the pairs
  5324. )
  5325. {
  5326. return (
  5327. iPairs * sizeof(MSV1_0_AV_PAIR) + // space for the pairs' headers
  5328. iPairsLen + // space for pairs' values
  5329. sizeof(MSV1_0_AV_PAIR) // space for the EOL
  5330. );
  5331. }
  5332. NTSTATUS
  5333. MsvpAvlToString(
  5334. IN PUNICODE_STRING AvlString,
  5335. IN MSV1_0_AVID AvId,
  5336. IN OUT LPWSTR *szAvlString
  5337. )
  5338. {
  5339. PMSV1_0_AV_PAIR pAV;
  5340. MSV1_0_AV_PAIR AV = {0};
  5341. *szAvlString = NULL;
  5342. if ( AvlString->Buffer == NULL || AvlString->Length == 0 )
  5343. {
  5344. return STATUS_SUCCESS;
  5345. }
  5346. pAV = MsvpAvlGet(
  5347. (PMSV1_0_AV_PAIR)AvlString->Buffer,
  5348. AvId,
  5349. AvlString->Length
  5350. );
  5351. if ( pAV != NULL )
  5352. {
  5353. LPWSTR szResult;
  5354. RtlCopyMemory(&AV, pAV, sizeof(AV));
  5355. szResult = NtLmAllocate( AV.AvLen + sizeof(WCHAR) );
  5356. if ( szResult == NULL )
  5357. {
  5358. SspPrint(( SSP_CRITICAL, "MsvpAvlToString: Error allocating memory\n"));
  5359. return STATUS_INSUFFICIENT_RESOURCES;
  5360. }
  5361. RtlCopyMemory( szResult, ( pAV + 1 ), AV.AvLen );
  5362. szResult[ AV.AvLen /sizeof(WCHAR) ] = L'\0';
  5363. *szAvlString = szResult;
  5364. }
  5365. return STATUS_SUCCESS;
  5366. }
  5367. NTSTATUS
  5368. MsvpAvlToFlag(
  5369. IN PUNICODE_STRING AvlString,
  5370. IN MSV1_0_AVID AvId,
  5371. IN OUT ULONG *ulAvlFlag
  5372. )
  5373. {
  5374. PMSV1_0_AV_PAIR pAV;
  5375. *ulAvlFlag = 0;
  5376. if ( AvlString->Buffer == NULL || AvlString->Length == 0 )
  5377. {
  5378. return STATUS_SUCCESS;
  5379. }
  5380. pAV = MsvpAvlGet(
  5381. (PMSV1_0_AV_PAIR)AvlString->Buffer,
  5382. AvId,
  5383. AvlString->Length
  5384. );
  5385. if ( pAV != NULL )
  5386. {
  5387. if( pAV->AvLen == sizeof( *ulAvlFlag ) )
  5388. {
  5389. CopyMemory( ulAvlFlag, (pAV+1), sizeof(ULONG) );
  5390. return STATUS_SUCCESS;
  5391. }
  5392. }
  5393. return STATUS_NOT_FOUND;
  5394. }