windows-nt/Source/XPSP1/NT/ds/security/base/lsa/server/neglsa.cxx
2020-09-26 16:20:57 +08:00

3272 lines
81 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1995.
//
// File: neglsa.cxx
//
// Contents: General (both win9x and nt) functions
//
// Classes:
//
// Functions:
//
// History: 02-09-00 RichardW Created - split from negotiat.cxx
//
//----------------------------------------------------------------------------
#include <lsapch.hxx>
#ifdef WIN32_CHICAGO
#include <kerb.hxx>
#endif // WIN32_CHICAGO
extern "C"
{
#include <align.h>
#include <lm.h>
#include <dsgetdc.h>
#include <cryptdll.h>
#include <netlib.h>
#include <spmgr.h>
#include "sesmgr.h"
#include "spinit.h"
}
#include "negotiat.hxx"
#include <stdio.h>
GUID GUID_NULL = {0L, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
TOKEN_SOURCE LsapTokenSource = {"LSA", 0};
//
// Hardcoded english strings for LocalService and NetworkService
// since the account names may come from the registry (which isn't
// localized).
//
#define LOCALSERVICE_NAME L"LocalService"
#define NETWORKSERVICE_NAME L"NetworkService"
#define NTAUTHORITY_NAME L"NT AUTHORITY"
UNICODE_STRING LocalServiceName = { sizeof(LOCALSERVICE_NAME) - 2,
sizeof(LOCALSERVICE_NAME),
LOCALSERVICE_NAME };
UNICODE_STRING NetworkServiceName = { sizeof(NETWORKSERVICE_NAME) - 2,
sizeof(NETWORKSERVICE_NAME),
NETWORKSERVICE_NAME };
UNICODE_STRING NTAuthorityName = { sizeof(NTAUTHORITY_NAME) - 2,
sizeof(NTAUTHORITY_NAME),
NTAUTHORITY_NAME };
HANDLE NegEventLogHandle = NULL ;
ULONG NegEventLogLevel = 3 ;
//
// RELOCATE_ONE - Relocate a single pointer in a client buffer.
//
// Note: this macro is dependent on parameter names as indicated in the
// description below. On error, this macro goes to 'Cleanup' with
// 'Status' set to the NT Status code.
//
// The MaximumLength is forced to be Length.
//
// Define a macro to relocate a pointer in the buffer the client passed in
// to be relative to 'ProtocolSubmitBuffer' rather than being relative to
// 'ClientBufferBase'. The result is checked to ensure the pointer and
// the data pointed to is within the first 'SubmitBufferSize' of the
// 'ProtocolSubmitBuffer'.
//
// The relocated field must be aligned to a WCHAR boundary.
//
// _q - Address of UNICODE_STRING structure which points to data to be
// relocated
//
#define RELOCATE_ONE( _q ) \
{ \
ULONG_PTR Offset; \
\
Offset = (((PUCHAR)((_q)->Buffer)) - ((PUCHAR)ClientBufferBase)); \
if ( Offset >= SubmitBufferSize || \
Offset + (_q)->Length > SubmitBufferSize || \
!COUNT_IS_ALIGNED( Offset, ALIGN_WCHAR) ) { \
\
Status = STATUS_INVALID_PARAMETER; \
goto Cleanup; \
} \
\
(_q)->Buffer = (PWSTR)(((PUCHAR)ProtocolSubmitBuffer) + Offset); \
(_q)->MaximumLength = (_q)->Length ; \
}
//
// NULL_RELOCATE_ONE - Relocate a single (possibly NULL) pointer in a client
// buffer.
//
// This macro special cases a NULL pointer then calls RELOCATE_ONE. Hence
// it has all the restrictions of RELOCATE_ONE.
//
//
// _q - Address of UNICODE_STRING structure which points to data to be
// relocated
//
#define NULL_RELOCATE_ONE( _q ) \
{ \
if ( (_q)->Buffer == NULL ) { \
if ( (_q)->Length != 0 ) { \
Status = STATUS_INVALID_PARAMETER; \
goto Cleanup; \
} \
} else if ( (_q)->Length == 0 ) { \
(_q)->Buffer = NULL; \
} else { \
RELOCATE_ONE( _q ); \
} \
}
#define MAX_EVENT_STRINGS 8
extern SECPKG_PRIMARY_CRED NegPrimarySystemCredentials;
//
// Local function prototypes
//
NTSTATUS
NegpMakeServiceToken(
IN ULONG ulAccountId,
OUT PLUID pLogonId,
OUT PVOID *ProfileBuffer,
OUT PULONG ProfileBufferLength,
OUT PNTSTATUS ApiSubStatus,
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 * CachedCredentials
);
BOOL
NegpIsLocalOrNetworkService(
IN PVOID ProtocolSubmitBuffer,
IN PVOID ClientBufferBase,
IN ULONG SubmitBufferSize,
OUT PUNICODE_STRING *AccountName,
OUT PUNICODE_STRING *AuthenticatingAuthority,
OUT PUNICODE_STRING *MachineName,
OUT PULONG pulAccountId
);
VOID
NegpReportEvent(
IN WORD EventType,
IN DWORD EventId,
IN DWORD Category,
IN NTSTATUS Status,
IN DWORD NumberOfStrings,
...
)
{
va_list arglist;
ULONG i;
PWSTR Strings[ MAX_EVENT_STRINGS ];
BOOLEAN Allocated[ MAX_EVENT_STRINGS ] = { 0 };
WCHAR StatusString[ MAX_PATH ];
WCHAR FinalStatus[ MAX_PATH ];
HANDLE Dll ;
BOOL FreeDll = FALSE ;
DWORD rv;
if (NegEventLogHandle == NULL )
{
//
// only log identical event once per hour.
// note that LSA doesn't cleanup this 'handle' during shutdown,
// to avoid preventing log failures during shutdown.
//
NegEventLogHandle = NetpEventlogOpen( L"LSASRV", 60000*60 );
if ( NegEventLogHandle == NULL )
{
return ;
}
}
//
// We're not supposed to be logging this, so nuke it
//
if ((NegEventLogLevel & (1 << EventType)) == 0)
{
return ;
}
//
// Look at the strings, if they were provided
//
va_start( arglist, NumberOfStrings );
if (NumberOfStrings > MAX_EVENT_STRINGS) {
NumberOfStrings = MAX_EVENT_STRINGS;
}
for (i=0; i<NumberOfStrings; i++) {
PUNICODE_STRING String = va_arg( arglist, PUNICODE_STRING );
//
// Make sure strings are NULL-terminated. Do it in place for
// those strings whose buffers can accomodate an additional character,
// and allocate memory for the rest.
//
if ( String->MaximumLength > String->Length ) {
Strings[i] = String->Buffer;
} else {
Strings[i] = ( PWSTR )LsapAllocatePrivateHeap( String->Length + sizeof( WCHAR ));
if ( Strings[i] == NULL ) {
goto Cleanup;
}
Allocated[i] = TRUE;
RtlCopyMemory( Strings[i], String->Buffer, String->Length );
}
Strings[i][String->Length / sizeof( WCHAR )] = L'\0';
}
if ( Status != 0 )
{
static HMODULE NtDll;
static HMODULE Kernel32;
//
// Map the status code:
//
//
// The value "type" is not known. Try and figure out what it
// is.
//
if ( (Status & 0xC0000000) == 0xC0000000 )
{
//
// Easy: NTSTATUS failure case
//
if( NtDll == NULL )
{
NtDll = GetModuleHandleW( L"NTDLL.DLL" );
}
Dll = NtDll;
}
else if ( ( Status & 0xF0000000 ) == 0xD0000000 )
{
//
// HRESULT from NTSTATUS
//
if( NtDll == NULL )
{
NtDll = GetModuleHandleW( L"NTDLL.DLL" );
}
Dll = NtDll;
Status &= 0xCFFFFFFF ;
}
else if ( ( Status & 0x80000000 ) == 0x80000000 )
{
//
// Note, this can overlap with NTSTATUS warning area. In that
// case, force the NTSTATUS.
//
if( Kernel32 == NULL )
{
Kernel32 = GetModuleHandleW( L"KERNEL32.DLL" );
}
Dll = Kernel32;
}
else
{
//
// Sign bit is off. Explore some known ranges:
//
if ( (Status >= WSABASEERR) && (Status <= WSABASEERR + 1000 ))
{
Dll = LoadLibraryW( L"ws2_32.dll" );
FreeDll = TRUE ;
}
else if ( ( Status >= NERR_BASE ) && ( Status <= MAX_NERR ) )
{
Dll = LoadLibraryW( L"netmsg.dll" );
FreeDll = TRUE ;
}
else
{
if( Kernel32 == NULL )
{
Kernel32 = GetModuleHandleW( L"KERNEL32.DLL" );
}
Dll = Kernel32;
}
}
if (!FormatMessage(
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_FROM_HMODULE,
Dll,
Status,
0,
StatusString,
MAX_PATH,
NULL ) )
{
StatusString[0] = L' ';
StatusString[1] = L'\0';
}
if ( Status < 0 )
{
_snwprintf( FinalStatus, MAX_PATH, L"\"%ws (%#x)\"", StatusString, Status );
}
else
{
_snwprintf( FinalStatus, MAX_PATH, L"\"%ws (%#x, %d)\"", StatusString, Status, Status );
}
if ( NumberOfStrings < MAX_EVENT_STRINGS )
{
Strings[ NumberOfStrings++ ] = FinalStatus ;
}
}
//
// Report the event to the eventlog service
//
NetpEventlogWriteEx(
NegEventLogHandle,
EventType,
Category,
EventId,
NumberOfStrings,
0,
Strings,
NULL
);
Cleanup:
for ( i = 0 ; i < NumberOfStrings ; i++ ) {
if ( Allocated[i] ) {
LsapFreePrivateHeap( Strings[i] );
}
}
}
//+---------------------------------------------------------------------------
//
// Function: NegLsaPolicyChangeCallback
//
// Synopsis: Called by the policy engine in the LSA when local policy changes,
// allowing us to update our state about the machine account, etc.
//
// Arguments: ChangedInfoClass - Class of policy that changed
//
// History: 2-9-00 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
VOID
NTAPI
NegLsaPolicyChangeCallback(
IN POLICY_NOTIFICATION_INFORMATION_CLASS ChangedInfoClass
)
{
PLSAPR_POLICY_INFORMATION Policy = NULL ;
NTSTATUS Status ;
GUID GuidNull = GUID_NULL ;
PLSAP_LOGON_SESSION LogonSession = NULL ;
BOOL Uplevel ;
//
// Right now, only domain information is interesting
//
if ( ChangedInfoClass != PolicyNotifyDnsDomainInformation )
{
return ;
}
DebugLog(( DEB_TRACE_NEG, "Domain change, updating state\n" ));
//
// Reset the trust list. The next time someone asks for the trust
// list, it will have expired and they will get a fresh one.
//
NegTrustTime.QuadPart = 0 ;
//
// Obtain the new policy settings
//
Status = LsaIQueryInformationPolicyTrusted(
PolicyDnsDomainInformation,
&Policy );
if ( NT_SUCCESS( Status ) )
{
//
// If the domain has a GUID, then it is an uplevel domain
//
if ( Policy->PolicyDnsDomainInfo.DomainGuid == GuidNull )
{
Uplevel = FALSE ;
}
else
{
Uplevel = TRUE ;
}
//
// Done with the policy info
//
LsaIFree_LSAPR_POLICY_INFORMATION(
PolicyDnsDomainInformation,
Policy
);
NegUplevelDomain = Uplevel ;
//
// Update the package ID for the local system logon session
// Note, any additional special logon sessions will need to be
// updated as well.
//
LogonSession = LsapLocateLogonSession( &SystemLogonId );
if ( LogonSession )
{
if ( Uplevel )
{
LogonSession->CreatingPackage = NegPackageId ;
}
else
{
LogonSession->CreatingPackage = NtlmPackageId ;
}
LsapReleaseLogonSession( LogonSession );
}
}
{
ULONG Size;
static WCHAR NegNetbiosComputerName[ MAX_COMPUTERNAME_LENGTH + 1 ];
static WCHAR NegDnsComputerName[ DNS_MAX_NAME_BUFFER_LENGTH + 1 ];
//
// refresh the computer names.
// note we could avoid taking the lock around these calls if we dyna-alloc'd
// and freed the existing buffers. We don't expect this path to be hit often,
// so avoid the hassle.
//
NegWriteLockComputerNames();
Size = sizeof(NegDnsComputerName) / sizeof(WCHAR);
//
// Note, if this fails, it's ok. We just won't be able to make
// some optimizations later.
//
if(!GetComputerNameExW(
ComputerNamePhysicalDnsFullyQualified,
NegDnsComputerName,
&Size
))
{
NegDnsComputerName[ 0 ] = L'\0';
}
RtlInitUnicodeString( &NegDnsComputerName_U, NegDnsComputerName );
Size = sizeof(NegNetbiosComputerName) / sizeof(WCHAR);
if(!GetComputerNameExW(
ComputerNamePhysicalNetBIOS,
NegNetbiosComputerName,
&Size
))
{
NegNetbiosComputerName[ 0 ] = L'\0';
}
RtlInitUnicodeString( &NegNetbiosComputerName_U, NegNetbiosComputerName );
NegUnlockComputerNames();
}
}
//+---------------------------------------------------------------------------
//
// Function: NegParamChange
//
// Synopsis: Called by LSA whenever the LSA registry key changes
//
// Arguments: [p] --
//
// History: 5-11-00 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
DWORD
WINAPI
NegParamChange(
PVOID p
)
{
NTSTATUS Status ;
PSECPKG_EVENT_NOTIFY Notify;
HKEY LsaKey ;
Notify = (PSECPKG_EVENT_NOTIFY) p;
if ( Notify->EventClass != NOTIFY_CLASS_REGISTRY_CHANGE )
{
return( 0 );
}
if ( RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
TEXT("System\\CurrentControlSet\\Control\\Lsa"),
0,
KEY_READ,
&LsaKey ) == 0 )
{
NegpReadRegistryParameters( LsaKey );
RegCloseKey( LsaKey );
}
return 0 ;
}
//+---------------------------------------------------------------------------
//
// Function: NegEnumPackagePrefixesCall
//
// Synopsis: LsaCallPackage routine to identify the prefxes (or OIDs) for
// all the packages, for SASL support.
//
// Arguments:
//
// History:
//
// Notes:
//
//----------------------------------------------------------------------------
NTSTATUS
NegEnumPackagePrefixesCall(
IN PLSA_CLIENT_REQUEST ClientRequest,
IN PVOID ProtocolSubmitBuffer,
IN PVOID ClientBufferBase,
IN ULONG SubmitBufferLength,
OUT PVOID *ProtocolReturnBuffer,
OUT PULONG ReturnBufferLength,
OUT PNTSTATUS ProtocolStatus
)
{
UCHAR PrefixBuffer[ NEGOTIATE_MAX_PREFIX ];
NTSTATUS Status ;
PNEGOTIATE_PACKAGE_PREFIXES Prefixes ;
PNEGOTIATE_PACKAGE_PREFIX Prefix ;
PNEGOTIATE_PACKAGE_PREFIX_WOW PrefixWow ;
ULONG PackageCount ;
BOOL WowClient = FALSE ;
PNEG_PACKAGE Package ;
PLIST_ENTRY Scan ;
SECPKG_CALL_INFO CallInfo ;
ULONG Size ;
LsapGetCallInfo( &CallInfo );
if ( CallInfo.Attributes & SECPKG_CALL_WOWCLIENT )
{
WowClient = TRUE;
}
NegReadLockList();
Size = sizeof( NEGOTIATE_PACKAGE_PREFIXES ) +
sizeof( NEGOTIATE_PACKAGE_PREFIX ) * ( NegPackageCount + 1 );
Prefixes = (PNEGOTIATE_PACKAGE_PREFIXES) LsapAllocatePrivateHeap( Size );
if ( !Prefixes )
{
NegUnlockList();
return SEC_E_INSUFFICIENT_MEMORY ;
}
Prefixes->MessageType = NegEnumPackagePrefixes ;
Prefixes->Offset = sizeof( NEGOTIATE_PACKAGE_PREFIXES );
//
// We're going to do one or the other, but initializing them both
// makes the compiler happier.
//
Prefix = (PNEGOTIATE_PACKAGE_PREFIX) ( Prefixes + 1 );
PrefixWow = (PNEGOTIATE_PACKAGE_PREFIX_WOW) ( Prefixes + 1);
if ( WowClient )
{
PrefixWow->PackageId = (ULONG) NegPackageId ;
PrefixWow->PrefixLen = sizeof( NegSpnegoMechEncodedOid );
PrefixWow->PackageDataA = NULL ;
PrefixWow->PackageDataW = NULL ;
RtlCopyMemory( PrefixWow->Prefix,
NegSpnegoMechEncodedOid,
sizeof( NegSpnegoMechEncodedOid ) );
PrefixWow++ ;
}
else
{
Prefix->PackageId = NegPackageId ;
Prefix->PrefixLen = sizeof( NegSpnegoMechEncodedOid );
Prefix->PackageDataA = NULL ;
Prefix->PackageDataW = NULL ;
RtlCopyMemory( Prefix->Prefix,
NegSpnegoMechEncodedOid,
sizeof( NegSpnegoMechEncodedOid ) );
Prefix++ ;
}
PackageCount = 1 ;
Scan = NegPackageList.Flink ;
while ( Scan != &NegPackageList )
{
Package = CONTAINING_RECORD( Scan, NEG_PACKAGE, List );
if ( !WowClient )
{
Prefix->PackageId = Package->LsaPackage->dwPackageID ;
Prefix->PrefixLen = Package->PrefixLen ;
Prefix->PackageDataA = NULL ;
Prefix->PackageDataW = NULL ;
RtlCopyMemory( Prefix->Prefix,
Package->Prefix,
Package->PrefixLen );
Prefix++ ;
PackageCount++ ;
}
else
{
if ( Package->LsaPackage->fPackage & SP_WOW_SUPPORT )
{
PrefixWow->PackageId = (ULONG) Package->LsaPackage->dwPackageID ;
PrefixWow->PrefixLen = Package->PrefixLen ;
PrefixWow->PackageDataA = NULL ;
PrefixWow->PackageDataW = NULL ;
RtlCopyMemory( PrefixWow->Prefix,
Package->Prefix,
Package->PrefixLen );
PrefixWow++ ;
PackageCount++ ;
}
}
Scan = Scan->Flink ;
}
NegUnlockList();
//
// Set the final count of packages:
//
Prefixes->PrefixCount = PackageCount ;
Status = LsapAllocateClientBuffer(
NULL,
Size,
ProtocolReturnBuffer );
if ( NT_SUCCESS( Status ) )
{
Status = LsapCopyToClient(
Prefixes,
*ProtocolReturnBuffer,
Size );
*ReturnBufferLength = Size ;
}
LsapFreePrivateHeap( Prefixes );
return Status ;
}
NTSTATUS
NegGetCallerNameCall(
IN PLSA_CLIENT_REQUEST ClientRequest,
IN PVOID ProtocolSubmitBuffer,
IN PVOID ClientBufferBase,
IN ULONG SubmitBufferLength,
OUT PVOID *ProtocolReturnBuffer,
OUT PULONG ReturnBufferLength,
OUT PNTSTATUS ProtocolStatus
)
{
PNEGOTIATE_CALLER_NAME_REQUEST Request ;
PNEGOTIATE_CALLER_NAME_RESPONSE Response ;
PNEGOTIATE_CALLER_NAME_RESPONSE_WOW ResponseWow ;
SECPKG_CLIENT_INFO ClientInfo ;
SECPKG_CALL_INFO CallInfo ;
NTSTATUS Status ;
PNEG_LOGON_SESSION LogonSession ;
ULONG ClientSize ;
PUCHAR Where ;
PVOID ClientBuffer ;
BOOL ReCheckAccess = FALSE ;
*ProtocolReturnBuffer = NULL ;
*ReturnBufferLength = 0 ;
*ProtocolStatus = 0 ;
if ( SubmitBufferLength != sizeof( NEGOTIATE_CALLER_NAME_REQUEST ) )
{
return STATUS_INVALID_PARAMETER ;
}
Request = (PNEGOTIATE_CALLER_NAME_REQUEST) ProtocolSubmitBuffer ;
Status = LsapGetClientInfo( &ClientInfo );
if ( !NT_SUCCESS( Status ) )
{
return Status ;
}
LsapGetCallInfo( &CallInfo );
if ( RtlIsZeroLuid( &Request->LogonId ) )
{
Request->LogonId = ClientInfo.LogonId ;
}
else
{
if ( !RtlEqualLuid( &ClientInfo.LogonId, &Request->LogonId ) )
{
ReCheckAccess = TRUE ;
}
}
LogonSession = NegpLocateLogonSession( &Request->LogonId );
if ( LogonSession )
{
if ( ReCheckAccess )
{
if ( !RtlEqualLuid( &ClientInfo.LogonId, &LogonSession->ParentLogonId ) )
{
Status = STATUS_ACCESS_DENIED ;
goto AccessDeniedError ;
}
}
if ( LogonSession->AlternateName.Buffer == NULL )
{
//
// No alternate ID
//
Status = STATUS_NO_SUCH_LOGON_SESSION ;
goto AccessDeniedError ;
}
ClientSize = sizeof( NEGOTIATE_CALLER_NAME_RESPONSE ) +
LogonSession->AlternateName.Length + sizeof( WCHAR );
Response = (PNEGOTIATE_CALLER_NAME_RESPONSE) LsapAllocatePrivateHeap( ClientSize );
if ( Response )
{
ClientBuffer = LsapClientAllocate( ClientSize );
if ( ClientBuffer )
{
Where = (PUCHAR) ClientBuffer + sizeof( NEGOTIATE_CALLER_NAME_RESPONSE ) ;
//
// If a WOW client, copy these to the 32bit locations. Note
// that this will leave a "hole," but that's ok.
//
if ( CallInfo.Attributes & SECPKG_CALL_WOWCLIENT )
{
ResponseWow = (PNEGOTIATE_CALLER_NAME_RESPONSE_WOW) Response ;
ResponseWow->MessageType = NegGetCallerName ;
ResponseWow->CallerName = PtrToUlong(Where) ;
}
else
{
Response->MessageType = NegGetCallerName ;
Response->CallerName = (PWSTR) Where ;
}
RtlCopyMemory( (Response + 1),
LogonSession->AlternateName.Buffer,
LogonSession->AlternateName.Length );
*ProtocolReturnBuffer = ClientBuffer ;
*ReturnBufferLength = ClientSize ;
*ProtocolStatus = 0 ;
Status = LsapCopyToClient( Response,
ClientBuffer,
ClientSize ) ;
}
else
{
Status = STATUS_NO_MEMORY ;
}
LsapFreePrivateHeap( Response );
}
AccessDeniedError:
NegpDerefLogonSession( LogonSession );
}
else
{
Status = STATUS_NO_SUCH_LOGON_SESSION ;
}
*ProtocolStatus = Status ;
return STATUS_SUCCESS ;
}
PNEG_LOGON_SESSION
NegpBuildLogonSession(
PLUID LogonId,
ULONG_PTR LogonPackage,
ULONG_PTR DefaultPackage
)
{
PNEG_LOGON_SESSION LogonSession ;
LogonSession = (PNEG_LOGON_SESSION) LsapAllocatePrivateHeap( sizeof( NEG_LOGON_SESSION ) );
if ( LogonSession )
{
LogonSession->LogonId = *LogonId ;
LogonSession->CreatingPackage = LogonPackage ;
LogonSession->DefaultPackage = DefaultPackage ;
LogonSession->RefCount = 2 ;
RtlEnterCriticalSection( &NegLogonSessionListLock );
InsertHeadList( &NegLogonSessionList, &LogonSession->List );
RtlLeaveCriticalSection( &NegLogonSessionListLock );
}
return LogonSession ;
}
VOID
NegpDerefLogonSession(
PNEG_LOGON_SESSION LogonSession
)
{
BOOL FreeIt = FALSE ;
RtlEnterCriticalSection( &NegLogonSessionListLock );
LogonSession->RefCount-- ;
if ( LogonSession->RefCount == 0 )
{
RemoveEntryList( &LogonSession->List );
FreeIt = TRUE ;
}
RtlLeaveCriticalSection( &NegLogonSessionListLock );
if ( FreeIt )
{
if ( LogonSession->AlternateName.Buffer )
{
LsapFreePrivateHeap( LogonSession->AlternateName.Buffer );
}
LsapFreePrivateHeap( LogonSession );
}
}
VOID
NegpDerefLogonSessionById(
PLUID LogonId
)
{
BOOL FreeIt = FALSE ;
PLIST_ENTRY Scan ;
PNEG_LOGON_SESSION LogonSession = NULL;
RtlEnterCriticalSection( &NegLogonSessionListLock );
Scan = NegLogonSessionList.Flink ;
while ( Scan != &NegLogonSessionList )
{
LogonSession = CONTAINING_RECORD( Scan, NEG_LOGON_SESSION, List );
if ( RtlEqualLuid( LogonId, &LogonSession->LogonId ) )
{
LogonSession->RefCount -- ;
if ( LogonSession->RefCount == 0 )
{
RemoveEntryList( &LogonSession->List );
FreeIt = TRUE ;
}
break;
}
LogonSession = NULL ;
Scan = Scan->Flink ;
}
RtlLeaveCriticalSection( &NegLogonSessionListLock );
if ( FreeIt )
{
DsysAssert( LogonSession != NULL );
if ( LogonSession->AlternateName.Buffer )
{
LsapFreePrivateHeap( LogonSession->AlternateName.Buffer );
}
LsapFreePrivateHeap( LogonSession );
}
}
PNEG_LOGON_SESSION
NegpLocateLogonSession(
PLUID LogonId
)
{
PLIST_ENTRY Scan ;
PNEG_LOGON_SESSION LogonSession = NULL;
RtlEnterCriticalSection( &NegLogonSessionListLock );
Scan = NegLogonSessionList.Flink ;
while ( Scan != &NegLogonSessionList )
{
LogonSession = CONTAINING_RECORD( Scan, NEG_LOGON_SESSION, List );
if ( RtlEqualLuid( LogonId, &LogonSession->LogonId ) )
{
break;
}
LogonSession = NULL ;
Scan = Scan->Flink ;
}
if ( LogonSession )
{
LogonSession->RefCount++ ;
}
RtlLeaveCriticalSection( &NegLogonSessionListLock );
return LogonSession ;
}
NTSTATUS
NTAPI
NegpMapLogonRequest(
IN PVOID ProtocolSubmitBuffer,
IN PVOID ClientBufferBase,
IN ULONG SubmitBufferSize,
OUT PMSV1_0_INTERACTIVE_LOGON * LogonInfo
)
{
NTSTATUS Status = STATUS_SUCCESS ;
PMSV1_0_INTERACTIVE_LOGON Authentication = NULL;
PSECURITY_SEED_AND_LENGTH SeedAndLength;
UCHAR Seed;
//
// Ensure this is really an interactive logon.
//
Authentication =
(PMSV1_0_INTERACTIVE_LOGON) ProtocolSubmitBuffer;
if ( Authentication->MessageType != MsV1_0InteractiveLogon ) {
DebugLog(( DEB_ERROR, "Neg: 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 ||
Authentication->Password.Length > PWLEN ) {
DebugLog(( DEB_ERROR, "Neg: 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 );
//
// Now decode the password, if necessary
//
if (Seed != 0 ) {
__try {
RtlRunDecodeUnicodeString( Seed, &Authentication->Password);
} __except(EXCEPTION_EXECUTE_HANDLER) {
Status = STATUS_ILL_FORMED_PASSWORD;
goto Cleanup;
}
}
*LogonInfo = Authentication ;
Cleanup:
return Status ;
}
VOID
NegpDerefTrustList(
PNEG_TRUST_LIST TrustList
)
{
RtlEnterCriticalSection( &NegTrustListLock );
TrustList->RefCount-- ;
if ( TrustList->RefCount == 0 )
{
if( NegTrustList == TrustList )
{
NegTrustList = NULL;
}
RtlLeaveCriticalSection( &NegTrustListLock );
NetApiBufferFree( TrustList->Trusts );
LsapFreePrivateHeap( TrustList );
return;
}
RtlLeaveCriticalSection( &NegTrustListLock );
}
PNEG_TRUST_LIST
NegpGetTrustList(
VOID
)
{
PDS_DOMAIN_TRUSTS Trusts = NULL;
DWORD TrustCount ;
PNEG_TRUST_LIST TrustList = NULL ;
DWORD NetStatus ;
LARGE_INTEGER Now ;
BOOLEAN TrustListLocked = TRUE;
GetSystemTimeAsFileTime( (LPFILETIME) &Now );
RtlEnterCriticalSection( &NegTrustListLock );
if ( Now.QuadPart > NegTrustTime.QuadPart + FIFTEEN_MINUTES )
{
if( NegTrustList != NULL && NegTrustList->RefCount == 1 )
{
NegpDerefTrustList( NegTrustList );
NegTrustList = NULL;
}
}
if( NegTrustList != NULL )
{
TrustList = NegTrustList ;
TrustList->RefCount++ ;
goto Cleanup;
}
RtlLeaveCriticalSection( &NegTrustListLock );
TrustListLocked = FALSE;
NetStatus = DsEnumerateDomainTrustsW( NULL,
DS_DOMAIN_IN_FOREST |
DS_DOMAIN_PRIMARY,
&Trusts,
&TrustCount );
if ( NetStatus != 0 )
{
goto Cleanup;
}
TrustList = (PNEG_TRUST_LIST) LsapAllocatePrivateHeap( sizeof( NEG_TRUST_LIST ) );
if( TrustList == NULL )
{
goto Cleanup;
}
TrustList->RefCount = 2 ;
TrustList->TrustCount = TrustCount ;
TrustList->Trusts = Trusts ;
RtlEnterCriticalSection( &NegTrustListLock );
TrustListLocked = TRUE;
if( NegTrustList != NULL )
{
PNEG_TRUST_LIST FreeTrustList = TrustList;
TrustList = NegTrustList ;
TrustList->RefCount++ ;
RtlLeaveCriticalSection( &NegTrustListLock );
TrustListLocked = FALSE;
LsapFreePrivateHeap( FreeTrustList );
goto Cleanup;
}
Trusts = NULL;
NegTrustList = TrustList ;
NegTrustTime = Now ;
Cleanup:
if( TrustListLocked )
{
RtlLeaveCriticalSection( &NegTrustListLock );
}
if( Trusts != NULL )
{
NetApiBufferFree( Trusts );
}
return TrustList ;
}
NEG_DOMAIN_TYPES
NegpIsUplevelDomain(
PLUID LogonId,
SECURITY_LOGON_TYPE LogonType,
PUNICODE_STRING Domain
)
{
PNEG_TRUST_LIST TrustList ;
UNICODE_STRING String ;
ULONG i ;
BOOL IsUplevel = FALSE ;
LONG Error ;
PDOMAIN_CONTROLLER_INFOW Info ;
UNREFERENCED_PARAMETER(LogonId);
//
// Case #1. Local logons are not uplevel, and should be allowed to
// use NTLM right off the bat. Therefore, return false
//
if ( RtlEqualUnicodeString(
Domain,
&MachineName,
TRUE ) )
{
return NegLocalDomain;
}
if ( LogonType == CachedInteractive )
{
return NegUpLevelDomain;
}
//
// Case #2. We logged on to a domain, but we need to check if it is
// an uplevel domain in our forest. If it is, then return true, otherwise
// it's not an uplevel domain and NTLM is acceptable. If we can't obtain the
// trust list, assume downlevel.
//
TrustList = NegpGetTrustList();
if ( TrustList )
{
for ( i = 0 ; i < TrustList->TrustCount ; i++ )
{
RtlInitUnicodeString( &String, TrustList->Trusts[i].NetbiosDomainName );
if ( RtlEqualUnicodeString( Domain,
&String,
TRUE ) )
{
//
// Hit, check it
//
if ( ( TrustList->Trusts[i].DnsDomainName != NULL ) &&
( TrustList->Trusts[i].Flags & DS_DOMAIN_IN_FOREST ) )
{
IsUplevel = TRUE ;
break;
}
}
}
NegpDerefTrustList( TrustList );
}
if ( IsUplevel )
{
return NegUpLevelDomain ;
}
//
// Case #3 - if this is an uplevel domain we live in, then the answer returned
// by netlogon is authoritative:
//
if ( NegUplevelDomain )
{
return NegDownLevelDomain ;
}
//
// Case #4 - if we are living in a downlevel domain, netlogon won't know if the domain
// we just talked to is uplevel or downlevel. So, we need to figure out directly.
//
Error = DsGetDcNameW(
NULL,
Domain->Buffer,
NULL,
NULL,
DS_DIRECTORY_SERVICE_REQUIRED,
&Info );
if ( Error == 0 )
{
IsUplevel = TRUE ;
NetApiBufferFree( Info );
}
else
{
IsUplevel = FALSE ;
}
return ( IsUplevel ? NegUpLevelTrustedDomain : NegDownLevelDomain );
}
//+-------------------------------------------------------------------------
//
// Function: NegpCloneLogonSession
//
// Synopsis: Handles a NewCredentials type of logon.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
NTAPI
NegpCloneLogonSession(
IN PVOID ProtocolSubmitBuffer,
IN PVOID ClientBufferBase,
IN ULONG SubmitBufferSize,
OUT PVOID *ProfileBuffer,
OUT PULONG ProfileBufferLength,
OUT PLUID NewLogonId,
OUT PNTSTATUS ApiSubStatus,
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 * CachedCredentials
)
{
NTSTATUS Status ;
HANDLE hToken ;
UCHAR SmallBuffer[ 128 ];
PLSA_TOKEN_INFORMATION_V1 TokenInfo = NULL ;
PTOKEN_USER User = NULL ;
PTOKEN_GROUPS Groups = NULL ;
PTOKEN_GROUPS FinalGroups = NULL ;
PTOKEN_PRIMARY_GROUP PrimaryGroup = NULL ;
TOKEN_STATISTICS TokenStats ;
PLSAP_LOGON_SESSION LogonSession ;
PNEG_LOGON_SESSION NegLogonSession ;
LUID LogonId ;
LUID LocalServiceLuid = LOCALSERVICE_LUID;
LUID NetworkServiceLuid = NETWORKSERVICE_LUID;
LUID LocalSystemLuid = SYSTEM_LUID;
BOOL fFilterGroups;
PMSV1_0_INTERACTIVE_LOGON Authentication = NULL;
DWORD Size ;
ULONG i ;
ULONG j ;
PWSTR AltName, AltNameScan ;
Status = LsapImpersonateClient();
if ( !NT_SUCCESS( Status ) )
{
return Status ;
}
//
// Open the token for the lifetime of this call. This makes sure that the
// client doesn't die on us, taking out the logon session (potentially).
//
Status = NtOpenThreadToken(
NtCurrentThread(),
TOKEN_QUERY,
TRUE,
&hToken );
RevertToSelf();
if ( !NT_SUCCESS( Status ) )
{
return Status ;
}
//
// Grovel the token, and build up a list of groups that we will use for
// the new token. Only non-builtin groups will be used.
//
TokenInfo = (PLSA_TOKEN_INFORMATION_V1) LsapAllocateLsaHeap(
sizeof( LSA_TOKEN_INFORMATION_V1) );
if ( !TokenInfo )
{
Status = STATUS_NO_MEMORY ;
goto Clone_Exit ;
}
Status = NtQueryInformationToken(
hToken,
TokenStatistics,
&TokenStats,
sizeof( TokenStats ),
&Size );
if ( !NT_SUCCESS( Status ) )
{
goto Clone_Exit ;
}
TokenInfo->ExpirationTime = TokenStats.ExpirationTime ;
User = (PTOKEN_USER) SmallBuffer ;
Size = sizeof( SmallBuffer );
Status = NtQueryInformationToken(
hToken,
TokenUser,
User,
Size,
&Size );
if ( ( Status == STATUS_BUFFER_OVERFLOW ) ||
( Status == STATUS_BUFFER_TOO_SMALL ) )
{
User = (PTOKEN_USER) LsapAllocatePrivateHeap( Size );
if ( User )
{
Status = NtQueryInformationToken(
hToken,
TokenUser,
User,
Size,
&Size );
}
else
{
Status = STATUS_NO_MEMORY ;
}
}
if ( !NT_SUCCESS( Status ) )
{
goto Clone_Exit ;
}
TokenInfo->User.User.Attributes = 0 ;
TokenInfo->User.User.Sid = LsapAllocateLsaHeap( RtlLengthSid( User->User.Sid ) );
if ( TokenInfo->User.User.Sid )
{
RtlCopyMemory( TokenInfo->User.User.Sid,
User->User.Sid,
RtlLengthSid( User->User.Sid ) );
}
else
{
Status = STATUS_NO_MEMORY ;
goto Clone_Exit ;
}
if ( User != (PTOKEN_USER) SmallBuffer )
{
LsapFreePrivateHeap( User );
}
User = NULL ;
Status = NtQueryInformationToken(
hToken,
TokenGroups,
NULL,
0,
&Size );
if ( ( Status != STATUS_BUFFER_OVERFLOW ) &&
( Status != STATUS_BUFFER_TOO_SMALL ) )
{
goto Clone_Exit ;
}
Groups = (PTOKEN_GROUPS) LsapAllocatePrivateHeap( Size );
if ( !Groups )
{
Status = STATUS_NO_MEMORY ;
goto Clone_Exit ;
}
Status = NtQueryInformationToken(
hToken,
TokenGroups,
Groups,
Size,
&Size );
if ( !NT_SUCCESS( Status ) )
{
goto Clone_Exit ;
}
//
// Grovel through the group list, and ditch those we don't care about.
// i always travels ahead of j. Skip groups that have one or two RIDs,
// because those are the builtin ones.
//
FinalGroups = (PTOKEN_GROUPS) LsapAllocatePrivateHeap( sizeof( TOKEN_GROUPS ) +
Groups->GroupCount * sizeof( SID_AND_ATTRIBUTES ) );
if ( !FinalGroups )
{
goto Clone_Exit ;
}
//
// Don't filter out groups for tokens where the LSA has hardcoded info on
// how to build the token. If we filter in those cases, we strip out SIDs
// that won't be replaced by the LSA policy/filter routines later on.
//
fFilterGroups = !RtlEqualLuid(&TokenStats.AuthenticationId, &LocalSystemLuid) &&
!RtlEqualLuid(&TokenStats.AuthenticationId, &LocalServiceLuid) &&
!RtlEqualLuid(&TokenStats.AuthenticationId, &NetworkServiceLuid);
for ( i = 0, j = 0 ; i < Groups->GroupCount ; i++ )
{
if ( !fFilterGroups || *RtlSubAuthorityCountSid( Groups->Groups[ i ].Sid ) > 2 )
{
FinalGroups->Groups[ j ].Attributes = Groups->Groups[ i ].Attributes ;
Status = LsapDuplicateSid2( &FinalGroups->Groups[ j ].Sid,
Groups->Groups[ i ].Sid );
j++ ;
if( !NT_SUCCESS(Status) )
{
break;
}
}
}
FinalGroups->GroupCount = j ;
if ( !NT_SUCCESS( Status ) )
{
goto Clone_Exit ;
}
//
// whew. Set this in the token info, and null out the other pointer
// so we don't free it inadvertantly.
//
TokenInfo->Groups = FinalGroups ;
FinalGroups = NULL ;
//
// Determine the primary group:
//
PrimaryGroup = (PTOKEN_PRIMARY_GROUP) SmallBuffer ;
Size = sizeof( SmallBuffer );
Status = NtQueryInformationToken(
hToken,
TokenPrimaryGroup,
PrimaryGroup,
Size,
&Size );
if ( ( Status == STATUS_BUFFER_OVERFLOW ) ||
( Status == STATUS_BUFFER_TOO_SMALL ) )
{
PrimaryGroup = (PTOKEN_PRIMARY_GROUP) LsapAllocatePrivateHeap( Size );
Status = NtQueryInformationToken(
hToken,
TokenPrimaryGroup,
PrimaryGroup,
Size,
&Size );
}
if ( !NT_SUCCESS( Status ) )
{
goto Clone_Exit ;
}
TokenInfo->PrimaryGroup.PrimaryGroup = LsapAllocateLsaHeap( RtlLengthSid( PrimaryGroup->PrimaryGroup ) );
if ( TokenInfo->PrimaryGroup.PrimaryGroup )
{
RtlCopyMemory( TokenInfo->PrimaryGroup.PrimaryGroup,
PrimaryGroup->PrimaryGroup,
RtlLengthSid( PrimaryGroup->PrimaryGroup ) );
}
else
{
Status = STATUS_NO_MEMORY ;
goto Clone_Exit ;
}
if ( PrimaryGroup != (PTOKEN_PRIMARY_GROUP) SmallBuffer )
{
LsapFreePrivateHeap( PrimaryGroup );
}
PrimaryGroup = NULL ;
//
// Almost there -- now dupe the privileges
//
TokenInfo->Privileges = NULL ;
Size = 0;
Status = NtQueryInformationToken(
hToken,
TokenPrivileges,
TokenInfo->Privileges,
Size,
&Size );
if ( ( Status == STATUS_BUFFER_OVERFLOW ) ||
( Status == STATUS_BUFFER_TOO_SMALL ) )
{
TokenInfo->Privileges = (PTOKEN_PRIVILEGES) LsapAllocateLsaHeap(Size);
if (TokenInfo->Privileges == NULL)
{
Status = STATUS_NO_MEMORY;
}
else
{
Status = NtQueryInformationToken(
hToken,
TokenPrivileges,
TokenInfo->Privileges,
Size,
&Size);
}
}
if ( !NT_SUCCESS( Status ) )
{
goto Clone_Exit;
}
//
// Ok, we have completed the Token Information. Now, we need to parse
// the supplied buffer to come up with creds for the other packages, since
// that's what this is all about
//
Status = NegpMapLogonRequest(
ProtocolSubmitBuffer,
ClientBufferBase,
SubmitBufferSize,
&Authentication );
if ( !NT_SUCCESS( Status ) )
{
goto Clone_Exit ;
}
//
// Stuff the names:
//
*AccountName = (PUNICODE_STRING) LsapAllocateLsaHeap( sizeof( UNICODE_STRING ) );
if ( ! ( *AccountName ) )
{
Status = STATUS_NO_MEMORY ;
goto Clone_Exit ;
}
*AuthenticatingAuthority = (PUNICODE_STRING) LsapAllocateLsaHeap( sizeof( UNICODE_STRING ) );
if ( ! ( *AuthenticatingAuthority ) )
{
Status = STATUS_NO_MEMORY ;
goto Clone_Exit ;
}
*MachineName = (PUNICODE_STRING) LsapAllocateLsaHeap( sizeof( UNICODE_STRING ) );
if ( ! ( *MachineName ) )
{
Status = STATUS_NO_MEMORY ;
goto Clone_Exit ;
}
//
// Tokens that the LSA normally constructs (SYSTEM, LocalService, NetworkService)
// have special names for returned as part of SID --> name lookups that don't
// necessarily match what's in the token as the account/authority names. Cruft
// up the new token with these special names instead of the standard ones.
//
if (fFilterGroups)
{
Status = LsapGetLogonSessionAccountInfo(
&TokenStats.AuthenticationId,
(*AccountName),
(*AuthenticatingAuthority) );
}
else
{
LSAP_WELL_KNOWN_SID_INDEX dwIndex = LsapLocalSystemSidIndex;
if (RtlEqualLuid(&TokenStats.AuthenticationId, &LocalServiceLuid))
{
dwIndex = LsapLocalServiceSidIndex;
}
else if (RtlEqualLuid(&TokenStats.AuthenticationId, &NetworkServiceLuid))
{
dwIndex = LsapNetworkServiceSidIndex;
}
Status = LsapDuplicateString(*AccountName,
LsapDbWellKnownSidName(dwIndex));
if ( !NT_SUCCESS( Status ) )
{
goto Clone_Exit ;
}
Status = LsapDuplicateString(*AuthenticatingAuthority,
LsapDbWellKnownSidDescription(dwIndex));
}
if ( !NT_SUCCESS( Status ) )
{
goto Clone_Exit ;
}
//
// Construct the credential data to pass on to the other packages:
//
Status = LsapDuplicateString( &PrimaryCredentials->DomainName,
&Authentication->LogonDomainName );
if ( !NT_SUCCESS( Status ) )
{
goto Clone_Exit ;
}
Status = LsapDuplicateString( &PrimaryCredentials->DownlevelName,
&Authentication->UserName );
if ( !NT_SUCCESS( Status ) )
{
goto Clone_Exit ;
}
Status = LsapDuplicateString( &PrimaryCredentials->Password,
&Authentication->Password );
if ( !NT_SUCCESS( Status ) )
{
goto Clone_Exit ;
}
Status = LsapDuplicateSid( &PrimaryCredentials->UserSid,
TokenInfo->User.User.Sid );
if ( !NT_SUCCESS( Status ) )
{
goto Clone_Exit ;
}
PrimaryCredentials->Flags = PRIMARY_CRED_CLEAR_PASSWORD ;
//
// Let the LSA do the rest:
//
NtAllocateLocallyUniqueId( &LogonId );
Status = LsapCreateLogonSession( &LogonId );
if ( !NT_SUCCESS( Status ) )
{
goto Clone_Exit ;
}
NegLogonSession = NegpBuildLogonSession( &LogonId,
NegPackageId,
NegPackageId );
if ( !NegLogonSession )
{
Status = STATUS_NO_MEMORY ;
goto Clone_Exit;
}
AltName = (PWSTR) LsapAllocatePrivateHeap( Authentication->UserName.Length +
Authentication->LogonDomainName.Length +
2 * sizeof( WCHAR ) );
if ( AltName )
{
AltNameScan = AltName ;
RtlCopyMemory( AltNameScan,
Authentication->LogonDomainName.Buffer,
Authentication->LogonDomainName.Length );
AltNameScan += Authentication->LogonDomainName.Length / sizeof(WCHAR) ;
*AltNameScan++ = L'\\';
RtlCopyMemory( AltNameScan,
Authentication->UserName.Buffer,
Authentication->UserName.Length );
AltNameScan += Authentication->UserName.Length / sizeof( WCHAR ) ;
*AltNameScan++ = L'\0';
RtlInitUnicodeString( &NegLogonSession->AlternateName, AltName );
}
NegLogonSession->ParentLogonId = TokenStats.AuthenticationId ;
NegpDerefLogonSession( NegLogonSession );
PrimaryCredentials->LogonId = LogonId ;
*ProfileBuffer = NULL ;
*ProfileBufferLength = 0 ;
*NewLogonId = LogonId ;
*ApiSubStatus = STATUS_SUCCESS ;
*TokenInformationType = LsaTokenInformationV1 ;
*TokenInformation = TokenInfo ;
TokenInfo = NULL ;
*CachedCredentials = NULL ;
Clone_Exit:
if ( Groups )
{
LsapFreePrivateHeap( Groups );
}
if ( FinalGroups )
{
LsapFreeTokenGroups( FinalGroups );
}
if ( TokenInfo )
{
if ( TokenInfo->User.User.Sid )
{
LsapFreeLsaHeap( TokenInfo->User.User.Sid );
}
if ( TokenInfo->Groups )
{
LsapFreeTokenGroups( TokenInfo->Groups );
}
if ( TokenInfo->Privileges )
{
LsapFreeLsaHeap( TokenInfo->Privileges );
}
LsapFreeLsaHeap( TokenInfo );
}
if ( (User != NULL) &&
(User != (PTOKEN_USER) SmallBuffer ) )
{
LsapFreePrivateHeap( User );
}
if ( hToken != NULL )
{
NtClose( hToken );
}
if ( !NT_SUCCESS( Status ) )
{
if ( *AuthenticatingAuthority )
{
if ( (*AuthenticatingAuthority)->Buffer )
{
LsapFreeLsaHeap( (*AuthenticatingAuthority)->Buffer );
}
LsapFreeLsaHeap( *AuthenticatingAuthority );
*AuthenticatingAuthority = NULL;
}
if ( *AccountName )
{
if ( (*AccountName)->Buffer )
{
LsapFreeLsaHeap( (*AccountName)->Buffer );
}
LsapFreeLsaHeap( *AccountName );
*AccountName = NULL;
}
if ( *MachineName )
{
if ( (*MachineName)->Buffer )
{
LsapFreeLsaHeap( (*MachineName)->Buffer );
}
LsapFreeLsaHeap( *MachineName );
*MachineName = NULL;
}
}
return Status ;
}
//+-------------------------------------------------------------------------
//
// Function: NegLogonUserEx2
//
// Synopsis: Handles service, batch, and interactive logons
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
NTAPI
NegLogonUserEx2(
IN PLSA_CLIENT_REQUEST ClientRequest,
IN SECURITY_LOGON_TYPE LogonType,
IN PVOID ProtocolSubmitBuffer,
IN PVOID ClientBufferBase,
IN ULONG SubmitBufferSize,
OUT PVOID *ProfileBuffer,
OUT PULONG ProfileBufferLength,
OUT PLUID NewLogonId,
OUT PNTSTATUS ApiSubStatus,
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 * CachedCredentials
)
{
NTSTATUS Status = STATUS_NO_LOGON_SERVERS;
PNEG_PACKAGE Package;
PVOID LocalSubmitBuffer = NULL;
ULONG_PTR CurrentPackageId = GetCurrentPackageId();
PLSAP_SECURITY_PACKAGE * LogonPackages = NULL;
PNEG_LOGON_SESSION LogonSession ;
PWSTR AltName, AltNameScan ;
NEG_DOMAIN_TYPES DomainType ;
ULONG LogonPackageCount = 0;
ULONG Index;
if ( LogonType == NewCredentials )
{
return NegpCloneLogonSession(
ProtocolSubmitBuffer,
ClientBufferBase,
SubmitBufferSize,
ProfileBuffer,
ProfileBufferLength,
NewLogonId,
ApiSubStatus,
TokenInformationType,
TokenInformation,
AccountName,
AuthenticatingAuthority,
MachineName,
PrimaryCredentials,
CachedCredentials
);
}
//
// Allocate a local copy of the submit buffer so each package can
// mark it up.
//
LocalSubmitBuffer = LsapAllocateLsaHeap(SubmitBufferSize);
if (LocalSubmitBuffer == NULL)
{
return(STATUS_INSUFFICIENT_RESOURCES);
}
if ( LogonType == Service )
{
ULONG ulAccountId;
//
// Copy the submit buffer for the package to mark up
//
RtlCopyMemory(
LocalSubmitBuffer,
ProtocolSubmitBuffer,
SubmitBufferSize
);
if (NegpIsLocalOrNetworkService(
LocalSubmitBuffer,
ClientBufferBase,
SubmitBufferSize,
AccountName,
AuthenticatingAuthority,
MachineName,
&ulAccountId))
{
Status = NegpMakeServiceToken(
ulAccountId,
NewLogonId,
ProfileBuffer,
ProfileBufferLength,
ApiSubStatus,
TokenInformationType,
TokenInformation,
AccountName,
AuthenticatingAuthority,
MachineName,
PrimaryCredentials,
CachedCredentials
);
goto Cleanup;
}
}
LogonPackages = (PLSAP_SECURITY_PACKAGE *) LsapAllocateLsaHeap( NegPackageCount * sizeof(PLSAP_SECURITY_PACKAGE));
if (LogonPackages == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
NegReadLockList();
Package = (PNEG_PACKAGE) NegPackageList.Flink ;
while ( (PVOID) Package != (PVOID) &NegPackageList )
{
if (((Package->LsaPackage->fCapabilities & SECPKG_FLAG_LOGON) != 0) &&
((Package->Flags & NEG_PACKAGE_EXTRA_OID ) == 0 ) &&
(Package->LsaPackage->FunctionTable.LogonUserEx2 != NULL))
{
LogonPackages[LogonPackageCount++] = Package->LsaPackage;
}
Package = (PNEG_PACKAGE) Package->List.Flink ;
}
NegUnlockList();
for (Index = 0; Index < LogonPackageCount; Index++)
{
//
// Cleanup the old audit strings so they don't get leaked
// when the package re-allocates them.
//
if ((*MachineName) != NULL) {
if ((*MachineName)->Buffer != NULL) {
LsapFreeLsaHeap( (*MachineName)->Buffer );
}
LsapFreeLsaHeap( (*MachineName) );
(*MachineName) = NULL;
}
if ((*AccountName) != NULL) {
if ((*AccountName)->Buffer != NULL) {
LsapFreeLsaHeap( (*AccountName)->Buffer );
}
LsapFreeLsaHeap( (*AccountName) );
(*AccountName) = NULL ;
}
if ((*AuthenticatingAuthority) != NULL) {
if ((*AuthenticatingAuthority)->Buffer != NULL) {
LsapFreeLsaHeap( (*AuthenticatingAuthority)->Buffer );
}
LsapFreeLsaHeap( (*AuthenticatingAuthority) );
(*AuthenticatingAuthority) = NULL ;
}
//
// Copy the submit buffer for the package to mark up
//
RtlCopyMemory(
LocalSubmitBuffer,
ProtocolSubmitBuffer,
SubmitBufferSize
);
SetCurrentPackageId(LogonPackages[Index]->dwPackageID);
Status = LogonPackages[Index]->FunctionTable.LogonUserEx2(
ClientRequest,
LogonType,
LocalSubmitBuffer,
ClientBufferBase,
SubmitBufferSize,
ProfileBuffer,
ProfileBufferLength,
NewLogonId,
ApiSubStatus,
TokenInformationType,
TokenInformation,
AccountName,
AuthenticatingAuthority,
MachineName,
PrimaryCredentials,
CachedCredentials
);
SetCurrentPackageId(CurrentPackageId);
//
// Bug 226401 If the local machine secret is different from the
// machine account password, kerberos can't decrpyt the workstation
// ticket and returns STATUS_TRUSTED_RELATIONSHIP_FAILURE (used to
// return STATUS_TRUST_FAILURE). If this is a DC, we
// should fall back to NTLM.
//
if (Status == STATUS_TRUST_FAILURE ||
Status == STATUS_TRUSTED_RELATIONSHIP_FAILURE)
{
if (NegMachineState & SECPKG_STATE_DOMAIN_CONTROLLER)
{
Status = STATUS_NO_LOGON_SERVERS;
}
}
if ((Status != STATUS_NO_LOGON_SERVERS) &&
(Status != STATUS_NETLOGON_NOT_STARTED) &&
(Status != SEC_E_NO_AUTHENTICATING_AUTHORITY) &&
(Status != SEC_E_ETYPE_NOT_SUPP) &&
(Status != STATUS_KDC_UNKNOWN_ETYPE) &&
(Status != STATUS_NO_TRUST_SAM_ACCOUNT) &&
(Status != STATUS_INVALID_PARAMETER) &&
(Status != STATUS_INVALID_LOGON_TYPE) &&
(Status != STATUS_INVALID_INFO_CLASS ) &&
(Status != STATUS_NETWORK_UNREACHABLE) &&
(Status != STATUS_BAD_VALIDATION_CLASS) )
{
break;
}
}
if ( NT_SUCCESS( Status ) )
{
LogonSession = NegpBuildLogonSession(
NewLogonId,
LogonPackages[ Index ]->dwPackageID,
LogonPackages[ Index ]->dwPackageID );
if ( LogonSession )
{
if ( LogonPackages[ Index ]->dwRPCID == NTLMSP_RPCID )
{
DebugLog(( DEB_TRACE_NEG, "NTLM Logon\n" ));
//
// Check if this is an uplevel or downlevel domain
//
DomainType = NegpIsUplevelDomain( NewLogonId, LogonType, *AuthenticatingAuthority );
if( DomainType == NegLocalDomain )
{
//
// change from Win2k:
// allow full negotiation range by default for local logons.
// Kerberos now quickly fails local account originated operations.
//
LogonSession->DefaultPackage = NegPackageId ;
}
if ( DomainType == NegUpLevelDomain )
{
if( (LogonType == CachedInteractive) &&
((PrimaryCredentials->Flags >> PRIMARY_CRED_LOGON_PACKAGE_SHIFT) == NTLMSP_RPCID)
)
{
//
// leave the package as NTLM.
//
} else {
LogonSession->DefaultPackage = NEG_INVALID_PACKAGE ;
}
}
else if ( DomainType == NegUpLevelTrustedDomain )
{
LogonSession->DefaultPackage = NegPackageId ;
}
else
{
//
// leave the DefaultPackage as NTLM.
//
NOTHING;
}
}
//
// Create the name:
//
AltName = (PWSTR) LsapAllocatePrivateHeap(
(*AccountName)->Length +
(*AuthenticatingAuthority)->Length +
2 * sizeof( WCHAR ) );
if ( AltName )
{
AltNameScan = AltName ;
RtlCopyMemory( AltNameScan,
(*AuthenticatingAuthority)->Buffer,
(*AuthenticatingAuthority)->Length );
AltNameScan += (*AuthenticatingAuthority)->Length / sizeof(WCHAR) ;
*AltNameScan++ = L'\\';
RtlCopyMemory( AltNameScan,
(*AccountName)->Buffer,
(*AccountName)->Length );
AltNameScan += (*AccountName)->Length / sizeof( WCHAR ) ;
*AltNameScan++ = L'\0';
RtlInitUnicodeString( &LogonSession->AlternateName, AltName );
}
NegpDerefLogonSession( LogonSession );
}
}
Cleanup:
if( LogonPackages )
{
LsapFreeLsaHeap(LogonPackages);
}
if( LocalSubmitBuffer )
{
ZeroMemory(LocalSubmitBuffer, SubmitBufferSize);
LsapFreeLsaHeap(LocalSubmitBuffer);
}
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: NegpMakeServiceToken
//
// Synopsis: Handles the logon for LocalService and NetworkService
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes: On error, this routine frees the buffers allocated by
// the prior call to NegpIsLocalOrNetworkService
//
//--------------------------------------------------------------------------
NTSTATUS
NegpMakeServiceToken(
IN ULONG ulAccountId,
OUT PLUID pLogonId,
OUT PVOID *ProfileBuffer,
OUT PULONG ProfileBufferLength,
OUT PNTSTATUS ApiSubStatus,
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 * CachedCredentials
)
{
NTSTATUS Status;
LPBYTE pBuffer;
ULONG ulTokenLength;
PSID Owner;
PSID UserSid;
ULONG DaclLength;
PACL pDacl;
DWORD dwSidLen;
HMODULE hStringsResource;
LUID SystemId = SYSTEM_LUID;
PTOKEN_GROUPS pGroups;
PSID_AND_ATTRIBUTES pSidAndAttrs;
ULONG NormalGroupAttributes;
TIME_FIELDS TimeFields;
SECPKG_CLIENT_INFO ClientInfo;
SID_IDENTIFIER_AUTHORITY IdentifierAuthority = SECURITY_NT_AUTHORITY;
PLSA_TOKEN_INFORMATION_V2 pTokenInfo = NULL;
PLSAP_LOGON_SESSION pLogonSession = NULL;
//
// Don't allow untrusted clients to call this API.
//
Status = LsapGetClientInfo(&ClientInfo);
if (!NT_SUCCESS(Status))
{
goto ErrorExit;
}
if (!RtlEqualLuid(&ClientInfo.LogonId, &SystemId))
{
Status = STATUS_ACCESS_DENIED;
goto ErrorExit;
}
if (ulAccountId == LSAP_SID_NAME_LOCALSERVICE)
{
LUID LocalServiceId = LOCALSERVICE_LUID;
UNICODE_STRING EmptyString = {0, 0, NULL};
UserSid = LsapLocalServiceSid;
PrimaryCredentials->LogonId = LocalServiceId;
*pLogonId = LocalServiceId;
Status = LsapDuplicateSid(&PrimaryCredentials->UserSid,
UserSid);
if (!NT_SUCCESS(Status))
{
goto ErrorExit;
}
Status = LsapDuplicateString(&PrimaryCredentials->DomainName,
&EmptyString);
if (!NT_SUCCESS(Status))
{
goto ErrorExit;
}
Status = LsapDuplicateString(&PrimaryCredentials->DownlevelName,
&EmptyString);
if (!NT_SUCCESS(Status))
{
goto ErrorExit;
}
Status = LsapDuplicateString(&PrimaryCredentials->Password,
&EmptyString);
if (!NT_SUCCESS(Status))
{
goto ErrorExit;
}
}
else
{
LUID NetworkServiceId = NETWORKSERVICE_LUID;
ASSERT( ulAccountId == LSAP_SID_NAME_NETWORKSERVICE );
UserSid = LsapNetworkServiceSid;
Status = NegpCopyCredsToBuffer(&NegPrimarySystemCredentials,
NULL,
PrimaryCredentials,
NULL);
if (!NT_SUCCESS(Status))
{
goto ErrorExit;
}
PrimaryCredentials->LogonId = NetworkServiceId;
*pLogonId = NetworkServiceId;
Status = LsapDuplicateSid(&PrimaryCredentials->UserSid,
UserSid);
if (!NT_SUCCESS(Status))
{
goto ErrorExit;
}
}
*CachedCredentials = NULL;
ASSERT(UserSid != NULL);
//
// Make sure there's a logon session for this account
//
pLogonSession = LsapLocateLogonSession(pLogonId);
if (pLogonSession == NULL)
{
Status = LsapCreateLogonSession(pLogonId);
pLogonSession = LsapLocateLogonSession(pLogonId);
if( pLogonSession == NULL )
{
if (!NT_SUCCESS(Status))
{
goto ErrorExit;
}
ASSERT(pLogonSession != NULL);
Status = STATUS_NO_SUCH_LOGON_SESSION;
goto ErrorExit;
}
Status = STATUS_SUCCESS;
}
//
// Compute the length of the default DACL for the token
//
DaclLength = (ULONG) sizeof(ACL)
+ 2 * ((ULONG) sizeof(ACCESS_ALLOWED_ACE) - sizeof(ULONG))
+ RtlLengthSid( LsapLocalSystemSid )
+ RtlLengthSid( UserSid );
#define NUM_TOKEN_GROUPS 4
//
// The SIDs are 4-byte aligned so this shouldn't cause problems
// with unaligned accesses (as long as the DACL stays at the
// end of the buffer).
//
ulTokenLength = sizeof(LSA_TOKEN_INFORMATION_V2)
+ RtlLengthSid( UserSid ) // User
+ sizeof(TOKEN_GROUPS)
+ (NUM_TOKEN_GROUPS - 1) * sizeof(SID_AND_ATTRIBUTES)
+ RtlLengthSid( UserSid ) // Primary group
+ RtlLengthSid( LsapWorldSid ) // Groups
+ RtlLengthSid( LsapAuthenticatedUserSid )
+ RtlLengthSid( LsapLocalSid )
+ RtlLengthSid( LsapAliasUsersSid )
+ RtlLengthSid( UserSid ) // Owner
+ DaclLength; // DefaultDacl
pTokenInfo = (PLSA_TOKEN_INFORMATION_V2) LsapAllocateLsaHeap(ulTokenLength);
if (pTokenInfo == NULL)
{
Status = STATUS_NO_MEMORY;
goto ErrorExit;
}
//
// Set up expiration time
//
TimeFields.Year = 3000;
TimeFields.Month = 1;
TimeFields.Day = 1;
TimeFields.Hour = 1;
TimeFields.Minute = 1;
TimeFields.Second = 1;
TimeFields.Milliseconds = 1;
TimeFields.Weekday = 1;
RtlTimeFieldsToTime( &TimeFields, &pTokenInfo->ExpirationTime );
//
// Set up the groups -- do this first so the nested pointers
// don't cause 64-bit alignment faults
//
pGroups = (PTOKEN_GROUPS) (pTokenInfo + 1);
pSidAndAttrs = pGroups->Groups;
pBuffer = (LPBYTE) (pSidAndAttrs + NUM_TOKEN_GROUPS);
pGroups->GroupCount = NUM_TOKEN_GROUPS;
#undef NUM_TOKEN_GROUPS
//
// Set up the attributes to be assigned to groups
//
NormalGroupAttributes = (SE_GROUP_MANDATORY |
SE_GROUP_ENABLED_BY_DEFAULT |
SE_GROUP_ENABLED);
dwSidLen = RtlLengthSid(LsapWorldSid);
RtlCopySid(dwSidLen, pBuffer, LsapWorldSid);
pSidAndAttrs[0].Sid = (PSID) pBuffer;
pBuffer += dwSidLen;
dwSidLen = RtlLengthSid(LsapAuthenticatedUserSid);
RtlCopySid(dwSidLen, pBuffer, LsapAuthenticatedUserSid);
pSidAndAttrs[1].Sid = (PSID) pBuffer;
pBuffer += dwSidLen;
dwSidLen = RtlLengthSid(LsapLocalSid);
RtlCopySid(dwSidLen, pBuffer, LsapLocalSid);
pSidAndAttrs[2].Sid = (PSID) pBuffer;
pBuffer += dwSidLen;
dwSidLen = RtlLengthSid(LsapAliasUsersSid);
RtlCopySid(dwSidLen, pBuffer, LsapAliasUsersSid);
pSidAndAttrs[3].Sid = (PSID) pBuffer;
pBuffer += dwSidLen;
pSidAndAttrs[0].Attributes = NormalGroupAttributes;
pSidAndAttrs[1].Attributes = NormalGroupAttributes;
pSidAndAttrs[2].Attributes = NormalGroupAttributes;
pSidAndAttrs[3].Attributes = NormalGroupAttributes;
pTokenInfo->Groups = pGroups;
//
// Set up the user ID
//
dwSidLen = RtlLengthSid(UserSid);
RtlCopySid(dwSidLen, (PSID) pBuffer, UserSid);
pTokenInfo->User.User.Sid = (PSID) pBuffer;
pTokenInfo->User.User.Attributes = 0;
pBuffer += dwSidLen;
//
// Establish the primary group
//
dwSidLen = RtlLengthSid(UserSid);
RtlCopySid(dwSidLen, (PSID) pBuffer, UserSid);
pTokenInfo->PrimaryGroup.PrimaryGroup = (PSID) pBuffer;
pBuffer += dwSidLen;
//
// Set the default owner
//
dwSidLen = RtlLengthSid(UserSid);
RtlCopySid(dwSidLen, (PSID) pBuffer, UserSid);
pTokenInfo->Owner.Owner = (PSID) pBuffer;
pBuffer += dwSidLen;
//
// Privileges -- none by default (set by policy)
//
pTokenInfo->Privileges = NULL;
//
// Set up the default DACL for token. Give system full reign of terror.
//
pDacl = (PACL) pBuffer;
Status = RtlCreateAcl(pDacl, DaclLength, ACL_REVISION2);
ASSERT( NT_SUCCESS(Status) );
Status = RtlAddAccessAllowedAce(pDacl,
ACL_REVISION2,
GENERIC_ALL,
LsapLocalSystemSid);
ASSERT( NT_SUCCESS(Status) );
Status = RtlAddAccessAllowedAce(pDacl,
ACL_REVISION2,
GENERIC_ALL,
UserSid);
ASSERT( NT_SUCCESS(Status) );
RtlCopyMemory(pBuffer, pDacl, DaclLength);
pTokenInfo->DefaultDacl.DefaultDacl = (PACL) pBuffer;
pBuffer += DaclLength;
ASSERT( (ULONG)(ULONG_PTR)(pBuffer - (LPBYTE) pTokenInfo) == ulTokenLength );
//
// Now fill in the out parameters
//
//
// LocalService/NetworkService have no profile
//
*ProfileBuffer = NULL;
*ProfileBufferLength = 0;
*TokenInformationType = LsaTokenInformationV2;
*TokenInformation = pTokenInfo;
LsapReleaseLogonSession(pLogonSession);
*ApiSubStatus = STATUS_SUCCESS;
return STATUS_SUCCESS;
ErrorExit:
if (*AuthenticatingAuthority)
{
LsapFreeLsaHeap((*AuthenticatingAuthority)->Buffer);
LsapFreeLsaHeap(*AuthenticatingAuthority);
*AuthenticatingAuthority = NULL;
}
if (*AccountName)
{
LsapFreeLsaHeap((*AccountName)->Buffer);
LsapFreeLsaHeap(*AccountName);
*AccountName = NULL;
}
if (*MachineName)
{
LsapFreeLsaHeap((*MachineName)->Buffer);
LsapFreeLsaHeap(*MachineName);
*MachineName = NULL;
}
LsapFreeLsaHeap(PrimaryCredentials->UserSid);
PrimaryCredentials->UserSid = NULL;
LsapFreeLsaHeap(PrimaryCredentials->DomainName.Buffer);
RtlZeroMemory(&PrimaryCredentials->DomainName, sizeof(UNICODE_STRING));
LsapFreeLsaHeap(PrimaryCredentials->DownlevelName.Buffer);
RtlZeroMemory(&PrimaryCredentials->DownlevelName, sizeof(UNICODE_STRING));
LsapFreeLsaHeap(PrimaryCredentials->Password.Buffer);
RtlZeroMemory(&PrimaryCredentials->Password, sizeof(UNICODE_STRING));
if (pLogonSession)
{
LsapReleaseLogonSession(pLogonSession);
}
LsapFreeLsaHeap(pTokenInfo);
*ApiSubStatus = Status;
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: NegpIsLocalOrNetworkService
//
// Synopsis: Allocates memory for the account name, machine name,
// and authenticating authority for LocalService and
// NetworkService logons
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes: The caller is responsible for freeing the allocated
// buffers if this routine succeeds
//
//--------------------------------------------------------------------------
BOOL
NegpIsLocalOrNetworkService(
IN PVOID ProtocolSubmitBuffer,
IN PVOID ClientBufferBase,
IN ULONG SubmitBufferSize,
OUT PUNICODE_STRING *AccountName,
OUT PUNICODE_STRING *AuthenticatingAuthority,
OUT PUNICODE_STRING *MachineName,
OUT PULONG pulAccountId
)
{
NTSTATUS Status;
PUNICODE_STRING pTempAccount;
PUNICODE_STRING pTempAuthority;
PMSV1_0_INTERACTIVE_LOGON Authentication = NULL;
*AccountName = NULL;
*AuthenticatingAuthority = NULL;
*MachineName = NULL;
//
// Parse the supplied buffer to come up with creds
//
Status = NegpMapLogonRequest(
ProtocolSubmitBuffer,
ClientBufferBase,
SubmitBufferSize,
&Authentication);
if (!NT_SUCCESS(Status))
{
goto ErrorExit ;
}
//
// Get the info to compare for LocalService first. Check both the
// localized version and the non-localized version (which could come
// from the registry).
//
pTempAuthority = &WellKnownSids[LsapLocalServiceSidIndex].DomainName;
pTempAccount = &WellKnownSids[LsapLocalServiceSidIndex].Name;
if (RtlCompareUnicodeString(&NTAuthorityName,
&Authentication->LogonDomainName,
TRUE) == 0)
{
//
// Hardcoded "NT AUTHORITY" -- check hardcoded
// "LocalService" and "NetworkService" and
// localize it here on a match.
//
if (RtlCompareUnicodeString(&LocalServiceName,
&Authentication->UserName,
TRUE) == 0)
{
pTempAccount = &WellKnownSids[LsapLocalServiceSidIndex].Name;
*pulAccountId = LSAP_SID_NAME_LOCALSERVICE;
Status = STATUS_SUCCESS;
}
else if (RtlCompareUnicodeString(&NetworkServiceName,
&Authentication->UserName,
TRUE) == 0)
{
pTempAccount = &WellKnownSids[LsapNetworkServiceSidIndex].Name;
*pulAccountId = LSAP_SID_NAME_NETWORKSERVICE;
Status = STATUS_SUCCESS;
}
else
{
Status = STATUS_NO_SUCH_USER;
}
}
else
{
Status = STATUS_NO_SUCH_USER;
}
//
// Hardcoded comparison failed -- try localized versions. Note that
// we have to check again (rather than using an "else" with the "if"
// above) since "NT AUTHORITY" may be both the hardcoded and the
// localized name (e.g., in English). Since Status is already a
// failure code, let the failure cases trickle through.
//
if (!NT_SUCCESS(Status))
{
if (RtlCompareUnicodeString(pTempAuthority,
&Authentication->LogonDomainName,
TRUE) == 0)
{
//
// Localized "NT AUTHORITY" -- check localized
// "LocalService" and "NetworkService"
//
pTempAccount = &WellKnownSids[LsapLocalServiceSidIndex].Name;
if (RtlCompareUnicodeString(pTempAccount,
&Authentication->UserName,
TRUE) == 0)
{
*pulAccountId = LSAP_SID_NAME_LOCALSERVICE;
Status = STATUS_SUCCESS;
}
else
{
pTempAccount = &WellKnownSids[LsapNetworkServiceSidIndex].Name;
if (RtlCompareUnicodeString(pTempAccount,
&Authentication->UserName,
TRUE) == 0)
{
*pulAccountId = LSAP_SID_NAME_NETWORKSERVICE;
Status = STATUS_SUCCESS;
}
else
{
Status = STATUS_NO_SUCH_USER;
}
}
}
else
{
Status = STATUS_NO_SUCH_USER;
}
}
if (!NT_SUCCESS(Status))
{
goto ErrorExit;
}
//
// Stuff the names:
//
*AccountName = (PUNICODE_STRING) LsapAllocateLsaHeap(sizeof(UNICODE_STRING));
if (!(*AccountName))
{
Status = STATUS_NO_MEMORY;
goto ErrorExit;
}
Status = LsapDuplicateString(*AccountName,
pTempAccount);
if (!NT_SUCCESS(Status))
{
goto ErrorExit;
}
*AuthenticatingAuthority = (PUNICODE_STRING) LsapAllocateLsaHeap(sizeof(UNICODE_STRING));
if (!(*AuthenticatingAuthority))
{
Status = STATUS_NO_MEMORY;
goto ErrorExit;
}
Status = LsapDuplicateString(*AuthenticatingAuthority,
pTempAuthority);
if (!NT_SUCCESS(Status))
{
goto ErrorExit;
}
*MachineName = (PUNICODE_STRING) LsapAllocateLsaHeap(sizeof(UNICODE_STRING));
if (!(*MachineName))
{
Status = STATUS_NO_MEMORY;
goto ErrorExit ;
}
return TRUE;
ErrorExit:
if (*AuthenticatingAuthority)
{
LsapFreeLsaHeap((*AuthenticatingAuthority)->Buffer);
LsapFreeLsaHeap(*AuthenticatingAuthority);
*AuthenticatingAuthority = NULL;
}
if (*AccountName)
{
LsapFreeLsaHeap((*AccountName)->Buffer);
LsapFreeLsaHeap(*AccountName);
*AccountName = NULL;
}
if (*MachineName)
{
LsapFreeLsaHeap((*MachineName)->Buffer);
LsapFreeLsaHeap(*MachineName);
*MachineName = NULL;
}
return FALSE;
}
BOOL
NegpRearrangeMechsIfNeccessary(
struct MechTypeList ** RealMechList,
PSECURITY_STRING Target,
PBOOL DirectPacket
)
{
PWSTR FirstSlash ;
PWSTR SecondSlash = NULL;
PNEG_TRUST_LIST TrustList = NULL ;
UNICODE_STRING TargetHost ;
UNICODE_STRING SpnPrefix;
UNICODE_STRING HttpPrefix;
struct MechTypeList * MechList ;
struct MechTypeList * Reorder = NULL ;
struct MechTypeList * Trailer ;
BOOL Rearranged = FALSE ;
BOOL HttpSpn = FALSE;
if ( Target == NULL )
{
DebugLog(( DEB_TRACE_NEG, "Loopback detected for local target (no targetname)\n"));
goto loopback;
}
if ( Target->Length == 0 )
{
DebugLog(( DEB_TRACE_NEG, "Loopback detected for local target (no targetname)\n"));
goto loopback;
}
//
// Now, examine the target and determine if it is referring to us
//
FirstSlash = wcschr( Target->Buffer, L'/' );
if ( !FirstSlash )
{
//
// Not an SPN, we're out of here
//
return FALSE ;
} else {
*FirstSlash = L'\0';
}
//
// HACK HACK HACK: We've got to call NTLM directly in
// the loopback case, or Wininet can't handle our "extra" nego trips....
//
RtlInitUnicodeString(
&SpnPrefix,
Target->Buffer
);
RtlInitUnicodeString(
&HttpPrefix,
L"HTTP"
);
if (RtlEqualUnicodeString(
&SpnPrefix,
&HttpPrefix,
TRUE
))
{
HttpSpn = TRUE;
DebugLog((DEB_TRACE_NEG, "Found HTTP SPN, about to force NTLM directly\n"));
}
*FirstSlash = L'/';
// END HACK END HACK
FirstSlash++ ;
//
// if this is a svc/instance/domain style SPN, ignore the trailer
// portion
//
SecondSlash = wcschr( Target->Buffer, L'/' );
if ( SecondSlash )
{
*SecondSlash = L'\0';
}
RtlInitUnicodeString( &TargetHost, FirstSlash );
NegReadLockComputerNames();
if (!RtlEqualUnicodeString( &TargetHost, &NegDnsComputerName_U, TRUE ) &&
!RtlEqualUnicodeString( &TargetHost, &NegNetbiosComputerName_U, TRUE ) &&
!RtlEqualUnicodeString( &TargetHost, &NegLocalHostName_U, TRUE ) )
{
NegUnlockComputerNames();
goto Cleanup ;
}
NegUnlockComputerNames();
//
// We have a loopback condition. The target we are going to is our own host
// name. So, scan through the mech list, and find the NTLM mech (if present)
// and bump it up.
//
#if DBG
if ( SecondSlash )
{
//
// for debug builds, reset the string now for the dump message, so that
// we can make sure the right targets are being caught. Free builds will
// reset this at the cleanup stage.
//
*SecondSlash = L'/';
SecondSlash = NULL ;
}
#endif
DebugLog(( DEB_TRACE_NEG, "Loopback detected for target %ws\n", Target->Buffer ));
loopback:
MechList = *RealMechList ;
Trailer = NULL ;
while ( MechList )
{
if ( (MechList->value != NULL) &&
(NegpCompareOid( MechList->value, NegNtlmMechOid ) == 0)
)
{
//
// Found NTLM. Unlink it and put it at the head of the new list
//
Reorder = MechList ;
if ( Trailer )
{
Trailer->next = MechList->next ;
}
else
{
// update original pointer
*RealMechList = MechList->next ;
}
MechList = MechList->next ;
Reorder->next = NULL ;
}
else
{
Trailer = MechList ;
MechList = MechList->next ;
}
}
//
// Reorder points to the NTLM element, if there are NTLM creds. If not, this is NULL.
// Now, append the rest of the list
//
if ( Reorder )
{
Reorder->next = *RealMechList ;
*RealMechList = Reorder ;
Rearranged = TRUE ;
}
// Only set this if everything else went well
// HACK PART 2
*DirectPacket = HttpSpn;
Cleanup:
if ( SecondSlash )
{
*SecondSlash = L'/';
}
return Rearranged ;
}