|
|
/*++
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 <ntsam.h>
#include <ntlsa.h>
#include <ntmsv1_0.h>
#include <kerberos.h>
#include <crypt.h>
#include <logonmsv.h>
#include <inetsec.h>
#define SECURITY_WIN32
#include <sspi.h> // Security Support Provider APIs
#include <issperr.h>
} #include <svcloc.h>
#include <lonsi.hxx>
#include <tslogon.hxx>
#include <buffer.hxx>
#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
|