NT4/private/windows/base/advapi/logon32.c
2020-09-30 17:12:29 +02:00

1984 lines
53 KiB
C

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1993.
//
// File: logon32.c
//
// Contents:
//
// Classes:
//
// Functions:
//
// History: 9-30-94 RichardW Created
//
//----------------------------------------------------------------------------
#include "advapi.h"
#include <crypt.h>
#include <mpr.h>
#include <ntlsa.h>
#include <ntmsv1_0.h>
#include <wchar.h>
#include <stdlib.h>
#include <lmcons.h>
//
// 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 *);
//
// 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;
HINSTANCE Logon32MprHandle = NULL;
LOGONNOTIFYFN Logon32LogonNotify = NULL;
RTL_CRITICAL_SECTION Logon32Lock;
#define LockLogon() RtlEnterCriticalSection( &Logon32Lock )
#define UnlockLogon() RtlLeaveCriticalSection( &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
//+---------------------------------------------------------------------------
//
// Function: Logon32Initialize
//
// Synopsis: Initializes the critical section
//
// Arguments: [hMod] --
// [Reason] --
// [Context] --
//
//----------------------------------------------------------------------------
BOOL
Logon32Initialize(
IN PVOID hMod,
IN ULONG Reason,
IN PCONTEXT Context)
{
NTSTATUS Status;
if (Reason == DLL_PROCESS_ATTACH)
{
Status = RtlInitializeCriticalSection( &Logon32Lock );
return( Status == STATUS_SUCCESS );
}
return( TRUE );
}
/***************************************************************************\
* CreateLogonSid
*
* Creates a logon sid for a new logon.
*
* If LogonId is non NULL, on return the LUID that is part of the logon
* sid is returned here.
*
* History:
* 12-05-91 Davidc Created
\***************************************************************************/
PSID
L32CreateLogonSid(
PLUID LogonId OPTIONAL
)
{
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);
}
/*******************************************************************
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.
RichardW 10-Jan-95 Liberated from sockets and stuck in base
********************************************************************/
BOOL
L32GetDefaultDomainName(
PUNICODE_STRING pDomainName
)
{
OBJECT_ATTRIBUTES ObjectAttributes;
NTSTATUS NtStatus;
INT Result;
DWORD err = 0;
LSA_HANDLE LsaPolicyHandle = NULL;
PPOLICY_ACCOUNT_DOMAIN_INFO DomainInfo = NULL;
PUNICODE_STRING pDomain;
if (Logon32DomainName[0] != L'\0')
{
RtlInitUnicodeString(pDomainName, Logon32DomainName);
return(TRUE);
}
//
// 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 ) )
{
BaseSetLastNTError(NtStatus);
return(FALSE);
}
//
// Query the domain information from the policy object.
//
NtStatus = LsaQueryInformationPolicy( LsaPolicyHandle,
PolicyAccountDomainInformation,
(PVOID *) &DomainInfo );
if (!NT_SUCCESS(NtStatus))
{
BaseSetLastNTError(NtStatus);
LsaClose(LsaPolicyHandle);
return(FALSE);
}
(void) LsaClose(LsaPolicyHandle);
//
// Copy the domain name into our cache, and
//
CopyMemory( Logon32DomainName,
DomainInfo->DomainName.Buffer,
DomainInfo->DomainName.Length );
//
// Null terminate it appropriately
//
Logon32DomainName[DomainInfo->DomainName.Length / sizeof(WCHAR)] = L'\0';
//
// Clean up
//
LsaFreeMemory( (PVOID)DomainInfo );
//
// And init the string
//
RtlInitUnicodeString(pDomainName, Logon32DomainName);
return TRUE;
} // GetDefaultDomainName
//+---------------------------------------------------------------------------
//
// Function: L32pInitLsa
//
// Synopsis: Initialize connection with LSA
//
// Arguments: (none)
//
// History: 4-21-95 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
L32pInitLsa(void)
{
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);
}
GetModuleFileNameA(NULL, MyName, MAX_PATH);
ModuleName = strrchr(MyName, '\\');
if (!ModuleName)
{
ModuleName = MyName;
}
//
// 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 );
}
//+---------------------------------------------------------------------------
//
// Function: L32pLogonUser
//
// Synopsis: Wraps up the call to LsaLogonUser
//
// Arguments: [LsaHandle] --
// [AuthenticationPackage] --
// [LogonType] --
// [UserName] --
// [Domain] --
// [Password] --
// [LogonSid] --
// [LogonId] --
// [LogonToken] --
// [Quotas] --
// [pProfileBuffer] --
// [pProfileBufferLength] --
// [pSubStatus] --
//
// History: 4-24-95 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
NTSTATUS
L32pLogonUser(
IN HANDLE LsaHandle,
IN ULONG AuthenticationPackage,
IN SECURITY_LOGON_TYPE LogonType,
IN PUNICODE_STRING UserName,
IN PUNICODE_STRING Domain,
IN PUNICODE_STRING Password,
IN PSID LogonSid,
OUT PLUID LogonId,
OUT PHANDLE LogonToken,
OUT PQUOTA_LIMITS Quotas,
OUT PVOID *pProfileBuffer,
OUT PULONG pProfileBufferLength,
OUT PNTSTATUS pSubStatus
)
{
NTSTATUS Status;
STRING OriginName;
TOKEN_SOURCE SourceContext;
PMSV1_0_INTERACTIVE_LOGON MsvAuthInfo;
PMSV1_0_LM20_LOGON MsvNetAuthInfo;
PVOID AuthInfoBuf;
ULONG AuthInfoSize;
PTOKEN_GROUPS TokenGroups;
PSID LocalSid;
WCHAR ComputerName[ MAX_COMPUTERNAME_LENGTH + 1 ];
DWORD ComputerNameLength;
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, "Advapi ", sizeof(SourceContext.SourceName)); // LATER from res file
Status = NtAllocateLocallyUniqueId(&SourceContext.SourceIdentifier);
if (!NT_SUCCESS(Status)) {
return(Status);
}
//
// Set logon origin
//
RtlInitString(&OriginName, "LogonUser API");
//
// For network logons, do the magic.
//
if ( LogonType == Network )
{
ComputerNameLength = MAX_COMPUTERNAME_LENGTH + 1;
if (!GetComputerNameW( ComputerName, &ComputerNameLength ) )
{
return( STATUS_INVALID_PARAMETER );
}
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 = 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, ComputerName );
//
// 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 = (PUCHAR)
((PBYTE) (MsvNetAuthInfo->Workstation.Buffer) +
MsvNetAuthInfo->Workstation.MaximumLength );
MsvNetAuthInfo->CaseSensitiveChallengeResponse.Length =
NT_RESPONSE_LENGTH;
MsvNetAuthInfo->CaseSensitiveChallengeResponse.MaximumLength =
NT_RESPONSE_LENGTH;
RtlCalculateNtOwfPassword(
Password,
& PasswordHash );
RtlCalculateNtResponse(
& Challenge.NtChallenge,
& PasswordHash,
(PNT_RESPONSE) MsvNetAuthInfo->CaseSensitiveChallengeResponse.Buffer );
//
// 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 = LmPasswordBuf;
LmPassword.Length = LmPassword.MaximumLength = LM20_PWLEN + 1;
Status = RtlUpcaseUnicodeStringToOemString(
& LmPassword,
Password,
FALSE );
if ( NT_SUCCESS(Status) )
{
MsvNetAuthInfo->CaseInsensitiveChallengeResponse.Buffer = (PUCHAR)
((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,
(PLM_RESPONSE) MsvNetAuthInfo->CaseInsensitiveChallengeResponse.Buffer );
}
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 ) );
}
}
else
{
//
// Build logon structure for non-network logons - service,
// batch, interactive
//
AuthInfoSize = sizeof(MSV1_0_INTERACTIVE_LOGON) +
sizeof(WCHAR)*(wcslen(UserName->Buffer) + 1 +
wcslen(Domain->Buffer) + 1 +
wcslen(Password->Buffer) + 1 );
MsvAuthInfo = AuthInfoBuf = RtlAllocateHeap(RtlProcessHeap(),
HEAP_ZERO_MEMORY,
AuthInfoSize);
if (MsvAuthInfo == NULL) {
return(STATUS_NO_MEMORY);
}
//
// This authentication buffer will be used for a logon attempt
//
MsvAuthInfo->MessageType = MsV1_0InteractiveLogon;
//
// Copy the user name into the authentication buffer
//
MsvAuthInfo->UserName.Length =
(USHORT)sizeof(WCHAR)*wcslen(UserName->Buffer);
MsvAuthInfo->UserName.MaximumLength =
MsvAuthInfo->UserName.Length + sizeof(WCHAR);
MsvAuthInfo->UserName.Buffer = (PWSTR)(MsvAuthInfo+1);
wcscpy(MsvAuthInfo->UserName.Buffer, UserName->Buffer);
//
// Copy the domain name into the authentication buffer
//
MsvAuthInfo->LogonDomainName.Length =
(USHORT)sizeof(WCHAR)*wcslen(Domain->Buffer);
MsvAuthInfo->LogonDomainName.MaximumLength =
MsvAuthInfo->LogonDomainName.Length + sizeof(WCHAR);
MsvAuthInfo->LogonDomainName.Buffer = (PWSTR)
((PBYTE)(MsvAuthInfo->UserName.Buffer) +
MsvAuthInfo->UserName.MaximumLength);
wcscpy(MsvAuthInfo->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.
//
MsvAuthInfo->Password.Length =
(USHORT)sizeof(WCHAR)*wcslen(Password->Buffer);
MsvAuthInfo->Password.MaximumLength =
MsvAuthInfo->Password.Length + sizeof(WCHAR);
MsvAuthInfo->Password.Buffer = (PWSTR)
((PBYTE)(MsvAuthInfo->LogonDomainName.Buffer) +
MsvAuthInfo->LogonDomainName.MaximumLength);
wcscpy(MsvAuthInfo->Password.Buffer, Password->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
);
if ( NT_SUCCESS( Status ) )
{
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 sucker on
//
Status = LsaLogonUser (
LsaHandle,
&OriginName,
LogonType,
AuthenticationPackage,
AuthInfoBuf,
AuthInfoSize,
TokenGroups,
&SourceContext,
pProfileBuffer,
pProfileBufferLength,
LogonId,
LogonToken,
Quotas,
pSubStatus
);
RtlFreeSid(LocalSid);
}
//
// 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) )
{
L32pNotifyMpr(AuthInfoBuf, LogonId);
}
//
// Discard authentication buffer
//
RtlFreeHeap(RtlProcessHeap(), 0, AuthInfoBuf);
return(Status);
}
//+---------------------------------------------------------------------------
//
// Function: LogonUserA
//
// Synopsis: ANSI wrapper for LogonUserW. See description below
//
// Arguments: [lpszUsername] --
// [lpszDomain] --
// [lpszPassword] --
// [dwLogonType] --
// [dwLogonProvider] --
// [phToken] --
//
// History: 4-25-95 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
WINAPI
LogonUserA(
LPSTR lpszUsername,
LPSTR lpszDomain,
LPSTR lpszPassword,
DWORD dwLogonType,
DWORD dwLogonProvider,
HANDLE * phToken
)
{
UNICODE_STRING Username;
UNICODE_STRING Domain;
UNICODE_STRING Password;
NTSTATUS Status;
BOOL bRet;
Username.Buffer = NULL;
Domain.Buffer = NULL;
Password.Buffer = NULL;
Status = RtlCreateUnicodeStringFromAsciiz(&Username, lpszUsername);
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
bRet = FALSE;
goto Cleanup;
}
Status = RtlCreateUnicodeStringFromAsciiz(&Domain, lpszDomain);
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
bRet = FALSE;
goto Cleanup;
}
Status = RtlCreateUnicodeStringFromAsciiz(&Password, lpszPassword);
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
bRet = FALSE;
goto Cleanup;
}
bRet = LogonUserW( Username.Buffer,
Domain.Buffer,
Password.Buffer,
dwLogonType,
dwLogonProvider,
phToken);
Cleanup:
if (Username.Buffer)
{
RtlFreeUnicodeString(&Username);
}
if (Domain.Buffer)
{
RtlFreeUnicodeString(&Domain);
}
if (Password.Buffer)
{
RtlZeroMemory(Password.Buffer, Password.Length);
RtlFreeUnicodeString(&Password);
}
return(bRet);
}
//+---------------------------------------------------------------------------
//
// Function: LogonUserW
//
// Synopsis: Logs a user on via plaintext password, username and domain
// name via the LSA.
//
// Arguments: [lpszUsername] -- User name
// [lpszDomain] -- Domain name
// [lpszPassword] -- Password
// [dwLogonType] -- Logon type
// [dwLogonProvider] -- Provider
// [phToken] -- Returned handle to primary token
//
// History: 4-25-95 RichardW Created
//
// Notes: Requires SeTcbPrivilege, and will enable it if not already
// present.
//
//----------------------------------------------------------------------------
BOOL
WINAPI
LogonUserW(
PWSTR lpszUsername,
PWSTR lpszDomain,
PWSTR lpszPassword,
DWORD dwLogonType,
DWORD dwLogonProvider,
HANDLE * phToken
)
{
NTSTATUS Status;
ULONG PackageId;
UNICODE_STRING Username;
UNICODE_STRING Domain;
UNICODE_STRING Password;
LUID LogonId;
PSID pLogonSid;
PVOID Profile;
ULONG ProfileLength;
NTSTATUS SubStatus;
SECURITY_LOGON_TYPE LogonType;
//
// 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_INTERACTIVE:
LogonType = Interactive;
break;
case LOGON32_LOGON_BATCH:
LogonType = Batch;
break;
case LOGON32_LOGON_SERVICE:
LogonType = Service;
break;
case LOGON32_LOGON_NETWORK:
LogonType = Network;
break;
default:
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
return(FALSE);
break;
}
//
// 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())
{
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);
}
//
// Initialize the token handle, if the pointer is invalid, then catch
// the exception now.
//
*phToken = NULL;
//
// Parse that domain. Note, if the special token . is passed in for
// domain, we will use the right value from the LSA, meaning AccountDomain.
// If the domain is null, the lsa will talk to the local domain, the
// primary domain, and then on from there...
//
if (lpszDomain && *lpszDomain)
{
if ((lpszDomain[0] == L'.') &&
(lpszDomain[1] == L'\0') )
{
if (!L32GetDefaultDomainName(&Domain))
{
return(FALSE);
}
}
else
RtlInitUnicodeString(&Domain, lpszDomain);
}
else
{
RtlInitUnicodeString(&Domain, lpszDomain);
}
//
// Finally, init the password
//
RtlInitUnicodeString(&Password, lpszPassword);
//
// 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 = L32pLogonUser(
Logon32LsaHandle,
Logon32MsvHandle,
LogonType,
&Username,
&Domain,
&Password,
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)
{
LsaFreeReturnBuffer(Profile);
}
return(TRUE);
}
//+---------------------------------------------------------------------------
//
// Function: ImpersonateLoggedOnUser
//
// Synopsis: Duplicates the token passed in if it is primary, and assigns
// it to the thread that called.
//
// Arguments: [hToken] --
//
// History: 1-10-95 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
WINAPI
ImpersonateLoggedOnUser(
HANDLE hToken
)
{
TOKEN_TYPE Type;
ULONG cbType;
HANDLE hImpToken;
NTSTATUS Status;
SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
OBJECT_ATTRIBUTES ObjectAttributes;
BOOL fCloseImp;
Status = NtQueryInformationToken(
hToken,
TokenType,
&Type,
sizeof(TOKEN_TYPE),
&cbType);
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
return(FALSE);
}
if (Type == TokenPrimary)
{
InitializeObjectAttributes(
&ObjectAttributes,
NULL,
0L,
NULL,
NULL);
SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
SecurityQualityOfService.EffectiveOnly = FALSE;
ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService;
Status = NtDuplicateToken( hToken,
TOKEN_IMPERSONATE | TOKEN_QUERY,
&ObjectAttributes,
FALSE,
TokenImpersonation,
&hImpToken
);
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
return(FALSE);
}
fCloseImp = TRUE;
}
else
{
hImpToken = hToken;
fCloseImp = FALSE;
}
Status = NtSetInformationThread(
NtCurrentThread(),
ThreadImpersonationToken,
(PVOID) &hImpToken,
sizeof(hImpToken)
);
if (fCloseImp)
{
(void) NtClose(hImpToken);
}
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
return(FALSE);
}
return(TRUE);
}
//+---------------------------------------------------------------------------
//
// Function: L32SetProcessToken
//
// Synopsis: Sets the primary token for the new process.
//
// Arguments: [psd] --
// [hProcess] --
// [hThread] --
// [hToken] --
//
// History: 4-25-95 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
L32SetProcessToken(
PSECURITY_DESCRIPTOR psd,
HANDLE hProcess,
HANDLE hThread,
HANDLE hToken
)
{
NTSTATUS Status, AdjustStatus;
PROCESS_ACCESS_TOKEN PrimaryTokenInfo;
HANDLE TokenToAssign;
OBJECT_ATTRIBUTES ObjectAttributes;
BOOLEAN WasEnabled;
HANDLE NullHandle;
//
// Check for a NULL token. (No need to do anything)
// The process will run in the parent process's context and inherit
// the default ACL from the parent process's token.
//
if (hToken == NULL)
{
return(TRUE);
}
//
// A primary token can only be assigned to one process.
// Duplicate the logon token so we can assign one to the new
// process.
//
InitializeObjectAttributes(
&ObjectAttributes,
NULL,
0,
NULL,
psd
);
Status = NtDuplicateToken(
hToken, // Duplicate this token
0, // Same desired access
&ObjectAttributes,
FALSE, // EffectiveOnly
TokenPrimary, // TokenType
&TokenToAssign // Duplicate token handle stored here
);
if (!NT_SUCCESS(Status)) {
return(FALSE);
}
//
// Set the process's primary token. This is actually much more complex
// to implement in a single API, but we'll live with it. This MUST be
// called when we are not impersonating! The client generally does *not*
// have the SeAssignPrimary privilege
//
//
// Enable the required privilege
//
Status = RtlImpersonateSelf( SecurityImpersonation );
if ( NT_SUCCESS( Status ) )
{
Status = RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, TRUE,
TRUE, &WasEnabled);
if (NT_SUCCESS(Status)) {
PrimaryTokenInfo.Token = TokenToAssign;
PrimaryTokenInfo.Thread = hThread;
Status = NtSetInformationProcess(
hProcess,
ProcessAccessToken,
(PVOID)&PrimaryTokenInfo,
(ULONG)sizeof(PROCESS_ACCESS_TOKEN)
);
//
// Restore the privilege to its previous state
//
if (!WasEnabled)
{
AdjustStatus = RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE,
WasEnabled, TRUE, &WasEnabled);
if (NT_SUCCESS(Status)) {
Status = AdjustStatus;
}
}
}
//
// Revert back to process.
//
NullHandle = NULL;
AdjustStatus = NtSetInformationThread(
NtCurrentThread(),
ThreadImpersonationToken,
(PVOID) &NullHandle,
sizeof( HANDLE ) );
if ( NT_SUCCESS( Status ) )
{
Status = AdjustStatus;
}
} else {
NOTHING;
}
//
// We're finished with the token handle
//
CloseHandle(TokenToAssign);
if (!NT_SUCCESS(Status)) {
BaseSetLastNTError(Status);
}
return (NT_SUCCESS(Status));
}
//+---------------------------------------------------------------------------
//
// Function: L32SetProcessQuotas
//
// Synopsis: Updates the quotas for the process
//
// Arguments: [hProcess] --
//
// History: 4-25-95 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
L32SetProcessQuotas(
HANDLE hProcess)
{
NTSTATUS Status = STATUS_SUCCESS;
NTSTATUS AdjustStatus = STATUS_SUCCESS;
QUOTA_LIMITS RequestedLimits;
BOOLEAN WasEnabled;
HANDLE NullHandle;
RequestedLimits = Logon32QuotaLimits;
RequestedLimits.MinimumWorkingSetSize = 0;
RequestedLimits.MaximumWorkingSetSize = 0;
//
// Set the process's quota. This MUST be
// called when we are not impersonating! The client generally does *not*
// have the SeIncreaseQuota privilege.
//
Status = RtlImpersonateSelf( SecurityImpersonation );
if ( NT_SUCCESS( Status ) )
{
if (RequestedLimits.PagedPoolLimit != 0) {
Status = RtlAdjustPrivilege(SE_INCREASE_QUOTA_PRIVILEGE, TRUE,
TRUE, &WasEnabled);
if ( NT_SUCCESS( Status ) )
{
Status = NtSetInformationProcess(
hProcess,
ProcessQuotaLimits,
(PVOID)&RequestedLimits,
(ULONG)sizeof(QUOTA_LIMITS)
);
if (!WasEnabled)
{
AdjustStatus = RtlAdjustPrivilege(SE_INCREASE_QUOTA_PRIVILEGE,
WasEnabled, FALSE, &WasEnabled);
if (NT_SUCCESS(Status)) {
Status = AdjustStatus;
}
}
}
}
NullHandle = NULL;
AdjustStatus = NtSetInformationThread(
NtCurrentThread(),
ThreadImpersonationToken,
(PVOID) &NullHandle,
sizeof( HANDLE ) );
if ( NT_SUCCESS( Status ) )
{
Status = AdjustStatus;
}
}
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
return(FALSE);
}
return(TRUE);
}
//+---------------------------------------------------------------------------
//
// Function: L32CommonCreate
//
// Synopsis:
//
// Effects:
//
// Arguments: [CreateFlags] -- Flags (see top of file)
// [hToken] -- Primary token to use
// [lpProcessInfo] -- Process Info
//
// History: 1-20-95 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
L32CommonCreate(
DWORD CreateFlags,
HANDLE hToken,
LPPROCESS_INFORMATION lpProcessInfo
)
{
PTOKEN_DEFAULT_DACL pDefDacl;
DWORD cDefDacl = 0;
NTSTATUS Status;
PSECURITY_DESCRIPTOR psd;
unsigned char buf[SECURITY_DESCRIPTOR_MIN_LENGTH];
BOOL Success;
TOKEN_TYPE Type;
DWORD dummy;
HANDLE hThreadToken;
HANDLE hNull;
#ifdef ALLOW_IMPERSONATION_TOKENS
HANDLE hTempToken;
#endif
//
// Determine type of token, since a non primary token will not work
// on a process. Now, we could duplicate it into a primary token,
// and whack it into the process, but that leaves the process possibly
// without credentials.
//
Status = NtQueryInformationToken(hToken, TokenType,
(PUCHAR) &Type, sizeof(Type), &dummy);
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
NtTerminateProcess(lpProcessInfo->hProcess, ERROR_ACCESS_DENIED);
NtClose(lpProcessInfo->hProcess);
NtClose(lpProcessInfo->hThread);
RtlZeroMemory( lpProcessInfo, sizeof( PROCESS_INFORMATION ) );
return(FALSE);
}
if (Type != TokenPrimary)
{
#ifdef ALLOW_IMPERSONATION_TOKENS
OBJECT_ATTRIBUTES ObjectAttributes;
InitializeObjectAttributes(
&ObjectAttributes,
NULL,
0L,
NULL,
NULL);
SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
SecurityQualityOfService.EffectiveOnly = FALSE;
ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService;
Status = NtDuplicateToken( hToken,
TOKEN_IMPERSONATE | TOKEN_QUERY,
&ObjectAttributes,
FALSE,
TokenPrimary,
&hTempToken
);
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(Status);
NtTerminateProcess(lpProcessInfo->hProcess, ERROR_ACCESS_DENIED);
NtClose(lpProcessInfo->hProcess);
NtClose(lpProcessInfo->hThread);
RtlZeroMemory( lpProcessInfo, sizeof( PROCESS_INFORMATION ) );
return(FALSE);
}
hToken = hTempToken;
#else // !ALLOW_IMPERSONATION_TOKENS
BaseSetLastNTError(STATUS_BAD_TOKEN_TYPE);
NtTerminateProcess(lpProcessInfo->hProcess, ERROR_ACCESS_DENIED);
NtClose(lpProcessInfo->hProcess);
NtClose(lpProcessInfo->hThread);
RtlZeroMemory( lpProcessInfo, sizeof( PROCESS_INFORMATION ) );
return(FALSE);
#endif
}
#ifdef ALLOW_IMPERSONATION_TOKENS
else
{
hTempToken = NULL;
}
#endif
//
// Okay, get the default DACL from the token. This DACL will be
// applied to the process. Note that the creator of this process may
// not be able to open it again after the DACL is applied. However,
// since the caller already has a valid handle (in ProcessInfo), they
// can keep doing things.
//
pDefDacl = NULL;
Status = NtQueryInformationToken(hToken,
TokenDefaultDacl,
NULL, 0, &cDefDacl);
if (NT_SUCCESS(Status) || (Status == STATUS_BUFFER_TOO_SMALL))
{
pDefDacl = RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, cDefDacl);
if (pDefDacl)
{
Status = NtQueryInformationToken( hToken,
TokenDefaultDacl,
pDefDacl, cDefDacl,
&cDefDacl);
}
else
{
Status = STATUS_NO_MEMORY;
}
}
if (!NT_SUCCESS(Status))
{
if (pDefDacl)
{
RtlFreeHeap(RtlProcessHeap(), 0, pDefDacl);
}
//
// Our failure mantra: Set the last error, kill the process (since it
// is suspended, and hasn't actually started yet, we can do this safely)
// close the handles, and return false.
//
#ifdef ALLOW_IMPERSONATION_TOKENS
if (hTempToken)
{
NtClose( hTempToken );
}
#endif
BaseSetLastNTError(Status);
NtTerminateProcess(lpProcessInfo->hProcess, ERROR_ACCESS_DENIED);
NtClose(lpProcessInfo->hProcess);
NtClose(lpProcessInfo->hThread);
RtlZeroMemory( lpProcessInfo, sizeof( PROCESS_INFORMATION ) );
return(FALSE);
}
psd = (PSECURITY_DESCRIPTOR) buf;
InitializeSecurityDescriptor(psd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(psd, TRUE, pDefDacl->DefaultDacl, FALSE);
if (CreateFlags & (COMMON_CREATE_PROCESSSD | COMMON_CREATE_THREADSD))
{
//
// Now, based on what we're told:
//
if (CreateFlags & COMMON_CREATE_PROCESSSD)
{
Success = SetKernelObjectSecurity( lpProcessInfo->hProcess,
DACL_SECURITY_INFORMATION,
psd);
}
//
// Ah, WOW apps created through here don't have thread handles,
// so check:
//
if ((Success) &&
(CreateFlags & COMMON_CREATE_THREADSD))
{
if ( lpProcessInfo->hThread )
{
Success = SetKernelObjectSecurity( lpProcessInfo->hThread,
DACL_SECURITY_INFORMATION,
psd);
}
}
}
else
{
Success = TRUE;
}
if (Success)
{
//
// Unfortunately, this is usually called when we are impersonating,
// because one does not want to start a process for a user that he
// does not actually have access to. However, this user also does
// not have (usually) AssignPrimary and IncreaseQuota privileges.
// So, if we are impersonating, we open the thread token, then
// stop impersonating, saving the token away to restore later.
//
Status = NtOpenThreadToken( NtCurrentThread(),
TOKEN_IMPERSONATE,
TRUE,
&hThreadToken);
if (NT_SUCCESS(Status))
{
//
// Okay, stop impersonating:
//
hNull = NULL;
Status = NtSetInformationThread(
NtCurrentThread(),
ThreadImpersonationToken,
(PVOID) &hNull,
sizeof(hNull)
);
}
else
{
hThreadToken = NULL;
}
//
// Okay, we've set the process security descriptor. Now, set the
// process primary token to the right thing
//
if (L32SetProcessToken( psd,
lpProcessInfo->hProcess,
lpProcessInfo->hThread,
hToken))
{
#ifdef ALLOW_IMPERSONATION_TOKENS
if (hTempToken)
{
NtClose(hTempToken);
}
#endif
//
// That worked. Now adjust the quota to be something reasonable
//
if (L32SetProcessQuotas(lpProcessInfo->hProcess))
{
//
// If we're not supposed to leave it suspended, resume the
// thread and let it run...
//
if ((CreateFlags & COMMON_CREATE_SUSPENDED) == 0)
{
ResumeThread(lpProcessInfo->hThread);
}
RtlFreeHeap(RtlProcessHeap(), 0, pDefDacl);
if (hThreadToken)
{
Status = NtSetInformationThread(
NtCurrentThread(),
ThreadImpersonationToken,
(PVOID) &hThreadToken,
sizeof(hThreadToken)
);
NtClose(hThreadToken);
}
return(TRUE);
}
}
//
// If we were impersonating before, resume impersonating here
//
if (hThreadToken)
{
Status = NtSetInformationThread(
NtCurrentThread(),
ThreadImpersonationToken,
(PVOID) &hThreadToken,
sizeof(hThreadToken)
);
//
// Done with this now.
//
NtClose(hThreadToken);
}
}
//
// Failure mantra again...
//
if (pDefDacl)
{
RtlFreeHeap(RtlProcessHeap(), 0, pDefDacl);
}
NtTerminateProcess(lpProcessInfo->hProcess, ERROR_ACCESS_DENIED);
NtClose(lpProcessInfo->hProcess);
NtClose(lpProcessInfo->hThread);
RtlZeroMemory( lpProcessInfo, sizeof( PROCESS_INFORMATION ) );
return(FALSE);
}
//+---------------------------------------------------------------------------
//
// Function: CreateProcessAsUserA
//
// Synopsis: ANSI wrapper for CreateProcessAsUserW
//
// Arguments: [hToken] --
// [lpApplicationName] --
// [lpCommandLine] --
// [lpProcessAttributes] --
// [lpThreadAttributes] --
// [bInheritHandles] --
// [dwCreationFlags] --
// [lpEnvironment] --
// [lpCurrentDirectory] --
// [lpStartupInfo] --
// [lpProcessInformation] --
//
// History: 4-25-95 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
WINAPI
CreateProcessAsUserA(
HANDLE hToken,
LPCSTR lpApplicationName,
LPSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory,
LPSTARTUPINFOA lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
)
{
DWORD CreateFlags;
CreateFlags = (dwCreationFlags & CREATE_SUSPENDED ? COMMON_CREATE_SUSPENDED : 0);
if (!CreateProcessA(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags | CREATE_SUSPENDED | CREATE_SEPARATE_WOW_VDM,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation))
{
return(FALSE);
}
CreateFlags |= (lpProcessAttributes ? 0 : COMMON_CREATE_PROCESSSD);
CreateFlags |= (lpThreadAttributes ? 0 : COMMON_CREATE_THREADSD);
return(L32CommonCreate(CreateFlags, hToken, lpProcessInformation));
}
//+---------------------------------------------------------------------------
//
// Function: CreateProcessAsUserW
//
// Synopsis: Creates a process running as the user in hToken.
//
// Arguments: [hToken] -- Handle to a Primary Token to use
// [lpApplicationName] -- as CreateProcess() q.v.
// [lpCommandLine] --
// [lpProcessAttributes] --
// [lpThreadAttributes] --
// [bInheritHandles] --
// [dwCreationFlags] --
// [lpEnvironment] --
// [lpCurrentDirectory] --
// [lpStartupInfo] --
// [lpProcessInformation] --
//
// History: 4-25-95 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
WINAPI
CreateProcessAsUserW(
HANDLE hToken,
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
)
{
DWORD CreateFlags;
CreateFlags = (dwCreationFlags & CREATE_SUSPENDED ? COMMON_CREATE_SUSPENDED : 0);
if (!CreateProcessW(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags | CREATE_SUSPENDED | CREATE_SEPARATE_WOW_VDM,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation))
{
return(FALSE);
}
CreateFlags |= (lpProcessAttributes ? 0 : COMMON_CREATE_PROCESSSD);
CreateFlags |= (lpThreadAttributes ? 0 : COMMON_CREATE_THREADSD);
return(L32CommonCreate(CreateFlags, hToken, lpProcessInformation));
}