3272 lines
81 KiB
C++
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 ;
|
|
|
|
}
|
|
|
|
|
|
|