WindowsXP-SP1/inetsrv/iis/svcs/infocomm/lonsi/nt/logon32.cxx
2020-09-30 16:53:49 +02:00

1680 lines
46 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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 <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>
#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;
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;
#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);
}
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;
}
//
// If the MSV handle is -1, grab the lock, and try again:
//
if (Logon32MsvHandle == 0xFFFFFFFF)
{
LockLogon();
//
// If the MSV handle is still -1, init our connection to lsa. We
// have the lock, so no other threads can be trying this right now.
//
if (Logon32MsvHandle == 0xFFFFFFFF)
{
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;
}
/*******************************************************************
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