//+--------------------------------------------------------------------------- // // 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 #ifdef WIN32_CHICAGO #include #endif // WIN32_CHICAGO extern "C" { #include #include #include #include #include #include #include "sesmgr.h" #include "spinit.h" } #include "negotiat.hxx" #include 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; iMaximumLength > 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 ; }