6549 lines
182 KiB
C
6549 lines
182 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 - 1999 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
nlmain.c
|
||
|
||
Abstract:
|
||
|
||
This file contains the initialization and dispatch routines
|
||
for the LAN Manager portions of the MSV1_0 authentication package.
|
||
|
||
Author:
|
||
|
||
Jim Kelly 11-Apr-1991
|
||
|
||
Revision History:
|
||
25-Apr-1991 (cliffv)
|
||
Added interactive logon support for PDK.
|
||
|
||
Chandana Surlu 21-Jul-1996
|
||
Stolen from \\kernel\razzle3\src\security\msv1_0\nlmain.c
|
||
|
||
JClark 28-Jun-2000
|
||
Added WMI Trace Logging Support
|
||
|
||
--*/
|
||
|
||
#include <global.h>
|
||
|
||
#include "msp.h"
|
||
#undef EXTERN
|
||
#define NLP_ALLOCATE
|
||
#include "nlp.h"
|
||
#undef NLP_ALLOCATE
|
||
|
||
#include <lmsname.h> // Service Names
|
||
|
||
#include <safeboot.h>
|
||
|
||
#include <confname.h> // NETSETUPP_NETLOGON_JD_STOPPED
|
||
|
||
#include "nlpcache.h" // logon cache prototypes
|
||
|
||
#include "trace.h" // wmi tracing goo
|
||
|
||
#include "msvwow.h"
|
||
|
||
NTSTATUS
|
||
NlpMapLogonDomain(
|
||
OUT PUNICODE_STRING MappedDomain,
|
||
IN PUNICODE_STRING LogonDomain
|
||
);
|
||
|
||
NTSTATUS
|
||
NlInitialize(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initialize NETLOGON portion of msv1_0 authentication package.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Status:
|
||
|
||
STATUS_SUCCESS - Indicates NETLOGON successfully initialized.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
LPWSTR ComputerName;
|
||
DWORD ComputerNameLength = MAX_COMPUTERNAME_LENGTH + 1;
|
||
NT_PRODUCT_TYPE NtProductType;
|
||
UNICODE_STRING TempUnicodeString;
|
||
HKEY Key ;
|
||
int err ;
|
||
ULONG Size ;
|
||
ULONG Type ;
|
||
ULONG Value ;
|
||
|
||
//
|
||
// Initialize global data
|
||
//
|
||
|
||
NlpEnumerationHandle = 0;
|
||
NlpLogonAttemptCount = 0;
|
||
|
||
|
||
NlpComputerName.Buffer = NULL;
|
||
RtlInitUnicodeString( &NlpPrimaryDomainName, NULL );
|
||
NlpSamDomainName.Buffer = NULL;
|
||
NlpSamDomainId = NULL;
|
||
NlpSamDomainHandle = NULL;
|
||
|
||
|
||
|
||
//
|
||
// Get the name of this machine.
|
||
//
|
||
|
||
ComputerName = I_NtLmAllocate(
|
||
ComputerNameLength * sizeof(WCHAR) );
|
||
|
||
if (ComputerName == NULL ||
|
||
!GetComputerNameW( ComputerName, &ComputerNameLength )) {
|
||
|
||
SspPrint((SSP_MISC, "Cannot get computername %lX\n", GetLastError() ));
|
||
|
||
NlpLanmanInstalled = FALSE;
|
||
I_NtLmFree( ComputerName );
|
||
ComputerName = NULL;
|
||
} else {
|
||
|
||
NlpLanmanInstalled = TRUE;
|
||
}
|
||
|
||
//
|
||
// For Safe mode boot (minimal, no networking)
|
||
// turn off the lanmaninstalled flag, since no network components will
|
||
// be started.
|
||
//
|
||
|
||
err = RegOpenKeyExW(
|
||
HKEY_LOCAL_MACHINE,
|
||
L"System\\CurrentControlSet\\Control\\SafeBoot\\Option",
|
||
0,
|
||
KEY_READ,
|
||
&Key );
|
||
|
||
if ( err == ERROR_SUCCESS )
|
||
{
|
||
Value = 0 ;
|
||
Size = sizeof( ULONG );
|
||
|
||
err = RegQueryValueExW(
|
||
Key,
|
||
L"OptionValue",
|
||
0,
|
||
&Type,
|
||
(PUCHAR) &Value,
|
||
&Size );
|
||
|
||
RegCloseKey( Key );
|
||
|
||
if ( err == ERROR_SUCCESS )
|
||
{
|
||
NtLmGlobalSafeBoot = TRUE;
|
||
|
||
if ( Value == SAFEBOOT_MINIMAL )
|
||
{
|
||
NlpLanmanInstalled = FALSE ;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
RtlInitUnicodeString( &NlpComputerName, ComputerName );
|
||
|
||
//
|
||
// Determine if this machine is running Windows NT or Lanman NT.
|
||
// LanMan NT runs on a domain controller.
|
||
//
|
||
|
||
if ( !RtlGetNtProductType( &NtProductType ) ) {
|
||
SspPrint((SSP_MISC, "Nt Product Type undefined (WinNt assumed)\n" ));
|
||
NtProductType = NtProductWinNt;
|
||
}
|
||
|
||
NlpWorkstation = (BOOLEAN)(NtProductType != NtProductLanManNt);
|
||
|
||
|
||
|
||
//
|
||
// Initialize any locks.
|
||
//
|
||
|
||
RtlInitializeResource(&NlpActiveLogonLock);
|
||
|
||
//
|
||
// initialize the cache - creates a critical section is all
|
||
//
|
||
|
||
NlpCacheInitialize();
|
||
|
||
|
||
//
|
||
// Attempt to load Netlogon.dll
|
||
//
|
||
|
||
NlpLoadNetlogonDll();
|
||
|
||
#ifdef COMPILED_BY_DEVELOPER
|
||
SspPrint((SSP_CRITICAL, "COMPILED_BY_DEVELOPER breakpoint.\n"));
|
||
DbgBreakPoint();
|
||
#endif // COMPILED_BY_DEVELOPER
|
||
|
||
|
||
|
||
//
|
||
// Initialize useful encryption constants
|
||
//
|
||
|
||
Status = RtlCalculateLmOwfPassword( "", &NlpNullLmOwfPassword );
|
||
ASSERT( NT_SUCCESS(Status) );
|
||
|
||
RtlInitUnicodeString(&TempUnicodeString, NULL);
|
||
Status = RtlCalculateNtOwfPassword(&TempUnicodeString,
|
||
&NlpNullNtOwfPassword);
|
||
ASSERT( NT_SUCCESS(Status) );
|
||
|
||
//
|
||
// Initialize the SubAuthentication Dlls
|
||
//
|
||
|
||
Msv1_0SubAuthenticationInitialization();
|
||
|
||
|
||
|
||
|
||
#ifdef notdef
|
||
//
|
||
// If we weren't successful,
|
||
// Clean up global resources we intended to initialize.
|
||
//
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
if ( NlpComputerName.Buffer != NULL ) {
|
||
MIDL_user_free( NlpComputerName.Buffer );
|
||
}
|
||
|
||
}
|
||
#endif // notdef
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
NTSTATUS
|
||
NlWaitForEvent(
|
||
LPWSTR EventName,
|
||
ULONG Timeout
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Wait up to Timeout seconds for EventName to be triggered.
|
||
|
||
Arguments:
|
||
|
||
EventName - Name of event to wait on
|
||
|
||
Timeout - Timeout for event (in seconds).
|
||
|
||
Return Status:
|
||
|
||
STATUS_SUCCESS - Indicates NETLOGON successfully initialized.
|
||
STATUS_NETLOGON_NOT_STARTED - Timeout occurred.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
HANDLE EventHandle;
|
||
OBJECT_ATTRIBUTES EventAttributes;
|
||
UNICODE_STRING EventNameString;
|
||
LARGE_INTEGER LocalTimeout;
|
||
|
||
|
||
//
|
||
// Create an event for us to wait on.
|
||
//
|
||
|
||
RtlInitUnicodeString( &EventNameString, EventName);
|
||
InitializeObjectAttributes( &EventAttributes, &EventNameString, 0, 0, NULL);
|
||
|
||
Status = NtCreateEvent(
|
||
&EventHandle,
|
||
SYNCHRONIZE,
|
||
&EventAttributes,
|
||
NotificationEvent,
|
||
(BOOLEAN) FALSE // The event is initially not signaled
|
||
);
|
||
|
||
if ( !NT_SUCCESS(Status)) {
|
||
|
||
//
|
||
// If the event already exists, the server beat us to creating it.
|
||
// Just open it.
|
||
//
|
||
|
||
if( Status == STATUS_OBJECT_NAME_EXISTS ||
|
||
Status == STATUS_OBJECT_NAME_COLLISION ) {
|
||
|
||
Status = NtOpenEvent( &EventHandle,
|
||
SYNCHRONIZE,
|
||
&EventAttributes );
|
||
|
||
}
|
||
if ( !NT_SUCCESS(Status)) {
|
||
SspPrint((SSP_MISC, "OpenEvent failed %lx\n", Status ));
|
||
return Status;
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Wait for NETLOGON to initialize. Wait a maximum of Timeout seconds.
|
||
//
|
||
|
||
LocalTimeout.QuadPart = ((LONGLONG)(Timeout)) * (-10000000);
|
||
Status = NtWaitForSingleObject( EventHandle, (BOOLEAN)FALSE, &LocalTimeout);
|
||
(VOID) NtClose( EventHandle );
|
||
|
||
if ( !NT_SUCCESS(Status) || Status == STATUS_TIMEOUT ) {
|
||
if ( Status == STATUS_TIMEOUT ) {
|
||
Status = STATUS_NETLOGON_NOT_STARTED; // Map to an error condition
|
||
}
|
||
return Status;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
NlDoingSetup(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Returns TRUE if we're running setup.
|
||
|
||
Arguments:
|
||
|
||
NONE.
|
||
|
||
Return Status:
|
||
|
||
TRUE - We're currently running setup
|
||
FALSE - We're not running setup or aren't sure.
|
||
|
||
--*/
|
||
|
||
{
|
||
LONG RegStatus;
|
||
|
||
HKEY KeyHandle = NULL;
|
||
DWORD ValueType;
|
||
DWORD Value;
|
||
DWORD ValueSize;
|
||
|
||
//
|
||
// Open the key for HKLM\SYSTEM\Setup
|
||
//
|
||
|
||
RegStatus = RegOpenKeyExA(
|
||
HKEY_LOCAL_MACHINE,
|
||
"SYSTEM\\Setup",
|
||
0, //Reserved
|
||
KEY_QUERY_VALUE,
|
||
&KeyHandle );
|
||
|
||
if ( RegStatus != ERROR_SUCCESS ) {
|
||
SspPrint((SSP_INIT, "NlDoingSetup: Cannot open registy key 'HKLM\\SYSTEM\\Setup' %ld.\n",
|
||
RegStatus ));
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Get the value that says whether we're doing setup.
|
||
//
|
||
|
||
ValueSize = sizeof(Value);
|
||
RegStatus = RegQueryValueExA(
|
||
KeyHandle,
|
||
"SystemSetupInProgress",
|
||
0,
|
||
&ValueType,
|
||
(LPBYTE)&Value,
|
||
&ValueSize );
|
||
|
||
RegCloseKey( KeyHandle );
|
||
|
||
if ( RegStatus != ERROR_SUCCESS ) {
|
||
SspPrint((SSP_INIT, "NlDoingSetup: Cannot query value of 'HKLM\\SYSTEM\\Setup\\SystemSetupInProgress' %ld.\n",
|
||
RegStatus ));
|
||
return FALSE;
|
||
}
|
||
|
||
if ( ValueType != REG_DWORD ) {
|
||
SspPrint((SSP_INIT, "NlDoingSetup: value of 'HKLM\\SYSTEM\\Setup\\SystemSetupInProgress'is not a REG_DWORD %ld.\n",
|
||
ValueType ));
|
||
return FALSE;
|
||
}
|
||
|
||
if ( ValueSize != sizeof(Value) ) {
|
||
SspPrint((SSP_INIT, "NlDoingSetup: value size of 'HKLM\\SYSTEM\\Setup\\SystemSetupInProgress'is not 4 %ld.\n",
|
||
ValueSize ));
|
||
return FALSE;
|
||
}
|
||
|
||
if ( Value != 1 ) {
|
||
// KdPrint(( "NlDoingSetup: not doing setup\n" ));
|
||
return FALSE;
|
||
}
|
||
|
||
SspPrint((SSP_INIT, "NlDoingSetup: doing setup\n" ));
|
||
return TRUE;
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NlWaitForNetlogon(
|
||
ULONG Timeout
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Wait up to Timeout seconds for the netlogon service to start.
|
||
|
||
Arguments:
|
||
|
||
Timeout - Timeout for event (in seconds).
|
||
|
||
Return Status:
|
||
|
||
STATUS_SUCCESS - Indicates NETLOGON successfully initialized.
|
||
STATUS_NETLOGON_NOT_STARTED - Timeout occurred.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
NET_API_STATUS NetStatus;
|
||
SC_HANDLE ScManagerHandle = NULL;
|
||
SC_HANDLE ServiceHandle = NULL;
|
||
SERVICE_STATUS ServiceStatus;
|
||
LPQUERY_SERVICE_CONFIG ServiceConfig;
|
||
LPQUERY_SERVICE_CONFIG AllocServiceConfig = NULL;
|
||
QUERY_SERVICE_CONFIG DummyServiceConfig;
|
||
DWORD ServiceConfigSize;
|
||
|
||
|
||
//
|
||
// If the netlogon service is currently running,
|
||
// skip the rest of the tests.
|
||
//
|
||
|
||
Status = NlWaitForEvent( L"\\NETLOGON_SERVICE_STARTED", 0 );
|
||
|
||
if ( NT_SUCCESS(Status) ) {
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// If we're in setup,
|
||
// don't bother waiting for netlogon to start.
|
||
//
|
||
|
||
if ( NlDoingSetup() ) {
|
||
return STATUS_NETLOGON_NOT_STARTED;
|
||
}
|
||
|
||
//
|
||
// Open a handle to the Netlogon Service.
|
||
//
|
||
|
||
ScManagerHandle = OpenSCManager(
|
||
NULL,
|
||
NULL,
|
||
SC_MANAGER_CONNECT );
|
||
|
||
if (ScManagerHandle == NULL) {
|
||
SspPrint((SSP_MISC, "NlWaitForNetlogon: OpenSCManager failed: "
|
||
"%lu\n", GetLastError()));
|
||
Status = STATUS_NETLOGON_NOT_STARTED;
|
||
goto Cleanup;
|
||
}
|
||
|
||
ServiceHandle = OpenService(
|
||
ScManagerHandle,
|
||
SERVICE_NETLOGON,
|
||
SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG );
|
||
|
||
if ( ServiceHandle == NULL ) {
|
||
SspPrint((SSP_MISC, "NlWaitForNetlogon: OpenService failed: "
|
||
"%lu\n", GetLastError()));
|
||
Status = STATUS_NETLOGON_NOT_STARTED;
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
//
|
||
// If the Netlogon service isn't configured to be automatically started
|
||
// by the service controller, don't bother waiting for it to start.
|
||
//
|
||
// ?? Pass "DummyServiceConfig" and "sizeof(..)" since QueryService config
|
||
// won't allow a null pointer, yet.
|
||
|
||
if ( QueryServiceConfig(
|
||
ServiceHandle,
|
||
&DummyServiceConfig,
|
||
sizeof(DummyServiceConfig),
|
||
&ServiceConfigSize )) {
|
||
|
||
ServiceConfig = &DummyServiceConfig;
|
||
|
||
} else {
|
||
|
||
NetStatus = GetLastError();
|
||
if ( NetStatus != ERROR_INSUFFICIENT_BUFFER ) {
|
||
SspPrint((SSP_MISC, "NlWaitForNetlogon: QueryServiceConfig failed: "
|
||
"%lu\n", NetStatus));
|
||
Status = STATUS_NETLOGON_NOT_STARTED;
|
||
goto Cleanup;
|
||
}
|
||
|
||
AllocServiceConfig = I_NtLmAllocate( ServiceConfigSize );
|
||
ServiceConfig = AllocServiceConfig;
|
||
|
||
if ( AllocServiceConfig == NULL ) {
|
||
Status = STATUS_NO_MEMORY;
|
||
goto Cleanup;
|
||
}
|
||
|
||
if ( !QueryServiceConfig(
|
||
ServiceHandle,
|
||
ServiceConfig,
|
||
ServiceConfigSize,
|
||
&ServiceConfigSize )) {
|
||
|
||
SspPrint((SSP_MISC, "NlWaitForNetlogon: QueryServiceConfig "
|
||
"failed again: %lu\n", GetLastError()));
|
||
Status = STATUS_NETLOGON_NOT_STARTED;
|
||
goto Cleanup;
|
||
}
|
||
}
|
||
|
||
if ( ServiceConfig->dwStartType != SERVICE_AUTO_START ) {
|
||
SspPrint((SSP_MISC, "NlWaitForNetlogon: Netlogon start type invalid:"
|
||
"%lu\n", ServiceConfig->dwStartType ));
|
||
Status = STATUS_NETLOGON_NOT_STARTED;
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Loop waiting for the netlogon service to start.
|
||
// (Convert Timeout to a number of 10 second iterations)
|
||
//
|
||
|
||
Timeout = (Timeout+9)/10;
|
||
for (;;) {
|
||
|
||
|
||
//
|
||
// Query the status of the Netlogon service.
|
||
//
|
||
|
||
if (! QueryServiceStatus( ServiceHandle, &ServiceStatus )) {
|
||
|
||
SspPrint((SSP_MISC, "NlWaitForNetlogon: QueryServiceStatus failed: "
|
||
"%lu\n", GetLastError() ));
|
||
Status = STATUS_NETLOGON_NOT_STARTED;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Return or continue waiting depending on the state of
|
||
// the netlogon service.
|
||
//
|
||
|
||
switch( ServiceStatus.dwCurrentState) {
|
||
case SERVICE_RUNNING:
|
||
Status = STATUS_SUCCESS;
|
||
goto Cleanup;
|
||
|
||
case SERVICE_STOPPED:
|
||
|
||
//
|
||
// If Netlogon failed to start,
|
||
// error out now. The caller has waited long enough to start.
|
||
//
|
||
if ( ServiceStatus.dwWin32ExitCode != ERROR_SERVICE_NEVER_STARTED ){
|
||
|
||
SspPrint((SSP_MISC, "NlWaitForNetlogon: "
|
||
"Netlogon service couldn't start: %lu %lx\n",
|
||
ServiceStatus.dwWin32ExitCode,
|
||
ServiceStatus.dwWin32ExitCode ));
|
||
if ( ServiceStatus.dwWin32ExitCode == ERROR_SERVICE_SPECIFIC_ERROR ) {
|
||
SspPrint((SSP_MISC, " Service specific error code: %lu %lx\n",
|
||
ServiceStatus.dwServiceSpecificExitCode,
|
||
ServiceStatus.dwServiceSpecificExitCode ));
|
||
}
|
||
Status = STATUS_NETLOGON_NOT_STARTED;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// If Netlogon has never been started on this boot,
|
||
// continue waiting for it to start.
|
||
//
|
||
|
||
break;
|
||
|
||
//
|
||
// If Netlogon is trying to start up now,
|
||
// continue waiting for it to start.
|
||
//
|
||
case SERVICE_START_PENDING:
|
||
break;
|
||
|
||
//
|
||
// Any other state is bogus.
|
||
//
|
||
default:
|
||
SspPrint((SSP_MISC, "NlWaitForNetlogon: "
|
||
"Invalid service state: %lu\n",
|
||
ServiceStatus.dwCurrentState ));
|
||
Status = STATUS_NETLOGON_NOT_STARTED;
|
||
goto Cleanup;
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Wait ten seconds for the netlogon service to start.
|
||
// If it has successfully started, just return now.
|
||
//
|
||
|
||
Status = NlWaitForEvent( L"\\NETLOGON_SERVICE_STARTED", 10 );
|
||
|
||
if ( Status != STATUS_NETLOGON_NOT_STARTED ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// If we've waited long enough for netlogon to start,
|
||
// time out now.
|
||
//
|
||
|
||
if ( (--Timeout) == 0 ) {
|
||
Status = STATUS_NETLOGON_NOT_STARTED;
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
}
|
||
|
||
/* NOT REACHED */
|
||
|
||
Cleanup:
|
||
if ( ScManagerHandle != NULL ) {
|
||
(VOID) CloseServiceHandle(ScManagerHandle);
|
||
}
|
||
if ( ServiceHandle != NULL ) {
|
||
(VOID) CloseServiceHandle(ServiceHandle);
|
||
}
|
||
if ( AllocServiceConfig != NULL ) {
|
||
I_NtLmFree( AllocServiceConfig );
|
||
}
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NlSamInitialize(
|
||
ULONG Timeout
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initialize the MSV1_0 Authentication Package's communication to the SAM
|
||
database. This initialization will take place once immediately prior
|
||
to the first actual use of the SAM database.
|
||
|
||
Arguments:
|
||
|
||
Timeout - Timeout for event (in seconds).
|
||
|
||
Return Status:
|
||
|
||
STATUS_SUCCESS - Indicates NETLOGON successfully initialized.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
//
|
||
// locals that are staging area for globals.
|
||
//
|
||
|
||
UNICODE_STRING PrimaryDomainName;
|
||
PSID SamDomainId = NULL;
|
||
UNICODE_STRING SamDomainName;
|
||
SAMPR_HANDLE SamDomainHandle = NULL;
|
||
BOOLEAN UasCompatibilityRequired;
|
||
|
||
UNICODE_STRING DnsTreeName;
|
||
|
||
PLSAPR_POLICY_INFORMATION PolicyPrimaryDomainInfo = NULL;
|
||
PLSAPR_POLICY_INFORMATION PolicyAccountDomainInfo = NULL;
|
||
|
||
SAMPR_HANDLE SamHandle = NULL;
|
||
#ifdef SAM
|
||
PSAMPR_DOMAIN_INFO_BUFFER DomainInfo = NULL;
|
||
#endif // SAM
|
||
|
||
PrimaryDomainName.Buffer = NULL;
|
||
SamDomainName.Buffer = NULL;
|
||
DnsTreeName.Buffer = NULL;
|
||
|
||
|
||
//
|
||
// Wait for SAM to finish initialization.
|
||
//
|
||
|
||
Status = NlWaitForEvent( L"\\SAM_SERVICE_STARTED", Timeout );
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Determine the DomainName and DomainId of the Account Database
|
||
//
|
||
|
||
Status = I_LsarQueryInformationPolicy( NtLmGlobalPolicyHandle,
|
||
PolicyAccountDomainInformation,
|
||
&PolicyAccountDomainInfo );
|
||
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
if ( PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainSid == NULL ||
|
||
PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainName.Length == 0 ) {
|
||
SspPrint((SSP_MISC, "Account domain info from LSA invalid.\n"));
|
||
Status = STATUS_NO_SUCH_DOMAIN;
|
||
goto Cleanup;
|
||
}
|
||
|
||
Status = I_LsarQueryInformationPolicy(
|
||
NtLmGlobalPolicyHandle,
|
||
PolicyPrimaryDomainInformation,
|
||
&PolicyPrimaryDomainInfo );
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
if ( PolicyPrimaryDomainInfo->PolicyPrimaryDomainInfo.Name.Length == 0 )
|
||
{
|
||
SspPrint((SSP_CRITICAL, "Primary domain info from LSA invalid.\n"));
|
||
Status = STATUS_NO_SUCH_DOMAIN;
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
//
|
||
// save PrimaryDomainName
|
||
//
|
||
|
||
PrimaryDomainName.Length = PolicyPrimaryDomainInfo->PolicyPrimaryDomainInfo.Name.Length;
|
||
PrimaryDomainName.MaximumLength = PrimaryDomainName.Length;
|
||
|
||
PrimaryDomainName.Buffer =
|
||
(PWSTR)I_NtLmAllocate( PrimaryDomainName.MaximumLength );
|
||
|
||
if ( PrimaryDomainName.Buffer == NULL ) {
|
||
Status = STATUS_NO_MEMORY;
|
||
goto Cleanup;
|
||
}
|
||
|
||
RtlCopyMemory( PrimaryDomainName.Buffer,
|
||
PolicyPrimaryDomainInfo->PolicyPrimaryDomainInfo.Name.Buffer,
|
||
PrimaryDomainName.Length );
|
||
|
||
|
||
//
|
||
// Save the domain id of this domain
|
||
//
|
||
|
||
SamDomainId = I_NtLmAllocate(
|
||
RtlLengthSid( PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainSid )
|
||
);
|
||
|
||
|
||
if ( SamDomainId == NULL ) {
|
||
Status = STATUS_NO_MEMORY;
|
||
goto Cleanup;
|
||
}
|
||
|
||
RtlCopyMemory( SamDomainId,
|
||
PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainSid,
|
||
RtlLengthSid( PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainSid ));
|
||
|
||
|
||
//
|
||
// Save the name of the account database on this machine.
|
||
//
|
||
// On a workstation, the account database is refered to by the machine
|
||
// name and not the database name.
|
||
|
||
// The above being true, the machine name is set to MACHINENAME during
|
||
// setup and for the duration when the machine has a real machine name
|
||
// until the end of setup, NlpSamDomainName will still have MACHINENAME.
|
||
// This is not what the caller expects to authenticate against, so we
|
||
// force a look from the Lsa all the time.
|
||
|
||
// We assume that NlpSamDomainName will get the right info from the Lsa
|
||
|
||
SamDomainName.Length = PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainName.Length;
|
||
SamDomainName.MaximumLength = (USHORT)
|
||
(SamDomainName.Length + sizeof(WCHAR));
|
||
|
||
SamDomainName.Buffer =
|
||
I_NtLmAllocate( SamDomainName.MaximumLength );
|
||
|
||
if ( SamDomainName.Buffer == NULL ) {
|
||
Status = STATUS_NO_MEMORY;
|
||
goto Cleanup;
|
||
}
|
||
|
||
RtlCopyMemory( SamDomainName.Buffer,
|
||
PolicyAccountDomainInfo->PolicyAccountDomainInfo.DomainName.Buffer,
|
||
SamDomainName.MaximumLength );
|
||
|
||
|
||
//
|
||
// Open our connection with SAM
|
||
//
|
||
|
||
Status = I_SamIConnect( NULL, // No server name
|
||
&SamHandle,
|
||
SAM_SERVER_CONNECT,
|
||
(BOOLEAN) TRUE ); // Indicate we are privileged
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
SamHandle = NULL;
|
||
SspPrint((SSP_CRITICAL, "Cannot SamIConnect %lX\n", Status));
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Open the domain.
|
||
//
|
||
|
||
Status = I_SamrOpenDomain( SamHandle,
|
||
DOMAIN_ALL_ACCESS,
|
||
SamDomainId,
|
||
&SamDomainHandle );
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
SamDomainHandle = NULL;
|
||
SspPrint((SSP_CRITICAL, "Cannot SamrOpenDomain %lX\n", Status));
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// query the TreeName (since SAM was not up during package initialization)
|
||
// update the various globals.
|
||
//
|
||
|
||
|
||
if( !NlpSamInitialized )
|
||
{
|
||
//
|
||
// make the query before taking the exclusive lock, to avoid possible
|
||
// deadlock conditions.
|
||
//
|
||
|
||
SsprQueryTreeName( &DnsTreeName );
|
||
}
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
RtlAcquireResourceExclusive(&NtLmGlobalCritSect, TRUE);
|
||
|
||
if( !NlpSamInitialized ) {
|
||
|
||
NlpPrimaryDomainName = PrimaryDomainName;
|
||
NlpSamDomainId = SamDomainId;
|
||
NlpSamDomainName = SamDomainName;
|
||
NlpSamDomainHandle = SamDomainHandle;
|
||
|
||
if( NtLmGlobalUnicodeDnsTreeName.Buffer )
|
||
{
|
||
NtLmFree( NtLmGlobalUnicodeDnsTreeName.Buffer );
|
||
}
|
||
|
||
NtLmGlobalUnicodeDnsTreeName = DnsTreeName;
|
||
SsprUpdateTargetInfo();
|
||
|
||
|
||
NlpSamInitialized = TRUE;
|
||
|
||
//
|
||
// mark locals invalid so they don't get freed.
|
||
//
|
||
|
||
PrimaryDomainName.Buffer = NULL;
|
||
SamDomainId = NULL;
|
||
SamDomainName.Buffer = NULL;
|
||
SamDomainHandle = NULL;
|
||
DnsTreeName.Buffer = NULL;
|
||
}
|
||
|
||
RtlReleaseResource(&NtLmGlobalCritSect);
|
||
|
||
|
||
Cleanup:
|
||
|
||
if( DnsTreeName.Buffer )
|
||
{
|
||
NtLmFree( DnsTreeName.Buffer );
|
||
}
|
||
|
||
if ( PrimaryDomainName.Buffer != NULL ) {
|
||
I_NtLmFree( PrimaryDomainName.Buffer );
|
||
}
|
||
|
||
if ( SamDomainName.Buffer != NULL ) {
|
||
I_NtLmFree( SamDomainName.Buffer );
|
||
}
|
||
|
||
if ( SamDomainHandle != NULL ) {
|
||
(VOID) I_SamrCloseHandle( &SamDomainHandle );
|
||
}
|
||
|
||
if ( SamDomainId != NULL ) {
|
||
I_NtLmFree( SamDomainId );
|
||
}
|
||
|
||
if ( PolicyAccountDomainInfo != NULL ) {
|
||
I_LsaIFree_LSAPR_POLICY_INFORMATION( PolicyAccountDomainInformation,
|
||
PolicyAccountDomainInfo );
|
||
}
|
||
|
||
if ( PolicyPrimaryDomainInfo != NULL ) {
|
||
I_LsaIFree_LSAPR_POLICY_INFORMATION( PolicyPrimaryDomainInformation,
|
||
PolicyPrimaryDomainInfo );
|
||
}
|
||
|
||
if ( SamHandle != NULL ) {
|
||
(VOID) I_SamrCloseHandle( &SamHandle );
|
||
}
|
||
|
||
|
||
return Status;
|
||
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
MspLm20Challenge (
|
||
IN PLSA_CLIENT_REQUEST ClientRequest,
|
||
IN PVOID ProtocolSubmitBuffer,
|
||
IN PVOID ClientBufferBase,
|
||
IN ULONG SubmitBufferSize,
|
||
OUT PVOID *ProtocolReturnBuffer,
|
||
OUT PULONG ReturnBufferSize,
|
||
OUT PNTSTATUS ProtocolStatus
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the dispatch routine for LsaCallAuthenticationPackage()
|
||
with a message type of MsV1_0Lm20ChallengeRequest. It is called by
|
||
the LanMan server to determine the Challenge to pass back to a
|
||
redirector trying to establish a connection to the server. The server
|
||
is responsible remembering this Challenge and passing in back to this
|
||
authentication package on a subsequent MsV1_0Lm20Logon request.
|
||
|
||
Arguments:
|
||
|
||
The arguments to this routine are identical to those of LsaApCallPackage.
|
||
Only the special attributes of these parameters as they apply to
|
||
this routine are mentioned here.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the service completed successfully.
|
||
|
||
STATUS_QUOTA_EXCEEDED - This error indicates that the logon
|
||
could not be completed because the client does not have
|
||
sufficient quota to allocate the return buffer.
|
||
|
||
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PMSV1_0_LM20_CHALLENGE_REQUEST ChallengeRequest;
|
||
PMSV1_0_LM20_CHALLENGE_RESPONSE ChallengeResponse;
|
||
CLIENT_BUFFER_DESC ClientBufferDesc;
|
||
|
||
|
||
UNREFERENCED_PARAMETER( ClientBufferBase );
|
||
|
||
ASSERT( sizeof(LM_CHALLENGE) == MSV1_0_CHALLENGE_LENGTH );
|
||
|
||
NlpInitClientBuffer( &ClientBufferDesc, ClientRequest );
|
||
|
||
//
|
||
// Ensure the specified Submit Buffer is of reasonable size and
|
||
// relocate all of the pointers to be relative to the LSA allocated
|
||
// buffer.
|
||
//
|
||
|
||
if ( SubmitBufferSize < sizeof(MSV1_0_LM20_CHALLENGE_REQUEST) ) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
|
||
ChallengeRequest = (PMSV1_0_LM20_CHALLENGE_REQUEST) ProtocolSubmitBuffer;
|
||
|
||
ASSERT( ChallengeRequest->MessageType == MsV1_0Lm20ChallengeRequest );
|
||
|
||
//
|
||
// Allocate a buffer to return to the caller.
|
||
//
|
||
|
||
*ReturnBufferSize = sizeof(MSV1_0_LM20_CHALLENGE_RESPONSE);
|
||
|
||
Status = NlpAllocateClientBuffer( &ClientBufferDesc,
|
||
sizeof(MSV1_0_LM20_CHALLENGE_RESPONSE),
|
||
*ReturnBufferSize );
|
||
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
ChallengeResponse = (PMSV1_0_LM20_CHALLENGE_RESPONSE) ClientBufferDesc.MsvBuffer;
|
||
|
||
//
|
||
// Fill in the return buffer.
|
||
//
|
||
|
||
ChallengeResponse->MessageType = MsV1_0Lm20ChallengeRequest;
|
||
|
||
//
|
||
// Compute a random seed.
|
||
//
|
||
|
||
Status = SspGenerateRandomBits(
|
||
ChallengeResponse->ChallengeToClient,
|
||
MSV1_0_CHALLENGE_LENGTH
|
||
);
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Flush the buffer to the client's address space.
|
||
//
|
||
|
||
Status = NlpFlushClientBuffer( &ClientBufferDesc,
|
||
ProtocolReturnBuffer );
|
||
|
||
|
||
Cleanup:
|
||
|
||
//
|
||
// If we weren't successful, free the buffer in the clients address space.
|
||
//
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
NlpFreeClientBuffer( &ClientBufferDesc );
|
||
}
|
||
|
||
//
|
||
// Return status to the caller.
|
||
//
|
||
|
||
*ProtocolStatus = Status;
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
#define NULL_SESSION_REQUESTED RETURN_RESERVED_PARAMETER
|
||
|
||
NTSTATUS
|
||
MspLm20GetChallengeResponse (
|
||
IN PLSA_CLIENT_REQUEST ClientRequest,
|
||
IN PVOID ProtocolSubmitBuffer,
|
||
IN PVOID ClientBufferBase,
|
||
IN ULONG SubmitBufferSize,
|
||
OUT PVOID *ProtocolReturnBuffer,
|
||
OUT PULONG ReturnBufferSize,
|
||
OUT PNTSTATUS ProtocolStatus
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the dispatch routine for LsaCallAuthenticationPackage()
|
||
with a message type of MsV1_0Lm20GetChallengeResponse. It is called by
|
||
the LanMan redirector to determine the Challenge Response to pass to a
|
||
server when trying to establish a connection to the server.
|
||
|
||
This routine is passed a Challenge from the server. This routine encrypts
|
||
the challenge with either the specified password or with the password
|
||
implied by the specified Logon Id.
|
||
|
||
Two Challenge responses are returned. One is based on the Unicode password
|
||
as given to the Authentication package. The other is based on that
|
||
password converted to a multi-byte character set (e.g., ASCII) and upper
|
||
cased. The redirector should use whichever (or both) challenge responses
|
||
as it needs them.
|
||
|
||
Arguments:
|
||
|
||
The arguments to this routine are identical to those of LsaApCallPackage.
|
||
Only the special attributes of these parameters as they apply to
|
||
this routine are mentioned here.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the service completed successfully.
|
||
|
||
STATUS_QUOTA_EXCEEDED - This error indicates that the logon
|
||
could not be completed because the client does not have
|
||
sufficient quota to allocate the return buffer.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PMSV1_0_GETCHALLENRESP_REQUEST GetRespRequest;
|
||
|
||
CLIENT_BUFFER_DESC ClientBufferDesc;
|
||
PMSV1_0_GETCHALLENRESP_RESPONSE GetRespResponse;
|
||
|
||
PMSV1_0_PRIMARY_CREDENTIAL Credential = NULL;
|
||
PMSV1_0_PRIMARY_CREDENTIAL PrimaryCredential = NULL;
|
||
MSV1_0_PRIMARY_CREDENTIAL BuiltCredential;
|
||
|
||
//
|
||
// Responses to return to the caller.
|
||
//
|
||
LM_RESPONSE LmResponse;
|
||
STRING LmResponseString;
|
||
|
||
NT_RESPONSE NtResponse;
|
||
STRING NtResponseString;
|
||
|
||
PMSV1_0_NTLM3_RESPONSE pNtlm3Response = NULL;
|
||
|
||
UNICODE_STRING UserName;
|
||
UNICODE_STRING LogonDomainName;
|
||
USER_SESSION_KEY UserSessionKey;
|
||
UCHAR LanmanSessionKey[MSV1_0_LANMAN_SESSION_KEY_LENGTH];
|
||
|
||
UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH];
|
||
UCHAR ChallengeFromClient[MSV1_0_CHALLENGE_LENGTH];
|
||
ULONG NtLmProtocolSupported;
|
||
|
||
//
|
||
// Initialization
|
||
//
|
||
|
||
NlpInitClientBuffer( &ClientBufferDesc, ClientRequest );
|
||
|
||
RtlInitUnicodeString( &UserName, NULL );
|
||
RtlInitUnicodeString( &LogonDomainName, NULL );
|
||
|
||
RtlZeroMemory( &UserSessionKey, sizeof(UserSessionKey) );
|
||
RtlZeroMemory( LanmanSessionKey, sizeof(LanmanSessionKey) );
|
||
|
||
//
|
||
// If no credentials are associated with the client, a null session
|
||
// will be used. For a downlevel server, the null session response is
|
||
// a 1-byte null string (\0). Initialize LmResponseString to the
|
||
// null session response.
|
||
//
|
||
|
||
RtlInitString( &LmResponseString, "" );
|
||
LmResponseString.Length = 1;
|
||
|
||
//
|
||
// Initialize the NT response to the NT null session credentials,
|
||
// which are zero length.
|
||
//
|
||
|
||
RtlInitString( &NtResponseString, NULL );
|
||
|
||
//
|
||
// Ensure the specified Submit Buffer is of reasonable size and
|
||
// relocate all of the pointers to be relative to the LSA allocated
|
||
// buffer.
|
||
//
|
||
|
||
if ( SubmitBufferSize < sizeof(MSV1_0_GETCHALLENRESP_REQUEST_V1) ) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
|
||
GetRespRequest = (PMSV1_0_GETCHALLENRESP_REQUEST) ProtocolSubmitBuffer;
|
||
|
||
ASSERT( GetRespRequest->MessageType == MsV1_0Lm20GetChallengeResponse );
|
||
|
||
if ( (GetRespRequest->ParameterControl & USE_PRIMARY_PASSWORD) == 0 ) {
|
||
RELOCATE_ONE( &GetRespRequest->Password );
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// If we don't support the request (such as the caller is asking for an
|
||
// LM challenge response and we do't support it, return an error here.
|
||
//
|
||
|
||
NtLmProtocolSupported = NtLmGlobalLmProtocolSupported;
|
||
|
||
//
|
||
// allow protocol to be downgraded to NTLM from NTLMv2 if so requested.
|
||
//
|
||
|
||
if( (NtLmProtocolSupported >= UseNtlm3) &&
|
||
(GetRespRequest->ParameterControl & GCR_ALLOW_NTLM) )
|
||
{
|
||
NtLmProtocolSupported = NoLm;
|
||
}
|
||
|
||
|
||
if ( (GetRespRequest->ParameterControl & RETURN_NON_NT_USER_SESSION_KEY) &&
|
||
NtLmProtocolSupported == NoLm ) {
|
||
|
||
Status = STATUS_NOT_SUPPORTED;
|
||
goto Cleanup;
|
||
}
|
||
|
||
if ( GetRespRequest->ParameterControl & GCR_MACHINE_CREDENTIAL )
|
||
{
|
||
SECPKG_CLIENT_INFO ClientInfo;
|
||
LUID SystemLuid = SYSTEM_LUID;
|
||
|
||
//
|
||
// if caller wants machine cred, check they are SYSTEM.
|
||
// if so, whack the LogonId to point at the machine logon.
|
||
//
|
||
|
||
Status = LsaFunctions->GetClientInfo( &ClientInfo );
|
||
|
||
if( !NT_SUCCESS(Status) )
|
||
{
|
||
goto Cleanup;
|
||
}
|
||
|
||
if(!RtlEqualLuid( &ClientInfo.LogonId, &SystemLuid ))
|
||
{
|
||
Status = STATUS_ACCESS_DENIED;
|
||
goto Cleanup;
|
||
}
|
||
|
||
GetRespRequest->LogonId = NtLmGlobalLuidMachineLogon;
|
||
}
|
||
|
||
//
|
||
// if caller wants NTLM++, so be it...
|
||
//
|
||
|
||
if ( (GetRespRequest->ParameterControl & GCR_NTLM3_PARMS) ) {
|
||
PMSV1_0_AV_PAIR pAV;
|
||
|
||
UCHAR TargetInfoBuffer[3*sizeof(MSV1_0_AV_PAIR) + (DNS_MAX_NAME_LENGTH+CNLEN+2)*sizeof(WCHAR)];
|
||
|
||
NULL_RELOCATE_ONE( &GetRespRequest->UserName );
|
||
NULL_RELOCATE_ONE( &GetRespRequest->LogonDomainName );
|
||
NULL_RELOCATE_ONE( &GetRespRequest->ServerName );
|
||
|
||
|
||
// if target is just a domain name or domain name followed by
|
||
// server name, make it into an AV pair list
|
||
if (!(GetRespRequest->ParameterControl & GCR_TARGET_INFO)) {
|
||
UNICODE_STRING DomainName;
|
||
UNICODE_STRING ServerName;
|
||
unsigned int i;
|
||
|
||
//
|
||
// check length of name to make sure it fits in my buffer
|
||
//
|
||
|
||
if (GetRespRequest->ServerName.Length > (DNS_MAX_NAME_LENGTH+CNLEN+2)*sizeof(WCHAR)) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// init AV list in temp buffer
|
||
//
|
||
|
||
pAV = MsvpAvlInit(TargetInfoBuffer);
|
||
|
||
//
|
||
// see if there's a NULL in the middle of the server name
|
||
// that indicates that it's really a domain name followed by a server name
|
||
//
|
||
|
||
DomainName = GetRespRequest->ServerName;
|
||
ServerName.Length = 0;
|
||
|
||
for (i = 0; i < (DomainName.Length/sizeof(WCHAR)); i++) {
|
||
if ( DomainName.Buffer[i] == L'\0' )
|
||
{
|
||
// take length of domain name without the NULL
|
||
DomainName.Length = (USHORT) i*sizeof(WCHAR);
|
||
// adjust server name and length to point after the domain name
|
||
ServerName.Length = GetRespRequest->ServerName.Length - (i+1)*sizeof(WCHAR);
|
||
ServerName.Buffer = GetRespRequest->ServerName.Buffer + (i+1);
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// strip off possible trailing null after the server name
|
||
//
|
||
|
||
for (i = 0; i < (ServerName.Length / sizeof(WCHAR)); i++) {
|
||
if (ServerName.Buffer[i] == L'\0')
|
||
{
|
||
ServerName.Length = (USHORT)i*sizeof(WCHAR);
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// put both names in the AV list (if both exist)
|
||
//
|
||
|
||
MsvpAvlAdd(pAV, MsvAvNbDomainName, &DomainName, sizeof(TargetInfoBuffer));
|
||
if (ServerName.Length > 0) {
|
||
MsvpAvlAdd(pAV, MsvAvNbComputerName, &ServerName, sizeof(TargetInfoBuffer));
|
||
}
|
||
|
||
//
|
||
// make the request point at AV list instead of names.
|
||
//
|
||
|
||
GetRespRequest->ServerName.Length = (USHORT)MsvpAvlLen(pAV, sizeof(TargetInfoBuffer));
|
||
GetRespRequest->ServerName.Buffer = (PWCHAR)pAV;
|
||
}
|
||
|
||
//
|
||
// if we're only using NTLMv2 or better, then complain if either
|
||
// computer name or server name missing
|
||
//
|
||
|
||
if (NtLmProtocolSupported >= RefuseNtlm3NoTarget) {
|
||
pAV = (PMSV1_0_AV_PAIR)GetRespRequest->ServerName.Buffer;
|
||
if ((pAV==NULL) ||
|
||
MsvpAvlGet(pAV, MsvAvNbDomainName, GetRespRequest->ServerName.Length) == NULL ||
|
||
MsvpAvlGet(pAV, MsvAvNbComputerName, GetRespRequest->ServerName.Length) == NULL) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// If the caller wants information from the credentials of a specified
|
||
// LogonId, get those credentials from the LSA.
|
||
//
|
||
// If there are no such credentials,
|
||
// tell the caller to use the NULL session.
|
||
//
|
||
|
||
#define PRIMARY_CREDENTIAL_NEEDED \
|
||
(RETURN_PRIMARY_LOGON_DOMAINNAME | \
|
||
RETURN_PRIMARY_USERNAME | \
|
||
USE_PRIMARY_PASSWORD )
|
||
|
||
if ( ((GetRespRequest->ParameterControl & PRIMARY_CREDENTIAL_NEEDED) != 0 ) && ((GetRespRequest->ParameterControl & NULL_SESSION_REQUESTED) == 0)) {
|
||
|
||
Status = NlpGetPrimaryCredential(
|
||
&GetRespRequest->LogonId,
|
||
&PrimaryCredential,
|
||
NULL );
|
||
|
||
if ( NT_SUCCESS(Status) ) {
|
||
|
||
if ( GetRespRequest->ParameterControl & RETURN_PRIMARY_USERNAME ) {
|
||
UserName = PrimaryCredential->UserName;
|
||
}
|
||
|
||
if ( GetRespRequest->ParameterControl &
|
||
RETURN_PRIMARY_LOGON_DOMAINNAME ) {
|
||
|
||
#ifndef DONT_MAP_DOMAIN_ON_REQUEST
|
||
//
|
||
// Map the user's logon domain against the current mapping
|
||
// in the registry.
|
||
//
|
||
|
||
Status = NlpMapLogonDomain(
|
||
&LogonDomainName,
|
||
&PrimaryCredential->LogonDomainName
|
||
);
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
#else
|
||
LogonDomainName = PrimaryCredential->LogonDomainName;
|
||
#endif
|
||
}
|
||
|
||
} else if ( Status == STATUS_NO_SUCH_LOGON_SESSION ||
|
||
Status == STATUS_UNSUCCESSFUL ) {
|
||
|
||
//
|
||
// Clean up the status code
|
||
//
|
||
|
||
Status = STATUS_NO_SUCH_LOGON_SESSION;
|
||
|
||
//
|
||
// If the caller wants at least the password from the primary
|
||
// credential, just use a NULL session primary credential.
|
||
//
|
||
|
||
if ( (GetRespRequest->ParameterControl & USE_PRIMARY_PASSWORD ) ==
|
||
USE_PRIMARY_PASSWORD ) {
|
||
|
||
PrimaryCredential = NULL;
|
||
|
||
//
|
||
// If part of the information was supplied by the caller,
|
||
// report the error to the caller.
|
||
//
|
||
} else {
|
||
SspPrint((SSP_CRITICAL, "MspLm20GetChallengeResponse: cannot "
|
||
" GetPrimaryCredential %lx\n", Status ));
|
||
goto Cleanup;
|
||
}
|
||
} else {
|
||
SspPrint((SSP_CRITICAL, "MspLm20GetChallengeResponse: cannot "
|
||
" GetPrimaryCredential %lx\n", Status ));
|
||
goto Cleanup;
|
||
}
|
||
|
||
Credential = PrimaryCredential;
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// If the caller passed in a password to use,
|
||
// use it to build a credential.
|
||
//
|
||
|
||
if ( (GetRespRequest->ParameterControl & USE_PRIMARY_PASSWORD) == 0 ) {
|
||
|
||
NlpPutOwfsInPrimaryCredential( &GetRespRequest->Password,
|
||
&BuiltCredential );
|
||
|
||
//
|
||
// Use the newly allocated credential to get the password information
|
||
// from.
|
||
//
|
||
Credential = &BuiltCredential;
|
||
|
||
}
|
||
|
||
//
|
||
// Build the appropriate response.
|
||
//
|
||
|
||
if ( Credential != NULL ) {
|
||
|
||
//
|
||
// If the DC is asserted to have been upgraded, we should use NTLM3
|
||
// if caller supplies the NTLM3 parameters
|
||
//
|
||
|
||
if ((NtLmProtocolSupported >= UseNtlm3) &&
|
||
(GetRespRequest->ParameterControl & GCR_NTLM3_PARMS)
|
||
) {
|
||
|
||
USHORT Ntlm3ResponseSize;
|
||
UNICODE_STRING Ntlm3UserName;
|
||
UNICODE_STRING Ntlm3LogonDomainName;
|
||
UNICODE_STRING Ntlm3ServerName;
|
||
|
||
// use the server name supplied by the caller
|
||
Ntlm3ServerName = GetRespRequest->ServerName;
|
||
|
||
// even if user name and domain are supplied, use current logged
|
||
// in user if so requested
|
||
|
||
if (GetRespRequest->ParameterControl & USE_PRIMARY_PASSWORD) {
|
||
Ntlm3UserName = Credential->UserName;
|
||
Ntlm3LogonDomainName = Credential->LogonDomainName;
|
||
} else {
|
||
Ntlm3UserName = GetRespRequest->UserName;
|
||
Ntlm3LogonDomainName = GetRespRequest->LogonDomainName;
|
||
}
|
||
|
||
//
|
||
// Allocate the response
|
||
//
|
||
|
||
Ntlm3ResponseSize =
|
||
sizeof(MSV1_0_NTLM3_RESPONSE) + Ntlm3ServerName.Length;
|
||
|
||
pNtlm3Response = (*Lsa.AllocatePrivateHeap)( Ntlm3ResponseSize );
|
||
|
||
if ( pNtlm3Response == NULL ) {
|
||
SspPrint((SSP_CRITICAL, "MspLm20GetChallengeResponse: No memory %ld\n",
|
||
Ntlm3ResponseSize ));
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Cleanup;
|
||
}
|
||
|
||
MsvpLm20GetNtlm3ChallengeResponse(
|
||
&Credential->NtOwfPassword,
|
||
&Ntlm3UserName,
|
||
&Ntlm3LogonDomainName,
|
||
&Ntlm3ServerName,
|
||
GetRespRequest->ChallengeToClient,
|
||
pNtlm3Response,
|
||
(PMSV1_0_LM3_RESPONSE)&LmResponse,
|
||
&UserSessionKey,
|
||
(PLM_SESSION_KEY)LanmanSessionKey
|
||
);
|
||
|
||
NtResponseString.Buffer = (PUCHAR) pNtlm3Response;
|
||
NtResponseString.Length = Ntlm3ResponseSize;
|
||
LmResponseString.Buffer = (PUCHAR) &LmResponse;
|
||
LmResponseString.Length = sizeof(LmResponse);
|
||
} else {
|
||
|
||
//
|
||
// if requested, generate our own challenge, and mix it with that
|
||
// of the server's
|
||
//
|
||
|
||
if (GetRespRequest->ParameterControl & GENERATE_CLIENT_CHALLENGE) {
|
||
|
||
SspGenerateRandomBits(ChallengeFromClient, MSV1_0_CHALLENGE_LENGTH);
|
||
|
||
#ifdef USE_CONSTANT_CHALLENGE
|
||
RtlZeroMemory(ChallengeFromClient, MSV1_0_CHALLENGE_LENGTH);
|
||
#endif
|
||
|
||
RtlCopyMemory(
|
||
ChallengeToClient,
|
||
GetRespRequest->ChallengeToClient,
|
||
MSV1_0_CHALLENGE_LENGTH
|
||
);
|
||
|
||
MsvpCalculateNtlm2Challenge (
|
||
GetRespRequest->ChallengeToClient,
|
||
ChallengeFromClient,
|
||
GetRespRequest->ChallengeToClient
|
||
);
|
||
|
||
}
|
||
|
||
Status = RtlCalculateNtResponse(
|
||
(PNT_CHALLENGE) GetRespRequest->ChallengeToClient,
|
||
&Credential->NtOwfPassword,
|
||
&NtResponse );
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
//
|
||
// send the client challenge back in the LM response slot if we made one
|
||
//
|
||
if (GetRespRequest->ParameterControl & GENERATE_CLIENT_CHALLENGE) {
|
||
|
||
RtlZeroMemory(
|
||
&LmResponse,
|
||
sizeof(LmResponse)
|
||
);
|
||
|
||
RtlCopyMemory(
|
||
&LmResponse,
|
||
ChallengeFromClient,
|
||
MSV1_0_CHALLENGE_LENGTH
|
||
);
|
||
//
|
||
// Return the LM response if policy set that way for backwards compatibility.
|
||
//
|
||
|
||
} else if ((NtLmProtocolSupported <= AllowLm) ) {
|
||
Status = RtlCalculateLmResponse(
|
||
(PLM_CHALLENGE) GetRespRequest->ChallengeToClient,
|
||
&Credential->LmOwfPassword,
|
||
&LmResponse );
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
//
|
||
// Can't return LM response -- so use NT response
|
||
// (to allow LM_KEY generatation)
|
||
//
|
||
|
||
} else {
|
||
RtlCopyMemory(
|
||
&LmResponse,
|
||
&NtResponse,
|
||
sizeof(LmResponse)
|
||
);
|
||
}
|
||
|
||
NtResponseString.Buffer = (PUCHAR) &NtResponse;
|
||
NtResponseString.Length = sizeof(NtResponse);
|
||
LmResponseString.Buffer = (PUCHAR) &LmResponse;
|
||
LmResponseString.Length = sizeof(LmResponse);
|
||
|
||
//
|
||
// Compute the session keys
|
||
//
|
||
|
||
if (GetRespRequest->ParameterControl & GENERATE_CLIENT_CHALLENGE) {
|
||
|
||
//
|
||
// assert: we're talking to an NT4-SP4 or later server
|
||
// and the user's DC hasn't been upgraded to NTLM++
|
||
// generate session key from MD4(NT hash) -
|
||
// aka NtUserSessionKey - that is different for each session
|
||
//
|
||
|
||
Status = RtlCalculateUserSessionKeyNt(
|
||
&NtResponse,
|
||
&Credential->NtOwfPassword,
|
||
&UserSessionKey );
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
MsvpCalculateNtlm2SessionKeys(
|
||
&UserSessionKey,
|
||
ChallengeToClient,
|
||
ChallengeFromClient,
|
||
(PUSER_SESSION_KEY)&UserSessionKey,
|
||
(PLM_SESSION_KEY)LanmanSessionKey
|
||
);
|
||
|
||
} else if ( GetRespRequest->ParameterControl & RETURN_NON_NT_USER_SESSION_KEY){
|
||
|
||
//
|
||
// If the redir didn't negotiate an NT protocol with the server,
|
||
// use the lanman session key.
|
||
//
|
||
|
||
if ( Credential->LmPasswordPresent ) {
|
||
|
||
ASSERT( sizeof(UserSessionKey) >= sizeof(LanmanSessionKey) );
|
||
|
||
RtlCopyMemory( &UserSessionKey,
|
||
&Credential->LmOwfPassword,
|
||
sizeof(LanmanSessionKey) );
|
||
}
|
||
|
||
} else {
|
||
|
||
if ( !Credential->NtPasswordPresent ) {
|
||
|
||
RtlCopyMemory( &Credential->NtOwfPassword,
|
||
&NlpNullNtOwfPassword,
|
||
sizeof(Credential->NtOwfPassword) );
|
||
}
|
||
|
||
Status = RtlCalculateUserSessionKeyNt(
|
||
&NtResponse,
|
||
&Credential->NtOwfPassword,
|
||
&UserSessionKey );
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
goto Cleanup;
|
||
}
|
||
}
|
||
|
||
if ( Credential->LmPasswordPresent ) {
|
||
RtlCopyMemory( LanmanSessionKey,
|
||
&Credential->LmOwfPassword,
|
||
sizeof(LanmanSessionKey) );
|
||
}
|
||
|
||
} // UseNtlm3
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Allocate a buffer to return to the caller.
|
||
//
|
||
|
||
*ReturnBufferSize = sizeof(MSV1_0_GETCHALLENRESP_RESPONSE) +
|
||
LogonDomainName.Length + sizeof(WCHAR) +
|
||
UserName.Length + sizeof(WCHAR) +
|
||
NtResponseString.Length + sizeof(WCHAR) +
|
||
LmResponseString.Length + sizeof(WCHAR);
|
||
|
||
Status = NlpAllocateClientBuffer( &ClientBufferDesc,
|
||
sizeof(MSV1_0_GETCHALLENRESP_RESPONSE),
|
||
*ReturnBufferSize );
|
||
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
GetRespResponse = (PMSV1_0_GETCHALLENRESP_RESPONSE) ClientBufferDesc.MsvBuffer;
|
||
|
||
|
||
//
|
||
// Fill in the return buffer.
|
||
//
|
||
|
||
GetRespResponse->MessageType = MsV1_0Lm20GetChallengeResponse;
|
||
RtlCopyMemory( GetRespResponse->UserSessionKey,
|
||
&UserSessionKey,
|
||
sizeof(UserSessionKey));
|
||
RtlCopyMemory( GetRespResponse->LanmanSessionKey,
|
||
LanmanSessionKey,
|
||
sizeof(LanmanSessionKey) );
|
||
|
||
|
||
//
|
||
// Copy the logon domain name (the string may be empty)
|
||
//
|
||
|
||
NlpPutClientString( &ClientBufferDesc,
|
||
&GetRespResponse->LogonDomainName,
|
||
&LogonDomainName );
|
||
|
||
//
|
||
// Copy the user name (the string may be empty)
|
||
//
|
||
|
||
NlpPutClientString( &ClientBufferDesc,
|
||
&GetRespResponse->UserName,
|
||
&UserName );
|
||
|
||
//
|
||
// Copy the Challenge Responses to the client buffer.
|
||
//
|
||
|
||
NlpPutClientString(
|
||
&ClientBufferDesc,
|
||
(PUNICODE_STRING)
|
||
&GetRespResponse->CaseSensitiveChallengeResponse,
|
||
(PUNICODE_STRING) &NtResponseString );
|
||
|
||
NlpPutClientString(
|
||
&ClientBufferDesc,
|
||
(PUNICODE_STRING)
|
||
&GetRespResponse->CaseInsensitiveChallengeResponse,
|
||
(PUNICODE_STRING)&LmResponseString );
|
||
|
||
|
||
//
|
||
// Flush the buffer to the client's address space.
|
||
//
|
||
|
||
Status = NlpFlushClientBuffer( &ClientBufferDesc,
|
||
ProtocolReturnBuffer );
|
||
|
||
Cleanup:
|
||
|
||
//
|
||
// If we weren't successful, free the buffer in the clients address space.
|
||
//
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
NlpFreeClientBuffer( &ClientBufferDesc );
|
||
}
|
||
|
||
//
|
||
// Cleanup locally used resources
|
||
//
|
||
|
||
if ( PrimaryCredential != NULL ) {
|
||
(*Lsa.FreeLsaHeap)( PrimaryCredential );
|
||
}
|
||
|
||
#ifndef DONT_MAP_DOMAIN_ON_REQUEST
|
||
|
||
if (LogonDomainName.Buffer != NULL) {
|
||
NtLmFree(LogonDomainName.Buffer);
|
||
}
|
||
#endif
|
||
|
||
if ( pNtlm3Response != NULL ) {
|
||
(*Lsa.FreePrivateHeap)( pNtlm3Response );
|
||
}
|
||
|
||
//
|
||
// Return status to the caller.
|
||
//
|
||
|
||
*ProtocolStatus = Status;
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
MspLm20EnumUsers (
|
||
IN PLSA_CLIENT_REQUEST ClientRequest,
|
||
IN PVOID ProtocolSubmitBuffer,
|
||
IN PVOID ClientBufferBase,
|
||
IN ULONG SubmitBufferSize,
|
||
OUT PVOID *ProtocolReturnBuffer,
|
||
OUT PULONG ReturnBufferSize,
|
||
OUT PNTSTATUS ProtocolStatus
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the dispatch routine for LsaCallAuthenticationPackage()
|
||
with a message type of MsV1_0Lm20EnumerateUsers. This routine
|
||
enumerates all of the interactive, service, and batch logons to the MSV1_0
|
||
authentication package.
|
||
|
||
Arguments:
|
||
|
||
The arguments to this routine are identical to those of LsaApCallPackage.
|
||
Only the special attributes of these parameters as they apply to
|
||
this routine are mentioned here.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the service completed successfully.
|
||
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PMSV1_0_ENUMUSERS_REQUEST EnumRequest;
|
||
PMSV1_0_ENUMUSERS_RESPONSE EnumResponse;
|
||
CLIENT_BUFFER_DESC ClientBufferDesc;
|
||
ULONG LogonCount = 0;
|
||
PACTIVE_LOGON Logon;
|
||
BOOLEAN ActiveLogonsAreLocked = FALSE;
|
||
|
||
PUCHAR Where;
|
||
|
||
//
|
||
// Ensure the specified Submit Buffer is of reasonable size and
|
||
// relocate all of the pointers to be relative to the LSA allocated
|
||
// buffer.
|
||
//
|
||
|
||
NlpInitClientBuffer( &ClientBufferDesc, ClientRequest );
|
||
UNREFERENCED_PARAMETER( ClientBufferBase );
|
||
|
||
if ( SubmitBufferSize < sizeof(MSV1_0_ENUMUSERS_REQUEST) ) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
|
||
EnumRequest = (PMSV1_0_ENUMUSERS_REQUEST) ProtocolSubmitBuffer;
|
||
|
||
ASSERT( EnumRequest->MessageType == MsV1_0EnumerateUsers );
|
||
|
||
//
|
||
// Count the current number of active logons
|
||
//
|
||
|
||
NlpLockActiveLogonsRead();
|
||
ActiveLogonsAreLocked = TRUE;
|
||
|
||
for( Logon = NlpActiveLogons; Logon != NULL; Logon = Logon->Next )
|
||
{
|
||
//
|
||
// don't count the machine account logon.
|
||
//
|
||
|
||
if( RtlEqualLuid(&NtLmGlobalLuidMachineLogon, &Logon->LogonId) )
|
||
{
|
||
continue;
|
||
}
|
||
|
||
LogonCount ++;
|
||
}
|
||
|
||
//
|
||
// Allocate a buffer to return to the caller.
|
||
//
|
||
|
||
*ReturnBufferSize = sizeof(MSV1_0_ENUMUSERS_RESPONSE) +
|
||
LogonCount * (sizeof(LUID) + sizeof(ULONG));
|
||
|
||
|
||
Status = NlpAllocateClientBuffer( &ClientBufferDesc,
|
||
sizeof(MSV1_0_ENUMUSERS_RESPONSE),
|
||
*ReturnBufferSize );
|
||
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
EnumResponse = (PMSV1_0_ENUMUSERS_RESPONSE) ClientBufferDesc.MsvBuffer;
|
||
|
||
//
|
||
// Fill in the return buffer.
|
||
//
|
||
|
||
EnumResponse->MessageType = MsV1_0EnumerateUsers;
|
||
EnumResponse->NumberOfLoggedOnUsers = LogonCount;
|
||
|
||
Where = (PUCHAR)(EnumResponse + 1);
|
||
|
||
//
|
||
// Loop through the Active Logon Table copying the LogonId of each session.
|
||
//
|
||
|
||
EnumResponse->LogonIds = (PLUID)(ClientBufferDesc.UserBuffer +
|
||
(Where - ClientBufferDesc.MsvBuffer));
|
||
for( Logon = NlpActiveLogons; Logon != NULL; Logon = Logon->Next )
|
||
{
|
||
//
|
||
// don't count the machine account logon.
|
||
//
|
||
|
||
if( RtlEqualLuid(&NtLmGlobalLuidMachineLogon, &Logon->LogonId) )
|
||
{
|
||
continue;
|
||
}
|
||
|
||
*((PLUID)Where) = Logon->LogonId,
|
||
Where += sizeof(LUID);
|
||
}
|
||
|
||
//
|
||
// Loop through the Active Logon Table copying the EnumHandle of
|
||
// each session.
|
||
//
|
||
|
||
EnumResponse->EnumHandles = (PULONG)(ClientBufferDesc.UserBuffer +
|
||
(Where - ClientBufferDesc.MsvBuffer));
|
||
for( Logon = NlpActiveLogons; Logon != NULL; Logon = Logon->Next )
|
||
{
|
||
//
|
||
// don't count the machine account logon.
|
||
//
|
||
|
||
if( RtlEqualLuid(&NtLmGlobalLuidMachineLogon, &Logon->LogonId) )
|
||
{
|
||
continue;
|
||
}
|
||
|
||
*((PULONG)Where) = Logon->EnumHandle,
|
||
Where += sizeof(ULONG);
|
||
}
|
||
|
||
//
|
||
// Flush the buffer to the client's address space.
|
||
//
|
||
|
||
Status = NlpFlushClientBuffer( &ClientBufferDesc,
|
||
ProtocolReturnBuffer );
|
||
|
||
Cleanup:
|
||
|
||
//
|
||
// Be sure to unlock the lock on the Active logon list.
|
||
//
|
||
|
||
if ( ActiveLogonsAreLocked ) {
|
||
NlpUnlockActiveLogons();
|
||
}
|
||
|
||
//
|
||
// If we weren't successful, free the buffer in the clients address space.
|
||
//
|
||
|
||
if ( !NT_SUCCESS(Status)) {
|
||
NlpFreeClientBuffer( &ClientBufferDesc );
|
||
}
|
||
|
||
//
|
||
// Return status to the caller.
|
||
//
|
||
|
||
*ProtocolStatus = Status;
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
MspLm20GetUserInfo (
|
||
IN PLSA_CLIENT_REQUEST ClientRequest,
|
||
IN PVOID ProtocolSubmitBuffer,
|
||
IN PVOID ClientBufferBase,
|
||
IN ULONG SubmitBufferSize,
|
||
OUT PVOID *ProtocolReturnBuffer,
|
||
OUT PULONG ReturnBufferSize,
|
||
OUT PNTSTATUS ProtocolStatus
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the dispatch routine for LsaCallAuthenticationPackage()
|
||
with a message type of MsV1_0GetUserInfo. This routine
|
||
returns information describing a particular Logon Id.
|
||
|
||
Arguments:
|
||
|
||
The arguments to this routine are identical to those of LsaApCallPackage.
|
||
Only the special attributes of these parameters as they apply to
|
||
this routine are mentioned here.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the service completed successfully.
|
||
|
||
STATUS_QUOTA_EXCEEDED - This error indicates that the logon
|
||
could not be completed because the client does not have
|
||
sufficient quota to allocate the return buffer.
|
||
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
PMSV1_0_GETUSERINFO_REQUEST GetInfoRequest;
|
||
PMSV1_0_GETUSERINFO_RESPONSE GetInfoResponse = NULL;
|
||
|
||
CLIENT_BUFFER_DESC ClientBufferDesc;
|
||
|
||
BOOLEAN ActiveLogonsAreLocked = FALSE;
|
||
PACTIVE_LOGON *ActiveLogon;
|
||
PACTIVE_LOGON Logon;
|
||
ULONG SidLength;
|
||
|
||
//
|
||
// Ensure the specified Submit Buffer is of reasonable size and
|
||
// relocate all of the pointers to be relative to the LSA allocated
|
||
// buffer.
|
||
//
|
||
|
||
NlpInitClientBuffer( &ClientBufferDesc, ClientRequest );
|
||
|
||
UNREFERENCED_PARAMETER( ClientBufferBase );
|
||
|
||
if ( SubmitBufferSize < sizeof(MSV1_0_GETUSERINFO_REQUEST) ) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
|
||
GetInfoRequest = (PMSV1_0_GETUSERINFO_REQUEST) ProtocolSubmitBuffer;
|
||
|
||
ASSERT( GetInfoRequest->MessageType == MsV1_0GetUserInfo );
|
||
|
||
//
|
||
// Find the Active logon entry for this particular Logon Id.
|
||
//
|
||
|
||
NlpLockActiveLogonsRead();
|
||
ActiveLogonsAreLocked = TRUE;
|
||
|
||
if (!NlpFindActiveLogon( &GetInfoRequest->LogonId, &ActiveLogon )){
|
||
Status = STATUS_NO_SUCH_LOGON_SESSION;
|
||
goto Cleanup;
|
||
}
|
||
|
||
Logon = *ActiveLogon;
|
||
|
||
//
|
||
// Allocate a buffer to return to the caller.
|
||
//
|
||
|
||
SidLength = RtlLengthSid( Logon->UserSid );
|
||
*ReturnBufferSize = sizeof(MSV1_0_GETUSERINFO_RESPONSE) +
|
||
Logon->UserName.Length + sizeof(WCHAR) +
|
||
Logon->LogonDomainName.Length + sizeof(WCHAR) +
|
||
Logon->LogonServer.Length + sizeof(WCHAR) +
|
||
SidLength;
|
||
|
||
|
||
Status = NlpAllocateClientBuffer( &ClientBufferDesc,
|
||
sizeof(MSV1_0_GETUSERINFO_RESPONSE),
|
||
*ReturnBufferSize );
|
||
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
GetInfoResponse = (PMSV1_0_GETUSERINFO_RESPONSE) ClientBufferDesc.MsvBuffer;
|
||
|
||
|
||
//
|
||
// Fill in the return buffer.
|
||
//
|
||
|
||
GetInfoResponse->MessageType = MsV1_0GetUserInfo;
|
||
GetInfoResponse->LogonType = Logon->LogonType;
|
||
|
||
//
|
||
// Copy ULONG aligned data first
|
||
//
|
||
|
||
GetInfoResponse->UserSid = ClientBufferDesc.UserBuffer +
|
||
ClientBufferDesc.StringOffset;
|
||
|
||
RtlCopyMemory( ClientBufferDesc.MsvBuffer + ClientBufferDesc.StringOffset,
|
||
Logon->UserSid,
|
||
SidLength );
|
||
|
||
ClientBufferDesc.StringOffset += SidLength;
|
||
|
||
//
|
||
// Copy WCHAR aligned data
|
||
//
|
||
|
||
NlpPutClientString( &ClientBufferDesc,
|
||
&GetInfoResponse->UserName,
|
||
&Logon->UserName );
|
||
|
||
NlpPutClientString( &ClientBufferDesc,
|
||
&GetInfoResponse->LogonDomainName,
|
||
&Logon->LogonDomainName );
|
||
|
||
NlpPutClientString( &ClientBufferDesc,
|
||
&GetInfoResponse->LogonServer,
|
||
&Logon->LogonServer );
|
||
|
||
|
||
//
|
||
// Flush the buffer to the client's address space.
|
||
//
|
||
|
||
Status = NlpFlushClientBuffer( &ClientBufferDesc,
|
||
ProtocolReturnBuffer );
|
||
|
||
Cleanup:
|
||
|
||
//
|
||
// Be sure to unlock the lock on the Active logon list.
|
||
//
|
||
|
||
if ( ActiveLogonsAreLocked ) {
|
||
NlpUnlockActiveLogons();
|
||
}
|
||
|
||
//
|
||
// If we weren't successful, free the buffer in the clients address space.
|
||
//
|
||
|
||
if ( !NT_SUCCESS(Status)) {
|
||
NlpFreeClientBuffer( &ClientBufferDesc );
|
||
}
|
||
|
||
//
|
||
// Return status to the caller.
|
||
//
|
||
|
||
*ProtocolStatus = Status;
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
MspLm20ReLogonUsers (
|
||
IN PLSA_CLIENT_REQUEST ClientRequest,
|
||
IN PVOID ProtocolSubmitBuffer,
|
||
IN PVOID ClientBufferBase,
|
||
IN ULONG SubmitBufferSize,
|
||
OUT PVOID *ProtocolReturnBuffer,
|
||
OUT PULONG ReturnBufferSize,
|
||
OUT PNTSTATUS ProtocolStatus
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the dispatch routine for LsaCallAuthenticationPackage()
|
||
with a message type of MsV1_0RelogonUsers. For each logon session
|
||
which was validated by the specified domain controller, the logon session
|
||
is re-established with that same domain controller.
|
||
|
||
Arguments:
|
||
|
||
The arguments to this routine are identical to those of LsaApCallPackage.
|
||
Only the special attributes of these parameters as they apply to
|
||
this routine are mentioned here.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the service completed successfully.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
UNREFERENCED_PARAMETER( ClientRequest );
|
||
UNREFERENCED_PARAMETER( ProtocolSubmitBuffer);
|
||
UNREFERENCED_PARAMETER( ClientBufferBase);
|
||
UNREFERENCED_PARAMETER( SubmitBufferSize);
|
||
UNREFERENCED_PARAMETER( ReturnBufferSize);
|
||
|
||
*ProtocolReturnBuffer = NULL;
|
||
*ProtocolStatus = STATUS_NOT_IMPLEMENTED;
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
MspLm20GenericPassthrough (
|
||
IN PLSA_CLIENT_REQUEST ClientRequest,
|
||
IN PVOID ProtocolSubmitBuffer,
|
||
IN PVOID ClientBufferBase,
|
||
IN ULONG SubmitBufferSize,
|
||
OUT PVOID *ProtocolReturnBuffer,
|
||
OUT PULONG ReturnBufferSize,
|
||
OUT PNTSTATUS ProtocolStatus
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the dispatch routine for LsaCallAuthenticationPackage()
|
||
with a message type of MsV1_0Lm20GenericPassthrough. It is called by
|
||
a client wishing to make a CallAuthenticationPackage call against
|
||
a domain controller.
|
||
|
||
Arguments:
|
||
|
||
The arguments to this routine are identical to those of LsaApCallPackage.
|
||
Only the special attributes of these parameters as they apply to
|
||
this routine are mentioned here.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the service completed successfully.
|
||
|
||
STATUS_QUOTA_EXCEEDED - This error indicates that the logon
|
||
could not be completed because the client does not have
|
||
sufficient quota to allocate the return buffer.
|
||
|
||
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PMSV1_0_PASSTHROUGH_REQUEST PassthroughRequest;
|
||
PMSV1_0_PASSTHROUGH_RESPONSE PassthroughResponse;
|
||
CLIENT_BUFFER_DESC ClientBufferDesc;
|
||
BOOLEAN Authoritative;
|
||
PNETLOGON_VALIDATION_GENERIC_INFO ValidationGeneric = NULL;
|
||
|
||
NETLOGON_GENERIC_INFO LogonGeneric;
|
||
PNETLOGON_LOGON_IDENTITY_INFO LogonInformation;
|
||
|
||
//
|
||
// WMI tracing helper struct
|
||
//
|
||
NTLM_TRACE_INFO TraceInfo = {0};
|
||
|
||
//
|
||
// Begin tracing a logon user
|
||
//
|
||
if (NtlmGlobalEventTraceFlag){
|
||
|
||
//
|
||
// Trace header goo
|
||
//
|
||
SET_TRACE_HEADER(TraceInfo,
|
||
NtlmGenericPassthroughGuid,
|
||
EVENT_TRACE_TYPE_START,
|
||
WNODE_FLAG_TRACED_GUID,
|
||
0);
|
||
|
||
TraceEvent(NtlmGlobalTraceLoggerHandle,
|
||
(PEVENT_TRACE_HEADER)&TraceInfo);
|
||
}
|
||
|
||
|
||
|
||
NlpInitClientBuffer( &ClientBufferDesc, ClientRequest );
|
||
*ProtocolStatus = STATUS_SUCCESS;
|
||
|
||
//
|
||
// Ensure the specified Submit Buffer is of reasonable size and
|
||
// relocate all of the pointers to be relative to the LSA allocated
|
||
// buffer.
|
||
//
|
||
|
||
if ( SubmitBufferSize < sizeof(MSV1_0_PASSTHROUGH_REQUEST) ) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
PassthroughRequest = (PMSV1_0_PASSTHROUGH_REQUEST) ProtocolSubmitBuffer;
|
||
|
||
RELOCATE_ONE( &PassthroughRequest->DomainName );
|
||
RELOCATE_ONE( &PassthroughRequest->PackageName );
|
||
|
||
//
|
||
// Make sure the buffer fits in the supplied size
|
||
//
|
||
|
||
if (PassthroughRequest->LogonData != NULL) {
|
||
|
||
if (PassthroughRequest->LogonData + PassthroughRequest->DataLength <
|
||
PassthroughRequest->LogonData ) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
|
||
if ((ULONG_PTR)ClientBufferBase + SubmitBufferSize < (ULONG_PTR)ClientBufferBase ) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
|
||
if (PassthroughRequest->LogonData + PassthroughRequest->DataLength >
|
||
(PUCHAR) ClientBufferBase + SubmitBufferSize) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Reset the pointers for the validation data
|
||
//
|
||
|
||
PassthroughRequest->LogonData =
|
||
(PUCHAR) PassthroughRequest -
|
||
(ULONG_PTR) ClientBufferBase +
|
||
(ULONG_PTR) PassthroughRequest->LogonData;
|
||
|
||
}
|
||
|
||
//
|
||
// Build the structure to pass to Netlogon
|
||
//
|
||
|
||
RtlZeroMemory(
|
||
&LogonGeneric,
|
||
sizeof(LogonGeneric)
|
||
);
|
||
|
||
LogonGeneric.Identity.LogonDomainName = PassthroughRequest->DomainName;
|
||
LogonGeneric.PackageName = PassthroughRequest->PackageName;
|
||
LogonGeneric.LogonData = PassthroughRequest->LogonData;
|
||
LogonGeneric.DataLength = PassthroughRequest->DataLength;
|
||
|
||
LogonInformation =
|
||
(PNETLOGON_LOGON_IDENTITY_INFO) &LogonGeneric;
|
||
|
||
//
|
||
// Call Netlogon to remote the request
|
||
//
|
||
|
||
//
|
||
// Wait for NETLOGON to finish initialization.
|
||
//
|
||
|
||
if ( !NlpNetlogonInitialized ) {
|
||
|
||
Status = NlWaitForNetlogon( NETLOGON_STARTUP_TIME );
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
if ( Status != STATUS_NETLOGON_NOT_STARTED ) {
|
||
goto Cleanup;
|
||
}
|
||
} else {
|
||
NlpNetlogonInitialized = TRUE;
|
||
}
|
||
}
|
||
|
||
if ( NlpNetlogonInitialized ) {
|
||
|
||
//
|
||
// Trace the domain name and package name
|
||
//
|
||
if (NtlmGlobalEventTraceFlag){
|
||
|
||
//Header goo
|
||
SET_TRACE_HEADER(TraceInfo,
|
||
NtlmGenericPassthroughGuid,
|
||
EVENT_TRACE_TYPE_INFO,
|
||
WNODE_FLAG_TRACED_GUID|WNODE_FLAG_USE_MOF_PTR,
|
||
4);
|
||
|
||
SET_TRACE_USTRING(TraceInfo,
|
||
TRACE_PASSTHROUGH_DOMAIN,
|
||
LogonGeneric.Identity.LogonDomainName);
|
||
|
||
SET_TRACE_USTRING(TraceInfo,
|
||
TRACE_PASSTHROUGH_PACKAGE,
|
||
LogonGeneric.PackageName);
|
||
|
||
TraceEvent(
|
||
NtlmGlobalTraceLoggerHandle,
|
||
(PEVENT_TRACE_HEADER)&TraceInfo
|
||
);
|
||
}
|
||
|
||
Status = (*NlpNetLogonSamLogon)(
|
||
NULL, // Server name
|
||
NULL, // Computer name
|
||
NULL, // Authenticator
|
||
NULL, // ReturnAuthenticator
|
||
NetlogonGenericInformation,
|
||
(LPBYTE) &LogonInformation,
|
||
NetlogonValidationGenericInfo2,
|
||
(LPBYTE *) &ValidationGeneric,
|
||
&Authoritative );
|
||
|
||
//
|
||
// Reset Netlogon initialized flag if local netlogon cannot be
|
||
// reached.
|
||
// (Use a more explicit status code)
|
||
//
|
||
|
||
if ( Status == RPC_NT_SERVER_UNAVAILABLE ||
|
||
Status == RPC_NT_UNKNOWN_IF ||
|
||
Status == STATUS_NETLOGON_NOT_STARTED ) {
|
||
Status = STATUS_NETLOGON_NOT_STARTED;
|
||
NlpNetlogonInitialized = FALSE;
|
||
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// no netlogon: see if the request is destined for the local domain,
|
||
// to allow WORKGROUP support.
|
||
//
|
||
|
||
if ( LogonInformation->LogonDomainName.Length == 0 ||
|
||
(LogonInformation->LogonDomainName.Length != 0 &&
|
||
RtlEqualDomainName( &NlpSamDomainName,
|
||
&LogonInformation->LogonDomainName ) )
|
||
) {
|
||
|
||
|
||
PNETLOGON_GENERIC_INFO GenericInfo;
|
||
NETLOGON_VALIDATION_GENERIC_INFO GenericValidation;
|
||
NTSTATUS ProtocolStatus;
|
||
|
||
GenericInfo = (PNETLOGON_GENERIC_INFO) LogonInformation;
|
||
GenericValidation.ValidationData = NULL;
|
||
GenericValidation.DataLength = 0;
|
||
|
||
//
|
||
// unwrap passthrough message and pass it off to dispatch.
|
||
//
|
||
|
||
Status = LsaICallPackagePassthrough(
|
||
&GenericInfo->PackageName,
|
||
0, // Indicate pointers are relative.
|
||
GenericInfo->LogonData,
|
||
GenericInfo->DataLength,
|
||
(PVOID *) &GenericValidation.ValidationData,
|
||
&GenericValidation.DataLength,
|
||
&ProtocolStatus
|
||
);
|
||
|
||
if(NT_SUCCESS( Status ) )
|
||
Status = ProtocolStatus;
|
||
|
||
|
||
//
|
||
// If the call succeeded, allocate the return message.
|
||
//
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
PNETLOGON_VALIDATION_GENERIC_INFO ReturnInfo;
|
||
ULONG ValidationLength;
|
||
|
||
ValidationLength = sizeof(*ReturnInfo) + GenericValidation.DataLength;
|
||
|
||
ReturnInfo = (PNETLOGON_VALIDATION_GENERIC_INFO) MIDL_user_allocate(
|
||
ValidationLength
|
||
);
|
||
|
||
if (ReturnInfo != NULL) {
|
||
if ( GenericValidation.DataLength == 0 ||
|
||
GenericValidation.ValidationData == NULL ) {
|
||
ReturnInfo->DataLength = 0;
|
||
ReturnInfo->ValidationData = NULL;
|
||
} else {
|
||
|
||
ReturnInfo->DataLength = GenericValidation.DataLength;
|
||
ReturnInfo->ValidationData = (PUCHAR) (ReturnInfo + 1);
|
||
|
||
RtlCopyMemory(
|
||
ReturnInfo->ValidationData,
|
||
GenericValidation.ValidationData,
|
||
ReturnInfo->DataLength );
|
||
|
||
}
|
||
|
||
ValidationGeneric = ReturnInfo;
|
||
|
||
} else {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
if (GenericValidation.ValidationData != NULL) {
|
||
LsaIFreeReturnBuffer(GenericValidation.ValidationData);
|
||
}
|
||
|
||
}
|
||
|
||
|
||
} else {
|
||
Status = STATUS_NETLOGON_NOT_STARTED;
|
||
}
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Allocate a buffer to return to the caller.
|
||
//
|
||
|
||
*ReturnBufferSize = sizeof(MSV1_0_PASSTHROUGH_RESPONSE) +
|
||
ValidationGeneric->DataLength;
|
||
|
||
Status = NlpAllocateClientBuffer( &ClientBufferDesc,
|
||
sizeof(MSV1_0_PASSTHROUGH_RESPONSE),
|
||
*ReturnBufferSize );
|
||
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
PassthroughResponse = (PMSV1_0_PASSTHROUGH_RESPONSE) ClientBufferDesc.MsvBuffer;
|
||
|
||
//
|
||
// Fill in the return buffer.
|
||
//
|
||
|
||
PassthroughResponse->MessageType = MsV1_0GenericPassthrough;
|
||
PassthroughResponse->DataLength = ValidationGeneric->DataLength;
|
||
PassthroughResponse->ValidationData = ClientBufferDesc.UserBuffer + sizeof(MSV1_0_PASSTHROUGH_RESPONSE);
|
||
|
||
|
||
RtlCopyMemory(
|
||
PassthroughResponse + 1,
|
||
ValidationGeneric->ValidationData,
|
||
ValidationGeneric->DataLength
|
||
);
|
||
|
||
//
|
||
// Flush the buffer to the client's address space.
|
||
//
|
||
|
||
Status = NlpFlushClientBuffer( &ClientBufferDesc,
|
||
ProtocolReturnBuffer );
|
||
|
||
|
||
Cleanup:
|
||
|
||
if (ValidationGeneric != NULL) {
|
||
MIDL_user_free(ValidationGeneric);
|
||
}
|
||
|
||
if ( !NT_SUCCESS(Status)) {
|
||
NlpFreeClientBuffer( &ClientBufferDesc );
|
||
}
|
||
|
||
if (NtlmGlobalEventTraceFlag){
|
||
|
||
//
|
||
// Trace header goo
|
||
//
|
||
SET_TRACE_HEADER(TraceInfo,
|
||
NtlmGenericPassthroughGuid,
|
||
EVENT_TRACE_TYPE_END,
|
||
WNODE_FLAG_TRACED_GUID,
|
||
0);
|
||
|
||
TraceEvent(NtlmGlobalTraceLoggerHandle,
|
||
(PEVENT_TRACE_HEADER)&TraceInfo);
|
||
}
|
||
|
||
*ProtocolStatus = Status;
|
||
return(STATUS_SUCCESS);
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
MspLm20CacheLogon (
|
||
IN PLSA_CLIENT_REQUEST ClientRequest,
|
||
IN PVOID ProtocolSubmitBuffer,
|
||
IN PVOID ClientBufferBase,
|
||
IN ULONG SubmitBufferSize,
|
||
OUT PVOID *ProtocolReturnBuffer,
|
||
OUT PULONG ReturnBufferSize,
|
||
OUT PNTSTATUS ProtocolStatus
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the dispatch routine for LsaCallAuthenticationPackage()
|
||
with a message type of MsV1_0Lm20CacheLogon. It is called by
|
||
a client wishing to cache logon information in the logon cache
|
||
|
||
Arguments:
|
||
|
||
The arguments to this routine are identical to those of LsaApCallPackage.
|
||
Only the special attributes of these parameters as they apply to
|
||
this routine are mentioned here.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the service completed successfully.
|
||
|
||
STATUS_QUOTA_EXCEEDED - This error indicates that the logon
|
||
could not be completed because the client does not have
|
||
sufficient quota to allocate the return buffer.
|
||
|
||
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PMSV1_0_CACHE_LOGON_REQUEST CacheRequest;
|
||
|
||
PNETLOGON_INTERACTIVE_INFO LogonInfo;
|
||
NETLOGON_VALIDATION_SAM_INFO4 ValidationInfo;
|
||
|
||
PVOID SupplementalCacheData = NULL;
|
||
ULONG SupplementalCacheDataLength = 0;
|
||
ULONG CacheRequestFlags = 0;
|
||
|
||
//
|
||
// NOTE: this entry point only allows callers within the LSA process
|
||
//
|
||
|
||
if (ClientRequest != NULL) {
|
||
*ProtocolStatus = STATUS_ACCESS_DENIED;
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
|
||
CacheRequest = (PMSV1_0_CACHE_LOGON_REQUEST) ProtocolSubmitBuffer;
|
||
|
||
|
||
|
||
|
||
if ( SubmitBufferSize <= sizeof( MSV1_0_CACHE_LOGON_REQUEST_OLD ) ||
|
||
SubmitBufferSize > sizeof( MSV1_0_CACHE_LOGON_REQUEST ))
|
||
{
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
|
||
if ( SubmitBufferSize >= sizeof( MSV1_0_CACHE_LOGON_REQUEST_W2K ))
|
||
{
|
||
SupplementalCacheData = CacheRequest->SupplementalCacheData;
|
||
SupplementalCacheDataLength = CacheRequest->SupplementalCacheDataLength;
|
||
|
||
if ( SubmitBufferSize == sizeof( MSV1_0_CACHE_LOGON_REQUEST ))
|
||
{
|
||
CacheRequestFlags = CacheRequest->RequestFlags;
|
||
}
|
||
|
||
}
|
||
|
||
LogonInfo = (PNETLOGON_INTERACTIVE_INFO) CacheRequest->LogonInformation;
|
||
|
||
if( (CacheRequestFlags & MSV1_0_CACHE_LOGON_REQUEST_INFO4) == 0 )
|
||
{
|
||
RtlZeroMemory( &ValidationInfo, sizeof(ValidationInfo));
|
||
RtlCopyMemory( &ValidationInfo,
|
||
CacheRequest->ValidationInformation,
|
||
sizeof(NETLOGON_VALIDATION_SAM_INFO2) );
|
||
} else {
|
||
RtlCopyMemory( &ValidationInfo,
|
||
CacheRequest->ValidationInformation,
|
||
sizeof(NETLOGON_VALIDATION_SAM_INFO4) );
|
||
}
|
||
|
||
|
||
|
||
*ProtocolStatus = STATUS_SUCCESS;
|
||
|
||
|
||
if (( CacheRequestFlags & MSV1_0_CACHE_LOGON_DELETE_ENTRY) != 0 )
|
||
{
|
||
*ProtocolStatus = NlpDeleteCacheEntry( LogonInfo );
|
||
}
|
||
else
|
||
//
|
||
// Actually add the cache entry
|
||
//
|
||
{
|
||
|
||
*ProtocolStatus = NlpAddCacheEntry(
|
||
LogonInfo,
|
||
&ValidationInfo,
|
||
SupplementalCacheData,
|
||
SupplementalCacheDataLength,
|
||
CacheRequestFlags
|
||
);
|
||
|
||
}
|
||
|
||
Cleanup:
|
||
|
||
return(STATUS_SUCCESS);
|
||
|
||
UNREFERENCED_PARAMETER( ClientRequest);
|
||
UNREFERENCED_PARAMETER( ProtocolReturnBuffer);
|
||
UNREFERENCED_PARAMETER( ClientBufferBase);
|
||
UNREFERENCED_PARAMETER( ReturnBufferSize);
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
MspLm20CacheLookup (
|
||
IN PLSA_CLIENT_REQUEST ClientRequest,
|
||
IN PVOID ProtocolSubmitBuffer,
|
||
IN PVOID ClientBufferBase,
|
||
IN ULONG SubmitBufferSize,
|
||
OUT PVOID *ProtocolReturnBuffer,
|
||
OUT PULONG ReturnBufferSize,
|
||
OUT PNTSTATUS ProtocolStatus
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the dispatch routine for LsaCallAuthenticationPackage()
|
||
with a message type of MsV1_0Lm20CacheLookup. It is called by
|
||
a client wishing to extract cache logon information and optionally
|
||
verify the credential.
|
||
|
||
Arguments:
|
||
|
||
The arguments to this routine are identical to those of LsaApCallPackage.
|
||
Only the special attributes of these parameters as they apply to
|
||
this routine are mentioned here.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the service completed successfully.
|
||
|
||
STATUS_QUOTA_EXCEEDED - This error indicates that the logon
|
||
could not be completed because the client does not have
|
||
sufficient quota to allocate the return buffer.
|
||
|
||
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PMSV1_0_CACHE_LOOKUP_REQUEST CacheRequest;
|
||
PMSV1_0_CACHE_LOOKUP_RESPONSE CacheResponse;
|
||
NETLOGON_LOGON_IDENTITY_INFO LogonInfo;
|
||
PNETLOGON_VALIDATION_SAM_INFO4 ValidationInfo = NULL;
|
||
CACHE_PASSWORDS cachePasswords;
|
||
CLIENT_BUFFER_DESC ClientBufferDesc;
|
||
|
||
PNT_OWF_PASSWORD pNtOwfPassword = NULL;
|
||
NT_OWF_PASSWORD ComputedNtOwfPassword;
|
||
|
||
PVOID SupplementalCacheData = NULL;
|
||
ULONG SupplementalCacheDataLength;
|
||
|
||
//
|
||
// Ensure the client is from the LSA process
|
||
//
|
||
|
||
if (ClientRequest != NULL) {
|
||
*ProtocolStatus = STATUS_ACCESS_DENIED;
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
|
||
NlpInitClientBuffer( &ClientBufferDesc, ClientRequest );
|
||
|
||
|
||
*ProtocolStatus = STATUS_SUCCESS;
|
||
|
||
|
||
//
|
||
// Ensure the specified Submit Buffer is of reasonable size and
|
||
// relocate all of the pointers to be relative to the LSA allocated
|
||
// buffer.
|
||
//
|
||
|
||
if ( SubmitBufferSize < sizeof(MSV1_0_CACHE_LOOKUP_REQUEST) ) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
|
||
CacheRequest = (PMSV1_0_CACHE_LOOKUP_REQUEST) ProtocolSubmitBuffer;
|
||
RtlZeroMemory(
|
||
&LogonInfo,
|
||
sizeof(LogonInfo)
|
||
);
|
||
|
||
//
|
||
// NOTE: this submit call only supports in-process calls within the LSA
|
||
// so buffers within the submit buffer are assumed to be valid and
|
||
// hence not validated in the same way that out-proc calls are.
|
||
//
|
||
|
||
LogonInfo.LogonDomainName = CacheRequest->DomainName;
|
||
LogonInfo.UserName = CacheRequest->UserName;
|
||
|
||
|
||
|
||
if( CacheRequest->CredentialType != MSV1_0_CACHE_LOOKUP_CREDTYPE_NONE &&
|
||
CacheRequest->CredentialType != MSV1_0_CACHE_LOOKUP_CREDTYPE_RAW &&
|
||
CacheRequest->CredentialType != MSV1_0_CACHE_LOOKUP_CREDTYPE_NTOWF ) {
|
||
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
//
|
||
// get the cache entry
|
||
//
|
||
|
||
*ProtocolStatus = NlpGetCacheEntry(
|
||
&LogonInfo,
|
||
&ValidationInfo,
|
||
&cachePasswords,
|
||
&SupplementalCacheData,
|
||
&SupplementalCacheDataLength
|
||
);
|
||
|
||
if (!NT_SUCCESS(*ProtocolStatus)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
if( CacheRequest->CredentialType == MSV1_0_CACHE_LOOKUP_CREDTYPE_NONE ) {
|
||
if( CacheRequest->CredentialInfoLength != 0 ) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
}
|
||
|
||
//
|
||
// verify the password, if necessary.
|
||
//
|
||
|
||
if( CacheRequest->CredentialType == MSV1_0_CACHE_LOOKUP_CREDTYPE_RAW ) {
|
||
|
||
//
|
||
// convert RAW to NTOWF.
|
||
//
|
||
|
||
UNICODE_STRING TempPassword;
|
||
|
||
if( CacheRequest->CredentialInfoLength > 0xFFFF ) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
|
||
TempPassword.Buffer = (PWSTR)&CacheRequest->CredentialSubmitBuffer;
|
||
TempPassword.Length = (USHORT)CacheRequest->CredentialInfoLength;
|
||
TempPassword.MaximumLength = TempPassword.Length;
|
||
|
||
pNtOwfPassword = &ComputedNtOwfPassword;
|
||
|
||
Status = RtlCalculateNtOwfPassword( &TempPassword, pNtOwfPassword );
|
||
|
||
if( !NT_SUCCESS( Status ) ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// now, convert the request to NT_OWF style.
|
||
//
|
||
|
||
CacheRequest->CredentialType = MSV1_0_CACHE_LOOKUP_CREDTYPE_NTOWF;
|
||
CacheRequest->CredentialInfoLength = sizeof( NT_OWF_PASSWORD );
|
||
|
||
}
|
||
|
||
if( CacheRequest->CredentialType == MSV1_0_CACHE_LOOKUP_CREDTYPE_NTOWF ) {
|
||
if( CacheRequest->CredentialInfoLength != sizeof( NT_OWF_PASSWORD ) ) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
|
||
if( !cachePasswords.SecretPasswords.NtPasswordPresent ) {
|
||
Status = STATUS_LOGON_FAILURE;
|
||
goto Cleanup;
|
||
}
|
||
|
||
if( pNtOwfPassword == NULL ) {
|
||
pNtOwfPassword = (PNT_OWF_PASSWORD)&CacheRequest->CredentialSubmitBuffer;
|
||
}
|
||
|
||
Status = NlpComputeSaltedHashedPassword(
|
||
pNtOwfPassword,
|
||
pNtOwfPassword,
|
||
&ValidationInfo->EffectiveName
|
||
);
|
||
|
||
if(!NT_SUCCESS( Status )) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
if(RtlCompareMemory(
|
||
pNtOwfPassword,
|
||
&cachePasswords.SecretPasswords.NtOwfPassword,
|
||
sizeof( NT_OWF_PASSWORD )
|
||
) != sizeof(NT_OWF_PASSWORD) )
|
||
{
|
||
Status = STATUS_LOGON_FAILURE;
|
||
goto Cleanup;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Return the validation info here.
|
||
//
|
||
|
||
*ReturnBufferSize = sizeof(MSV1_0_CACHE_LOOKUP_RESPONSE);
|
||
|
||
Status = NlpAllocateClientBuffer( &ClientBufferDesc,
|
||
sizeof(MSV1_0_CACHE_LOOKUP_RESPONSE),
|
||
*ReturnBufferSize );
|
||
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
CacheResponse = (PMSV1_0_CACHE_LOOKUP_RESPONSE) ClientBufferDesc.MsvBuffer;
|
||
|
||
//
|
||
// Fill in the return buffer.
|
||
//
|
||
|
||
CacheResponse->MessageType = MsV1_0CacheLookup;
|
||
CacheResponse->ValidationInformation = ValidationInfo;
|
||
|
||
CacheResponse->SupplementalCacheData = SupplementalCacheData;
|
||
CacheResponse->SupplementalCacheDataLength = SupplementalCacheDataLength;
|
||
|
||
//
|
||
// Flush the buffer to the client's address space.
|
||
//
|
||
|
||
Status = NlpFlushClientBuffer( &ClientBufferDesc,
|
||
ProtocolReturnBuffer );
|
||
|
||
|
||
Cleanup:
|
||
|
||
if ( !NT_SUCCESS(Status)) {
|
||
NlpFreeClientBuffer( &ClientBufferDesc );
|
||
|
||
if (ValidationInfo != NULL) {
|
||
MIDL_user_free( ValidationInfo );
|
||
}
|
||
|
||
if (SupplementalCacheData != NULL) {
|
||
MIDL_user_free( SupplementalCacheData );
|
||
}
|
||
}
|
||
|
||
ZeroMemory( &ComputedNtOwfPassword, sizeof( ComputedNtOwfPassword ) );
|
||
ZeroMemory( &cachePasswords, sizeof(cachePasswords) );
|
||
|
||
return(STATUS_SUCCESS);
|
||
UNREFERENCED_PARAMETER( ClientBufferBase);
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
MspSetProcessOption(
|
||
IN PLSA_CLIENT_REQUEST ClientRequest,
|
||
IN PVOID ProtocolSubmitBuffer,
|
||
IN PVOID ClientBufferBase,
|
||
IN ULONG SubmitBufferSize,
|
||
OUT PVOID *ProtocolReturnBuffer,
|
||
OUT PULONG ReturnBufferSize,
|
||
OUT PNTSTATUS ProtocolStatus
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the dispatch routine for LsaCallAuthenticationPackage()
|
||
with a message type of MsV1_0SetProcessOption.
|
||
|
||
Arguments:
|
||
|
||
The arguments to this routine are identical to those of LsaApCallPackage.
|
||
Only the special attributes of these parameters as they apply to
|
||
this routine are mentioned here.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the service completed successfully.
|
||
|
||
STATUS_QUOTA_EXCEEDED - This error indicates that the logon
|
||
could not be completed because the client does not have
|
||
sufficient quota to allocate the return buffer.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PMSV1_0_SETPROCESSOPTION_REQUEST SetProcessOptionRequest;
|
||
|
||
*ProtocolStatus = STATUS_UNSUCCESSFUL;
|
||
|
||
UNREFERENCED_PARAMETER(ClientBufferBase);
|
||
UNREFERENCED_PARAMETER(ReturnBufferSize);
|
||
UNREFERENCED_PARAMETER(ProtocolReturnBuffer);
|
||
UNREFERENCED_PARAMETER(ClientRequest);
|
||
|
||
|
||
//
|
||
// Ensure the specified Submit Buffer is of reasonable size and
|
||
// relocate all of the pointers to be relative to the LSA allocated
|
||
// buffer.
|
||
//
|
||
|
||
if ( SubmitBufferSize < sizeof(MSV1_0_SETPROCESSOPTION_REQUEST) ) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
|
||
SetProcessOptionRequest = (PMSV1_0_SETPROCESSOPTION_REQUEST) ProtocolSubmitBuffer;
|
||
|
||
if( NtLmSetProcessOption(
|
||
SetProcessOptionRequest->ProcessOptions,
|
||
SetProcessOptionRequest->DisableOptions
|
||
) )
|
||
{
|
||
*ProtocolStatus = STATUS_SUCCESS;
|
||
}
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
Cleanup:
|
||
|
||
return(Status);
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
LsaApLogonUserEx2 (
|
||
IN PLSA_CLIENT_REQUEST ClientRequest,
|
||
IN SECURITY_LOGON_TYPE LogonType,
|
||
IN PVOID ProtocolSubmitBuffer,
|
||
IN PVOID ClientBufferBase,
|
||
IN ULONG SubmitBufferSize,
|
||
OUT PVOID *ProfileBuffer,
|
||
OUT PULONG ProfileBufferSize,
|
||
OUT PLUID LogonId,
|
||
OUT PNTSTATUS SubStatus,
|
||
OUT PLSA_TOKEN_INFORMATION_TYPE TokenInformationType,
|
||
OUT PVOID *TokenInformation,
|
||
OUT PUNICODE_STRING *AccountName,
|
||
OUT PUNICODE_STRING *AuthenticatingAuthority,
|
||
OUT PUNICODE_STRING *MachineName,
|
||
OUT PSECPKG_PRIMARY_CRED PrimaryCredentials,
|
||
OUT PSECPKG_SUPPLEMENTAL_CRED_ARRAY * SupplementalCredentials
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to authenticate a user logon attempt. This is
|
||
the user's initial logon. A new LSA logon session will be established
|
||
for the user and validation information for the user will be returned.
|
||
|
||
Arguments:
|
||
|
||
ClientRequest - Is a pointer to an opaque data structure
|
||
representing the client's request.
|
||
|
||
LogonType - Identifies the type of logon being attempted.
|
||
|
||
ProtocolSubmitBuffer - Supplies the authentication
|
||
information specific to the authentication package.
|
||
|
||
ClientBufferBase - Provides the address within the client
|
||
process at which the authentication information was resident.
|
||
This may be necessary to fix-up any pointers within the
|
||
authentication information buffer.
|
||
|
||
SubmitBufferSize - Indicates the Size, in bytes,
|
||
of the authentication information buffer.
|
||
|
||
ProfileBuffer - Is used to return the address of the profile
|
||
buffer in the client process. The authentication package is
|
||
responsible for allocating and returning the profile buffer
|
||
within the client process. However, if the LSA subsequently
|
||
encounters an error which prevents a successful logon, then
|
||
the LSA will take care of deallocating that buffer. This
|
||
buffer is expected to have been allocated with the
|
||
AllocateClientBuffer() service.
|
||
|
||
The format and semantics of this buffer are specific to the
|
||
authentication package.
|
||
|
||
ProfileBufferSize - Receives the Size (in bytes) of the
|
||
returned profile buffer.
|
||
|
||
SubStatus - If the logon failed due to account restrictions, the
|
||
reason for the failure should be returned via this parameter.
|
||
The reason is authentication-package specific. The substatus
|
||
values for authentication package "MSV1.0" are:
|
||
|
||
STATUS_INVALID_LOGON_HOURS
|
||
|
||
STATUS_INVALID_WORKSTATION
|
||
|
||
STATUS_PASSWORD_EXPIRED
|
||
|
||
STATUS_ACCOUNT_DISABLED
|
||
|
||
TokenInformationLevel - If the logon is successful, this field is
|
||
used to indicate what level of information is being returned
|
||
for inclusion in the Token to be created. This information
|
||
is returned via the TokenInformation parameter.
|
||
|
||
TokenInformation - If the logon is successful, this parameter is
|
||
used by the authentication package to return information to
|
||
be included in the token. The format and content of the
|
||
buffer returned is indicated by the TokenInformationLevel
|
||
return value.
|
||
|
||
AccountName - A Unicode string describing the account name
|
||
being logged on to. This parameter must always be returned
|
||
regardless of the success or failure of the operation.
|
||
|
||
AuthenticatingAuthority - A Unicode string describing the Authenticating
|
||
Authority for the logon. This string may optionally be omitted.
|
||
|
||
PrimaryCredentials - Returns primary credentials for handing to other
|
||
packages.
|
||
|
||
SupplementalCredentials - Array of supplemental credential blobs for
|
||
other packages.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the service completed successfully.
|
||
|
||
STATUS_QUOTA_EXCEEDED - This error indicates that the logon
|
||
could not be completed because the client does not have
|
||
sufficient quota to allocate the return buffer.
|
||
|
||
STATUS_NO_LOGON_SERVERS - Indicates that no domain controllers
|
||
are currently able to service the authentication request.
|
||
|
||
STATUS_LOGON_FAILURE - Indicates the logon attempt failed. No
|
||
indication as to the reason for failure is given, but typical
|
||
reasons include mispelled usernames, mispelled passwords.
|
||
|
||
STATUS_ACCOUNT_RESTRICTION - Indicates the user account and
|
||
password were legitimate, but that the user account has some
|
||
restriction preventing successful logon at this time.
|
||
|
||
STATUS_BAD_VALIDATION_CLASS - The authentication information
|
||
provided is not a validation class known to the specified
|
||
authentication package.
|
||
|
||
STATUS_INVALID_LOGON_CLASS - LogonType was invalid.
|
||
|
||
STATUS_LOGON_SESSION_COLLISION- Internal Error: A LogonId was selected for
|
||
this logon session. The selected LogonId already exists.
|
||
|
||
STATUS_NETLOGON_NOT_STARTED - The Sam Server or Netlogon service was
|
||
required to perform this function. The required server was not running.
|
||
|
||
STATUS_NO_MEMORY - Insufficient virtual memory or pagefile quota exists.
|
||
|
||
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
LSA_TOKEN_INFORMATION_TYPE LsaTokenInformationType = LsaTokenInformationV2;
|
||
|
||
PNETLOGON_VALIDATION_SAM_INFO4 NlpUser = NULL;
|
||
|
||
|
||
PACTIVE_LOGON LogonEntry = NULL;
|
||
BOOLEAN LogonEntryLinked = FALSE;
|
||
|
||
BOOLEAN LogonSessionCreated = FALSE;
|
||
BOOLEAN LogonCredentialAdded = FALSE;
|
||
ULONG Flags = 0;
|
||
BOOLEAN Authoritative;
|
||
BOOLEAN BadPasswordCountZeroed;
|
||
BOOLEAN StandaloneWorkstation = FALSE;
|
||
|
||
PSID UserSid = NULL;
|
||
|
||
PMSV1_0_PRIMARY_CREDENTIAL Credential = NULL;
|
||
ULONG CredentialSize;
|
||
|
||
PSECURITY_SEED_AND_LENGTH SeedAndLength;
|
||
UCHAR Seed;
|
||
|
||
PUNICODE_STRING WorkStationName = NULL;
|
||
|
||
// Need to figure out whether to delete the profile buffer
|
||
|
||
BOOLEAN fSubAuthEx = FALSE;
|
||
|
||
//
|
||
// deferred NTLM3 checks.
|
||
//
|
||
|
||
BOOLEAN fNtLm3 = FALSE;
|
||
|
||
//
|
||
// Whether to wait for network & netlogon. If we are attempting
|
||
// forced cached credentials logon, we will avoid doing so.
|
||
//
|
||
|
||
BOOLEAN fWaitForNetwork = TRUE;
|
||
|
||
//
|
||
// Temporary storage while we try to figure
|
||
// out what our username and authenticating
|
||
// authority is.
|
||
//
|
||
|
||
UNICODE_STRING TmpName;
|
||
WCHAR TmpNameBuffer[UNLEN];
|
||
UNICODE_STRING TmpAuthority;
|
||
WCHAR TmpAuthorityBuffer[DNS_MAX_NAME_LENGTH];
|
||
|
||
//
|
||
// Logon Information.
|
||
//
|
||
NETLOGON_LOGON_INFO_CLASS LogonLevel;
|
||
NETLOGON_INTERACTIVE_INFO LogonInteractive;
|
||
NETLOGON_NETWORK_INFO LogonNetwork;
|
||
PNETLOGON_LOGON_IDENTITY_INFO LogonInformation;
|
||
|
||
PMSV1_0_LM20_LOGON NetworkAuthentication = NULL;
|
||
|
||
//
|
||
// Secret information, if we are doing a service logon
|
||
//
|
||
LSAPR_HANDLE SecretHandle;
|
||
PLSAPR_CR_CIPHER_VALUE SecretCurrent = NULL;
|
||
UNICODE_STRING Prefix, SavedPassword;
|
||
BOOLEAN ServiceSecretLogon = FALSE;
|
||
PMSV1_0_INTERACTIVE_LOGON Authentication = NULL;
|
||
|
||
|
||
//
|
||
// Credential manager stored credentials.
|
||
//
|
||
|
||
UNICODE_STRING CredmanUserName;
|
||
UNICODE_STRING CredmanDomainName;
|
||
UNICODE_STRING CredmanPassword;
|
||
|
||
|
||
//
|
||
// WMI tracing helper struct
|
||
//
|
||
NTLM_TRACE_INFO TraceInfo = {0};
|
||
|
||
#if _WIN64
|
||
PVOID pTempSubmitBuffer = ProtocolSubmitBuffer;
|
||
SECPKG_CALL_INFO CallInfo;
|
||
BOOL fAllocatedSubmitBuffer = FALSE;
|
||
|
||
if( ClientRequest == (PLSA_CLIENT_REQUEST)( -1 ) )
|
||
{
|
||
//
|
||
// if the call originated inproc, the buffers have already been
|
||
// marshalled/etc.
|
||
//
|
||
|
||
ZeroMemory( &CallInfo, sizeof(CallInfo) );
|
||
} else {
|
||
if(!LsaFunctions->GetCallInfo(&CallInfo))
|
||
{
|
||
Status = STATUS_INTERNAL_ERROR;
|
||
goto Cleanup;
|
||
}
|
||
}
|
||
|
||
#endif
|
||
|
||
//
|
||
// CachedInteractive logons are treated same as Interactive except
|
||
// that we avoid hitting the network.
|
||
//
|
||
|
||
if (LogonType == CachedInteractive) {
|
||
fWaitForNetwork = FALSE;
|
||
LogonType = Interactive;
|
||
}
|
||
|
||
//
|
||
// Begin tracing a logon user
|
||
//
|
||
if (NtlmGlobalEventTraceFlag){
|
||
|
||
//
|
||
// Trace header goo
|
||
//
|
||
SET_TRACE_HEADER(TraceInfo,
|
||
NtlmLogonGuid,
|
||
EVENT_TRACE_TYPE_START,
|
||
WNODE_FLAG_TRACED_GUID,
|
||
0);
|
||
|
||
TraceEvent(NtlmGlobalTraceLoggerHandle,
|
||
(PEVENT_TRACE_HEADER)&TraceInfo);
|
||
}
|
||
|
||
//
|
||
// Initialize
|
||
//
|
||
|
||
*ProfileBuffer = NULL;
|
||
*SubStatus = STATUS_SUCCESS;
|
||
*AuthenticatingAuthority = NULL;
|
||
*AccountName = NULL;
|
||
|
||
TmpName.Buffer = TmpNameBuffer;
|
||
TmpName.MaximumLength = UNLEN * sizeof( WCHAR );
|
||
TmpName.Length = 0;
|
||
|
||
TmpAuthority.Buffer = TmpAuthorityBuffer;
|
||
TmpAuthority.MaximumLength = DNS_MAX_NAME_LENGTH * sizeof( WCHAR );
|
||
TmpAuthority.Length = 0;
|
||
|
||
CredmanUserName.Buffer = NULL;
|
||
CredmanDomainName.Buffer = NULL;
|
||
CredmanPassword.Buffer = NULL;
|
||
|
||
*SupplementalCredentials = 0;
|
||
|
||
RtlZeroMemory(
|
||
PrimaryCredentials,
|
||
sizeof(SECPKG_PRIMARY_CRED)
|
||
);
|
||
|
||
//
|
||
// Check the Authentication information and build a LogonInformation
|
||
// structure to pass to SAM or Netlogon.
|
||
//
|
||
// NOTE: Netlogon treats Service and Batch logons as if they are
|
||
// Interactive.
|
||
//
|
||
|
||
switch ( LogonType ) {
|
||
case Service:
|
||
case Interactive:
|
||
case Batch:
|
||
case NetworkCleartext:
|
||
case RemoteInteractive:
|
||
{
|
||
MSV1_0_PRIMARY_CREDENTIAL BuiltCredential;
|
||
|
||
#if _WIN64
|
||
|
||
|
||
//
|
||
// Expand the ProtocolSubmitBuffer to 64-bit pointers if this
|
||
// call came from a WOW client.
|
||
//
|
||
|
||
|
||
if (CallInfo.Attributes & SECPKG_CALL_WOWCLIENT)
|
||
{
|
||
Authentication =
|
||
(PMSV1_0_INTERACTIVE_LOGON) ProtocolSubmitBuffer;
|
||
|
||
Status = MsvConvertWOWInteractiveLogonBuffer(
|
||
ProtocolSubmitBuffer,
|
||
ClientBufferBase,
|
||
&SubmitBufferSize,
|
||
&pTempSubmitBuffer
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status))
|
||
{
|
||
goto Cleanup;
|
||
}
|
||
|
||
fAllocatedSubmitBuffer = TRUE;
|
||
|
||
//
|
||
// Some macros below expand out to use ProtocolSubmitBuffer directly.
|
||
// We've secretly replaced their usual ProtocolSubmitBuffer with
|
||
// pTempSubmitBuffer -- let's see if they can tell the difference.
|
||
//
|
||
|
||
ProtocolSubmitBuffer = pTempSubmitBuffer;
|
||
}
|
||
|
||
#endif // _WIN64
|
||
|
||
|
||
WorkStationName = &NlpComputerName;
|
||
|
||
//
|
||
// Ensure this is really an interactive logon.
|
||
//
|
||
|
||
Authentication =
|
||
(PMSV1_0_INTERACTIVE_LOGON) ProtocolSubmitBuffer;
|
||
|
||
|
||
if ( (Authentication->MessageType != MsV1_0InteractiveLogon ) &&
|
||
(Authentication->MessageType != MsV1_0WorkstationUnlockLogon) ) {
|
||
SspPrint((SSP_CRITICAL, "LsaApLogonUser: Bad Validation Class %d\n", Authentication->MessageType));
|
||
Status = STATUS_BAD_VALIDATION_CLASS;
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
|
||
|
||
//
|
||
// If the password length is greater than 255 (i.e., the
|
||
// upper byte of the length is non-zero) then the password
|
||
// has been run-encoded for privacy reasons. Get the
|
||
// run-encode seed out of the upper-byte of the length
|
||
// for later use.
|
||
//
|
||
//
|
||
|
||
|
||
|
||
SeedAndLength = (PSECURITY_SEED_AND_LENGTH)
|
||
&Authentication->Password.Length;
|
||
Seed = SeedAndLength->Seed;
|
||
SeedAndLength->Seed = 0;
|
||
|
||
//
|
||
// Enforce length restrictions on username and password.
|
||
//
|
||
|
||
if ( Authentication->UserName.Length > (UNLEN*sizeof(WCHAR)) ||
|
||
Authentication->Password.Length > (PWLEN*sizeof(WCHAR)) )
|
||
{
|
||
SspPrint((SSP_CRITICAL, "LsaApLogonUser: Name or password too long\n"));
|
||
Status = STATUS_NAME_TOO_LONG;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Relocate any pointers to be relative to 'Authentication'
|
||
//
|
||
|
||
NULL_RELOCATE_ONE( &Authentication->LogonDomainName );
|
||
|
||
RELOCATE_ONE( &Authentication->UserName );
|
||
|
||
NULL_RELOCATE_ONE( &Authentication->Password );
|
||
|
||
if( (Authentication->LogonDomainName.Length <= sizeof(WCHAR)) &&
|
||
(Authentication->Password.Length <= sizeof(WCHAR))
|
||
)
|
||
{
|
||
if(CredpProcessUserNameCredential(
|
||
&Authentication->UserName,
|
||
&CredmanUserName,
|
||
&CredmanDomainName,
|
||
&CredmanPassword
|
||
) == STATUS_SUCCESS)
|
||
{
|
||
Authentication->UserName = CredmanUserName;
|
||
Authentication->LogonDomainName = CredmanDomainName;
|
||
Authentication->Password = CredmanPassword;
|
||
}
|
||
}
|
||
|
||
#if 0
|
||
//
|
||
// Handle UPN and composite NETBIOS syntax
|
||
//
|
||
{
|
||
UNICODE_STRING User = Authentication->UserName;
|
||
UNICODE_STRING Domain = Authentication->LogonDomainName;
|
||
|
||
Status =
|
||
NtLmParseName(
|
||
&User,
|
||
&Domain,
|
||
FALSE
|
||
);
|
||
if(NT_SUCCESS(Status)){
|
||
Authentication->UserName = User;
|
||
Authentication->LogonDomainName = Domain;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
|
||
if ( LogonType == Service )
|
||
{
|
||
SECPKG_CALL_INFO CallInfo;
|
||
|
||
if( LsaFunctions->GetCallInfo(&CallInfo) &&
|
||
(CallInfo.Attributes & SECPKG_CALL_IS_TCB) )
|
||
{
|
||
//
|
||
// If we have a service logon, the password we got is likely the name of the secret
|
||
// that is holding the account password. Make sure to read that secret here
|
||
//
|
||
RtlInitUnicodeString( &Prefix, L"_SC_" );
|
||
if ( RtlPrefixUnicodeString( &Prefix, &Authentication->Password, TRUE ) )
|
||
{
|
||
|
||
Status = LsarOpenSecret( NtLmGlobalPolicyHandle,
|
||
( PLSAPR_UNICODE_STRING )&Authentication->Password,
|
||
SECRET_QUERY_VALUE,
|
||
&SecretHandle );
|
||
|
||
if ( NT_SUCCESS( Status ) )
|
||
{
|
||
|
||
Status = LsarQuerySecret( SecretHandle,
|
||
&SecretCurrent,
|
||
NULL,
|
||
NULL,
|
||
NULL );
|
||
|
||
if ( NT_SUCCESS( Status ) && (SecretCurrent != NULL) )
|
||
{
|
||
|
||
RtlCopyMemory( &SavedPassword,
|
||
&Authentication->Password,
|
||
sizeof( UNICODE_STRING ) );
|
||
Authentication->Password.Length = ( USHORT )SecretCurrent->Length;
|
||
Authentication->Password.MaximumLength =
|
||
( USHORT )SecretCurrent->MaximumLength;
|
||
Authentication->Password.Buffer = ( USHORT * )SecretCurrent->Buffer;
|
||
ServiceSecretLogon = TRUE;
|
||
}
|
||
|
||
LsarClose( &SecretHandle );
|
||
}
|
||
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
SspPrint((SSP_CRITICAL, "LsaApLogonUser: failed to querying service password\n"));
|
||
|
||
goto Cleanup;
|
||
}
|
||
|
||
}
|
||
//
|
||
// Now decode the password, if necessary
|
||
//
|
||
|
||
if (Seed != 0 ) {
|
||
try {
|
||
RtlRunDecodeUnicodeString( Seed, &Authentication->Password);
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
SspPrint((SSP_CRITICAL, "LsaApLogonUser: failed to decode password\n"));
|
||
Status = STATUS_ILL_FORMED_PASSWORD;
|
||
goto Cleanup;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Copy out the user name and Authenticating Authority so we can audit them.
|
||
//
|
||
|
||
RtlCopyUnicodeString( &TmpName, &Authentication->UserName );
|
||
|
||
if ( Authentication->LogonDomainName.Buffer != NULL ) {
|
||
|
||
RtlCopyUnicodeString( &TmpAuthority, &Authentication->LogonDomainName );
|
||
}
|
||
|
||
|
||
//
|
||
// Put the password in the PrimaryCredential to pass to the sundry security packages.
|
||
//
|
||
|
||
PrimaryCredentials->Password.Length = PrimaryCredentials->Password.MaximumLength =
|
||
Authentication->Password.Length;
|
||
PrimaryCredentials->Password.Buffer = (*Lsa.AllocateLsaHeap)(Authentication->Password.Length);
|
||
|
||
if (PrimaryCredentials->Password.Buffer == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Cleanup;
|
||
}
|
||
|
||
RtlCopyMemory(
|
||
PrimaryCredentials->Password.Buffer,
|
||
Authentication->Password.Buffer,
|
||
Authentication->Password.Length
|
||
);
|
||
PrimaryCredentials->Flags = PRIMARY_CRED_CLEAR_PASSWORD;
|
||
// PrimaryCredentials->Flags |= (RPC_C_AUTHN_WINNT << PRIMARY_CRED_LOGON_PACKAGE_SHIFT);
|
||
|
||
|
||
//
|
||
// We're all done with the cleartext password
|
||
// Don't let it get to the pagefile.
|
||
//
|
||
|
||
try {
|
||
if ( Authentication->Password.Buffer != NULL ) {
|
||
RtlEraseUnicodeString( &Authentication->Password );
|
||
}
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
SspPrint((SSP_CRITICAL, "LsaApLogonUser: failed to decode password\n"));
|
||
Status = STATUS_ILL_FORMED_PASSWORD;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Compute the OWF of the password.
|
||
//
|
||
|
||
NlpPutOwfsInPrimaryCredential( &PrimaryCredentials->Password,
|
||
&BuiltCredential );
|
||
|
||
|
||
//
|
||
// Define the description of the user to log on.
|
||
//
|
||
LogonLevel = NetlogonInteractiveInformation;
|
||
LogonInformation =
|
||
(PNETLOGON_LOGON_IDENTITY_INFO) &LogonInteractive;
|
||
|
||
LogonInteractive.Identity.LogonDomainName =
|
||
Authentication->LogonDomainName;
|
||
LogonInteractive.Identity.ParameterControl = 0;
|
||
|
||
LogonInteractive.Identity.UserName = Authentication->UserName;
|
||
LogonInteractive.Identity.Workstation = NlpComputerName;
|
||
|
||
|
||
LogonInteractive.LmOwfPassword = BuiltCredential.LmOwfPassword;
|
||
LogonInteractive.NtOwfPassword = BuiltCredential.NtOwfPassword;
|
||
|
||
}
|
||
|
||
break;
|
||
|
||
case Network:
|
||
{
|
||
PMSV1_0_LM20_LOGON Authentication;
|
||
|
||
|
||
//
|
||
// Expand the ProtocolSubmitBuffer to 64-bit pointers if this
|
||
// call came from a WOW client.
|
||
//
|
||
|
||
#if _WIN64
|
||
if (CallInfo.Attributes & SECPKG_CALL_WOWCLIENT)
|
||
{
|
||
Authentication =
|
||
(PMSV1_0_LM20_LOGON) ProtocolSubmitBuffer;
|
||
|
||
Status = MsvConvertWOWNetworkLogonBuffer(
|
||
ProtocolSubmitBuffer,
|
||
ClientBufferBase,
|
||
&SubmitBufferSize,
|
||
&pTempSubmitBuffer
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status))
|
||
{
|
||
goto Cleanup;
|
||
}
|
||
|
||
fAllocatedSubmitBuffer = TRUE;
|
||
|
||
//
|
||
// Some macros below expand out to use ProtocolSubmitBuffer directly.
|
||
// We've secretly replaced their usual ProtocolSubmitBuffer with
|
||
// pTempSubmitBuffer -- let's see if they can tell the difference.
|
||
//
|
||
|
||
ProtocolSubmitBuffer = pTempSubmitBuffer;
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Ensure this is really a network logon request.
|
||
//
|
||
|
||
Authentication =
|
||
(PMSV1_0_LM20_LOGON) ProtocolSubmitBuffer;
|
||
|
||
NetworkAuthentication = Authentication;
|
||
|
||
|
||
if ( Authentication->MessageType != MsV1_0Lm20Logon &&
|
||
Authentication->MessageType != MsV1_0SubAuthLogon &&
|
||
Authentication->MessageType != MsV1_0NetworkLogon )
|
||
{
|
||
SspPrint((SSP_CRITICAL, "LsaApLogonUser: Bad Validation Class\n"));
|
||
Status = STATUS_BAD_VALIDATION_CLASS;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Relocate any pointers to be relative to 'Authentication'
|
||
//
|
||
|
||
NULL_RELOCATE_ONE( &Authentication->LogonDomainName );
|
||
|
||
NULL_RELOCATE_ONE( &Authentication->UserName );
|
||
|
||
RELOCATE_ONE( &Authentication->Workstation );
|
||
|
||
#if 0
|
||
//
|
||
// Handle UPN and composite NETBIOS syntax
|
||
//
|
||
{
|
||
UNICODE_STRING User = Authentication->UserName;
|
||
UNICODE_STRING Domain = Authentication->LogonDomainName;
|
||
|
||
Status =
|
||
NtLmParseName(
|
||
&User,
|
||
&Domain,
|
||
FALSE
|
||
);
|
||
if(NT_SUCCESS(Status)){
|
||
Authentication->UserName = User;
|
||
Authentication->LogonDomainName = Domain;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
|
||
//
|
||
// Copy out the user name and Authenticating Authority so we can audit them.
|
||
//
|
||
|
||
if ( Authentication->UserName.Buffer != NULL ) {
|
||
|
||
RtlCopyUnicodeString( &TmpName, &Authentication->UserName );
|
||
}
|
||
|
||
if ( Authentication->LogonDomainName.Buffer != NULL ) {
|
||
|
||
RtlCopyUnicodeString( &TmpAuthority, &Authentication->LogonDomainName );
|
||
}
|
||
|
||
NULL_RELOCATE_ONE((PUNICODE_STRING)&Authentication->CaseSensitiveChallengeResponse );
|
||
|
||
NULL_RELOCATE_ONE((PUNICODE_STRING)&Authentication->CaseInsensitiveChallengeResponse );
|
||
|
||
|
||
//
|
||
// Define the description of the user to log on.
|
||
//
|
||
LogonLevel = NetlogonNetworkInformation;
|
||
LogonInformation =
|
||
(PNETLOGON_LOGON_IDENTITY_INFO) &LogonNetwork;
|
||
|
||
LogonNetwork.Identity.LogonDomainName =
|
||
Authentication->LogonDomainName;
|
||
|
||
if ( Authentication->MessageType == MsV1_0Lm20Logon ) {
|
||
LogonNetwork.Identity.ParameterControl = MSV1_0_CLEARTEXT_PASSWORD_ALLOWED;
|
||
} else {
|
||
BOOLEAN EnforceTcb = FALSE;
|
||
|
||
ASSERT( CLEARTEXT_PASSWORD_ALLOWED == MSV1_0_CLEARTEXT_PASSWORD_ALLOWED );
|
||
LogonNetwork.Identity.ParameterControl =
|
||
Authentication->ParameterControl;
|
||
|
||
// For NT 5.0 SubAuth Packages, there is a SubAuthPackageId. Stuff
|
||
// that into ParameterControl so pre 5.0 MsvSamValidate won't choke.
|
||
|
||
if ( Authentication->MessageType == MsV1_0SubAuthLogon )
|
||
{
|
||
PMSV1_0_SUBAUTH_LOGON SubAuthentication =
|
||
(PMSV1_0_SUBAUTH_LOGON) ProtocolSubmitBuffer;
|
||
|
||
// Need to not delete return buffers even in case of error
|
||
// for MsV1_0SubAuthLogon (includes arap).
|
||
|
||
fSubAuthEx = TRUE;
|
||
|
||
LogonNetwork.Identity.ParameterControl |=
|
||
(SubAuthentication->SubAuthPackageId << MSV1_0_SUBAUTHENTICATION_DLL_SHIFT) | MSV1_0_SUBAUTHENTICATION_DLL_EX;
|
||
|
||
EnforceTcb = TRUE ;
|
||
} else {
|
||
if( Authentication->ParameterControl & MSV1_0_SUBAUTHENTICATION_DLL )
|
||
{
|
||
EnforceTcb = TRUE;
|
||
}
|
||
}
|
||
|
||
|
||
if( EnforceTcb )
|
||
{
|
||
SECPKG_CALL_INFO CallInfo;
|
||
|
||
if(!LsaFunctions->GetCallInfo(&CallInfo) ||
|
||
(CallInfo.Attributes & SECPKG_CALL_IS_TCB) == 0)
|
||
{
|
||
SspPrint((SSP_CRITICAL, "LsaApLogonUser: subauth caller isn't privileged\n"));
|
||
Status = STATUS_ACCESS_DENIED;
|
||
goto Cleanup;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
LogonNetwork.Identity.UserName = Authentication->UserName;
|
||
LogonNetwork.Identity.Workstation = Authentication->Workstation;
|
||
|
||
WorkStationName = &Authentication->Workstation;
|
||
|
||
LogonNetwork.NtChallengeResponse =
|
||
Authentication->CaseSensitiveChallengeResponse;
|
||
LogonNetwork.LmChallengeResponse =
|
||
Authentication->CaseInsensitiveChallengeResponse;
|
||
ASSERT( LM_CHALLENGE_LENGTH ==
|
||
sizeof(Authentication->ChallengeToClient) );
|
||
|
||
|
||
//
|
||
// If using client challenge, then mix it with the server's challenge
|
||
// to get the challenge we pass on. It would make more sense to do this
|
||
// in MsvpPasswordValidate, except that would require the DCs to be upgraded.
|
||
// Doing it here only requires agreement between the client and server, because
|
||
// the modified challenge will be passed on to the DCs.
|
||
//
|
||
|
||
if ((Authentication->ParameterControl & MSV1_0_USE_CLIENT_CHALLENGE) &&
|
||
(Authentication->CaseSensitiveChallengeResponse.Length == NT_RESPONSE_LENGTH) &&
|
||
(Authentication->CaseInsensitiveChallengeResponse.Length >= MSV1_0_CHALLENGE_LENGTH))
|
||
{
|
||
MsvpCalculateNtlm2Challenge (
|
||
Authentication->ChallengeToClient,
|
||
Authentication->CaseInsensitiveChallengeResponse.Buffer,
|
||
(PUCHAR)&LogonNetwork.LmChallenge
|
||
);
|
||
|
||
} else {
|
||
RtlCopyMemory(
|
||
&LogonNetwork.LmChallenge,
|
||
Authentication->ChallengeToClient,
|
||
LM_CHALLENGE_LENGTH );
|
||
}
|
||
|
||
//
|
||
// if using NTLM3, then check that the target info is for this machine.
|
||
//
|
||
|
||
if ((Authentication->ParameterControl & MSV1_0_USE_CLIENT_CHALLENGE) &&
|
||
(Authentication->CaseSensitiveChallengeResponse.Length >= sizeof(MSV1_0_NTLM3_RESPONSE)))
|
||
{
|
||
|
||
fNtLm3 = TRUE;
|
||
|
||
//
|
||
// defer NTLM3 checks until later on when SAM initialized.
|
||
//
|
||
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Enforce length restrictions on username
|
||
//
|
||
|
||
if ( Authentication->UserName.Length > (UNLEN*sizeof(WCHAR)) )
|
||
{
|
||
SspPrint((SSP_CRITICAL, "LsaApLogonUser: Name too long\n"));
|
||
Status = STATUS_NAME_TOO_LONG;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// If this is a null session logon,
|
||
// just build a NULL token.
|
||
//
|
||
|
||
if ( Authentication->UserName.Length == 0 &&
|
||
Authentication->CaseSensitiveChallengeResponse.Length == 0 &&
|
||
(Authentication->CaseInsensitiveChallengeResponse.Length == 0 ||
|
||
(Authentication->CaseInsensitiveChallengeResponse.Length == 1 &&
|
||
*Authentication->CaseInsensitiveChallengeResponse.Buffer == '\0') ) ) {
|
||
|
||
LsaTokenInformationType = LsaTokenInformationNull;
|
||
}
|
||
}
|
||
|
||
break;
|
||
|
||
default:
|
||
Status = STATUS_INVALID_LOGON_TYPE;
|
||
goto CleanupShort;
|
||
}
|
||
|
||
|
||
//
|
||
// Allocate a LogonId for this logon session.
|
||
//
|
||
|
||
Status = NtAllocateLocallyUniqueId( LogonId );
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
NEW_TO_OLD_LARGE_INTEGER( (*LogonId), LogonInformation->LogonId );
|
||
|
||
|
||
PrimaryCredentials->LogonId = *LogonId;
|
||
|
||
//
|
||
// Create a new logon session
|
||
//
|
||
|
||
Status = (*Lsa.CreateLogonSession)( LogonId );
|
||
if( !NT_SUCCESS(Status) ) {
|
||
SspPrint((SSP_CRITICAL, "LsaApLogonUser: Collision from CreateLogonSession %x\n", Status));
|
||
goto Cleanup;
|
||
}
|
||
|
||
LogonSessionCreated = TRUE;
|
||
|
||
|
||
//
|
||
// Don't worry about SAM or the LSA if this is a Null Session logon.
|
||
//
|
||
// The server does a Null Session logon during initialization.
|
||
// It shouldn't have to wait for SAM to initialize.
|
||
//
|
||
|
||
if ( LsaTokenInformationType != LsaTokenInformationNull ) {
|
||
|
||
//
|
||
// If Sam is not yet initialized,
|
||
// do it now.
|
||
//
|
||
|
||
if ( !NlpSamInitialized ) {
|
||
Status = NlSamInitialize( SAM_STARTUP_TIME );
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
goto Cleanup;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If this is a workstation,
|
||
// differentiate between a standalone workstation and a member
|
||
// workstation.
|
||
//
|
||
// (This is is done on every logon, rather than during initialization,
|
||
// to allow the value to be changed via the UI).
|
||
//
|
||
|
||
if ( NlpWorkstation ) {
|
||
RtlAcquireResourceShared(&NtLmGlobalCritSect, TRUE);
|
||
StandaloneWorkstation = (NtLmGlobalTargetFlags == NTLMSSP_TARGET_TYPE_SERVER);
|
||
RtlReleaseResource(&NtLmGlobalCritSect);
|
||
|
||
} else {
|
||
StandaloneWorkstation = FALSE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Try again to load netlogon.dll
|
||
//
|
||
if ( NlpNetlogonDllHandle == NULL ) {
|
||
NlpLoadNetlogonDll();
|
||
}
|
||
|
||
//
|
||
// do NTLM3 processing that was deferred until now due to initialization
|
||
// requirements.
|
||
//
|
||
|
||
if( fNtLm3 )
|
||
{
|
||
|
||
PMSV1_0_AV_PAIR pAV;
|
||
PMSV1_0_NTLM3_RESPONSE pResp;
|
||
LONG iRespLen;
|
||
|
||
ULONG NtLmProtocolSupported = NtLmGlobalLmProtocolSupported;
|
||
|
||
|
||
|
||
//
|
||
// get the computer name from the response
|
||
//
|
||
|
||
pResp = (PMSV1_0_NTLM3_RESPONSE)
|
||
NetworkAuthentication->CaseSensitiveChallengeResponse.Buffer;
|
||
iRespLen = NetworkAuthentication->CaseSensitiveChallengeResponse.Length -
|
||
sizeof(MSV1_0_NTLM3_RESPONSE);
|
||
|
||
pAV = MsvpAvlGet((PMSV1_0_AV_PAIR)pResp->Buffer, MsvAvNbComputerName, iRespLen);
|
||
|
||
//
|
||
// if there is one (OK to be missing), see that it is us
|
||
// REVIEW -- only allow it to be missing if registry says OK?
|
||
//
|
||
|
||
if (pAV) {
|
||
UNICODE_STRING Candidate;
|
||
|
||
Candidate.Buffer = (PWSTR)(pAV+1);
|
||
Candidate.Length = (USHORT)(pAV->AvLen);
|
||
Candidate.MaximumLength = Candidate.Length;
|
||
|
||
if(!RtlEqualUnicodeString( &NlpComputerName, &Candidate, TRUE ))
|
||
{
|
||
SspPrint((SSP_WARNING, "LsaApLogonUserEx2 failed NbComputerName compare\n"));
|
||
Status = STATUS_LOGON_FAILURE;
|
||
goto Cleanup;
|
||
}
|
||
|
||
} else if (NtLmProtocolSupported >= RefuseNtlm3NoTarget) {
|
||
SspPrint((SSP_WARNING, "LsaApLogonUserEx2 no target supplied\n"));
|
||
Status = STATUS_LOGON_FAILURE;
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
//
|
||
// get the domain name from the response
|
||
//
|
||
|
||
pAV = MsvpAvlGet((PMSV1_0_AV_PAIR)pResp->Buffer, MsvAvNbDomainName, iRespLen);
|
||
|
||
//
|
||
// must exist and must be us.
|
||
//
|
||
|
||
if (pAV) {
|
||
|
||
UNICODE_STRING Candidate;
|
||
|
||
Candidate.Buffer = (PWSTR)(pAV+1);
|
||
Candidate.Length = pAV->AvLen;
|
||
Candidate.MaximumLength = pAV->AvLen;
|
||
|
||
|
||
if( StandaloneWorkstation ) {
|
||
if( !RtlEqualDomainName(&NlpComputerName, &Candidate) ) {
|
||
SspPrint((SSP_WARNING, "LsaApLogonUserEx2 failed NbDomainName compare\n"));
|
||
Status = STATUS_LOGON_FAILURE;
|
||
goto Cleanup;
|
||
}
|
||
|
||
} else {
|
||
if( !RtlEqualDomainName(&NlpPrimaryDomainName, &Candidate) ) {
|
||
SspPrint((SSP_WARNING, "LsaApLogonUserEx2 failed PrimaryDomainName compare\n"));
|
||
Status = STATUS_LOGON_FAILURE;
|
||
goto Cleanup;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
SspPrint((SSP_WARNING, "LsaApLogonUserEx2 domain name not supplied\n"));
|
||
Status = STATUS_LOGON_FAILURE;
|
||
goto Cleanup;
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Do the actual logon now.
|
||
//
|
||
//
|
||
// If a null token is being built,
|
||
// don't authenticate at all.
|
||
//
|
||
|
||
if ( LsaTokenInformationType == LsaTokenInformationNull ) {
|
||
|
||
/* Nothing to do here. */
|
||
|
||
|
||
//
|
||
// Call Sam directly to get the validation information when:
|
||
//
|
||
// The network is not installed, OR
|
||
// This is a standalone workstation (not a member of a domain).
|
||
// This is a workstation and we're logging onto an account on the
|
||
// workstation.
|
||
//
|
||
|
||
|
||
|
||
} else if ( NlpNetlogonDllHandle == NULL || !NlpLanmanInstalled ||
|
||
StandaloneWorkstation ||
|
||
( NlpWorkstation &&
|
||
LogonInformation->LogonDomainName.Length != 0 &&
|
||
RtlEqualDomainName( &NlpSamDomainName,
|
||
&LogonInformation->LogonDomainName )) ) {
|
||
|
||
// Allow guest logons only
|
||
|
||
DWORD AccountsToTry = MSVSAM_SPECIFIED | MSVSAM_GUEST;
|
||
|
||
if ((LogonType == Network) &&
|
||
(LogonNetwork.Identity.ParameterControl & MSV1_0_TRY_GUEST_ACCOUNT_ONLY))
|
||
{
|
||
AccountsToTry = MSVSAM_GUEST;
|
||
}
|
||
|
||
//
|
||
// for local logons, CachedInteractive is not supported.
|
||
//
|
||
|
||
if( !fWaitForNetwork )
|
||
{
|
||
Status = STATUS_NOT_SUPPORTED;
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
//
|
||
// Get the Validation information from the local SAM database
|
||
//
|
||
|
||
|
||
|
||
Status = MsvSamValidate(
|
||
NlpSamDomainHandle,
|
||
NlpUasCompatibilityRequired,
|
||
MsvApSecureChannel,
|
||
&NlpComputerName, // Logon Server is this machine
|
||
&NlpSamDomainName,
|
||
NlpSamDomainId,
|
||
LogonLevel,
|
||
LogonInformation,
|
||
NetlogonValidationSamInfo4,
|
||
(PVOID *) &NlpUser,
|
||
&Authoritative,
|
||
&BadPasswordCountZeroed,
|
||
AccountsToTry);
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
// So we don't get a LOGON COLLISION from the old msv package
|
||
|
||
Flags |= LOGON_BY_LOCAL;
|
||
|
||
//
|
||
// If we couldn't validate via one of the above mechanisms,
|
||
// call the local Netlogon service to get the validation information.
|
||
//
|
||
|
||
} else {
|
||
|
||
NTSTATUS NetlogonStatus = STATUS_SUCCESS;
|
||
|
||
//
|
||
// If we are attempting cached credentials logon avoid getting stuck
|
||
// on netlogon or the network.
|
||
//
|
||
|
||
if (fWaitForNetwork) {
|
||
|
||
//
|
||
// Wait for NETLOGON to finish initialization.
|
||
//
|
||
|
||
if ( !NlpNetlogonInitialized ) {
|
||
|
||
Status = NlWaitForNetlogon( NETLOGON_STARTUP_TIME );
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
if ( Status != STATUS_NETLOGON_NOT_STARTED ) {
|
||
goto Cleanup;
|
||
}
|
||
} else {
|
||
NlpNetlogonInitialized = TRUE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Actually call the netlogon service.
|
||
//
|
||
|
||
if ( NlpNetlogonInitialized ) {
|
||
Status = (*NlpNetLogonSamLogon)(
|
||
NULL, // Server name
|
||
NULL, // Computer name
|
||
NULL, // Authenticator
|
||
NULL, // ReturnAuthenticator
|
||
LogonLevel,
|
||
(LPBYTE) &LogonInformation,
|
||
NetlogonValidationSamInfo4,
|
||
(LPBYTE *) &NlpUser,
|
||
&Authoritative );
|
||
|
||
//
|
||
// Reset Netlogon initialized flag if local netlogon cannot be
|
||
// reached.
|
||
// (Use a more explicit status code)
|
||
//
|
||
|
||
if( !NT_SUCCESS(Status) )
|
||
{
|
||
//
|
||
// save the result from netlogon.
|
||
//
|
||
|
||
NetlogonStatus = Status;
|
||
|
||
switch (Status)
|
||
{
|
||
//
|
||
// for documented errors that netlogon can return
|
||
// for authoritative failures, leave the status code as-is.
|
||
//
|
||
|
||
case STATUS_NO_TRUST_LSA_SECRET:
|
||
case STATUS_TRUSTED_DOMAIN_FAILURE:
|
||
case STATUS_INVALID_INFO_CLASS:
|
||
case STATUS_TRUSTED_RELATIONSHIP_FAILURE:
|
||
case STATUS_ACCESS_DENIED:
|
||
case STATUS_NO_SUCH_USER:
|
||
case STATUS_WRONG_PASSWORD:
|
||
case STATUS_INVALID_LOGON_HOURS:
|
||
case STATUS_PASSWORD_EXPIRED:
|
||
case STATUS_ACCOUNT_DISABLED:
|
||
case STATUS_INVALID_PARAMETER:
|
||
case STATUS_PASSWORD_MUST_CHANGE:
|
||
case STATUS_ACCOUNT_EXPIRED:
|
||
case STATUS_ACCOUNT_LOCKED_OUT:
|
||
case STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT:
|
||
case STATUS_NOLOGON_SERVER_TRUST_ACCOUNT:
|
||
case STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT:
|
||
case STATUS_INVALID_WORKSTATION:
|
||
{
|
||
break;
|
||
}
|
||
|
||
//
|
||
// for errors that are known to occur during unexpected
|
||
// conditions, over-ride status to allow cache lookup.
|
||
//
|
||
|
||
case RPC_NT_SERVER_UNAVAILABLE:
|
||
case RPC_NT_UNKNOWN_IF:
|
||
case STATUS_NETLOGON_NOT_STARTED:
|
||
{
|
||
Status = STATUS_NETLOGON_NOT_STARTED;
|
||
NlpNetlogonInitialized = FALSE;
|
||
break;
|
||
}
|
||
|
||
// default will catch a host of RPC related errors.
|
||
// some mentioned below.
|
||
//case EPT_NT_NOT_REGISTERED:
|
||
//case RPC_NT_CALL_FAILED_DNE:
|
||
//case RPC_NT_SERVER_TOO_BUSY:
|
||
//case RPC_NT_CALL_FAILED:
|
||
default:
|
||
{
|
||
Status = STATUS_NETLOGON_NOT_STARTED;
|
||
NlpNetlogonInitialized = FALSE;
|
||
break;
|
||
}
|
||
} // switch
|
||
} // if
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// We want to force cached credentials path by behaving as if no
|
||
// network logon servers were available.
|
||
//
|
||
|
||
NetlogonStatus = STATUS_NO_LOGON_SERVERS;
|
||
Status = NetlogonStatus;
|
||
}
|
||
|
||
//
|
||
// If this is the requested domain,
|
||
// go directly to SAM if the netlogon service isn't available.
|
||
//
|
||
// We want to go to the netlogon service if it is available since it
|
||
// does special handling of bad passwords and account lockout. However,
|
||
// if the netlogon service is down, the local SAM database makes a
|
||
// better cache than any other mechanism.
|
||
//
|
||
|
||
if ( !NlpNetlogonInitialized &&
|
||
LogonInformation->LogonDomainName.Length != 0 &&
|
||
RtlEqualDomainName( &NlpSamDomainName,
|
||
&LogonInformation->LogonDomainName ) ) {
|
||
|
||
// Allow guest logons only
|
||
|
||
DWORD AccountsToTry = MSVSAM_SPECIFIED | MSVSAM_GUEST;
|
||
|
||
if ((LogonType == Network) &&
|
||
(LogonNetwork.Identity.ParameterControl & MSV1_0_TRY_GUEST_ACCOUNT_ONLY))
|
||
{
|
||
AccountsToTry = MSVSAM_GUEST;
|
||
}
|
||
|
||
//
|
||
// Get the Validation information from the local SAM database
|
||
//
|
||
|
||
Status = MsvSamValidate(
|
||
NlpSamDomainHandle,
|
||
NlpUasCompatibilityRequired,
|
||
MsvApSecureChannel,
|
||
&NlpComputerName, // Logon Server is this machine
|
||
&NlpSamDomainName,
|
||
NlpSamDomainId,
|
||
LogonLevel,
|
||
LogonInformation,
|
||
NetlogonValidationSamInfo4,
|
||
(PVOID *) &NlpUser,
|
||
&Authoritative,
|
||
&BadPasswordCountZeroed,
|
||
AccountsToTry);
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
// So we don't get a LOGON COLLISION from the old msv package
|
||
|
||
Flags |= LOGON_BY_LOCAL;
|
||
|
||
|
||
//
|
||
// If Netlogon was successful,
|
||
// add this user to the logon cache.
|
||
//
|
||
|
||
} else if ( NT_SUCCESS( Status ) ) {
|
||
|
||
//
|
||
// Indicate this session was validated by the Netlogon
|
||
// service.
|
||
//
|
||
|
||
Flags |= LOGON_BY_NETLOGON;
|
||
|
||
//
|
||
// Cache interactive logon information.
|
||
//
|
||
// NOTE: Batch and Service logons are treated
|
||
// the same as Interactive here.
|
||
//
|
||
|
||
if (LogonType == Interactive ||
|
||
LogonType == Service ||
|
||
LogonType == Batch ||
|
||
LogonType == RemoteInteractive) {
|
||
|
||
NTSTATUS ntStatus;
|
||
|
||
LogonInteractive.Identity.ParameterControl = RPC_C_AUTHN_WINNT;
|
||
|
||
ntStatus = NlpAddCacheEntry(
|
||
&LogonInteractive,
|
||
NlpUser,
|
||
NULL,
|
||
0,
|
||
MSV1_0_CACHE_LOGON_REQUEST_INFO4
|
||
);
|
||
}
|
||
|
||
//
|
||
// If Netlogon is simply not available at this time,
|
||
// try to logon through the cache.
|
||
//
|
||
// STATUS_NO_LOGON_SERVERS indicates the netlogon service couldn't
|
||
// contact a DC to handle this request.
|
||
//
|
||
// STATUS_NETLOGON_NOT_STARTED indicates the local netlogon service
|
||
// isn't running.
|
||
//
|
||
//
|
||
// We use the cache for ANY logon type. This not only allows a
|
||
// user to logon interactively, but it allows that same user to
|
||
// connect from another machine while the DC is down.
|
||
//
|
||
|
||
} else if ( Status == STATUS_NO_LOGON_SERVERS ||
|
||
Status == STATUS_NETLOGON_NOT_STARTED ) {
|
||
|
||
NTSTATUS ntStatus;
|
||
CACHE_PASSWORDS cachePasswords;
|
||
ULONG LocalFlags = 0;
|
||
|
||
//
|
||
// reset Status to NetlogonStatus if an error was encountered.
|
||
//
|
||
|
||
if(!NT_SUCCESS( NetlogonStatus ))
|
||
{
|
||
Status = NetlogonStatus;
|
||
}
|
||
|
||
//
|
||
// Try to logon via the cache.
|
||
//
|
||
//
|
||
|
||
ntStatus = NlpGetCacheEntry(LogonInformation, &NlpUser, &cachePasswords, NULL, NULL);
|
||
|
||
if (!NT_SUCCESS(ntStatus)) {
|
||
|
||
//
|
||
// The original status code is more interesting than
|
||
// the fact that the cache didn't work.
|
||
//
|
||
|
||
NlpUser = NULL; // NlpGetCacheEntry dirties this
|
||
goto Cleanup;
|
||
}
|
||
|
||
if( LogonType != Network )
|
||
{
|
||
|
||
//
|
||
// The cache information contains salted hashed passwords,
|
||
// so modify the logon information similarly.
|
||
//
|
||
|
||
ntStatus = NlpComputeSaltedHashedPassword(
|
||
&LogonInteractive.NtOwfPassword,
|
||
&LogonInteractive.NtOwfPassword,
|
||
&NlpUser->EffectiveName
|
||
);
|
||
if (!NT_SUCCESS(ntStatus)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
ntStatus = NlpComputeSaltedHashedPassword(
|
||
&LogonInteractive.LmOwfPassword,
|
||
&LogonInteractive.LmOwfPassword,
|
||
&NlpUser->EffectiveName
|
||
);
|
||
if (!NT_SUCCESS(ntStatus)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
} else {
|
||
|
||
PMSV1_0_PRIMARY_CREDENTIAL TempPrimaryCredential;
|
||
ULONG PrimaryCredentialSize;
|
||
|
||
//
|
||
// because the cache no longer stores OWFs, the cached salted OWF
|
||
// is not useful for validation for network logon.
|
||
// The only place we can get a OWF to match is the active logon
|
||
// cache
|
||
//
|
||
|
||
ntStatus = NlpGetPrimaryCredentialByUserDomain(
|
||
&LogonInformation->LogonDomainName,
|
||
&LogonInformation->UserName,
|
||
&TempPrimaryCredential,
|
||
&PrimaryCredentialSize
|
||
);
|
||
|
||
if(!NT_SUCCESS(ntStatus)) {
|
||
Status = STATUS_WRONG_PASSWORD;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// copy out the OWFs, then free the allocated buffer.
|
||
//
|
||
|
||
if( TempPrimaryCredential->NtPasswordPresent ) {
|
||
CopyMemory(&cachePasswords.SecretPasswords.NtOwfPassword, &TempPrimaryCredential->NtOwfPassword, sizeof(NT_OWF_PASSWORD));
|
||
cachePasswords.SecretPasswords.NtPasswordPresent = TRUE;
|
||
} else {
|
||
cachePasswords.SecretPasswords.NtPasswordPresent = FALSE;
|
||
}
|
||
|
||
if( TempPrimaryCredential->LmPasswordPresent ) {
|
||
CopyMemory(&cachePasswords.SecretPasswords.LmOwfPassword, &TempPrimaryCredential->LmOwfPassword, sizeof(LM_OWF_PASSWORD));
|
||
cachePasswords.SecretPasswords.LmPasswordPresent = TRUE;
|
||
} else {
|
||
cachePasswords.SecretPasswords.LmPasswordPresent = FALSE;
|
||
}
|
||
|
||
ZeroMemory( TempPrimaryCredential, PrimaryCredentialSize );
|
||
(*Lsa.FreeLsaHeap)( TempPrimaryCredential );
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Now we have the information from the cache, validate the
|
||
// user's password
|
||
//
|
||
|
||
if (!MsvpPasswordValidate(
|
||
NlpUasCompatibilityRequired,
|
||
LogonLevel,
|
||
(PVOID)LogonInformation,
|
||
&cachePasswords.SecretPasswords,
|
||
&LocalFlags,
|
||
&NlpUser->UserSessionKey,
|
||
(PLM_SESSION_KEY)
|
||
&NlpUser->ExpansionRoom[SAMINFO_LM_SESSION_KEY]
|
||
)) {
|
||
Status = STATUS_WRONG_PASSWORD;
|
||
goto Cleanup;
|
||
}
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
//
|
||
// The cache always returns a NETLOGONV_VALIDATION_SAM_INFO2
|
||
// structure so set the LOGON_EXTRA_SIDS flag, whether or not
|
||
// there are extra sids. Also, if there was a package ID indicated
|
||
// put it in the PrimaryCredentials and remove it from the
|
||
// NlpUser structure so it doesn't confuse anyone else.
|
||
//
|
||
|
||
PrimaryCredentials->Flags |= NlpUser->UserFlags & PRIMARY_CRED_PACKAGE_MASK;
|
||
NlpUser->UserFlags &= ~PRIMARY_CRED_PACKAGE_MASK;
|
||
NlpUser->UserFlags |= LOGON_CACHED_ACCOUNT | LOGON_EXTRA_SIDS | LocalFlags;
|
||
Flags |= LOGON_BY_CACHE;
|
||
|
||
//
|
||
// If the account is permanently dead on the domain controller,
|
||
// Flush this entry from the cache.
|
||
//
|
||
// Notice that STATUS_INVALID_LOGON_HOURS is not in the list below.
|
||
// This ensures a user will be able to remove his portable machine
|
||
// from the net and use it after hours.
|
||
//
|
||
// Notice the STATUS_WRONG_PASSWORD is not in the list below.
|
||
// We're as likely to flush the cache for typo'd passwords as anything
|
||
// else. What we'd really like to do is flush the cache if the
|
||
// password on the DC is different than the one in cache; but that's
|
||
// impossible to detect.
|
||
//
|
||
// ONLY DO THIS FOR INTERACTIVE LOGONS
|
||
// (not Service or Batch).
|
||
//
|
||
|
||
} else if ( ((LogonType == Interactive) || (LogonType == RemoteInteractive)) &&
|
||
(Status == STATUS_NO_SUCH_USER ||
|
||
Status == STATUS_INVALID_WORKSTATION ||
|
||
Status == STATUS_PASSWORD_EXPIRED ||
|
||
Status == STATUS_ACCOUNT_DISABLED) ) {
|
||
|
||
//
|
||
// Delete the cache entry
|
||
|
||
NTSTATUS ntStatus;
|
||
|
||
ntStatus = NlpDeleteCacheEntry(&LogonInteractive);
|
||
if(!NT_SUCCESS(ntStatus))
|
||
{
|
||
SspPrint((SSP_CRITICAL, "LsaApLogonUser: NlpDeleteCacheEntry returns %x\n", ntStatus));
|
||
}
|
||
|
||
goto Cleanup;
|
||
|
||
} else {
|
||
|
||
goto Cleanup;
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// if this is PersonalSKU, only allow DOMAIN_USER_RID_ADMIN to logon
|
||
// if doing safe-mode boot (NtLmGlobalSafeBoot == TRUE)
|
||
//
|
||
|
||
|
||
if( NlpUser &&
|
||
NlpUser->UserId == DOMAIN_USER_RID_ADMIN &&
|
||
!NtLmGlobalSafeBoot &&
|
||
NtLmGlobalPersonalSKU &&
|
||
NlpSamDomainId &&
|
||
RtlEqualSid( NlpUser->LogonDomainId, NlpSamDomainId )
|
||
)
|
||
{
|
||
Status = STATUS_ACCOUNT_RESTRICTION;
|
||
SspPrint((SSP_CRITICAL,
|
||
"LsaApLogonUser: For Personal SKU Administrator cannot log on except during safe mode boot\n"));
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// For everything except network logons,
|
||
// save the credentials in the LSA,
|
||
// create active logon table entry,
|
||
// return the interactive profile buffer.
|
||
//
|
||
|
||
if ( LogonType == Interactive ||
|
||
LogonType == Service ||
|
||
LogonType == Batch ||
|
||
LogonType == NetworkCleartext ||
|
||
LogonType == RemoteInteractive
|
||
)
|
||
{
|
||
PACTIVE_LOGON *ActiveLogon;
|
||
ULONG LogonEntrySize;
|
||
PUCHAR Where;
|
||
USHORT LogonCount;
|
||
ULONG UserSidSize;
|
||
UNICODE_STRING SamAccountName;
|
||
UNICODE_STRING NetbiosDomainName;
|
||
UNICODE_STRING DnsDomainName;
|
||
UNICODE_STRING Upn;
|
||
UNICODE_STRING LogonServer;
|
||
|
||
//
|
||
// Grab the various forms of the account name
|
||
//
|
||
|
||
NlpGetAccountNames( LogonInformation,
|
||
NlpUser,
|
||
&SamAccountName,
|
||
&NetbiosDomainName,
|
||
&DnsDomainName,
|
||
&Upn );
|
||
|
||
|
||
//
|
||
// Build the primary credential
|
||
//
|
||
//
|
||
|
||
#ifdef MAP_DOMAIN_NAMES_AT_LOGON
|
||
{
|
||
UNICODE_STRING MappedDomain;
|
||
RtlInitUnicodeString(
|
||
&MappedDomain,
|
||
NULL
|
||
);
|
||
|
||
Status = NlpMapLogonDomain(
|
||
&MappedDomain,
|
||
&NetbiosDomainName );
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
Status = NlpMakePrimaryCredential( &MappedDomain,
|
||
&SamAccountName,
|
||
&PrimaryCredentials->Password,
|
||
&Credential,
|
||
&CredentialSize );
|
||
|
||
if (MappedDomain.Buffer != NULL) {
|
||
NtLmFree(MappedDomain.Buffer);
|
||
}
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
}
|
||
#else
|
||
|
||
Status = NlpMakePrimaryCredential( &NetbiosDomainName,
|
||
&SamAccountName,
|
||
&PrimaryCredentials->Password,
|
||
&Credential,
|
||
&CredentialSize );
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
goto Cleanup;
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Add additional names to the logon session name map. Ignore failure
|
||
// as that just means GetUserNameEx calls for these name formats later
|
||
// on will be satisfied by hitting the wire.
|
||
//
|
||
|
||
if (NlpUser->FullName.Length != 0)
|
||
{
|
||
I_LsaIAddNameToLogonSession(LogonId, NameDisplay, &NlpUser->FullName);
|
||
}
|
||
|
||
if (Upn.Length != 0)
|
||
{
|
||
I_LsaIAddNameToLogonSession(LogonId, NameUserPrincipal, &Upn);
|
||
}
|
||
|
||
if (DnsDomainName.Length != 0)
|
||
{
|
||
I_LsaIAddNameToLogonSession(LogonId, NameDnsDomain, &DnsDomainName);
|
||
}
|
||
|
||
//
|
||
// Fill the username and domain name into the primary credential
|
||
// that's passed to the other security packages.
|
||
//
|
||
// The names filled in are the effective names after authentication.
|
||
// For instance, it isn't the UPN passed to this function.
|
||
//
|
||
|
||
PrimaryCredentials->DownlevelName.Length = PrimaryCredentials->DownlevelName.MaximumLength =
|
||
SamAccountName.Length;
|
||
PrimaryCredentials->DownlevelName.Buffer = (*Lsa.AllocateLsaHeap)(SamAccountName.Length);
|
||
|
||
if (PrimaryCredentials->DownlevelName.Buffer == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Cleanup;
|
||
}
|
||
|
||
RtlCopyMemory(
|
||
PrimaryCredentials->DownlevelName.Buffer,
|
||
SamAccountName.Buffer,
|
||
SamAccountName.Length
|
||
);
|
||
|
||
PrimaryCredentials->DomainName.Length = PrimaryCredentials->DomainName.MaximumLength =
|
||
NetbiosDomainName.Length;
|
||
|
||
PrimaryCredentials->DomainName.Buffer = (*Lsa.AllocateLsaHeap)(NetbiosDomainName.Length);
|
||
|
||
if (PrimaryCredentials->DomainName.Buffer == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Cleanup;
|
||
}
|
||
|
||
RtlCopyMemory(
|
||
PrimaryCredentials->DomainName.Buffer,
|
||
NetbiosDomainName.Buffer,
|
||
NetbiosDomainName.Length
|
||
);
|
||
|
||
RtlCopyMemory(&LogonServer, &NlpUser->LogonServer, sizeof(UNICODE_STRING));
|
||
|
||
if ( LogonServer.Length != 0 ) {
|
||
PrimaryCredentials->LogonServer.Length = PrimaryCredentials->LogonServer.MaximumLength =
|
||
LogonServer.Length;
|
||
PrimaryCredentials->LogonServer.Buffer = (*Lsa.AllocateLsaHeap)(LogonServer.Length);
|
||
|
||
if (PrimaryCredentials->LogonServer.Buffer == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Cleanup;
|
||
}
|
||
|
||
RtlCopyMemory(
|
||
PrimaryCredentials->LogonServer.Buffer,
|
||
LogonServer.Buffer,
|
||
LogonServer.Length
|
||
);
|
||
}
|
||
|
||
|
||
//
|
||
// Save the credential in the LSA.
|
||
//
|
||
|
||
Status = NlpAddPrimaryCredential( LogonId,
|
||
Credential,
|
||
CredentialSize );
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
SspPrint((SSP_CRITICAL, "LsaApLogonUser: error from AddCredential %lX\n",
|
||
Status));
|
||
goto Cleanup;
|
||
}
|
||
LogonCredentialAdded = TRUE;
|
||
|
||
//
|
||
// Build a Sid for this user.
|
||
//
|
||
|
||
UserSid = NlpMakeDomainRelativeSid( NlpUser->LogonDomainId,
|
||
NlpUser->UserId );
|
||
|
||
if ( UserSid == NULL ) {
|
||
Status = STATUS_NO_MEMORY;
|
||
SspPrint((SSP_CRITICAL, "LsaApLogonUser: No memory\n"));
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
|
||
PrimaryCredentials->UserSid = UserSid;
|
||
UserSid = NULL;
|
||
|
||
|
||
|
||
UserSidSize = RtlLengthSid( PrimaryCredentials->UserSid );
|
||
|
||
|
||
//
|
||
// Allocate an entry for the active logon table.
|
||
//
|
||
|
||
LogonEntrySize = ROUND_UP_COUNT(sizeof(ACTIVE_LOGON), ALIGN_DWORD) +
|
||
ROUND_UP_COUNT(UserSidSize, sizeof(WCHAR)) +
|
||
SamAccountName.Length + sizeof(WCHAR) +
|
||
NetbiosDomainName.Length + sizeof(WCHAR) +
|
||
NlpUser->LogonServer.Length + sizeof(WCHAR);
|
||
|
||
LogonEntry = I_NtLmAllocate( LogonEntrySize );
|
||
|
||
if ( LogonEntry == NULL ) {
|
||
Status = STATUS_NO_MEMORY;
|
||
SspPrint((SSP_CRITICAL, "LsaApLogonUser: No memory %ld\n",
|
||
sizeof(ACTIVE_LOGON)));
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Fill in the logon table entry.
|
||
//
|
||
|
||
Where = (PUCHAR)(LogonEntry + 1);
|
||
|
||
OLD_TO_NEW_LARGE_INTEGER(
|
||
LogonInformation->LogonId,
|
||
LogonEntry->LogonId );
|
||
|
||
LogonEntry->Flags = Flags;
|
||
LogonEntry->LogonType = LogonType;
|
||
|
||
//
|
||
// Copy DWORD aligned fields first.
|
||
//
|
||
|
||
Where = ROUND_UP_POINTER( Where, ALIGN_DWORD );
|
||
Status = RtlCopySid(UserSidSize, (PSID)Where, PrimaryCredentials->UserSid);
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
LogonEntry->UserSid = (PSID) Where;
|
||
Where += UserSidSize;
|
||
|
||
//
|
||
// Copy WCHAR aligned fields
|
||
//
|
||
|
||
Where = ROUND_UP_POINTER( Where, ALIGN_WCHAR );
|
||
NlpPutString( &LogonEntry->UserName,
|
||
&SamAccountName,
|
||
&Where );
|
||
|
||
NlpPutString( &LogonEntry->LogonDomainName,
|
||
&NetbiosDomainName,
|
||
&Where );
|
||
|
||
NlpPutString( &LogonEntry->LogonServer,
|
||
&NlpUser->LogonServer,
|
||
&Where );
|
||
|
||
|
||
//
|
||
// Get the next enumeration handle for this session.
|
||
//
|
||
|
||
LogonEntry->EnumHandle = (ULONG)InterlockedIncrement((PLONG)&NlpEnumerationHandle);
|
||
|
||
NlpLockActiveLogonsWrite();
|
||
|
||
//
|
||
// Insert this entry into the active logon table.
|
||
//
|
||
|
||
if (NlpFindActiveLogon( LogonId, &ActiveLogon )){
|
||
|
||
//
|
||
// This Logon ID is already in use.
|
||
//
|
||
|
||
NlpUnlockActiveLogons();
|
||
|
||
Status = STATUS_LOGON_SESSION_COLLISION;
|
||
SspPrint((SSP_CRITICAL,
|
||
"LsaApLogonUser: Collision from NlpFindActiveLogon\n"));
|
||
goto Cleanup;
|
||
}
|
||
|
||
LogonEntry->Next = *ActiveLogon;
|
||
*ActiveLogon = LogonEntry;
|
||
NlpUnlockActiveLogons();
|
||
|
||
LogonEntryLinked = TRUE;
|
||
|
||
|
||
//
|
||
// Ensure the LogonCount is at least as big as it is for this
|
||
// machine.
|
||
//
|
||
|
||
LogonCount = (USHORT) NlpCountActiveLogon( &NetbiosDomainName,
|
||
&SamAccountName );
|
||
if ( NlpUser->LogonCount < LogonCount ) {
|
||
NlpUser->LogonCount = LogonCount;
|
||
}
|
||
|
||
//
|
||
// Alocate the profile buffer to return to the client
|
||
//
|
||
|
||
Status = NlpAllocateInteractiveProfile(
|
||
ClientRequest,
|
||
(PMSV1_0_INTERACTIVE_PROFILE *) ProfileBuffer,
|
||
ProfileBufferSize,
|
||
NlpUser );
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
SspPrint((SSP_CRITICAL,
|
||
"LsaApLogonUser: Allocate Profile Failed: %lx\n", Status));
|
||
goto Cleanup;
|
||
}
|
||
|
||
} else if ( LogonType == Network ) {
|
||
|
||
|
||
//
|
||
// if doing client challenge, and it's a vanilla NTLM response,
|
||
// and it's not a null session, compute unique per-session session keys
|
||
// N.B: not needed if it's NTLM++, not possible if LM
|
||
//
|
||
|
||
if ((NetworkAuthentication->ParameterControl & MSV1_0_USE_CLIENT_CHALLENGE) &&
|
||
(NetworkAuthentication->CaseSensitiveChallengeResponse.Length == NT_RESPONSE_LENGTH ) && // vanilla NTLM response
|
||
(NetworkAuthentication->CaseInsensitiveChallengeResponse.Length >= MSV1_0_CHALLENGE_LENGTH ) &&
|
||
(NlpUser != NULL)) // NULL session iff NlpUser == NULL
|
||
{
|
||
MsvpCalculateNtlm2SessionKeys(
|
||
&NlpUser->UserSessionKey,
|
||
NetworkAuthentication->ChallengeToClient,
|
||
NetworkAuthentication->CaseInsensitiveChallengeResponse.Buffer,
|
||
(PUSER_SESSION_KEY)&NlpUser->UserSessionKey,
|
||
(PLM_SESSION_KEY)&NlpUser->ExpansionRoom[SAMINFO_LM_SESSION_KEY]
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Alocate the profile buffer to return to the client
|
||
//
|
||
|
||
Status = NlpAllocateNetworkProfile(
|
||
ClientRequest,
|
||
(PMSV1_0_LM20_LOGON_PROFILE *) ProfileBuffer,
|
||
ProfileBufferSize,
|
||
NlpUser,
|
||
LogonNetwork.Identity.ParameterControl );
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
SspPrint((SSP_CRITICAL,
|
||
"LsaApLogonUser: Allocate Profile Failed: %lx. This could also be a status for a subauth logon.\n", Status));
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
if( NlpUser != NULL )
|
||
{
|
||
UNICODE_STRING SamAccountName;
|
||
UNICODE_STRING NetbiosDomainName;
|
||
UNICODE_STRING DnsDomainName;
|
||
UNICODE_STRING Upn;
|
||
UNICODE_STRING LogonServer;
|
||
|
||
//
|
||
// Grab the various forms of the account name
|
||
//
|
||
|
||
NlpGetAccountNames( LogonInformation,
|
||
NlpUser,
|
||
&SamAccountName,
|
||
&NetbiosDomainName,
|
||
&DnsDomainName,
|
||
&Upn );
|
||
|
||
|
||
//
|
||
// Add additional names to the logon session name map. Ignore failure
|
||
// as that just means GetUserNameEx calls for these name formats later
|
||
// on will be satisfied by hitting the wire.
|
||
//
|
||
|
||
if (NlpUser->FullName.Length != 0)
|
||
{
|
||
I_LsaIAddNameToLogonSession(LogonId, NameDisplay, &NlpUser->FullName);
|
||
}
|
||
|
||
if (Upn.Length != 0)
|
||
{
|
||
I_LsaIAddNameToLogonSession(LogonId, NameUserPrincipal, &Upn);
|
||
}
|
||
|
||
if (DnsDomainName.Length != 0)
|
||
{
|
||
I_LsaIAddNameToLogonSession(LogonId, NameDnsDomain, &DnsDomainName);
|
||
}
|
||
|
||
//
|
||
// Fill the username and domain name into the primary credential
|
||
// that's passed to the other security packages.
|
||
//
|
||
// The names filled in are the effective names after authentication.
|
||
// For instance, it isn't the UPN passed to this function.
|
||
//
|
||
|
||
if( SamAccountName.Length == 0 )
|
||
{
|
||
SamAccountName = TmpName;
|
||
}
|
||
|
||
PrimaryCredentials->DownlevelName.Length = PrimaryCredentials->DownlevelName.MaximumLength =
|
||
SamAccountName.Length;
|
||
PrimaryCredentials->DownlevelName.Buffer = (*Lsa.AllocateLsaHeap)(SamAccountName.Length);
|
||
|
||
if (PrimaryCredentials->DownlevelName.Buffer == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Cleanup;
|
||
}
|
||
|
||
RtlCopyMemory(
|
||
PrimaryCredentials->DownlevelName.Buffer,
|
||
SamAccountName.Buffer,
|
||
SamAccountName.Length
|
||
);
|
||
|
||
PrimaryCredentials->DomainName.Length = PrimaryCredentials->DomainName.MaximumLength =
|
||
NetbiosDomainName.Length;
|
||
|
||
PrimaryCredentials->DomainName.Buffer = (*Lsa.AllocateLsaHeap)(NetbiosDomainName.Length);
|
||
|
||
if (PrimaryCredentials->DomainName.Buffer == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Cleanup;
|
||
}
|
||
|
||
RtlCopyMemory(
|
||
PrimaryCredentials->DomainName.Buffer,
|
||
NetbiosDomainName.Buffer,
|
||
NetbiosDomainName.Length
|
||
);
|
||
|
||
RtlCopyMemory(&LogonServer, &NlpUser->LogonServer, sizeof(UNICODE_STRING));
|
||
|
||
if ( LogonServer.Length != 0 ) {
|
||
PrimaryCredentials->LogonServer.Length = PrimaryCredentials->LogonServer.MaximumLength =
|
||
LogonServer.Length;
|
||
PrimaryCredentials->LogonServer.Buffer = (*Lsa.AllocateLsaHeap)(LogonServer.Length);
|
||
|
||
if (PrimaryCredentials->LogonServer.Buffer == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Cleanup;
|
||
}
|
||
|
||
RtlCopyMemory(
|
||
PrimaryCredentials->LogonServer.Buffer,
|
||
LogonServer.Buffer,
|
||
LogonServer.Length
|
||
);
|
||
}
|
||
|
||
//
|
||
// Build a Sid for this user.
|
||
//
|
||
|
||
UserSid = NlpMakeDomainRelativeSid( NlpUser->LogonDomainId,
|
||
NlpUser->UserId );
|
||
|
||
if ( UserSid == NULL ) {
|
||
Status = STATUS_NO_MEMORY;
|
||
SspPrint((SSP_CRITICAL, "LsaApLogonUser: No memory\n"));
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
PrimaryCredentials->UserSid = UserSid;
|
||
UserSid = NULL;
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Build the token information to return to the LSA
|
||
//
|
||
|
||
switch (LsaTokenInformationType) {
|
||
case LsaTokenInformationV2:
|
||
|
||
Status = NlpMakeTokenInformationV2(
|
||
NlpUser,
|
||
(PLSA_TOKEN_INFORMATION_V2 *)TokenInformation );
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
SspPrint((SSP_CRITICAL,
|
||
"LsaApLogonUser: MakeTokenInformationV2 Failed: %lx\n", Status));
|
||
goto Cleanup;
|
||
}
|
||
break;
|
||
|
||
case LsaTokenInformationNull:
|
||
{
|
||
PLSA_TOKEN_INFORMATION_NULL VNull;
|
||
|
||
VNull = (*Lsa.AllocateLsaHeap)(sizeof(LSA_TOKEN_INFORMATION_NULL) );
|
||
if ( VNull == NULL ) {
|
||
Status = STATUS_NO_MEMORY;
|
||
goto Cleanup;
|
||
}
|
||
|
||
VNull->Groups = NULL;
|
||
|
||
VNull->ExpirationTime.HighPart = 0x7FFFFFFF;
|
||
VNull->ExpirationTime.LowPart = 0xFFFFFFFF;
|
||
|
||
*TokenInformation = VNull;
|
||
}
|
||
|
||
}
|
||
|
||
*TokenInformationType = LsaTokenInformationType;
|
||
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
Cleanup:
|
||
|
||
NtLmFreePrivateHeap( CredmanUserName.Buffer );
|
||
NtLmFreePrivateHeap( CredmanDomainName.Buffer );
|
||
NtLmFreePrivateHeap( CredmanPassword.Buffer );
|
||
|
||
//
|
||
// Restore the saved password
|
||
//
|
||
if ( ServiceSecretLogon ) {
|
||
|
||
RtlCopyMemory( &Authentication->Password,
|
||
&SavedPassword,
|
||
sizeof( UNICODE_STRING ) );
|
||
|
||
//
|
||
// Free the secret value we read...
|
||
//
|
||
LsaIFree_LSAPR_CR_CIPHER_VALUE( SecretCurrent );
|
||
}
|
||
|
||
//
|
||
// If the logon wasn't successful,
|
||
// cleanup resources we would have returned to the caller.
|
||
//
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
|
||
if ( LogonSessionCreated ) {
|
||
(VOID)(*Lsa.DeleteLogonSession)( LogonId );
|
||
}
|
||
|
||
if ( LogonEntry != NULL ) {
|
||
if ( LogonEntryLinked ) {
|
||
LsaApLogonTerminated( LogonId );
|
||
} else {
|
||
if ( LogonCredentialAdded ) {
|
||
(VOID) NlpDeletePrimaryCredential(
|
||
LogonId );
|
||
}
|
||
I_NtLmFree( LogonEntry );
|
||
}
|
||
}
|
||
|
||
// Special case for MsV1_0SubAuthLogon (includes arap).
|
||
// (Don't free ProfileBuffer during error conditions which may not be fatal)
|
||
|
||
if (!fSubAuthEx)
|
||
{
|
||
if ( *ProfileBuffer != NULL ) {
|
||
if (ClientRequest != (PLSA_CLIENT_REQUEST) (-1))
|
||
(VOID)(*Lsa.FreeClientBuffer)( ClientRequest, *ProfileBuffer );
|
||
else
|
||
(VOID)(*Lsa.FreeLsaHeap)( *ProfileBuffer );
|
||
|
||
*ProfileBuffer = NULL;
|
||
}
|
||
}
|
||
|
||
if (PrimaryCredentials->DownlevelName.Buffer != NULL) {
|
||
(*Lsa.FreeLsaHeap)(PrimaryCredentials->DownlevelName.Buffer);
|
||
}
|
||
|
||
if (PrimaryCredentials->DomainName.Buffer != NULL) {
|
||
(*Lsa.FreeLsaHeap)(PrimaryCredentials->DomainName.Buffer);
|
||
}
|
||
|
||
if (PrimaryCredentials->Password.Buffer != NULL) {
|
||
|
||
RtlZeroMemory(
|
||
PrimaryCredentials->Password.Buffer,
|
||
PrimaryCredentials->Password.Length
|
||
);
|
||
|
||
(*Lsa.FreeLsaHeap)(PrimaryCredentials->Password.Buffer);
|
||
}
|
||
|
||
if (PrimaryCredentials->LogonServer.Buffer != NULL) {
|
||
(*Lsa.FreeLsaHeap)(PrimaryCredentials->LogonServer.Buffer);
|
||
}
|
||
|
||
RtlZeroMemory(
|
||
PrimaryCredentials,
|
||
sizeof(SECPKG_PRIMARY_CRED)
|
||
);
|
||
}
|
||
|
||
//
|
||
// Copy out Authenticating authority and user name.
|
||
//
|
||
|
||
if ( NT_SUCCESS(Status) && LsaTokenInformationType != LsaTokenInformationNull ) {
|
||
|
||
//
|
||
// Use the information from the NlpUser structure, since it gives
|
||
// us accurate information about what account we're logging on to,
|
||
// rather than who we were.
|
||
//
|
||
|
||
if( LogonType != Network )
|
||
{
|
||
TmpName = NlpUser->EffectiveName;
|
||
} else {
|
||
|
||
//
|
||
// older servers may not return the effectivename for non-guest network logon.
|
||
//
|
||
|
||
if( NlpUser->EffectiveName.Length != 0 )
|
||
{
|
||
TmpName = NlpUser->EffectiveName;
|
||
}
|
||
}
|
||
|
||
TmpAuthority = NlpUser->LogonDomainName;
|
||
}
|
||
|
||
*AccountName = (*Lsa.AllocateLsaHeap)( sizeof( UNICODE_STRING ) );
|
||
|
||
if ( *AccountName != NULL ) {
|
||
|
||
(*AccountName)->Buffer = (*Lsa.AllocateLsaHeap)(TmpName.Length + sizeof( UNICODE_NULL) );
|
||
|
||
if ( (*AccountName)->Buffer != NULL ) {
|
||
|
||
(*AccountName)->MaximumLength = TmpName.Length + sizeof( UNICODE_NULL );
|
||
RtlCopyUnicodeString( *AccountName, &TmpName );
|
||
|
||
} else {
|
||
|
||
RtlInitUnicodeString( *AccountName, NULL );
|
||
}
|
||
}
|
||
|
||
*AuthenticatingAuthority = (*Lsa.AllocateLsaHeap)( sizeof( UNICODE_STRING ) );
|
||
|
||
if ( *AuthenticatingAuthority != NULL ) {
|
||
|
||
(*AuthenticatingAuthority)->Buffer = (*Lsa.AllocateLsaHeap)( TmpAuthority.Length + sizeof( UNICODE_NULL ) );
|
||
|
||
if ( (*AuthenticatingAuthority)->Buffer != NULL ) {
|
||
|
||
(*AuthenticatingAuthority)->MaximumLength = (USHORT)(TmpAuthority.Length + sizeof( UNICODE_NULL ));
|
||
RtlCopyUnicodeString( *AuthenticatingAuthority, &TmpAuthority );
|
||
|
||
} else {
|
||
|
||
RtlInitUnicodeString( *AuthenticatingAuthority, NULL );
|
||
}
|
||
}
|
||
|
||
*MachineName = NULL;
|
||
|
||
if (WorkStationName != NULL) {
|
||
|
||
*MachineName = (*Lsa.AllocateLsaHeap)( sizeof( UNICODE_STRING ) );
|
||
|
||
if ( *MachineName != NULL ) {
|
||
|
||
(*MachineName)->Buffer = (*Lsa.AllocateLsaHeap)( WorkStationName->Length + sizeof( UNICODE_NULL ) );
|
||
|
||
if ( (*MachineName)->Buffer != NULL ) {
|
||
|
||
(*MachineName)->MaximumLength = (USHORT)(WorkStationName->Length + sizeof( UNICODE_NULL ));
|
||
RtlCopyUnicodeString( *MachineName, WorkStationName );
|
||
|
||
} else {
|
||
|
||
RtlInitUnicodeString( *MachineName, NULL );
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Map status codes to prevent specific information from being
|
||
// released about this user.
|
||
//
|
||
switch (Status) {
|
||
case STATUS_WRONG_PASSWORD:
|
||
case STATUS_NO_SUCH_USER:
|
||
case STATUS_DOMAIN_TRUST_INCONSISTENT:
|
||
|
||
//
|
||
// sleep 3 seconds to "discourage" dictionary attacks.
|
||
// Don't worry about interactive logon dictionary attacks.
|
||
// They will be slow anyway.
|
||
//
|
||
// per bug 171041, SField, RichardW, CliffV all decided this
|
||
// delay has almost zero value for Win2000. Offline attacks at
|
||
// sniffed wire traffic are more efficient and viable. Further,
|
||
// opimizations in logon code path make failed interactive logons
|
||
// very fast.
|
||
//
|
||
// if (LogonType != Interactive) {
|
||
// Sleep( 3000 );
|
||
// }
|
||
|
||
//
|
||
// This is for auditing. Make sure to clear it out before
|
||
// passing it out of LSA to the caller.
|
||
//
|
||
|
||
*SubStatus = Status;
|
||
Status = STATUS_LOGON_FAILURE;
|
||
break;
|
||
|
||
case STATUS_INVALID_LOGON_HOURS:
|
||
case STATUS_INVALID_WORKSTATION:
|
||
case STATUS_PASSWORD_EXPIRED:
|
||
case STATUS_ACCOUNT_DISABLED:
|
||
*SubStatus = Status;
|
||
Status = STATUS_ACCOUNT_RESTRICTION;
|
||
break;
|
||
|
||
//
|
||
// This shouldn't happen, but guard against it anyway.
|
||
//
|
||
case STATUS_ACCOUNT_RESTRICTION:
|
||
*SubStatus = STATUS_ACCOUNT_RESTRICTION;
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Cleanup locally used resources
|
||
//
|
||
|
||
if ( Credential != NULL ) {
|
||
(*Lsa.FreeLsaHeap)( Credential );
|
||
}
|
||
|
||
if ( NlpUser != NULL ) {
|
||
MIDL_user_free( NlpUser );
|
||
}
|
||
|
||
if ( UserSid != NULL ) {
|
||
(*Lsa.FreeLsaHeap)( UserSid );
|
||
}
|
||
|
||
//
|
||
// Cleanup short was added to avoid returning from the middle of the function.
|
||
//
|
||
CleanupShort:
|
||
|
||
//
|
||
// End tracing a logon user
|
||
//
|
||
if (NtlmGlobalEventTraceFlag){
|
||
|
||
UNICODE_STRING strTempDomain = {0};
|
||
|
||
//
|
||
// Trace header goo
|
||
//
|
||
SET_TRACE_HEADER(TraceInfo,
|
||
NtlmLogonGuid,
|
||
EVENT_TRACE_TYPE_END,
|
||
WNODE_FLAG_TRACED_GUID|WNODE_FLAG_USE_MOF_PTR,
|
||
6);
|
||
|
||
SET_TRACE_DATA(TraceInfo,
|
||
TRACE_LOGON_STATUS,
|
||
Status);
|
||
|
||
SET_TRACE_DATA(TraceInfo,
|
||
TRACE_LOGON_TYPE,
|
||
LogonType);
|
||
|
||
SET_TRACE_USTRING(TraceInfo,
|
||
TRACE_LOGON_USERNAME,
|
||
(**AccountName));
|
||
|
||
if(AuthenticatingAuthority)
|
||
strTempDomain = **AuthenticatingAuthority;
|
||
|
||
SET_TRACE_USTRING(TraceInfo,
|
||
TRACE_LOGON_DOMAINNAME,
|
||
strTempDomain);
|
||
|
||
TraceEvent(NtlmGlobalTraceLoggerHandle,
|
||
(PEVENT_TRACE_HEADER)&TraceInfo);
|
||
}
|
||
|
||
|
||
#if _WIN64
|
||
|
||
//
|
||
// Do this last since some of the cleanup code above may refer to addresses
|
||
// inside the pTempSubmitBuffer/ProtocolSubmitBuffer (e.g., copying out the
|
||
// Workstation name, etc).
|
||
//
|
||
|
||
if (fAllocatedSubmitBuffer)
|
||
{
|
||
NtLmFreePrivateHeap( pTempSubmitBuffer );
|
||
}
|
||
|
||
#endif // _WIN64
|
||
|
||
|
||
//
|
||
// Return status to the caller
|
||
//
|
||
|
||
return Status;
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
LsaApLogonTerminated (
|
||
IN PLUID LogonId
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to notify each authentication package when a logon
|
||
session terminates. A logon session terminates when the last token
|
||
referencing the logon session is deleted.
|
||
|
||
Arguments:
|
||
|
||
LogonId - Is the logon ID that just logged off.
|
||
|
||
Return Status:
|
||
|
||
None.
|
||
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
PACTIVE_LOGON LogonEntry;
|
||
PACTIVE_LOGON *ActiveLogon;
|
||
NETLOGON_INTERACTIVE_INFO LogonInteractive;
|
||
PNETLOGON_INTERACTIVE_INFO LogonInteractivePointer;
|
||
|
||
//
|
||
// Find the entry and de-link it from the active logon table.
|
||
//
|
||
|
||
// this scheme assumes we won't be called concurrently, multiple times,
|
||
// for the same LogonId. (would need to take write lock up front to support that).
|
||
//
|
||
|
||
|
||
NlpLockActiveLogonsWrite();
|
||
|
||
if ( !NlpFindActiveLogon( LogonId, &ActiveLogon ) ) {
|
||
NlpUnlockActiveLogons();
|
||
return;
|
||
}
|
||
|
||
LogonEntry = *ActiveLogon;
|
||
*ActiveLogon = LogonEntry->Next;
|
||
NlpUnlockActiveLogons();
|
||
|
||
|
||
//
|
||
// Delete the credential.
|
||
//
|
||
// (Currently the LSA deletes all of the credentials before calling
|
||
// the authentication package. This line is added to be compatible
|
||
// with a more reasonable LSA.)
|
||
//
|
||
|
||
(VOID) NlpDeletePrimaryCredential( &LogonEntry->LogonId );
|
||
|
||
//
|
||
// Deallocate the now orphaned entry.
|
||
//
|
||
|
||
I_NtLmFree( LogonEntry );
|
||
|
||
|
||
//
|
||
// NB: We don't delete the logon session or credentials.
|
||
// That will be done by the LSA itself after we return.
|
||
//
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Function: SspAcceptCredentials
|
||
//
|
||
// Synopsis: This routine is called after another package has logged
|
||
// a user on. The other package provides a user name and
|
||
// password and the Kerberos package will create a logon
|
||
// session for this user.
|
||
//
|
||
// Effects: Creates a logon session
|
||
//
|
||
// Arguments: LogonType - Type of logon, such as network or interactive
|
||
// PrimaryCredentials - Primary credentials for the account,
|
||
// containing a domain name, password, SID, etc.
|
||
// SupplementalCredentials - If present, contains credentials
|
||
// from the account itself.
|
||
//
|
||
// Requires:
|
||
//
|
||
// Returns:
|
||
//
|
||
// Notes:
|
||
//
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
SspAcceptCredentials(
|
||
IN SECURITY_LOGON_TYPE LogonType,
|
||
IN PSECPKG_PRIMARY_CRED PrimaryCredentials,
|
||
IN PSECPKG_SUPPLEMENTAL_CRED SupplementalCredentials
|
||
)
|
||
{
|
||
PMSV1_0_PRIMARY_CREDENTIAL Credential = NULL;
|
||
ULONG CredentialSize;
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
LUID SystemLuid = SYSTEM_LUID;
|
||
UNICODE_STRING DomainNameToUse;
|
||
PACTIVE_LOGON *ActiveLogon;
|
||
PACTIVE_LOGON LogonEntry = NULL;
|
||
ULONG LogonEntrySize;
|
||
ULONG UserSidSize;
|
||
PUCHAR Where;
|
||
USHORT LogonCount;
|
||
BOOLEAN LogonEntryLinked = FALSE;
|
||
BOOLEAN LsaCredentialAdded = FALSE;
|
||
PMSV1_0_SUPPLEMENTAL_CREDENTIAL MsvCredentials = NULL;
|
||
|
||
LUID CredentialLuid;
|
||
|
||
CredentialLuid = PrimaryCredentials->LogonId;
|
||
|
||
//
|
||
// If there is no cleartext password, bail out here because we
|
||
// can't build a real credential.
|
||
//
|
||
|
||
if ((PrimaryCredentials->Flags & PRIMARY_CRED_CLEAR_PASSWORD) == 0) {
|
||
if (!ARGUMENT_PRESENT(SupplementalCredentials)) {
|
||
Status = STATUS_SUCCESS;
|
||
goto Cleanup;
|
||
} else {
|
||
//
|
||
// Validate the MSV credentials
|
||
//
|
||
|
||
MsvCredentials = (PMSV1_0_SUPPLEMENTAL_CREDENTIAL) SupplementalCredentials->Credentials;
|
||
if (SupplementalCredentials->CredentialSize < sizeof(MSV1_0_SUPPLEMENTAL_CREDENTIAL))
|
||
{
|
||
//
|
||
// LOGLOG: bad credentials - ignore them
|
||
//
|
||
Status = STATUS_SUCCESS;
|
||
goto Cleanup;
|
||
}
|
||
if (MsvCredentials->Version != MSV1_0_CRED_VERSION)
|
||
{
|
||
Status = STATUS_SUCCESS;
|
||
goto Cleanup;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// stash the credential associated with SYSTEM under another logonID
|
||
// this is done so we can utilize that credential at a later time if
|
||
// requested by the caller.
|
||
//
|
||
|
||
if (RtlEqualLuid(
|
||
&CredentialLuid,
|
||
&SystemLuid
|
||
))
|
||
{
|
||
|
||
CredentialLuid = NtLmGlobalLuidMachineLogon;
|
||
}
|
||
|
||
//
|
||
// If this is an update, just change the password
|
||
//
|
||
|
||
if ((PrimaryCredentials->Flags & PRIMARY_CRED_UPDATE) != 0) {
|
||
if ((PrimaryCredentials->Flags & PRIMARY_CRED_CLEAR_PASSWORD) != 0) {
|
||
|
||
Status = NlpChangePasswordByLogonId(
|
||
&CredentialLuid,
|
||
&PrimaryCredentials->Password
|
||
);
|
||
} else {
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
|
||
if( NtLmLocklessGlobalPreferredDomainString.Buffer != NULL ) {
|
||
DomainNameToUse = NtLmLocklessGlobalPreferredDomainString;
|
||
} else {
|
||
DomainNameToUse = PrimaryCredentials->DomainName;
|
||
}
|
||
|
||
//
|
||
// Build the primary credential
|
||
//
|
||
|
||
if ((PrimaryCredentials->Flags & PRIMARY_CRED_CLEAR_PASSWORD) != 0) {
|
||
|
||
Status = NlpMakePrimaryCredential( &DomainNameToUse,
|
||
&PrimaryCredentials->DownlevelName,
|
||
&PrimaryCredentials->Password,
|
||
&Credential,
|
||
&CredentialSize );
|
||
} else {
|
||
Status = NlpMakePrimaryCredentialFromMsvCredential(
|
||
&DomainNameToUse,
|
||
&PrimaryCredentials->DownlevelName,
|
||
MsvCredentials,
|
||
&Credential,
|
||
&CredentialSize );
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
//
|
||
// Now create an entry in the active logon list
|
||
//
|
||
|
||
|
||
UserSidSize = RtlLengthSid( PrimaryCredentials->UserSid );
|
||
|
||
|
||
//
|
||
// Allocate an entry for the active logon table.
|
||
//
|
||
|
||
LogonEntrySize = ROUND_UP_COUNT(sizeof(ACTIVE_LOGON), ALIGN_DWORD) +
|
||
ROUND_UP_COUNT(UserSidSize, sizeof(WCHAR)) +
|
||
PrimaryCredentials->DownlevelName.Length + sizeof(WCHAR) +
|
||
PrimaryCredentials->DomainName.Length + sizeof(WCHAR) +
|
||
PrimaryCredentials->LogonServer.Length + sizeof(WCHAR);
|
||
|
||
LogonEntry = I_NtLmAllocate( LogonEntrySize );
|
||
|
||
if ( LogonEntry == NULL ) {
|
||
Status = STATUS_NO_MEMORY;
|
||
SspPrint((SSP_CRITICAL,"SpAcceptCredentials: No memory %ld\n",
|
||
sizeof(ACTIVE_LOGON)));
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Fill in the logon table entry.
|
||
//
|
||
|
||
Where = (PUCHAR)(LogonEntry + 1);
|
||
|
||
OLD_TO_NEW_LARGE_INTEGER(
|
||
CredentialLuid,
|
||
LogonEntry->LogonId );
|
||
|
||
//
|
||
// Indicate that this was a logon by another package because we don't want to
|
||
// notify Netlogon of the logoff.
|
||
//
|
||
|
||
LogonEntry->Flags = LOGON_BY_OTHER_PACKAGE;
|
||
LogonEntry->LogonType = LogonType;
|
||
|
||
//
|
||
// Copy DWORD aligned fields first.
|
||
//
|
||
|
||
Where = ROUND_UP_POINTER( Where, ALIGN_DWORD );
|
||
Status = RtlCopySid(UserSidSize, (PSID)Where, PrimaryCredentials->UserSid);
|
||
|
||
if ( !NT_SUCCESS(Status) ) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
LogonEntry->UserSid = (PSID) Where;
|
||
Where += UserSidSize;
|
||
|
||
//
|
||
// Copy WCHAR aligned fields
|
||
//
|
||
|
||
Where = ROUND_UP_POINTER( Where, ALIGN_WCHAR );
|
||
NlpPutString( &LogonEntry->UserName,
|
||
&PrimaryCredentials->DownlevelName,
|
||
&Where );
|
||
|
||
NlpPutString( &LogonEntry->LogonDomainName,
|
||
&PrimaryCredentials->DomainName,
|
||
&Where );
|
||
|
||
NlpPutString( &LogonEntry->LogonServer,
|
||
&PrimaryCredentials->LogonServer,
|
||
&Where );
|
||
|
||
|
||
|
||
//
|
||
// Insert this entry into the active logon table.
|
||
//
|
||
|
||
NlpLockActiveLogonsWrite();
|
||
if (NlpFindActiveLogon( &CredentialLuid, &ActiveLogon )){
|
||
|
||
//
|
||
// This Logon ID is already in use.
|
||
//
|
||
|
||
|
||
//
|
||
// Check to see if this was someone we logged on
|
||
//
|
||
|
||
if (((*ActiveLogon)->Flags & (LOGON_BY_CACHE | LOGON_BY_NETLOGON | LOGON_BY_LOCAL)) != 0) {
|
||
|
||
//
|
||
// Unlock early since we hold a write lock
|
||
//
|
||
NlpUnlockActiveLogons();
|
||
|
||
//
|
||
// We did the logon, so don't bother to add it again.
|
||
//
|
||
|
||
I_NtLmFree( LogonEntry );
|
||
(*Lsa.FreeLsaHeap)( Credential );
|
||
Credential = NULL;
|
||
Status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Unlock early since we hold a write lock
|
||
//
|
||
NlpUnlockActiveLogons();
|
||
|
||
Status = STATUS_LOGON_SESSION_COLLISION;
|
||
SspPrint((SSP_CRITICAL,
|
||
"SpAcceptCredentials: Collision from NlpFindActiveLogon\n"));
|
||
|
||
}
|
||
|
||
goto Cleanup;
|
||
}
|
||
|
||
LogonEntry->EnumHandle = (ULONG)InterlockedIncrement( (PLONG)&NlpEnumerationHandle );
|
||
|
||
LogonEntry->Next = *ActiveLogon;
|
||
*ActiveLogon = LogonEntry;
|
||
NlpUnlockActiveLogons();
|
||
|
||
LogonEntryLinked = TRUE;
|
||
|
||
//
|
||
// Save the credential in the LSA.
|
||
//
|
||
|
||
Status = NlpAddPrimaryCredential(
|
||
&CredentialLuid,
|
||
Credential,
|
||
CredentialSize
|
||
);
|
||
|
||
if ( !NT_SUCCESS( Status ) ) {
|
||
SspPrint((SSP_CRITICAL, "SpAcceptCredentials: error from AddCredential %lX\n",
|
||
Status));
|
||
goto Cleanup;
|
||
}
|
||
|
||
LsaCredentialAdded = TRUE;
|
||
|
||
Cleanup:
|
||
if (!NT_SUCCESS(Status)) {
|
||
if (LogonEntry != NULL) {
|
||
if (LogonEntryLinked) {
|
||
LsaApLogonTerminated( &CredentialLuid );
|
||
} else {
|
||
|
||
if ( LsaCredentialAdded ) {
|
||
(VOID) NlpDeletePrimaryCredential(
|
||
&CredentialLuid );
|
||
}
|
||
I_NtLmFree( LogonEntry );
|
||
}
|
||
} else if (Credential != NULL) {
|
||
|
||
if (LsaCredentialAdded) {
|
||
(VOID) NlpDeletePrimaryCredential(
|
||
&CredentialLuid
|
||
);
|
||
|
||
} else {
|
||
(*Lsa.FreeLsaHeap)( Credential );
|
||
Credential = NULL;
|
||
}
|
||
|
||
}
|
||
}
|
||
if ( Credential != NULL ) {
|
||
(*Lsa.FreeLsaHeap)( Credential );
|
||
}
|
||
|
||
|
||
return(Status);
|
||
}
|
||
|
||
//+-------------------------------------------------------------------------
|
||
//
|
||
// Function: NlpMapLogonDomain
|
||
//
|
||
// Synopsis: This routine is called while MSV1_0 package is logging
|
||
// a user on. The logon domain name is mapped to another
|
||
// domain to be stored in the credential.
|
||
//
|
||
// Effects: Allocates output string
|
||
//
|
||
// Arguments: MappedDomain - Receives mapped domain name
|
||
// LogonDomain - Domain to which user is logging on
|
||
//
|
||
// Requires:
|
||
//
|
||
// Returns:
|
||
//
|
||
// Notes:
|
||
//
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
NlpMapLogonDomain(
|
||
OUT PUNICODE_STRING MappedDomain,
|
||
IN PUNICODE_STRING LogonDomain
|
||
)
|
||
{
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
if( (NtLmLocklessGlobalMappedDomainString.Buffer == NULL) ||
|
||
!RtlEqualDomainName( LogonDomain, &NtLmLocklessGlobalMappedDomainString )
|
||
)
|
||
{
|
||
Status = NtLmDuplicateUnicodeString(
|
||
MappedDomain,
|
||
LogonDomain
|
||
);
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
if ( NtLmLocklessGlobalPreferredDomainString.Buffer == NULL )
|
||
{
|
||
Status = NtLmDuplicateUnicodeString(
|
||
MappedDomain,
|
||
LogonDomain
|
||
);
|
||
} else {
|
||
Status = NtLmDuplicateUnicodeString(
|
||
MappedDomain,
|
||
&NtLmLocklessGlobalPreferredDomainString
|
||
);
|
||
}
|
||
|
||
|
||
Cleanup:
|
||
return(Status);
|
||
}
|
||
|
||
|
||
|
||
// calculate NTLM2 challenge from client and server challenges
|
||
VOID
|
||
MsvpCalculateNtlm2Challenge (
|
||
IN UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH],
|
||
IN UCHAR ChallengeFromClient[MSV1_0_CHALLENGE_LENGTH],
|
||
OUT UCHAR Challenge[MSV1_0_CHALLENGE_LENGTH]
|
||
)
|
||
{
|
||
MD5_CTX Md5Context;
|
||
|
||
MD5Init(
|
||
&Md5Context
|
||
);
|
||
MD5Update(
|
||
&Md5Context,
|
||
ChallengeToClient,
|
||
MSV1_0_CHALLENGE_LENGTH
|
||
);
|
||
MD5Update(
|
||
&Md5Context,
|
||
ChallengeFromClient,
|
||
MSV1_0_CHALLENGE_LENGTH
|
||
);
|
||
MD5Final(
|
||
&Md5Context
|
||
);
|
||
ASSERT(MD5DIGESTLEN >= MSV1_0_CHALLENGE_LENGTH);
|
||
|
||
RtlCopyMemory(
|
||
Challenge,
|
||
Md5Context.digest,
|
||
MSV1_0_CHALLENGE_LENGTH
|
||
);
|
||
}
|
||
|
||
|
||
// calculate NTLM2 session keys from User session key given
|
||
// to us by the system with the user's account
|
||
|
||
VOID
|
||
MsvpCalculateNtlm2SessionKeys (
|
||
IN PUSER_SESSION_KEY NtUserSessionKey,
|
||
IN UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH],
|
||
IN UCHAR ChallengeFromClient[MSV1_0_CHALLENGE_LENGTH],
|
||
OUT PUSER_SESSION_KEY LocalUserSessionKey,
|
||
OUT PLM_SESSION_KEY LocalLmSessionKey
|
||
)
|
||
{
|
||
// SESSKEY = HMAC(NtUserSessionKey, (ChallengeToClient, ChallengeFromClient))
|
||
// Lm session key is first 8 bytes of session key
|
||
HMACMD5_CTX HMACMD5Context;
|
||
|
||
HMACMD5Init(
|
||
&HMACMD5Context,
|
||
(PUCHAR)NtUserSessionKey,
|
||
sizeof(*NtUserSessionKey)
|
||
);
|
||
HMACMD5Update(
|
||
&HMACMD5Context,
|
||
ChallengeToClient,
|
||
MSV1_0_CHALLENGE_LENGTH
|
||
);
|
||
HMACMD5Update(
|
||
&HMACMD5Context,
|
||
ChallengeFromClient,
|
||
MSV1_0_CHALLENGE_LENGTH
|
||
);
|
||
HMACMD5Final(
|
||
&HMACMD5Context,
|
||
(PUCHAR)LocalUserSessionKey
|
||
);
|
||
RtlCopyMemory(
|
||
LocalLmSessionKey,
|
||
LocalUserSessionKey,
|
||
sizeof(*LocalLmSessionKey)
|
||
);
|
||
}
|
||
|
||
|
||
// calculate NTLM3 OWF from credentials
|
||
VOID
|
||
MsvpCalculateNtlm3Owf (
|
||
IN PNT_OWF_PASSWORD pNtOwfPassword,
|
||
IN PUNICODE_STRING pUserName,
|
||
IN PUNICODE_STRING pLogonDomainName,
|
||
OUT UCHAR Ntlm3Owf[MSV1_0_NTLM3_OWF_LENGTH]
|
||
)
|
||
{
|
||
HMACMD5_CTX HMACMD5Context;
|
||
WCHAR UCUserName[UNLEN+1];
|
||
UNICODE_STRING UCUserNameString = {0, UNLEN, UCUserName};
|
||
|
||
RtlUpcaseUnicodeString(
|
||
&UCUserNameString,
|
||
pUserName,
|
||
FALSE
|
||
);
|
||
|
||
|
||
// Calculate NTLM3 OWF -- HMAC(MD4(P), (UserName, LogonDomainName))
|
||
|
||
HMACMD5Init(
|
||
&HMACMD5Context,
|
||
(PUCHAR)pNtOwfPassword,
|
||
sizeof(*pNtOwfPassword)
|
||
);
|
||
|
||
HMACMD5Update(
|
||
&HMACMD5Context,
|
||
(PUCHAR)UCUserNameString.Buffer,
|
||
pUserName->Length
|
||
);
|
||
|
||
HMACMD5Update(
|
||
&HMACMD5Context,
|
||
(PUCHAR)pLogonDomainName->Buffer,
|
||
pLogonDomainName->Length
|
||
);
|
||
|
||
HMACMD5Final(
|
||
&HMACMD5Context,
|
||
Ntlm3Owf
|
||
);
|
||
}
|
||
|
||
|
||
// calculate LM3 response from credentials
|
||
VOID
|
||
MsvpLm3Response (
|
||
IN PNT_OWF_PASSWORD pNtOwfPassword,
|
||
IN PUNICODE_STRING pUserName,
|
||
IN PUNICODE_STRING pLogonDomainName,
|
||
IN UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH],
|
||
IN PMSV1_0_LM3_RESPONSE pLm3Response,
|
||
OUT UCHAR Response[MSV1_0_NTLM3_RESPONSE_LENGTH]
|
||
)
|
||
{
|
||
HMACMD5_CTX HMACMD5Context;
|
||
UCHAR Ntlm3Owf[MSV1_0_NTLM3_OWF_LENGTH];
|
||
|
||
// get NTLM3 OWF
|
||
|
||
MsvpCalculateNtlm3Owf (
|
||
pNtOwfPassword,
|
||
pUserName,
|
||
pLogonDomainName,
|
||
Ntlm3Owf
|
||
);
|
||
|
||
// Calculate NTLM3 Response
|
||
// HMAC(Ntlm3Owf, (NS, V, HV, T, NC, S))
|
||
|
||
HMACMD5Init(
|
||
&HMACMD5Context,
|
||
Ntlm3Owf,
|
||
MSV1_0_NTLM3_OWF_LENGTH
|
||
);
|
||
|
||
HMACMD5Update(
|
||
&HMACMD5Context,
|
||
ChallengeToClient,
|
||
MSV1_0_CHALLENGE_LENGTH
|
||
);
|
||
|
||
HMACMD5Update(
|
||
&HMACMD5Context,
|
||
(PUCHAR)pLm3Response->ChallengeFromClient,
|
||
MSV1_0_CHALLENGE_LENGTH
|
||
);
|
||
|
||
ASSERT(MD5DIGESTLEN == MSV1_0_NTLM3_RESPONSE_LENGTH);
|
||
|
||
HMACMD5Final(
|
||
&HMACMD5Context,
|
||
Response
|
||
);
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
MsvpNtlm3Response (
|
||
IN PNT_OWF_PASSWORD pNtOwfPassword,
|
||
IN PUNICODE_STRING pUserName,
|
||
IN PUNICODE_STRING pLogonDomainName,
|
||
IN ULONG ServerNameLength,
|
||
IN UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH],
|
||
IN PMSV1_0_NTLM3_RESPONSE pNtlm3Response,
|
||
OUT UCHAR Response[MSV1_0_NTLM3_RESPONSE_LENGTH],
|
||
OUT PUSER_SESSION_KEY UserSessionKey,
|
||
OUT PLM_SESSION_KEY LmSessionKey
|
||
)
|
||
{
|
||
HMACMD5_CTX HMACMD5Context;
|
||
UCHAR Ntlm3Owf[MSV1_0_NTLM3_OWF_LENGTH];
|
||
|
||
// get NTLM3 OWF
|
||
|
||
MsvpCalculateNtlm3Owf (
|
||
pNtOwfPassword,
|
||
pUserName,
|
||
pLogonDomainName,
|
||
Ntlm3Owf
|
||
);
|
||
|
||
// Calculate NTLM3 Response
|
||
// HMAC(Ntlm3Owf, (NS, V, HV, T, NC, S))
|
||
|
||
HMACMD5Init(
|
||
&HMACMD5Context,
|
||
Ntlm3Owf,
|
||
MSV1_0_NTLM3_OWF_LENGTH
|
||
);
|
||
|
||
HMACMD5Update(
|
||
&HMACMD5Context,
|
||
ChallengeToClient,
|
||
MSV1_0_CHALLENGE_LENGTH
|
||
);
|
||
|
||
HMACMD5Update(
|
||
&HMACMD5Context,
|
||
&pNtlm3Response->RespType,
|
||
(MSV1_0_NTLM3_INPUT_LENGTH + ServerNameLength)
|
||
);
|
||
|
||
ASSERT(MD5DIGESTLEN == MSV1_0_NTLM3_RESPONSE_LENGTH);
|
||
|
||
HMACMD5Final(
|
||
&HMACMD5Context,
|
||
Response
|
||
);
|
||
|
||
// now compute the session keys
|
||
// HMAC(Kr, R)
|
||
HMACMD5Init(
|
||
&HMACMD5Context,
|
||
Ntlm3Owf,
|
||
MSV1_0_NTLM3_OWF_LENGTH
|
||
);
|
||
|
||
HMACMD5Update(
|
||
&HMACMD5Context,
|
||
Response,
|
||
MSV1_0_NTLM3_RESPONSE_LENGTH
|
||
);
|
||
|
||
ASSERT(MD5DIGESTLEN == MSV1_0_USER_SESSION_KEY_LENGTH);
|
||
HMACMD5Final(
|
||
&HMACMD5Context,
|
||
(PUCHAR)UserSessionKey
|
||
);
|
||
|
||
ASSERT(MSV1_0_LANMAN_SESSION_KEY_LENGTH <= MSV1_0_USER_SESSION_KEY_LENGTH);
|
||
RtlCopyMemory(
|
||
LmSessionKey,
|
||
UserSessionKey,
|
||
MSV1_0_LANMAN_SESSION_KEY_LENGTH);
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
MsvpLm20GetNtlm3ChallengeResponse (
|
||
IN PNT_OWF_PASSWORD pNtOwfPassword,
|
||
IN PUNICODE_STRING pUserName,
|
||
IN PUNICODE_STRING pLogonDomainName,
|
||
IN PUNICODE_STRING pServerName,
|
||
IN UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH],
|
||
OUT PMSV1_0_NTLM3_RESPONSE pNtlm3Response,
|
||
OUT PMSV1_0_LM3_RESPONSE pLm3Response,
|
||
OUT PUSER_SESSION_KEY UserSessionKey,
|
||
OUT PLM_SESSION_KEY LmSessionKey
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine calculates the NT and LM response for the NTLM3
|
||
authentication protocol
|
||
It generates the time stamp, version numbers, and
|
||
client challenge, and the NTLM3 and LM3 responses.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NTSTATUS Status;
|
||
|
||
// fill in version numbers, timestamp, and client's challenge
|
||
|
||
pNtlm3Response->RespType = 1;
|
||
pNtlm3Response->HiRespType = 1;
|
||
pNtlm3Response->Flags = 0;
|
||
pNtlm3Response->MsgWord = 0;
|
||
|
||
Status = NtQuerySystemTime ( (PLARGE_INTEGER)&pNtlm3Response->TimeStamp );
|
||
ASSERT( NT_SUCCESS(Status) );
|
||
|
||
SspGenerateRandomBits(
|
||
pNtlm3Response->ChallengeFromClient,
|
||
MSV1_0_CHALLENGE_LENGTH
|
||
);
|
||
|
||
#ifdef USE_CONSTANT_CHALLENGE
|
||
pNtlm3Response->TimeStamp = 0;
|
||
RtlZeroMemory(
|
||
pNtlm3Response->ChallengeFromClient,
|
||
MSV1_0_CHALLENGE_LENGTH
|
||
);
|
||
#endif
|
||
|
||
RtlCopyMemory(
|
||
pNtlm3Response->Buffer,
|
||
pServerName->Buffer,
|
||
pServerName->Length
|
||
);
|
||
|
||
// Calculate NTLM3 response, filling in response field
|
||
MsvpNtlm3Response (
|
||
pNtOwfPassword,
|
||
pUserName,
|
||
pLogonDomainName,
|
||
pServerName->Length,
|
||
ChallengeToClient,
|
||
pNtlm3Response,
|
||
pNtlm3Response->Response,
|
||
UserSessionKey,
|
||
LmSessionKey
|
||
);
|
||
|
||
// Use same challenge to compute the LM3 response
|
||
RtlCopyMemory(
|
||
pLm3Response->ChallengeFromClient,
|
||
pNtlm3Response->ChallengeFromClient,
|
||
MSV1_0_CHALLENGE_LENGTH
|
||
);
|
||
|
||
// Calculate LM3 response
|
||
MsvpLm3Response (
|
||
pNtOwfPassword,
|
||
pUserName,
|
||
pLogonDomainName,
|
||
ChallengeToClient,
|
||
pLm3Response,
|
||
pLm3Response->Response
|
||
);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
// MsvAvInit -- function to initialize AV pair list
|
||
|
||
PMSV1_0_AV_PAIR
|
||
MsvpAvlInit(
|
||
IN void * pAvList
|
||
)
|
||
{
|
||
PMSV1_0_AV_PAIR pAvPair;
|
||
|
||
pAvPair = (PMSV1_0_AV_PAIR)pAvList;
|
||
pAvPair->AvId = MsvAvEOL;
|
||
pAvPair->AvLen = 0;
|
||
return pAvPair;
|
||
}
|
||
|
||
// MsvpAvGet -- function to find a particular AV pair by ID
|
||
|
||
PMSV1_0_AV_PAIR
|
||
MsvpAvlGet(
|
||
IN PMSV1_0_AV_PAIR pAvList, // first pair of AV pair list
|
||
IN MSV1_0_AVID AvId, // AV pair to find
|
||
IN LONG cAvList // size of AV list
|
||
)
|
||
{
|
||
PMSV1_0_AV_PAIR pAvPair;
|
||
|
||
pAvPair = pAvList;
|
||
|
||
while (1) {
|
||
if (pAvPair->AvId == AvId)
|
||
return pAvPair;
|
||
if (pAvPair->AvId == MsvAvEOL)
|
||
return NULL;
|
||
cAvList -= (pAvPair->AvLen + sizeof(MSV1_0_AV_PAIR));
|
||
if (cAvList <= 0)
|
||
return NULL;
|
||
pAvPair = (PMSV1_0_AV_PAIR)((PUCHAR)pAvPair + pAvPair->AvLen + sizeof(MSV1_0_AV_PAIR));
|
||
}
|
||
}
|
||
|
||
// MsvpAvlLen -- function to find length of a AV list
|
||
|
||
ULONG
|
||
MsvpAvlLen(
|
||
IN PMSV1_0_AV_PAIR pAvList, // first pair of AV pair list
|
||
IN LONG cAvList // max size of AV list
|
||
)
|
||
{
|
||
PMSV1_0_AV_PAIR pCurPair;
|
||
|
||
// find the EOL
|
||
pCurPair = MsvpAvlGet(pAvList, MsvAvEOL, cAvList);
|
||
if( pCurPair == NULL )
|
||
return 0;
|
||
|
||
// compute length (not forgetting the EOL pair)
|
||
return (ULONG)(((PUCHAR)pCurPair - (PUCHAR)pAvList) + sizeof(MSV1_0_AV_PAIR));
|
||
}
|
||
|
||
// MsvpAvlAdd -- function to add an AV pair to a list
|
||
// assumes buffer is long enough!
|
||
// returns NULL on failure.
|
||
|
||
PMSV1_0_AV_PAIR
|
||
MsvpAvlAdd(
|
||
IN PMSV1_0_AV_PAIR pAvList, // first pair of AV pair list
|
||
IN MSV1_0_AVID AvId, // AV pair to add
|
||
IN PUNICODE_STRING pString, // value of pair
|
||
IN LONG cAvList // max size of AV list
|
||
)
|
||
{
|
||
PMSV1_0_AV_PAIR pCurPair;
|
||
|
||
// find the EOL
|
||
pCurPair = MsvpAvlGet(pAvList, MsvAvEOL, cAvList);
|
||
if( pCurPair == NULL )
|
||
return NULL;
|
||
|
||
//
|
||
// append the new AvPair (assume the buffer is long enough!)
|
||
//
|
||
|
||
pCurPair->AvId = (USHORT)AvId;
|
||
pCurPair->AvLen = (USHORT)pString->Length;
|
||
memcpy(pCurPair+1, pString->Buffer, pCurPair->AvLen);
|
||
|
||
// top it off with a new EOL
|
||
pCurPair = (PMSV1_0_AV_PAIR)((PUCHAR)pCurPair + sizeof(MSV1_0_AV_PAIR) + pCurPair->AvLen);
|
||
pCurPair->AvId = MsvAvEOL;
|
||
pCurPair->AvLen = 0;
|
||
|
||
return pCurPair;
|
||
}
|
||
|
||
|
||
// MsvpAvlSize -- fucntion to calculate length needed for an AV list
|
||
ULONG
|
||
MsvpAvlSize(
|
||
IN ULONG iPairs, // number of AV pairs response will include
|
||
IN ULONG iPairsLen // total size of values for the pairs
|
||
)
|
||
{
|
||
return (
|
||
iPairs * sizeof(MSV1_0_AV_PAIR) + // space for the pairs' headers
|
||
iPairsLen + // space for pairs' values
|
||
sizeof(MSV1_0_AV_PAIR) // space for the EOL
|
||
);
|
||
}
|
||
|
||
NTSTATUS
|
||
MsvpAvlToString(
|
||
IN PUNICODE_STRING AvlString,
|
||
IN MSV1_0_AVID AvId,
|
||
IN OUT LPWSTR *szAvlString
|
||
)
|
||
{
|
||
PMSV1_0_AV_PAIR pAV;
|
||
|
||
*szAvlString = NULL;
|
||
|
||
if( AvlString->Buffer == NULL || AvlString->Length == 0 )
|
||
{
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
pAV = MsvpAvlGet(
|
||
(PMSV1_0_AV_PAIR)AvlString->Buffer,
|
||
AvId,
|
||
AvlString->Length
|
||
);
|
||
|
||
if( pAV != NULL )
|
||
{
|
||
LPWSTR szAvInfo = (LPWSTR)( pAV+1 );
|
||
USHORT AvLen = pAV->AvLen;
|
||
|
||
LPWSTR szResult;
|
||
|
||
szResult = NtLmAllocate( AvLen + sizeof(WCHAR) );
|
||
if( szResult == NULL )
|
||
{
|
||
SspPrint(( SSP_CRITICAL, "MsvpAvlToString: Error allocating memory.\n"));
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlCopyMemory( szResult, szAvInfo, AvLen );
|
||
szResult[ AvLen/sizeof(WCHAR) ] = L'\0';
|
||
*szAvlString = szResult;
|
||
}
|
||
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
MsvpAvlToFlag(
|
||
IN PUNICODE_STRING AvlString,
|
||
IN MSV1_0_AVID AvId,
|
||
IN OUT ULONG *ulAvlFlag
|
||
)
|
||
{
|
||
PMSV1_0_AV_PAIR pAV;
|
||
|
||
*ulAvlFlag = 0;
|
||
|
||
if( AvlString->Buffer == NULL || AvlString->Length == 0 )
|
||
{
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
pAV = MsvpAvlGet(
|
||
(PMSV1_0_AV_PAIR)AvlString->Buffer,
|
||
AvId,
|
||
AvlString->Length
|
||
);
|
||
|
||
if( pAV != NULL )
|
||
{
|
||
if( pAV->AvLen == sizeof( *ulAvlFlag ) )
|
||
{
|
||
CopyMemory( ulAvlFlag, (pAV+1), sizeof(ULONG) );
|
||
return STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
|
||
return STATUS_NOT_FOUND;
|
||
}
|
||
|