Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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