/*++ Copyright (c) 1996 Microsoft Corporation Module Name: logon32.cxx Abstract: Provide a replacement for LogonUser to login a user as a net logon. Also support sub-authentication DLL IDs Author: Philippe Choquier (phillich) 10-january-1996 Created from base\advapi\logon32.c --*/ #include "lonsint.hxx" #pragma hdrstop extern "C" { #include #include #include #include #include #include #include #define SECURITY_WIN32 #include // Security Support Provider APIs #include } #include #include #include #include #if !defined(MSV1_0_RETURN_PASSWORD_EXPIRY) #define MSV1_0_RETURN_PASSWORD_EXPIRY 0x40 #endif // // We dynamically load mpr.dll (no big surprise there), in order to call // WNetLogonNotify, as defined in private\inc\mpr.h. This prototype matches // it -- consult the header file for all the parameters. // typedef (* LOGONNOTIFYFN)(LPCWSTR, PLUID, LPCWSTR, LPVOID, LPCWSTR, LPVOID, LPWSTR, LPVOID, LPWSTR *); #define LEN_ALIGN(a,b) (((a)+b-1)&~(b-1)) ULONG BaseSetLastNTError( IN NTSTATUS Status ) /*++ Routine Description: This API sets the "last error value" and the "last error string" based on the value of Status. For status codes that don't have a corresponding error string, the string is set to null. Arguments: Status - Supplies the status value to store as the last error value. Return Value: The corresponding Win32 error code that was stored in the "last error value" thread variable. --*/ { ULONG dwErrorCode; dwErrorCode = RtlNtStatusToDosError( Status ); SetLastError( dwErrorCode ); return( dwErrorCode ); } // // The QuotaLimits are global, because the defaults // are always used for accounts, based on server/wksta, and no one ever // calls lsasetaccountquota // HANDLE Logon32LsaHandle = NULL; ULONG Logon32MsvHandle = 0xFFFFFFFF; ULONG Logon32KerberosHandle = 0xFFFFFFFF; WCHAR Logon32DomainName[16] = L""; // NOTE: This should be DNLEN from // lmcons.h, but that would be a // lot of including QUOTA_LIMITS Logon32QuotaLimits; LOGONNOTIFYFN Logon32LogonNotify = NULL; HINSTANCE Logon32MprHandle = NULL; CRITICAL_SECTION Logon32Lock; BOOL fLsaInitialized = FALSE; #define LockLogon() EnterCriticalSection( &Logon32Lock ) #define UnlockLogon() LeaveCriticalSection( &Logon32Lock ) SID_IDENTIFIER_AUTHORITY L32SystemSidAuthority = SECURITY_NT_AUTHORITY; SID_IDENTIFIER_AUTHORITY L32LocalSidAuthority = SECURITY_LOCAL_SID_AUTHORITY; #define COMMON_CREATE_SUSPENDED 0x00000001 // Suspended, do not Resume() #define COMMON_CREATE_PROCESSSD 0x00000002 // Whack the process SD #define COMMON_CREATE_THREADSD 0x00000004 // Whack the thread SD BOOL IISLogon32Initialize( IN PVOID hMod, IN ULONG Reason, IN PCONTEXT Context) /*++ Routine Description: Initializes the critical section Arguments: hMod -- reserved, must be NULL Reason -- DLL_PROCESS_ATTACH or DLL_PROCESS_DETACH Context -- reserved, must be NULL Returns: TRUE if initialization success, else FALSE --*/ { return( TRUE ); } PSID L32CreateLogonSid( PLUID LogonId OPTIONAL ) /*++ Routine Description: Creates a logon sid for a new logon. Arguments: LogonId -- If non NULL, on return the LUID that is part of the logon sid is returned here. Returns: Logon SID or NULL if error --*/ { NTSTATUS Status; ULONG Length; PSID Sid; LUID Luid; // // Generate a locally unique id to include in the logon sid // Status = NtAllocateLocallyUniqueId(&Luid); if (!NT_SUCCESS(Status)) { return(NULL); } // // Allocate space for the sid and fill it in. // Length = RtlLengthRequiredSid(SECURITY_LOGON_IDS_RID_COUNT); Sid = (PSID)LocalAlloc(LMEM_FIXED, Length); if (Sid != NULL) { RtlInitializeSid(Sid, &L32SystemSidAuthority, SECURITY_LOGON_IDS_RID_COUNT); ASSERT(SECURITY_LOGON_IDS_RID_COUNT == 3); *(RtlSubAuthoritySid(Sid, 0)) = SECURITY_LOGON_IDS_RID; *(RtlSubAuthoritySid(Sid, 1 )) = Luid.HighPart; *(RtlSubAuthoritySid(Sid, 2 )) = Luid.LowPart; } // // Return the logon LUID if required. // if (LogonId != NULL) { *LogonId = Luid; } return(Sid); } BOOL L32pInitLsa( void ) /*++ Routine Description: Initialize connection with LSA Arguments: None Returns: TRUE if success, FALSE if error --*/ { char MyName[MAX_PATH]; char * ModuleName; STRING LogonProcessName; STRING PackageName; ULONG dummy; NTSTATUS Status; BOOLEAN WasEnabled; Status = RtlAdjustPrivilege(SE_TCB_PRIVILEGE, TRUE, FALSE, &WasEnabled); if (!NT_SUCCESS(Status)) { BaseSetLastNTError(Status); return(FALSE); } if (GetModuleFileNameA(NULL, MyName, MAX_PATH)) { ModuleName = strrchr(MyName, '\\'); if (!ModuleName) { ModuleName = MyName; } } else { BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND); return(FALSE); } // // Hookup to the LSA and locate our authentication package. // RtlInitString(&LogonProcessName, ModuleName); Status = LsaRegisterLogonProcess( &LogonProcessName, &Logon32LsaHandle, &dummy ); // // Turn off the privilege now. // if (!WasEnabled) { (VOID) RtlAdjustPrivilege(SE_TCB_PRIVILEGE, FALSE, FALSE, &WasEnabled); } if (!NT_SUCCESS(Status)) { BaseSetLastNTError(Status); return(FALSE); } // // Connect with the MSV1_0 authentication package // RtlInitString(&PackageName, "MICROSOFT_AUTHENTICATION_PACKAGE_V1_0"); Status = LsaLookupAuthenticationPackage ( Logon32LsaHandle, &PackageName, &Logon32MsvHandle ); if (!NT_SUCCESS(Status)) { BaseSetLastNTError(Status); (VOID) LsaDeregisterLogonProcess( Logon32LsaHandle ); Logon32LsaHandle = NULL; return(FALSE); } // // Get the kerberos package // RtlInitString(&PackageName, MICROSOFT_KERBEROS_NAME_A); Status = LsaLookupAuthenticationPackage ( Logon32LsaHandle, &PackageName, &Logon32KerberosHandle ); if (!NT_SUCCESS(Status)) { BaseSetLastNTError(Status); (VOID) LsaDeregisterLogonProcess( Logon32LsaHandle ); Logon32LsaHandle = NULL; Logon32MsvHandle = 0xFFFFFFFF; return(FALSE); } // // We are now initialized (duh) // fLsaInitialized = TRUE; return(TRUE); } //+--------------------------------------------------------------------------- // // Function: L32pNotifyMpr // // Synopsis: Loads the MPR DLL and notifies the network providers (like // csnw) so they know about this logon session and the credentials // // Arguments: [NewLogon] -- New logon information // [LogonId] -- Logon ID // // History: 4-24-95 RichardW Created // // Notes: // //---------------------------------------------------------------------------- BOOL L32pNotifyMpr( PMSV1_0_INTERACTIVE_LOGON NewLogon, PLUID LogonId ) { MSV1_0_INTERACTIVE_LOGON OldLogon; LPWSTR LogonScripts; DWORD status; if ( Logon32MprHandle == NULL ) { LockLogon(); if ( Logon32MprHandle == NULL) { Logon32MprHandle = LoadLibrary("mpr.dll"); if (Logon32MprHandle != NULL) { Logon32LogonNotify = (LOGONNOTIFYFN) GetProcAddress( Logon32MprHandle, "WNetLogonNotify"); } } UnlockLogon(); } if ( Logon32LogonNotify != NULL ) { CopyMemory(&OldLogon, NewLogon, sizeof(OldLogon)); status = Logon32LogonNotify( L"Windows NT Network Provider", LogonId, L"MSV1_0:Interactive", (LPVOID)NewLogon, L"MSV1_0:Interactive", (LPVOID)&OldLogon, L"SvcCtl", // StationName NULL, // StationHandle &LogonScripts); // LogonScripts if (status == NO_ERROR) { if (LogonScripts != NULL ) { (void) LocalFree(LogonScripts); } } return( TRUE ); } return( FALSE ); } NTSTATUS L32pLogonNetUser( IN HANDLE LsaHandle, IN ULONG AuthenticationPackage, IN SECURITY_LOGON_TYPE LogonType, IN PUNICODE_STRING UserName, IN PUNICODE_STRING Domain, IN PSTRING Password, IN PUNICODE_STRING Workstation, IN DWORD dwSubAuth, IN PSID LogonSid, OUT PLUID LogonId, OUT PHANDLE LogonToken, OUT PQUOTA_LIMITS Quotas, OUT PVOID *pProfileBuffer, OUT PULONG pProfileBufferLength, OUT PNTSTATUS pSubStatus ) /*++ Routine Description: Wraps up the call to LsaLogonUser Arguments: LsaHandle -- handle to LSA package AuthenticationPackage -- ID of authentication package to use LogonType -- Interactive, network, ... UserName -- User Name Domain -- Domain validating the user name Password -- clear text password, can be empty if a sub-auth package is used Workstation -- workstation where the login take place, can be NULL if local login dwSubAuth -- Sub-authentication DLL ID LogonSid -- Logon SID for this session LogonId -- created logon ID LogonToken -- created logon token Quotas -- quota info pProfileBuffer -- account profile pProfileBufferLength -- account profile length pSubStatus -- substatus for authentication failure Returns: 0 if success, else NT status --*/ { NTSTATUS Status; STRING OriginName; TOKEN_SOURCE SourceContext; PMSV1_0_LM20_LOGON MsvAuthInfo; PMSV1_0_LM20_LOGON MsvNetAuthInfo; PMSV1_0_INTERACTIVE_LOGON MsvInterAuthInfo; PMSV1_0_SUBAUTH_LOGON MsvSubAuthInfo; PVOID AuthInfoBuf; ULONG AuthInfoSize; PTOKEN_GROUPS TokenGroups; PSID LocalSid; UNICODE_STRING UnicodePassword; //WCHAR ComputerName[ MAX_COMPUTERNAME_LENGTH + 1 ]; DWORD ComputerNameLength; NT_RESPONSE NtResponse; LM_RESPONSE LmResponse; union { LUID Luid; NT_CHALLENGE NtChallenge; } Challenge; NT_OWF_PASSWORD PasswordHash; OEM_STRING LmPassword; UCHAR LmPasswordBuf[ LM20_PWLEN + 1 ]; LM_OWF_PASSWORD LmPasswordHash; #if DBG if (!RtlValidSid(LogonSid)) { return(STATUS_INVALID_PARAMETER); } #endif // // Initialize source context structure // strncpy(SourceContext.SourceName, "IIS ", sizeof(SourceContext.SourceName)); // LATER from res file Status = NtAllocateLocallyUniqueId(&SourceContext.SourceIdentifier); if (!NT_SUCCESS(Status)) { return(Status); } UnicodePassword.Buffer = NULL; // // Set logon origin // RtlInitString(&OriginName, "IIS security API"); // // For network logons, do the magic. // if ( LogonType == Network ) { #if 0 ComputerNameLength = MAX_COMPUTERNAME_LENGTH + 1; if (!GetComputerNameW( ComputerName, &ComputerNameLength ) ) { return( STATUS_INVALID_PARAMETER ); } #else ComputerNameLength = wcslen( Workstation->Buffer ); #endif if (!RtlCreateUnicodeStringFromAsciiz( &UnicodePassword, Password->Buffer )) { return STATUS_NO_MEMORY; } AuthInfoSize = sizeof( MSV1_0_LM20_LOGON ) + sizeof( WCHAR ) * ( wcslen( UserName->Buffer ) + 1 + wcslen( Domain->Buffer ) + 1 + ComputerNameLength + 1) + NT_RESPONSE_LENGTH + LM_RESPONSE_LENGTH ; MsvNetAuthInfo = (PMSV1_0_LM20_LOGON) (AuthInfoBuf = RtlAllocateHeap( RtlProcessHeap(), HEAP_ZERO_MEMORY, AuthInfoSize )); if ( !MsvNetAuthInfo ) { return( STATUS_NO_MEMORY ); } // // Start packing in the string // MsvNetAuthInfo->MessageType = MsV1_0NetworkLogon; // // Copy the user name into the authentication buffer // MsvNetAuthInfo->UserName.Length = (USHORT)sizeof(WCHAR)*wcslen(UserName->Buffer); MsvNetAuthInfo->UserName.MaximumLength = MsvNetAuthInfo->UserName.Length + sizeof(WCHAR); MsvNetAuthInfo->UserName.Buffer = (PWSTR)(MsvNetAuthInfo+1); wcscpy(MsvNetAuthInfo->UserName.Buffer, UserName->Buffer); // // Copy the domain name into the authentication buffer // MsvNetAuthInfo->LogonDomainName.Length = (USHORT)sizeof(WCHAR)*wcslen(Domain->Buffer); MsvNetAuthInfo->LogonDomainName.MaximumLength = MsvNetAuthInfo->LogonDomainName.Length + sizeof(WCHAR); MsvNetAuthInfo->LogonDomainName.Buffer = (PWSTR) ((PBYTE)(MsvNetAuthInfo->UserName.Buffer) + MsvNetAuthInfo->UserName.MaximumLength); wcscpy(MsvNetAuthInfo->LogonDomainName.Buffer, Domain->Buffer); // // Copy the workstation name into the buffer // MsvNetAuthInfo->Workstation.Length = (USHORT) (sizeof(WCHAR) * ComputerNameLength); MsvNetAuthInfo->Workstation.MaximumLength = MsvNetAuthInfo->Workstation.Length + sizeof(WCHAR); MsvNetAuthInfo->Workstation.Buffer = (PWSTR) ((PBYTE) (MsvNetAuthInfo->LogonDomainName.Buffer) + MsvNetAuthInfo->LogonDomainName.MaximumLength ); wcscpy( MsvNetAuthInfo->Workstation.Buffer, Workstation->Buffer ); // // Now, generate the bits for the challenge // Status = NtAllocateLocallyUniqueId( &Challenge.Luid ); if ( !NT_SUCCESS(Status) ) { RtlFreeHeap( RtlProcessHeap(), 0, MsvNetAuthInfo ); return( Status ); } RtlCopyMemory( MsvNetAuthInfo->ChallengeToClient, & Challenge, MSV1_0_CHALLENGE_LENGTH ); // // Set up space for response // MsvNetAuthInfo->CaseSensitiveChallengeResponse.Buffer = (PCHAR) ((PBYTE) (MsvNetAuthInfo->Workstation.Buffer) + MsvNetAuthInfo->Workstation.MaximumLength ); MsvNetAuthInfo->CaseSensitiveChallengeResponse.Length = NT_RESPONSE_LENGTH; MsvNetAuthInfo->CaseSensitiveChallengeResponse.MaximumLength = NT_RESPONSE_LENGTH; RtlCalculateNtOwfPassword( & UnicodePassword, & PasswordHash ); RtlCalculateNtResponse( & Challenge.NtChallenge, & PasswordHash, & NtResponse ); RtlCopyMemory( MsvNetAuthInfo->CaseSensitiveChallengeResponse.Buffer, & NtResponse, NT_RESPONSE_LENGTH ); // // Now do the painful LM compatible hash, so anyone who is maintaining // their account from a WfW machine will still have a password. // LmPassword.Buffer = (CHAR*)LmPasswordBuf; LmPassword.Length = LmPassword.MaximumLength = LM20_PWLEN + 1; Status = RtlUpcaseUnicodeStringToOemString( & LmPassword, & UnicodePassword, FALSE ); if ( NT_SUCCESS(Status) ) { MsvNetAuthInfo->CaseInsensitiveChallengeResponse.Buffer = (PCHAR) ((PBYTE) (MsvNetAuthInfo->CaseSensitiveChallengeResponse.Buffer) + MsvNetAuthInfo->CaseSensitiveChallengeResponse.MaximumLength ); MsvNetAuthInfo->CaseInsensitiveChallengeResponse.Length = LM_RESPONSE_LENGTH; MsvNetAuthInfo->CaseInsensitiveChallengeResponse.MaximumLength = LM_RESPONSE_LENGTH; RtlCalculateLmOwfPassword( LmPassword.Buffer, & LmPasswordHash ); ZeroMemory( LmPassword.Buffer, LmPassword.Length ); RtlCalculateLmResponse( & Challenge.NtChallenge, & LmPasswordHash, & LmResponse ); RtlCopyMemory( MsvNetAuthInfo->CaseInsensitiveChallengeResponse.Buffer, & LmResponse, LM_RESPONSE_LENGTH ); } else { // // If we're here, the NT (supplied) password is longer than the // limit allowed for LM passwords. NULL out the field, so that // MSV knows not to worry about it. // RtlZeroMemory( &MsvNetAuthInfo->CaseInsensitiveChallengeResponse, sizeof( STRING ) ); } MsvNetAuthInfo->ParameterControl = MSV1_0_RETURN_PASSWORD_EXPIRY; } else if ( LogonType == (SECURITY_LOGON_TYPE)IIS_Network ) { if (!RtlCreateUnicodeStringFromAsciiz( &UnicodePassword, Password->Buffer )) { return STATUS_NO_MEMORY; } // // Build logon structure for IIS network logons. We'll be using the subauth DLL // in this case // AuthInfoSize = sizeof(MSV1_0_SUBAUTH_LOGON) + sizeof(WCHAR)*(wcslen(UserName->Buffer) + 1 + wcslen(Domain->Buffer) + 1 + wcslen(Workstation->Buffer) + 1 ) + sizeof(WCHAR)*wcslen(UnicodePassword.Buffer) + LEN_ALIGN(strlen(Password->Buffer),sizeof(WCHAR)); AuthInfoBuf = RtlAllocateHeap( RtlProcessHeap(), HEAP_ZERO_MEMORY, AuthInfoSize); MsvSubAuthInfo = (PMSV1_0_SUBAUTH_LOGON)AuthInfoBuf; if (MsvSubAuthInfo == NULL) { return(STATUS_NO_MEMORY); } // // This authentication buffer will be used for a logon attempt // MsvSubAuthInfo->MessageType = MsV1_0SubAuthLogon; MsvSubAuthInfo->SubAuthPackageId = dwSubAuth; // // Copy the domain name into the authentication buffer // MsvSubAuthInfo->LogonDomainName.Length = (USHORT)sizeof(WCHAR)*wcslen(Domain->Buffer); MsvSubAuthInfo->LogonDomainName.MaximumLength = MsvSubAuthInfo->LogonDomainName.Length + sizeof(WCHAR); MsvSubAuthInfo->LogonDomainName.Buffer = (PWSTR)(MsvSubAuthInfo+1); wcscpy(MsvSubAuthInfo->LogonDomainName.Buffer, Domain->Buffer); // // Copy the user name into the authentication buffer // MsvSubAuthInfo->UserName.Length = (USHORT)sizeof(WCHAR)*wcslen(UserName->Buffer); MsvSubAuthInfo->UserName.MaximumLength = MsvSubAuthInfo->UserName.Length + sizeof(WCHAR); MsvSubAuthInfo->UserName.Buffer = (PWSTR) ((PBYTE)(MsvSubAuthInfo->LogonDomainName.Buffer) + MsvSubAuthInfo->LogonDomainName.MaximumLength); wcscpy(MsvSubAuthInfo->UserName.Buffer, UserName->Buffer); // // Copy the workstation // MsvSubAuthInfo->Workstation.Length = (USHORT)sizeof(WCHAR)*wcslen(Workstation->Buffer); MsvSubAuthInfo->Workstation.MaximumLength = MsvSubAuthInfo->Workstation.Length + sizeof(WCHAR); MsvSubAuthInfo->Workstation.Buffer = (PWSTR) ((PBYTE)(MsvSubAuthInfo->UserName.Buffer) + MsvSubAuthInfo->UserName.MaximumLength); wcscpy(MsvSubAuthInfo->Workstation.Buffer, Workstation->Buffer); memset( MsvSubAuthInfo->ChallengeToClient, '\0', sizeof(MsvSubAuthInfo->ChallengeToClient) ); MsvSubAuthInfo->AuthenticationInfo1.Buffer = ((PCHAR)(MsvSubAuthInfo->Workstation.Buffer) + MsvSubAuthInfo->Workstation.MaximumLength); MsvSubAuthInfo->AuthenticationInfo1.Length = (USHORT)sizeof(WCHAR)*wcslen(UnicodePassword.Buffer); MsvSubAuthInfo->AuthenticationInfo1.MaximumLength = MsvSubAuthInfo->AuthenticationInfo1.Length; memcpy( MsvSubAuthInfo->AuthenticationInfo1.Buffer, UnicodePassword.Buffer, MsvSubAuthInfo->AuthenticationInfo1.Length ); MsvSubAuthInfo->AuthenticationInfo2.Buffer = ((PCHAR)(MsvSubAuthInfo->AuthenticationInfo1.Buffer) + MsvSubAuthInfo->AuthenticationInfo1.MaximumLength); MsvSubAuthInfo->AuthenticationInfo2.Length = (USHORT)strlen(Password->Buffer); MsvSubAuthInfo->AuthenticationInfo2.MaximumLength = LEN_ALIGN(MsvSubAuthInfo->AuthenticationInfo2.Length,sizeof(WCHAR)); memcpy( MsvSubAuthInfo->AuthenticationInfo2.Buffer, Password->Buffer, MsvSubAuthInfo->AuthenticationInfo2.Length ); MsvSubAuthInfo->ParameterControl = (dwSubAuth << MSV1_0_SUBAUTHENTICATION_DLL_SHIFT) | MSV1_0_UPDATE_LOGON_STATISTICS | MSV1_0_DONT_TRY_GUEST_ACCOUNT | MSV1_0_CLEARTEXT_PASSWORD_ALLOWED | MSV1_0_RETURN_PASSWORD_EXPIRY | MSV1_0_SUBAUTHENTICATION_DLL_EX | MSV1_0_DISABLE_PERSONAL_FALLBACK ; LogonType = Network; } else { // // Build logon structure for non-network logons - service, // batch, interactive // if (!RtlCreateUnicodeStringFromAsciiz( &UnicodePassword, Password->Buffer )) { return STATUS_NO_MEMORY; } AuthInfoSize = sizeof(MSV1_0_INTERACTIVE_LOGON) + sizeof(WCHAR)*(wcslen(UserName->Buffer) + 1 + wcslen(Domain->Buffer) + 1 + wcslen(UnicodePassword.Buffer) + 1 ); AuthInfoBuf = RtlAllocateHeap( RtlProcessHeap(), HEAP_ZERO_MEMORY, AuthInfoSize); MsvInterAuthInfo = (PMSV1_0_INTERACTIVE_LOGON)AuthInfoBuf; if (MsvInterAuthInfo == NULL) { return STATUS_NO_MEMORY; } // // This authentication buffer will be used for a logon attempt // MsvInterAuthInfo->MessageType = MsV1_0InteractiveLogon; // // Copy the user name into the authentication buffer // MsvInterAuthInfo->UserName.Length = (USHORT)sizeof(WCHAR)*wcslen(UserName->Buffer); MsvInterAuthInfo->UserName.MaximumLength = MsvInterAuthInfo->UserName.Length + sizeof(WCHAR); MsvInterAuthInfo->UserName.Buffer = (PWSTR)(MsvInterAuthInfo+1); wcscpy(MsvInterAuthInfo->UserName.Buffer, UserName->Buffer); // // Copy the domain name into the authentication buffer // MsvInterAuthInfo->LogonDomainName.Length = (USHORT)sizeof(WCHAR)*wcslen(Domain->Buffer); MsvInterAuthInfo->LogonDomainName.MaximumLength = MsvInterAuthInfo->LogonDomainName.Length + sizeof(WCHAR); MsvInterAuthInfo->LogonDomainName.Buffer = (PWSTR) ((PBYTE)(MsvInterAuthInfo->UserName.Buffer) + MsvInterAuthInfo->UserName.MaximumLength); wcscpy(MsvInterAuthInfo->LogonDomainName.Buffer, Domain->Buffer); // // Copy the password into the authentication buffer // Hide it once we have copied it. Use the same seed value // that we used for the original password in pGlobals. // MsvInterAuthInfo->Password.Length = (USHORT)sizeof(WCHAR)*wcslen(UnicodePassword.Buffer); MsvInterAuthInfo->Password.MaximumLength = MsvInterAuthInfo->Password.Length + sizeof(WCHAR); MsvInterAuthInfo->Password.Buffer = (PWSTR) ((PBYTE)(MsvInterAuthInfo->LogonDomainName.Buffer) + MsvInterAuthInfo->LogonDomainName.MaximumLength); wcscpy(MsvInterAuthInfo->Password.Buffer, UnicodePassword.Buffer); } // // Create logon token groups // #define TOKEN_GROUP_COUNT 2 // We'll add the local SID and the logon SID TokenGroups = (PTOKEN_GROUPS) RtlAllocateHeap(RtlProcessHeap(), 0, sizeof(TOKEN_GROUPS) + (TOKEN_GROUP_COUNT - ANYSIZE_ARRAY) * sizeof(SID_AND_ATTRIBUTES)); if (TokenGroups == NULL) { RtlFreeHeap(RtlProcessHeap(), 0, AuthInfoBuf); return(STATUS_NO_MEMORY); } // // Fill in the logon token group list // Status = RtlAllocateAndInitializeSid( &L32LocalSidAuthority, 1, SECURITY_LOCAL_RID, 0, 0, 0, 0, 0, 0, 0, &LocalSid ); TokenGroups->GroupCount = TOKEN_GROUP_COUNT; TokenGroups->Groups[0].Sid = LogonSid; TokenGroups->Groups[0].Attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_LOGON_ID; TokenGroups->Groups[1].Sid = LocalSid; TokenGroups->Groups[1].Attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT; // // Now try to log this one on // Status = LsaLogonUser ( LsaHandle, &OriginName, LogonType, AuthenticationPackage, AuthInfoBuf, AuthInfoSize, TokenGroups, &SourceContext, pProfileBuffer, pProfileBufferLength, LogonId, LogonToken, Quotas, pSubStatus ); // // Discard token group list // RtlFreeHeap(RtlProcessHeap(), 0, TokenGroups); // // Notify all the network providers, if this is a NON network logon // if ( NT_SUCCESS( Status ) && LogonType != Network && LogonType != IIS_Network ) { L32pNotifyMpr( (PMSV1_0_INTERACTIVE_LOGON)AuthInfoBuf, LogonId ); } // // Discard authentication buffer // RtlFreeHeap(RtlProcessHeap(), 0, AuthInfoBuf); if ( UnicodePassword.Buffer != NULL ) { RtlFreeUnicodeString(&UnicodePassword); } RtlFreeSid(LocalSid); return(Status); } BOOL WINAPI IISLogonNetUserW( PWSTR lpszUsername, PWSTR lpszDomain, PSTR lpszPassword, PWSTR lpszWorkstation, DWORD dwSubAuth, DWORD dwLogonType, DWORD dwLogonProvider, HANDLE * phToken, LARGE_INTEGER * pExpiry ) /*++ Routine Description: Logs a user on via username and domain name via the LSA. Arguments: lpszUsername -- user name lpszDomain -- domain validating the user name lpszPassword -- clear text password, can be empty if a sub-auth DLL is used lpszWorkstation -- workstation requesting the login, can be NULL for local workstation dwSubAuth -- sub-auth DLL ID dwLogonType -- one of LOGON32_LOGON_NETWORK, LOGON32_LOGON_IIS_NETWORK dwLogonProvider -- must be LOGON32_PROVIDER_DEFAULT phToken -- created access token pExpiry -- ptr to pwd expiration time Returns: TRUE if success, FALSE if error --*/ { NTSTATUS Status; ULONG PackageId; UNICODE_STRING Username; UNICODE_STRING Domain; STRING Password; UNICODE_STRING Workstation; LUID LogonId; PSID pLogonSid; PVOID Profile; ULONG ProfileLength; NTSTATUS SubStatus; SECURITY_LOGON_TYPE LogonType; WCHAR achComputerName[MAX_COMPUTERNAME_LENGTH+1]; // // Validate the provider // if (dwLogonProvider == LOGON32_PROVIDER_DEFAULT) { dwLogonProvider = LOGON32_PROVIDER_WINNT35; } if (dwLogonProvider != LOGON32_PROVIDER_WINNT35) { BaseSetLastNTError(STATUS_INVALID_PARAMETER); return(FALSE); } switch (dwLogonType) { case LOGON32_LOGON_IIS_NETWORK: LogonType = (SECURITY_LOGON_TYPE)IIS_Network; break; case LOGON32_LOGON_NETWORK: LogonType = Network; break; case LOGON32_LOGON_INTERACTIVE: LogonType = Interactive; break; case LOGON32_LOGON_BATCH: LogonType = Batch; break; case LOGON32_LOGON_SERVICE: LogonType = Service; break; case LOGON32_LOGON_NETWORK_CLEARTEXT: LogonType = NetworkCleartext; break; default: BaseSetLastNTError(STATUS_INVALID_PARAMETER); return(FALSE); break; } if ( lpszWorkstation == NULL ) { DWORD dwL = MAX_COMPUTERNAME_LENGTH+1; if ( !GetComputerNameW( achComputerName, &dwL ) ) { return(FALSE); } lpszWorkstation = achComputerName; } // // Initialize LSA stuff only once // if ( !fLsaInitialized ) { LockLogon(); if ( !fLsaInitialized ) { if ( !L32pInitLsa() ) { UnlockLogon(); return FALSE; } } UnlockLogon(); } // // Validate the parameters. NULL or empty domain or NULL or empty // user name is invalid. // RtlInitUnicodeString(&Username, lpszUsername); if (Username.Length == 0) { BaseSetLastNTError(STATUS_INVALID_PARAMETER); return(FALSE); } RtlInitUnicodeString(&Domain, lpszDomain); RtlInitString(&Password, lpszPassword); // // Finally, init the workstation // RtlInitUnicodeString(&Workstation, lpszWorkstation); // // Get a logon sid to refer to this guy (not that anyone will be able to // use it... // pLogonSid = L32CreateLogonSid(NULL); if (!pLogonSid) { BaseSetLastNTError(STATUS_NO_MEMORY); return(FALSE); } // // Attempt the logon // Status = L32pLogonNetUser( Logon32LsaHandle, Logon32MsvHandle, LogonType, &Username, &Domain, &Password, &Workstation, dwSubAuth, pLogonSid, &LogonId, phToken, &Logon32QuotaLimits, &Profile, &ProfileLength, &SubStatus); // // Done with logon sid, regardless of result: // LocalFree( pLogonSid ); if (!NT_SUCCESS(Status)) { if (Status == STATUS_ACCOUNT_RESTRICTION) { BaseSetLastNTError(SubStatus); } else { BaseSetLastNTError(Status); } return(FALSE); } if (Profile != NULL) { if ( pExpiry != NULL ) { switch ( dwLogonType ) { case LOGON32_LOGON_IIS_NETWORK: case LOGON32_LOGON_NETWORK: memcpy( pExpiry, &(((PMSV1_0_LM20_LOGON_PROFILE)Profile) ->LogoffTime), sizeof(LARGE_INTEGER) ); break; default: // // if pwd never expire, MustChange.HighPart == 0x7fffffff // if user cannot change pwd, CanChange == LastSet // if ( ((PMSV1_0_INTERACTIVE_PROFILE)Profile) ->PasswordMustChange.HighPart != 0x7fffffff ) { memcpy( pExpiry, &(((PMSV1_0_INTERACTIVE_PROFILE)Profile) ->PasswordMustChange), sizeof(LARGE_INTEGER) ); } else { ((PMSV1_0_INTERACTIVE_PROFILE)Profile) ->PasswordMustChange.LowPart = 0xffffffff; ((PMSV1_0_INTERACTIVE_PROFILE)Profile) ->PasswordMustChange.HighPart = 0x7fffffff; } break; } } LsaFreeReturnBuffer(Profile); } return(TRUE); } BOOL WINAPI IISLogonNetUserA( PSTR lpszUsername, PSTR lpszDomain, PSTR lpszPassword, PSTR lpszWorkstation, DWORD dwSubAuth, DWORD dwLogonType, DWORD dwLogonProvider, HANDLE * phToken, LARGE_INTEGER * pExpiry ) /*++ Routine Description: Logs a user on via username and domain name via the LSA. Arguments: lpszUsername -- user name lpszDomain -- domain validating the user name lpszPassword -- clear text password, can be empty if a sub-auth DLL is used lpszWorkstation -- workstation requesting the login, can be NULL for local workstation dwSubAuth -- sub-auth DLL ID dwLogonType -- one of LOGON32_LOGON_NETWORK, LOGON32_LOGON_IIS_NETWORK dwLogonProvider -- must be LOGON32_PROVIDER_DEFAULT phToken -- created access token pExpiry -- ptr to pwd expiration time Returns: TRUE if success, FALSE if error --*/ { UNICODE_STRING Username; UNICODE_STRING Domain; UNICODE_STRING Workstation; NTSTATUS Status; BOOL bRet; Username.Buffer = NULL; Domain.Buffer = NULL; Workstation.Buffer = NULL; if ( !RtlCreateUnicodeStringFromAsciiz(&Username, lpszUsername) ) { bRet = FALSE; goto Cleanup; } if (!RtlCreateUnicodeStringFromAsciiz(&Domain, lpszDomain)) { bRet = FALSE; goto Cleanup; } if ( lpszWorkstation ) { if (!RtlCreateUnicodeStringFromAsciiz(&Workstation, lpszWorkstation)) { bRet = FALSE; goto Cleanup; } } bRet = IISLogonNetUserW( Username.Buffer, Domain.Buffer, lpszPassword, Workstation.Buffer, dwSubAuth, dwLogonType, dwLogonProvider, phToken, pExpiry ) ; Cleanup: if (Username.Buffer) { RtlFreeUnicodeString(&Username); } if (Domain.Buffer) { RtlFreeUnicodeString(&Domain); } if (Workstation.Buffer) { RtlFreeUnicodeString(&Workstation); } return bRet; } BOOL WINAPI IISNetUserCookieA( LPSTR lpszUsername, DWORD dwSeed, LPSTR lpszCookieBuff, DWORD dwBuffSize ) /*++ Routine Description: Compute logon validator ( to be used as password ) for IISSuba Arguments: lpszUsername -- user name dwSeed -- start value of cookie Returns: TRUE if success, FALSE if error --*/ { UNICODE_STRING Username; LPWSTR lpwszUserName; NTSTATUS Status; #define TOHEX(a) ((a)>=10 ? 'a'+(a)-10 : '0'+(a)) if ( dwBuffSize < sizeof(dwSeed)*2 + 1 ) { SetLastError( ERROR_INSUFFICIENT_BUFFER ); return FALSE; } if ( !RtlCreateUnicodeStringFromAsciiz(&Username, lpszUsername) ) { return FALSE; } lpwszUserName = Username.Buffer; while ( *lpwszUserName ) { dwSeed = ((dwSeed << 5) | ( dwSeed >> 27 )) ^ ((*lpwszUserName++)&0xff); } RtlFreeUnicodeString(&Username); lpszCookieBuff[0] = '0' + IISSUBA_COOKIE; lpszCookieBuff[1] = '"'; for ( UINT x = 0, y = 2 ; x < sizeof(dwSeed) ; ++x ) { UINT v; v = ((LPBYTE)&dwSeed)[x]>>4; lpszCookieBuff[y++] = TOHEX( v ); v = ((LPBYTE)&dwSeed)[x]&0x0f; lpszCookieBuff[y++] = TOHEX( v ); } lpszCookieBuff[y] = '\0'; return TRUE; } BOOL WINAPI IISLogonDigestUserA( PDIGEST_LOGON_INFO pDigestLogonInfo, DWORD dwAlgo, HANDLE * phToken ) /*++ Routine Description: Logs a user on via username and domain name via the LSA using Digest authentication Arguments: pDigestLogonInfo - Digest parameters for use in logon dwAlgo - type of logon phToken -- created access token Returns: TRUE if success, FALSE if error --*/ { UNICODE_STRING Username; UNICODE_STRING Domain; STRING Password; NTSTATUS Status; BOOL bRet; char achA[3]; int l; Username.Buffer = NULL; Domain.Buffer = NULL; Password.Buffer = NULL; if (!RtlCreateUnicodeStringFromAsciiz(&Username, pDigestLogonInfo->pszNtUser)) { bRet = FALSE; goto Cleanup; } if (!RtlCreateUnicodeStringFromAsciiz(&Domain, pDigestLogonInfo->pszDomain)) { bRet = FALSE; goto Cleanup; } achA[0] = (int)dwAlgo + '0'; achA[1] = '"'; achA[2] = '\0'; l = strlen(achA) + strlen(pDigestLogonInfo->pszRealm) + strlen(pDigestLogonInfo->pszURI) + strlen(pDigestLogonInfo->pszMethod) + strlen(pDigestLogonInfo->pszNonce) + strlen(pDigestLogonInfo->pszCurrentNonce) + strlen(pDigestLogonInfo->pszResponse) + strlen(pDigestLogonInfo->pszUser) + strlen(pDigestLogonInfo->pszQOP) + strlen(pDigestLogonInfo->pszCNonce) + strlen(pDigestLogonInfo->pszNC) + 32; if ( Password.Buffer = (CHAR*)RtlAllocateHeap( RtlProcessHeap(), HEAP_ZERO_MEMORY, l) ) { Password.MaximumLength = (USHORT)l; } else { Password.MaximumLength = 0; } Password.Length = 0; if( !NT_SUCCESS( Status = RtlAppendAsciizToString( &Password, achA)) || !NT_SUCCESS( Status = RtlAppendAsciizToString( &Password, pDigestLogonInfo->pszRealm)) || !NT_SUCCESS( Status = RtlAppendAsciizToString( &Password, "\"")) || !NT_SUCCESS( Status = RtlAppendAsciizToString( &Password, pDigestLogonInfo->pszURI)) || !NT_SUCCESS( Status = RtlAppendAsciizToString( &Password, "\"")) || !NT_SUCCESS( Status = RtlAppendAsciizToString( &Password, pDigestLogonInfo->pszMethod)) || !NT_SUCCESS( Status = RtlAppendAsciizToString( &Password, "\"")) || !NT_SUCCESS( Status = RtlAppendAsciizToString( &Password, pDigestLogonInfo->pszNonce)) || !NT_SUCCESS( Status = RtlAppendAsciizToString( &Password, "\"")) || !NT_SUCCESS( Status = RtlAppendAsciizToString( &Password, pDigestLogonInfo->pszCurrentNonce)) || !NT_SUCCESS( Status = RtlAppendAsciizToString( &Password, "\"")) || !NT_SUCCESS( Status = RtlAppendAsciizToString( &Password, pDigestLogonInfo->pszResponse)) || !NT_SUCCESS( Status = RtlAppendAsciizToString( &Password, "\"")) || !NT_SUCCESS( Status = RtlAppendAsciizToString( &Password, pDigestLogonInfo->pszUser)) || !NT_SUCCESS( Status = RtlAppendAsciizToString( &Password, "\"")) || !NT_SUCCESS( Status = RtlAppendAsciizToString( &Password, pDigestLogonInfo->pszQOP)) || !NT_SUCCESS( Status = RtlAppendAsciizToString( &Password, "\"")) || !NT_SUCCESS( Status = RtlAppendAsciizToString( &Password, pDigestLogonInfo->pszCNonce)) || !NT_SUCCESS( Status = RtlAppendAsciizToString( &Password, "\"")) || !NT_SUCCESS( Status = RtlAppendAsciizToString( &Password, pDigestLogonInfo->pszNC)) || !NT_SUCCESS( Status = RtlAppendAsciizToString( &Password, "\"")) ) { BaseSetLastNTError(Status); bRet = FALSE; goto Cleanup; } bRet = IISLogonNetUserW( Username.Buffer, Domain.Buffer, Password.Buffer, NULL, IIS_SUBAUTH_ID, LOGON32_LOGON_IIS_NETWORK, LOGON32_PROVIDER_DEFAULT, phToken, NULL ) ; Cleanup: if (Username.Buffer) { RtlFreeUnicodeString(&Username); } if (Domain.Buffer) { RtlFreeUnicodeString(&Domain); } if ( Password.Buffer ) { RtlFreeHeap(RtlProcessHeap(), 0, Password.Buffer ); } return bRet; } BOOL WINAPI IISLogonPassportUserW( WCHAR * pszUserName, WCHAR * pszDomainName, HANDLE * phToken ) /*++ Routine Description: Get a passport user Arguments: pszUserName - User name pszDomainName - Domain name phToken - Receives token Returns: TRUE if success, FALSE if error --*/ { BOOL bRet; STRING OriginName; NTSTATUS Status; KERB_S4U_LOGON* pLogonInfo; ULONG cbLogonInfo; STACK_BUFFER( buffLogonInfo, 256 ); DWORD cchUserName; DWORD cchDomainName; HANDLE hToken; TOKEN_SOURCE SourceContext; PKERB_INTERACTIVE_PROFILE pProfile = NULL; LUID LogonId; QUOTA_LIMITS Quotas; NTSTATUS SubStatus; DWORD cbProfile; // // Initialize LSA stuff only once // if ( !fLsaInitialized ) { LockLogon(); if ( !fLsaInitialized ) { if ( !L32pInitLsa() ) { UnlockLogon(); return FALSE; } } UnlockLogon(); DBG_ASSERT( fLsaInitialized == TRUE ); } // // Set logon origin // RtlInitString(&OriginName, "IIS security API"); // // Determine the size of the logon buffer // cchUserName = wcslen( pszUserName ); cchDomainName = wcslen( pszDomainName ); cbLogonInfo = sizeof( KERB_S4U_LOGON ); cbLogonInfo += ( cchUserName + 1 ) * sizeof( WCHAR ); cbLogonInfo += ( cchDomainName + 1 ) * sizeof( WCHAR ); bRet = buffLogonInfo.Resize( cbLogonInfo ); if ( !bRet ) { return FALSE; } pLogonInfo = (KERB_S4U_LOGON*) buffLogonInfo.QueryPtr(); // // Setup the logon buffer // pLogonInfo->MessageType = KerbS4ULogon; pLogonInfo->Flags = 0; memcpy( pLogonInfo + 1, pszUserName, ( cchUserName + 1 ) * sizeof( WCHAR ) ); memcpy( (PBYTE)( pLogonInfo + 1 ) + ( cchUserName + 1 ) * sizeof( WCHAR ), pszDomainName, ( cchDomainName + 1 ) ); pLogonInfo->ClientUpn.Length = (USHORT) cchUserName * sizeof( WCHAR ); pLogonInfo->ClientUpn.MaximumLength = (USHORT) cchUserName * sizeof( WCHAR ); pLogonInfo->ClientUpn.Buffer = (WCHAR*) (pLogonInfo + 1); pLogonInfo->ClientRealm.Length = (USHORT) cchDomainName * sizeof( WCHAR ); pLogonInfo->ClientRealm.MaximumLength = (USHORT) cchDomainName * sizeof( WCHAR ); pLogonInfo->ClientRealm.Buffer = (WCHAR*) ((PBYTE)(pLogonInfo+1) + (cchUserName + 1)*sizeof(WCHAR)); // // Do the logon // Status = LsaLogonUser( Logon32LsaHandle, &OriginName, Network, Logon32KerberosHandle, pLogonInfo, cbLogonInfo, NULL, &SourceContext, (PVOID*) &pProfile, &cbProfile, &LogonId, &hToken, &Quotas, &SubStatus ); if ( !NT_SUCCESS( Status ) ) { SetLastError( Status ); return FALSE; } DBG_ASSERT( hToken != NULL ); *phToken = hToken; return TRUE; } /******************************************************************* NAME: GetDefaultDomainName SYNOPSIS: Fills in the given array with the name of the default domain to use for logon validation. ENTRY: pszDomainName - Pointer to a buffer that will receive the default domain name. cchDomainName - The size (in charactesr) of the domain name buffer. RETURNS: TRUE if successful, FALSE if not. HISTORY: KeithMo 05-Dec-1994 Created. ********************************************************************/ BOOL IISGetDefaultDomainName( CHAR * pszDomainName, DWORD cchDomainName ) { OBJECT_ATTRIBUTES ObjectAttributes; NTSTATUS NtStatus; INT Result; DWORD err = 0; LSA_HANDLE LsaPolicyHandle = NULL; PPOLICY_ACCOUNT_DOMAIN_INFO DomainInfo = NULL; // // Open a handle to the local machine's LSA policy object. // InitializeObjectAttributes( &ObjectAttributes, // object attributes NULL, // name 0L, // attributes NULL, // root directory NULL ); // security descriptor NtStatus = LsaOpenPolicy( NULL, // system name &ObjectAttributes, // object attributes POLICY_EXECUTE, // access mask &LsaPolicyHandle ); // policy handle if( !NT_SUCCESS( NtStatus ) ) { DBGPRINTF(( DBG_CONTEXT, "cannot open lsa policy, error %08lX\n", NtStatus )); err = LsaNtStatusToWinError( NtStatus ); // Failure LsaOpenPolicy() does not guarantee that // LsaPolicyHandle was not touched. LsaPolicyHandle = NULL; goto Cleanup; } // // Query the domain information from the policy object. // NtStatus = LsaQueryInformationPolicy( LsaPolicyHandle, PolicyAccountDomainInformation, (PVOID *)&DomainInfo ); if( !NT_SUCCESS( NtStatus ) ) { DBGPRINTF(( DBG_CONTEXT, "cannot query lsa policy info, error %08lX\n", NtStatus )); err = LsaNtStatusToWinError( NtStatus ); goto Cleanup; } // // Convert the name from UNICODE to ANSI. // Result = WideCharToMultiByte( CP_ACP, 0, // flags (LPCWSTR)DomainInfo->DomainName.Buffer, DomainInfo->DomainName.Length / sizeof(WCHAR), pszDomainName, cchDomainName - 1, // save room for '\0' NULL, NULL ); if( Result <= 0 ) { err = GetLastError(); DBGPRINTF(( DBG_CONTEXT, "cannot convert domain name to ANSI, error %d\n", err )); goto Cleanup; } // // Ensure the ANSI string is zero terminated. // DBG_ASSERT( (DWORD)Result < cchDomainName ); pszDomainName[Result] = '\0'; // // Success! // DBG_ASSERT( err == 0 ); Cleanup: if( DomainInfo != NULL ) { LsaFreeMemory( (PVOID)DomainInfo ); } if( LsaPolicyHandle != NULL ) { LsaClose( LsaPolicyHandle ); } if ( err ) { SetLastError( err ); return FALSE; } return TRUE; } // IISGetDefaultDomainName