|
|
//+---------------------------------------------------------------------------
//
// 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>
#define SECURITY_WIN32
#include <security.h>
#include <windows.h>
#include <winbase.h>
#include <winbasep.h>
#include <execsrv.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; ULONG Logon32NegoHandle = 0xFFFFFFFF; WCHAR Logon32DomainName[DNLEN+1] = L"";
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
BOOL WINAPI LogonUserCommonA( LPSTR lpszUsername, LPSTR lpszDomain, LPSTR lpszPassword, DWORD dwLogonType, DWORD dwLogonProvider, BOOL fExVersion, HANDLE * phToken, PSID * ppLogonSid, PVOID * ppProfileBuffer, DWORD * pdwProfileLength, PQUOTA_LIMITS pQuotaLimits );
BOOL WINAPI LogonUserCommonW( PWSTR lpszUsername, PWSTR lpszDomain, PWSTR lpszPassword, DWORD dwLogonType, DWORD dwLogonProvider, BOOL fExVersion, HANDLE * phToken, PSID * ppLogonSid, PVOID * ppProfileBuffer, DWORD * pdwProfileLength, PQUOTA_LIMITS pQuotaLimits );
//+---------------------------------------------------------------------------
//
// 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 ); }
/***************************************************************************\
* FindLogonSid * * Finds logon sid for a new logon from the access token. * \***************************************************************************/ PSID L32FindLogonSid( IN HANDLE hToken ) { PTOKEN_GROUPS pGroups = NULL; DWORD cbGroups; BYTE FastBuffer[ 512 ]; PTOKEN_GROUPS pSlowBuffer = NULL; UINT i; PSID Sid = NULL;
pGroups = (PTOKEN_GROUPS)FastBuffer; cbGroups = sizeof(FastBuffer);
if(!GetTokenInformation( hToken, TokenGroups, pGroups, cbGroups, &cbGroups )) { if( GetLastError() != ERROR_INSUFFICIENT_BUFFER ) { return NULL; }
pSlowBuffer = (PTOKEN_GROUPS)LocalAlloc(LMEM_FIXED, cbGroups);
if( pSlowBuffer == NULL ) { return NULL; }
pGroups = pSlowBuffer;
if(!GetTokenInformation( hToken, TokenGroups, pGroups, cbGroups, &cbGroups )) { goto Cleanup; } }
//
// Get the logon Sid by looping through the Sids in the token
//
for(i = 0 ; i < pGroups->GroupCount ; i++) { if(pGroups->Groups[i].Attributes & SE_GROUP_LOGON_ID) { DWORD dwSidLength;
//
// insure we are dealing with a valid Sid
//
if(!IsValidSid(pGroups->Groups[i].Sid)) { goto Cleanup; }
//
// get required allocation size to copy the Sid
//
dwSidLength = GetLengthSid(pGroups->Groups[i].Sid);
Sid = (PSID)LocalAlloc( LMEM_FIXED, dwSidLength ); if( Sid == NULL ) { goto Cleanup; }
CopySid(dwSidLength, Sid, pGroups->Groups[i].Sid);
break; } }
Cleanup:
if( pSlowBuffer ) { LocalFree( pSlowBuffer ); }
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) { STRING PackageName;
ULONG MsvHandle; ULONG NegoHandle;
NTSTATUS Status;
//
// Hookup to the LSA and locate our authentication package.
//
Status = LsaConnectUntrusted( &Logon32LsaHandle );
if (!NT_SUCCESS(Status)) { Logon32LsaHandle = NULL; goto Cleanup; }
//
// Connect with the MSV1_0 authentication package
//
RtlInitString(&PackageName, "MICROSOFT_AUTHENTICATION_PACKAGE_V1_0"); Status = LsaLookupAuthenticationPackage ( Logon32LsaHandle, &PackageName, &MsvHandle );
if (!NT_SUCCESS(Status)) { goto Cleanup; }
//
// Connect with the Negotiate authentication package
//
RtlInitString(&PackageName, NEGOSSP_NAME_A); Status = LsaLookupAuthenticationPackage ( Logon32LsaHandle, &PackageName, &NegoHandle );
if (!NT_SUCCESS(Status)) { goto Cleanup; }
//
// Wait until successful to update the 2 globals.
//
Logon32NegoHandle = NegoHandle; Logon32MsvHandle = MsvHandle;
Cleanup:
if( !NT_SUCCESS(Status) ) {
if( Logon32LsaHandle ) { (VOID) LsaDeregisterLogonProcess( Logon32LsaHandle ); Logon32LsaHandle = NULL; }
BaseSetLastNTError( Status ); 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; LUID LocalServiceLuid = LOCALSERVICE_LUID; LUID NetworkServiceLuid = NETWORKSERVICE_LUID;
if (RtlEqualLuid(LogonId, &LocalServiceLuid) || RtlEqualLuid(LogonId, &NetworkServiceLuid)) { //
// Don't notify providers for LocalService/NetworkService logons
//
return( TRUE ); }
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] --
// [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, 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; WCHAR ComputerName[ MAX_COMPUTERNAME_LENGTH + 1 ]; DWORD ComputerNameLength;
//
// 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 ) + UserName->Length + Domain->Length + sizeof(WCHAR) * (ComputerNameLength + 1) + Password->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 = UserName->Length; MsvNetAuthInfo->UserName.MaximumLength = MsvNetAuthInfo->UserName.Length;
MsvNetAuthInfo->UserName.Buffer = (PWSTR)(MsvNetAuthInfo+1); RtlCopyMemory( MsvNetAuthInfo->UserName.Buffer, UserName->Buffer, UserName->Length );
//
// Copy the domain name into the authentication buffer
//
MsvNetAuthInfo->LogonDomainName.Length = Domain->Length; MsvNetAuthInfo->LogonDomainName.MaximumLength = Domain->Length ;
MsvNetAuthInfo->LogonDomainName.Buffer = (PWSTR) ((PBYTE)(MsvNetAuthInfo->UserName.Buffer) + MsvNetAuthInfo->UserName.MaximumLength);
RtlCopyMemory( MsvNetAuthInfo->LogonDomainName.Buffer, Domain->Buffer, Domain->Length);
//
// 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 );
//
// Set up space for Password (Unicode)
//
MsvNetAuthInfo->CaseSensitiveChallengeResponse.Buffer = (PUCHAR) ((PBYTE) (MsvNetAuthInfo->Workstation.Buffer) + MsvNetAuthInfo->Workstation.MaximumLength );
MsvNetAuthInfo->CaseSensitiveChallengeResponse.Length = MsvNetAuthInfo->CaseSensitiveChallengeResponse.MaximumLength = Password->Length;
RtlCopyMemory( MsvNetAuthInfo->CaseSensitiveChallengeResponse.Buffer, Password->Buffer, Password->Length);
//
// Zero out the ascii password
//
RtlZeroMemory( &MsvNetAuthInfo->CaseInsensitiveChallengeResponse, sizeof(MsvNetAuthInfo->CaseInsensitiveChallengeResponse));
//
// to be consistent with Negotiate/Kerberos for _WINNT50 cases,
// allow machine accounts to be logged on.
//
MsvNetAuthInfo->ParameterControl = MSV1_0_CLEARTEXT_PASSWORD_ALLOWED | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT;
} else { //
// Build logon structure for non-network logons - service,
// batch, interactive, unlock, new credentials, networkcleartext
//
AuthInfoSize = sizeof(MSV1_0_INTERACTIVE_LOGON) + UserName->Length + Domain->Length + Password->Length;
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 = UserName->Length; MsvAuthInfo->UserName.MaximumLength = MsvAuthInfo->UserName.Length;
MsvAuthInfo->UserName.Buffer = (PWSTR)(MsvAuthInfo+1); RtlCopyMemory( MsvAuthInfo->UserName.Buffer, UserName->Buffer, UserName->Length );
//
// Copy the domain name into the authentication buffer
//
MsvAuthInfo->LogonDomainName.Length = Domain->Length; MsvAuthInfo->LogonDomainName.MaximumLength = MsvAuthInfo->LogonDomainName.Length;
MsvAuthInfo->LogonDomainName.Buffer = (PWSTR) ((PBYTE)(MsvAuthInfo->UserName.Buffer) + MsvAuthInfo->UserName.MaximumLength);
RtlCopyMemory( MsvAuthInfo->LogonDomainName.Buffer, Domain->Buffer, Domain->Length );
//
// 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 = Password->Length; MsvAuthInfo->Password.MaximumLength = MsvAuthInfo->Password.Length;
MsvAuthInfo->Password.Buffer = (PWSTR) ((PBYTE)(MsvAuthInfo->LogonDomainName.Buffer) + MsvAuthInfo->LogonDomainName.MaximumLength);
RtlCopyMemory( MsvAuthInfo->Password.Buffer, Password->Buffer, Password->Length );
}
//
// Now try to log this sucker on
//
Status = LsaLogonUser ( LsaHandle, &OriginName, LogonType, AuthenticationPackage, AuthInfoBuf, AuthInfoSize, NULL, &SourceContext, pProfileBuffer, pProfileBufferLength, LogonId, LogonToken, Quotas, pSubStatus );
//
// 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: LogonUserCommonA
//
// Synopsis: ANSI wrapper for LogonUserCommonW. See description below
//
// Arguments: [lpszUsername] --
// [lpszDomain] --
// [lpszPassword] --
// [dwLogonType] --
// [dwLogonProvider] --
// [fExVersion] --
// [phToken] --
// [ppLogonSid] --
// [ppProfileBuffer] --
// [pdwProfileLength] --
// [pQuotaLimits] --
//
// History: 2-15-2000 JSchwart Created from RichardW's LogonUserA
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL WINAPI LogonUserCommonA( LPSTR lpszUsername, LPSTR lpszDomain, LPSTR lpszPassword, DWORD dwLogonType, DWORD dwLogonProvider, BOOL fExVersion, HANDLE * phToken, PSID * ppLogonSid, PVOID * ppProfileBuffer, DWORD * pdwProfileLength, PQUOTA_LIMITS pQuotaLimits ) { UNICODE_STRING Username; UNICODE_STRING Domain; UNICODE_STRING Password; ANSI_STRING Temp ; NTSTATUS Status; BOOL bRet;
Username.Buffer = NULL; Domain.Buffer = NULL; Password.Buffer = NULL;
RtlInitAnsiString( &Temp, lpszUsername ); Status = RtlAnsiStringToUnicodeString( &Username, &Temp, TRUE ); if (!NT_SUCCESS( Status ) ) { BaseSetLastNTError(Status); bRet = FALSE; goto Cleanup; }
RtlInitAnsiString( &Temp, lpszDomain ); Status = RtlAnsiStringToUnicodeString(&Domain, &Temp, TRUE ); if (!NT_SUCCESS(Status)) { BaseSetLastNTError(Status); bRet = FALSE; goto Cleanup; }
RtlInitAnsiString( &Temp, lpszPassword ); Status = RtlAnsiStringToUnicodeString( &Password, &Temp, TRUE ); if (!NT_SUCCESS(Status)) { BaseSetLastNTError(Status); bRet = FALSE; goto Cleanup; }
bRet = LogonUserCommonW( Username.Buffer, Domain.Buffer, Password.Buffer, dwLogonType, dwLogonProvider, fExVersion, phToken, ppLogonSid, ppProfileBuffer, pdwProfileLength, pQuotaLimits );
Cleanup:
if (Username.Buffer) { RtlFreeUnicodeString(&Username); }
if (Domain.Buffer) { RtlFreeUnicodeString(&Domain); }
if (Password.Buffer) { RtlZeroMemory(Password.Buffer, Password.Length); RtlFreeUnicodeString(&Password); }
return(bRet); }
//+---------------------------------------------------------------------------
//
// 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 ) { return LogonUserCommonA(lpszUsername, lpszDomain, lpszPassword, dwLogonType, dwLogonProvider, FALSE, // LogonUserA
phToken, NULL, // ppLogonSid
NULL, // ppProfileBuffer
NULL, // pdwProfileLength
NULL); // pQuotaLimits
}
//+---------------------------------------------------------------------------
//
// Function: LogonUserExA
//
// Synopsis: ANSI wrapper for LogonUserExW. See description below
//
// Arguments: [lpszUsername] --
// [lpszDomain] --
// [lpszPassword] --
// [dwLogonType] --
// [dwLogonProvider] --
// [phToken] --
// [ppLogonSid] --
// [ppProfileBuffer] --
// [pdwProfileLength] --
// [pQuotaLimits] --
//
// History: 2-15-2000 JSchwart Created from RichardW's LogonUserW
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL WINAPI LogonUserExA( LPSTR lpszUsername, LPSTR lpszDomain, LPSTR lpszPassword, DWORD dwLogonType, DWORD dwLogonProvider, HANDLE * phToken, PSID * ppLogonSid, PVOID * ppProfileBuffer, DWORD * pdwProfileLength, PQUOTA_LIMITS pQuotaLimits ) { return LogonUserCommonA(lpszUsername, lpszDomain, lpszPassword, dwLogonType, dwLogonProvider, TRUE, // LogonUserExA
phToken, ppLogonSid, ppProfileBuffer, pdwProfileLength, pQuotaLimits); }
//+---------------------------------------------------------------------------
//
// Function: LogonUserCommonW
//
// Synopsis: Common code for LogonUserW and LogonUserExW. 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
// [fExVersion] -- LogonUserExW or LogonUserW
// [phToken] -- Returned handle to primary token
// [ppLogonSid] -- Returned logon sid
// [ppProfileBuffer] -- Returned user profile buffer
// [pdwProfileLength] -- Returned profile length
//
// History: 2-15-2000 JSchwart Created from RichardW's LogonUserW
//
// Notes: Requires SeTcbPrivilege, and will enable it if not already
// present.
//
//----------------------------------------------------------------------------
BOOL WINAPI LogonUserCommonW( PWSTR lpszUsername, PWSTR lpszDomain, PWSTR lpszPassword, DWORD dwLogonType, DWORD dwLogonProvider, BOOL fExVersion, HANDLE * phToken, PSID * ppLogonSid, PVOID * ppProfileBuffer, DWORD * pdwProfileLength, PQUOTA_LIMITS pQuotaLimits ) { NTSTATUS Status; ULONG PackageId; UNICODE_STRING Username; UNICODE_STRING Domain; UNICODE_STRING Password; HANDLE hTempToken; HANDLE * phTempToken; LUID LogonId; PVOID Profile; ULONG ProfileLength; NTSTATUS SubStatus = STATUS_SUCCESS; SECURITY_LOGON_TYPE LogonType;
//
// Validate the provider
//
if (dwLogonProvider == LOGON32_PROVIDER_DEFAULT) { dwLogonProvider = LOGON32_PROVIDER_WINNT50;
//
// if domain was not supplied, and username is not a UPN, use
// _WINNT40 to be compatible.
//
if((lpszUsername != NULL) && (lpszDomain == NULL || lpszDomain[ 0 ] == L'\0')) { if( wcschr( lpszUsername, '@' ) == NULL ) { dwLogonProvider = LOGON32_PROVIDER_WINNT40; } } }
if (dwLogonProvider > LOGON32_PROVIDER_WINNT50) { 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;
case LOGON32_LOGON_UNLOCK: LogonType = Unlock ; break;
case LOGON32_LOGON_NETWORK_CLEARTEXT: LogonType = NetworkCleartext ; break;
case LOGON32_LOGON_NEW_CREDENTIALS: LogonType = NewCredentials; break;
default: BaseSetLastNTError(STATUS_INVALID_PARAMETER); return(FALSE); break; }
//
// If the MSV handle is -1, grab the lock, and try again:
//
if (Logon32MsvHandle == 0xFFFFFFFF || Logon32NegoHandle == 0xFFFFFFFF) { LockLogon();
//
// If the MSV handle is still -1, init our connection to lsa. We
// have the lock, so no other threads can't be trying this right now.
//
if (Logon32MsvHandle == 0xFFFFFFFF || Logon32NegoHandle == 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); }
//
// Initialize/check parameters based on which API we're servicing.
//
if (!fExVersion) { //
// LogonUserW -- phToken is required. Initialize the token handle,
// if the pointer is invalid, then catch the exception now.
//
*phToken = NULL; phTempToken = phToken; } else { //
// LogonUserExW -- phToken, ppLogonSid, ppProfileBuffer, and
// pdwProfileLength are optional. Initialize as appropriate.
//
if (ARGUMENT_PRESENT(phToken)) { *phToken = NULL; phTempToken = phToken; } else { //
// Dummy token handle to use in the LsaLogonUser call
//
phTempToken = &hTempToken; }
if (ARGUMENT_PRESENT(ppLogonSid)) { *ppLogonSid = NULL; }
if (!!ppProfileBuffer ^ !!pdwProfileLength) { //
// Can't have one without the other...
//
BaseSetLastNTError(STATUS_INVALID_PARAMETER); return(FALSE); }
if (ARGUMENT_PRESENT(ppProfileBuffer)) { *ppProfileBuffer = NULL; *pdwProfileLength = 0; }
if (ARGUMENT_PRESENT(pQuotaLimits)) { RtlZeroMemory(pQuotaLimits, sizeof(QUOTA_LIMITS)); } }
//
// 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);
//
// Attempt the logon
//
Status = L32pLogonUser( Logon32LsaHandle, (dwLogonProvider == LOGON32_PROVIDER_WINNT50) ? Logon32NegoHandle : Logon32MsvHandle, LogonType, &Username, &Domain, &Password, &LogonId, phTempToken, pQuotaLimits ? pQuotaLimits : &Logon32QuotaLimits, &Profile, &ProfileLength, &SubStatus);
//
// Set output parameters based on which API we're servicing
//
// TODO: review cleanup code if something fails mid-stream.
//
if (!fExVersion) {
if (!NT_SUCCESS(Status)) { if (Status == STATUS_ACCOUNT_RESTRICTION) { BaseSetLastNTError(SubStatus); } else { BaseSetLastNTError(Status); }
return(FALSE); }
if (Profile != NULL) { LsaFreeReturnBuffer(Profile); } } else { //
// We may need the allocated buffers if all went well, so
// check the return status first.
//
if (!NT_SUCCESS(Status)) {
if (Status == STATUS_ACCOUNT_RESTRICTION) { BaseSetLastNTError(SubStatus); } else { BaseSetLastNTError(Status); }
return(FALSE); }
//
// The logon succeeded -- fill in the requested output parameters.
//
if (ARGUMENT_PRESENT(ppProfileBuffer)) { if (Profile != NULL) { ASSERT(ProfileLength != 0);
*ppProfileBuffer = Profile; *pdwProfileLength = ProfileLength; } } else { if (Profile != NULL) { LsaFreeReturnBuffer(Profile); } }
if (ARGUMENT_PRESENT(ppLogonSid)) { *ppLogonSid = L32FindLogonSid( *phTempToken ); }
if (!ARGUMENT_PRESENT(phToken)) { //
// Close the dummy token handle
//
CloseHandle(*phTempToken); } }
return(TRUE); }
//+---------------------------------------------------------------------------
//
// 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 ) { return LogonUserCommonW(lpszUsername, lpszDomain, lpszPassword, dwLogonType, dwLogonProvider, FALSE, // LogonUserW
phToken, NULL, // ppLogonSid
NULL, // ppProfileBuffer
NULL, // pdwProfileLength
NULL); // pQuotaLimits
}
//+---------------------------------------------------------------------------
//
// Function: LogonUserExW
//
// 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
// [ppLogonSid] -- Returned logon sid
// [ppProfileBuffer] -- Returned user profile buffer
// [pdwProfileLength] -- Returned profile length
// [pQuotaLimits] -- Returned quota limits
//
// History: 2-15-2000 JSchwart Created from RichardW's LogonUserW
//
// Notes: Requires SeTcbPrivilege, and will enable it if not already
// present.
//
//----------------------------------------------------------------------------
BOOL WINAPI LogonUserExW( PWSTR lpszUsername, PWSTR lpszDomain, PWSTR lpszPassword, DWORD dwLogonType, DWORD dwLogonProvider, HANDLE * phToken, PSID * ppLogonSid, PVOID * ppProfileBuffer, DWORD * pdwProfileLength, PQUOTA_LIMITS pQuotaLimits ) { return LogonUserCommonW(lpszUsername, lpszDomain, lpszPassword, dwLogonType, dwLogonProvider, TRUE, // LogonUserExW
phToken, ppLogonSid, ppProfileBuffer, pdwProfileLength, pQuotaLimits); }
//+---------------------------------------------------------------------------
//
// 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, BOOL AlreadyImpersonating ) { 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
//
if ( !AlreadyImpersonating ) { Status = RtlImpersonateSelf( SecurityImpersonation ); } else { Status = STATUS_SUCCESS ; }
if ( NT_SUCCESS( Status ) ) { //
// We now allow restricted tokens to passed in, so we don't
// fail if the privilege isn't held. Let the kernel deal with
// the possibilities.
//
Status = RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, TRUE, TRUE, &WasEnabled);
if ( !NT_SUCCESS( Status ) ) { WasEnabled = TRUE ; // Don't try to restore it.
}
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.
//
if ( !AlreadyImpersonating ) { 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
//
NtClose(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, BOOL AlreadyImpersonating ) { 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.
//
if ( !AlreadyImpersonating ) { 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; } } }
}
if ( !AlreadyImpersonating ) { 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 = TRUE; TOKEN_TYPE Type; DWORD dummy; HANDLE hThreadToken; HANDLE hNull; BOOL UsingImpToken = FALSE ;
#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
//
Success = L32SetProcessToken( psd, lpProcessInfo->hProcess, lpProcessInfo->hThread, hToken, FALSE );
if ( !Success && hThreadToken ) { Status = NtSetInformationThread( NtCurrentThread(), ThreadImpersonationToken, (PVOID) &hThreadToken, sizeof(hThreadToken) );
UsingImpToken = TRUE ;
Success = L32SetProcessToken( psd, lpProcessInfo->hProcess, lpProcessInfo->hThread, hToken, TRUE ); }
if ( Success ) { #ifdef ALLOW_IMPERSONATION_TOKENS
if (hTempToken) { NtClose(hTempToken); } #endif
//
// That worked. Now adjust the quota to be something reasonable
//
Success = L32SetProcessQuotas( lpProcessInfo->hProcess, UsingImpToken );
if ( (!Success) && (hThreadToken != NULL) && (UsingImpToken == FALSE ) ) { Status = NtSetInformationThread( NtCurrentThread(), ThreadImpersonationToken, (PVOID) &hThreadToken, sizeof(hThreadToken) );
UsingImpToken = TRUE ;
Success = L32SetProcessQuotas( lpProcessInfo->hProcess, TRUE );
} if ( Success ) { //
// 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: SaferiReplaceProcessThreadTokens
//
// Synopsis:
// Provides a privately exported function to replace the access token
// of a process and its primary thread of a new process before its
// execution has begun. The process is left in a suspended state
// after the token modification has been performed.
//
// Effects:
//
// Arguments: [NewTokenHandle] -- Primary token to use
// [ProcessHandle] -- Process handle
// [ThreadHandle] -- Handle of process's primary Thread
//
// History: 8-25-2000 JLawson Created
//
// Notes:
// This is merely a wrapper function that calls L32CommonCreate.
//
//----------------------------------------------------------------------------
BOOL WINAPI SaferiReplaceProcessThreadTokens( IN HANDLE NewTokenHandle, IN HANDLE ProcessHandle, IN HANDLE ThreadHandle ) { PROCESS_INFORMATION TempProcessInfo;
RtlZeroMemory( &TempProcessInfo, sizeof( PROCESS_INFORMATION ) ); TempProcessInfo.hProcess = ProcessHandle; TempProcessInfo.hThread = ThreadHandle; return (L32CommonCreate( COMMON_CREATE_PROCESSSD | COMMON_CREATE_THREADSD | COMMON_CREATE_SUSPENDED, NewTokenHandle, &TempProcessInfo)); }
//+---------------------------------------------------------------------------
//
// MarshallString
//
// Marshall in a UNICODE_NULL terminated WCHAR string
//
// ENTRY:
// pSource (input)
// Pointer to source string
//
// pBase (input)
// Base buffer pointer for normalizing the string pointer
//
// MaxSize (input)
// Maximum buffer size available
//
// ppPtr (input/output)
// Pointer to the current context pointer in the marshall buffer.
// This is updated as data is marshalled into the buffer
//
// pCount (input/output)
// Current count of data in the marshall buffer.
// This is updated as data is marshalled into the buffer
//
// EXIT:
// NULL - Error
// !=NULL "normalized" pointer to the string in reference to pBase
//
//+---------------------------------------------------------------------------
PWCHAR MarshallString( PCWSTR pSource, PCHAR pBase, ULONG MaxSize, PCHAR *ppPtr, PULONG pCount ) { ULONG Len; PCHAR ptr;
Len = wcslen( pSource ); Len++; // include the NULL;
Len *= sizeof(WCHAR); // convert to bytes
if( (*pCount + Len) > MaxSize ) { return( NULL ); }
RtlMoveMemory( *ppPtr, pSource, Len );
//
// the normalized ptr is the current count
//
// Sundown note: ptr is a zero-extension of *pCount.
ptr = (PCHAR)ULongToPtr(*pCount);
*ppPtr += Len; *pCount += Len;
return((PWCHAR)ptr); }
#if DBG
void DumpOutLastErrorString() { LPVOID lpMsgBuf;
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf, 0, NULL ); //
// Process any inserts in lpMsgBuf.
// ...
// Display the string.
//
KdPrint(("%s\n", (LPCTSTR)lpMsgBuf ));
//
// Free the buffer.
//
LocalFree( lpMsgBuf ); } #endif
#ifdef DBG
#define DBG_DumpOutLastError DumpOutLastErrorString();
#else
#define DBG_DumpOutLastError
#endif
//+---------------------------------------------------------------------------
//
// This function was originally defined in \nt\private\ole32\dcomss\olescm\execclt.cxx
//
// CreateRemoteSessionProcessW()
//
// Create a process on the given Terminal Server Session. This is in UNICODE
//
// ENTRY:
// SessionId (input)
// SessionId of Session to create process on
//
// Param1 (input/output)
// Comments
//
// Comments
// The security attribs are not used by the session, they are set to NULL
// We may consider to extend this feature in the future, assuming there is a
// need for it.
//
// EXIT:
// STATUS_SUCCESS - no error
//+---------------------------------------------------------------------------
BOOL CreateRemoteSessionProcessW( ULONG SessionId, BOOL System, HANDLE hToken, PCWSTR lpszImageName, PCWSTR lpszCommandLine, PSECURITY_ATTRIBUTES psaProcess, // these are ignored on the session side, set to NULL
PSECURITY_ATTRIBUTES psaThread, // these are ignored on the session side, set to NULL
BOOL fInheritHandles, DWORD fdwCreate, LPVOID lpvEnvionment, LPCWSTR lpszCurDir, LPSTARTUPINFOW pStartInfo, LPPROCESS_INFORMATION pProcInfo ) { BOOL Result = TRUE; HANDLE hPipe = NULL; WCHAR szPipeName[MAX_PATH]; PCHAR ptr; ULONG Count, AmountWrote, AmountRead; DWORD MyProcId; PEXECSRV_REQUEST pReq; EXECSRV_REPLY Rep; CHAR Buf[EXECSRV_BUFFER_SIZE]; ULONG MaxSize = EXECSRV_BUFFER_SIZE; DWORD rc; LPVOID lpMsgBuf; ULONG envSize=0; // size of the lpEnvironemt, if any
PWCHAR lpEnv;
#if DBG
if( lpszImageName ) KdPrint(("logon32.c: CreateRemoteSessionProcessW: lpszImageName %ws\n",lpszImageName));
if( lpszCommandLine ) KdPrint(("logon32.c: CreateRemoteSessionProcessW: lpszCommandLine %ws\n",lpszCommandLine)); #endif
//
// Winlogon handles all now. System flag tells it what to do
//
swprintf(szPipeName, EXECSRV_SYSTEM_PIPE_NAME, SessionId);
while ( TRUE ) { hPipe = CreateFileW( szPipeName, GENERIC_READ|GENERIC_WRITE, 0, // File share mode
NULL, // default security
OPEN_EXISTING, 0, // Attrs and flags
NULL // template file handle
);
if( hPipe == INVALID_HANDLE_VALUE ) { if (GetLastError() == ERROR_PIPE_BUSY) { if (!WaitNamedPipeW( szPipeName, 30000 )) { // 30 sec
KdPrint(("logon32.c: Waited too long for pipe name %ws\n", szPipeName)); return(FALSE); } } else { DBG_DumpOutLastError; KdPrint(("logon32.c: Could not create pipe name %ws\n", szPipeName)); return(FALSE); } } else { break; } }
//
// Get the handle to the current process
//
MyProcId = GetCurrentProcessId();
//
// setup the marshalling
//
ptr = Buf; Count = 0;
pReq = (PEXECSRV_REQUEST)ptr; ptr += sizeof(EXECSRV_REQUEST); Count += sizeof(EXECSRV_REQUEST);
//
// set the basic parameters
//
pReq->System = System; pReq->hToken = hToken; pReq->RequestingProcessId = MyProcId; pReq->fInheritHandles = fInheritHandles; pReq->fdwCreate = fdwCreate;
//
// marshall the ImageName string
//
if( lpszImageName ) { pReq->lpszImageName = MarshallString( lpszImageName, Buf, MaxSize, &ptr, &Count ); if (! pReq->lpszImageName) { Result = FALSE; goto Cleanup; } } else { pReq->lpszImageName = NULL; }
//
// marshall in the CommandLine string
//
if( lpszCommandLine ) { pReq->lpszCommandLine = MarshallString( lpszCommandLine, Buf, MaxSize, &ptr, &Count ); if ( ! pReq->lpszCommandLine ) { Result = FALSE; goto Cleanup; } } else { pReq->lpszCommandLine = NULL; }
//
// marshall in the CurDir string
//
if( lpszCurDir ) { pReq->lpszCurDir = MarshallString( lpszCurDir, Buf, MaxSize, &ptr, &Count ); if ( ! pReq->lpszCurDir ) { Result = FALSE; goto Cleanup; } } else { pReq->lpszCurDir = NULL; }
//
// marshall in the StartupInfo structure
//
RtlMoveMemory( &pReq->StartInfo, pStartInfo, sizeof(STARTUPINFO) );
//
// Now marshall the strings in STARTUPINFO
//
if( pStartInfo->lpDesktop ) { pReq->StartInfo.lpDesktop = MarshallString( pStartInfo->lpDesktop, Buf, MaxSize, &ptr, &Count ); if (! pReq->StartInfo.lpDesktop ) { Result = FALSE; goto Cleanup; } } else { pReq->StartInfo.lpDesktop = NULL; }
if( pStartInfo->lpTitle ) { pReq->StartInfo.lpTitle = MarshallString( pStartInfo->lpTitle, Buf, MaxSize, &ptr, &Count ); if ( !pReq->StartInfo.lpTitle ) { Result = FALSE; goto Cleanup; } } else { pReq->StartInfo.lpTitle = NULL; }
//
// WARNING: This version does not pass the following:
//
// Also saProcess and saThread are ignored right now and use
// the users default security on the remote WinStation
//
// Set things that are always NULL
//
pReq->StartInfo.lpReserved = NULL; // always NULL
if ( lpvEnvionment) { for ( lpEnv = (PWCHAR) lpvEnvionment; (*lpEnv ) && (envSize + Count < MaxSize ) ; lpEnv++) { while( *lpEnv ) { lpEnv++; envSize += 2; // we are dealing with wide chars
if ( envSize+Count >= MaxSize ) { // we have too many
// vars in the user's profile.
KdPrint(("\tEnv length too big = %d \n", envSize)); break; } } // this is the null which marked the end of the last env var.
envSize +=2;
} envSize += 2; // this is the final NULL
if ( Count + envSize < MaxSize ) { RtlMoveMemory( (PCHAR)&Buf[Count] ,lpvEnvionment, envSize ); // SUNDOWN: Count is zero-extended and store in lpvEnvironment.
// This zero-extension is valid. The consuming code [see tsext\notify\execsrv.c]
// considers lpvEnvironment as an offset (<2GB).
pReq->lpvEnvironment = (PCHAR)ULongToPtr(Count); ptr += envSize; // for the next guy
Count += envSize; // the count used so far
} else // no room left to make a complete copy
{ pReq->lpvEnvironment = NULL; }
} else { pReq->lpvEnvironment = NULL; }
//
// now fill in the total count
//
pReq->Size = Count;
#if DBG
KdPrint(("pReq->Size = %d, envSize = %d \n", pReq->Size , envSize )); #endif
//
// Now send the buffer out to the server
//
Result = WriteFile( hPipe, Buf, Count, &AmountWrote, NULL );
if( !Result ) { KdPrint(("logon32.c: Error %d sending request\n",GetLastError() )); goto Cleanup; }
//
// Now read the reply
//
Result = ReadFile( hPipe, &Rep, sizeof(Rep), &AmountRead, NULL );
if( !Result ) { KdPrint(("logon32.c: Error %d reading reply\n",GetLastError())); goto Cleanup; }
//
// Check the result
//
if( !Rep.Result ) { KdPrint(("logon32.c: Error %d in reply\n",Rep.LastError)); //
// set the error in the current thread to the returned error
//
Result = Rep.Result; SetLastError( Rep.LastError ); goto Cleanup; }
//
// We copy the PROCESS_INFO structure from the reply
// to the caller.
//
// The remote site has duplicated the handles into our
// process space for hProcess and hThread so that they will
// behave like CreateProcessW()
//
RtlMoveMemory( pProcInfo, &Rep.ProcInfo, sizeof( PROCESS_INFORMATION ) );
Cleanup: CloseHandle(hPipe);
KdPrint(("logon32.c:: Result 0x%x\n", Result));
return(Result); }
//+---------------------------------------------------------------------------
//
// 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] --
//
// Return Values
// If the function succeeds, the return value is nonzero.
// If the function fails, the return value is zero. To get extended error information, call GetLastError.
//
// History: 4-25-95 RichardW Created
// 1-14-98 AraBern add changes for Hydra
// 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; DWORD clientSessionID=0; DWORD currentSessionID=0; DWORD resultLength; HANDLE hTmpToken; DWORD curProcId ; NTSTATUS Status ;
CreateFlags = (dwCreationFlags & CREATE_SUSPENDED ? COMMON_CREATE_SUSPENDED : 0);
//
// get the sessionID (if zero then it means that we are on the console).
//
currentSessionID = NtCurrentPeb()->SessionId;
if ( !GetTokenInformation ( hToken, TokenSessionId , &clientSessionID,sizeof( DWORD), &resultLength ) ) { //
// get the access token for the client of this call
// get token instead of process since the client might have only
// impersonated the thread, not the process
//
DBG_DumpOutLastError; ASSERT( FALSE ); currentSessionID = 0;
//
// We should probably return FALSE here, but at this time we don't want to alter the
// non-Hydra code-execution-flow at all.
//
}
// KdPrint(("logon32.c: CreateProcessAsUserW(): clientSessionID = %d, currentSessionID = %d \n",
// clientSessionID, currentSessionID ));
if ( clientSessionID != currentSessionID ) { //
// If the client session ID is not the same as the current session ID, then, we are attempting
// to create a process on a remote session from the current session.
// This block of code is used to accomplish such process creation, it is Terminal-Server specific
//
BOOL bHaveImpersonated; HANDLE hCurrentThread; HANDLE hPrevToken = NULL; DWORD rc; TOKEN_TYPE tokenType;
//
// We must send the request to the remote session
// of the requestor
//
// NOTE: The current WinStationCreateProcessW() does not use
// the supplied security descriptor, but creates the
// process under the account of the logged on user.
//
//
// Stop impersonating before doing the WinStationCreateProcess.
// The remote winstation exec thread will launch the app under
// the users context. We must not be impersonating because this
// call only lets SYSTEM request the remote execute.
//
//
// Handle Inheritance is not allowed for cross session process creation
//
if (bInheritHandles) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE; }
hCurrentThread = GetCurrentThread();
//
// Init bHaveImpersonated to the FALSE state
//
bHaveImpersonated = FALSE;
//
// Since the caller of this function (runas-> SecLogon service ) has already
// impersonated the new (target) user, we do the OpenThreadToken with
// OpenAsSelf = TRUE
//
if ( OpenThreadToken( hCurrentThread, TOKEN_QUERY, TRUE, &hPrevToken ) ) {
bHaveImpersonated = TRUE;
if ( !RevertToSelf() ) { return FALSE; } }
//
// else, we are not impersonating, as reflected by the init value of bHaveImpersonated
//
rc = CreateRemoteSessionProcessW( clientSessionID, FALSE, // not creating a process for System
hToken, lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags | CREATE_SEPARATE_WOW_VDM, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation) ;
//
// Undo the effect of RevertToSelf() if we had impersoanted
//
if ( bHaveImpersonated ) { Status = NtSetInformationThread( NtCurrentThread(), ThreadImpersonationToken, &hPrevToken, sizeof( hPrevToken ) );
NtClose( hPrevToken ); }
if ( rc ) { return TRUE; } else { return FALSE; }
} else //
// this is the standard non-Hydra related call block
//
{ HANDLE hRestrictedToken = NULL; BOOL b = FALSE;
if (!CreateProcessInternalW(hToken, lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, (dwCreationFlags | CREATE_SUSPENDED | CREATE_SEPARATE_WOW_VDM), lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation, &hRestrictedToken)) { //
// The internal routine might return a token even in the failure case
// since it uses try-finally. Free the token if needed.
//
if (hRestrictedToken != NULL) { NtClose(hRestrictedToken); } return(FALSE); }
CreateFlags |= (lpProcessAttributes ? 0 : COMMON_CREATE_PROCESSSD); CreateFlags |= (lpThreadAttributes ? 0 : COMMON_CREATE_THREADSD);
//
// If a restricted token was returned, set it on the process.
// Else use the token provided by the caller.
//
if (hRestrictedToken == NULL) { b = (L32CommonCreate(CreateFlags, hToken, lpProcessInformation)); } else { b = (L32CommonCreate(CreateFlags, hRestrictedToken, lpProcessInformation)); NtClose(hRestrictedToken); }
return b; } }
/***************************************************************************\
* OemToCharW * * OemToCharW(pSrc, pDst) - Translates the OEM string at pSrc into * the Unicode string at pDst. pSrc == pDst is not legal. * * History: * This function was copied from NT\windows\Core\ntuser\client\oemxlate.c * \***************************************************************************/ BOOL WINAPI ConvertOemToCharW( LPCSTR pSrc, LPWSTR pDst) { int cch; if (pSrc == NULL || pDst == NULL) { return FALSE; } else if (pSrc == (LPCSTR)pDst) { /*
* MultiByteToWideChar() requires pSrc != pDst: fail this call. * LATER: Is this really true? */ return FALSE; }
cch = strlen(pSrc) + 1;
MultiByteToWideChar( CP_OEMCP, // Unicode -> OEM
MB_PRECOMPOSED | MB_USEGLYPHCHARS, // visual map to precomposed
(LPSTR)pSrc, cch, // source & length
pDst, // destination
cch); // max poss. precomposed length
return TRUE; }
//----------------------------------------------------------------------------
//
// Function: OemToCharW_WithAllocation()
//
// Synopsis: This func will allocated memory for the string ppDst which
// must be then deallocatd thru a call to LocalFree().
// If the passed in ansi string is NULL, then no memory
// is allocated, and a NULL is returned
//
// Arguments:
// LPCSTR [in] ansi string for which we want the wide version
// *LPWSTR [out] the wide version of ansi string
// Return:
// BOOL : TRUE if no errors.
// BOOL : FALSE if unable to allocated memory.
//
//----------------------------------------------------------------------------
BOOL WINAPI OemToCharW_WithAllocation( LPCSTR pSrc, LPWSTR *ppDst) { DWORD size;
if (pSrc) { size = strlen( pSrc );
*ppDst = ( WCHAR *) LocalAlloc(LMEM_FIXED, ( size + 1 ) * sizeof( WCHAR ) );
if ( ppDst ) { ConvertOemToCharW( pSrc, *ppDst ); return TRUE; } else return FALSE; } else { *ppDst = NULL; return TRUE; }
}
// ANSI wrapper for CreateRemoteSessionProcessW()
//
BOOL CreateRemoteSessionProcessA( ULONG SessionId, BOOL System, 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 ) { NTSTATUS st; BOOL rc,rc2; STARTUPINFOW WCHAR_StartupInfo; PWCHAR pWCHAR_AppName, pWCHAR_CommandLine, pWCHAR_CurDir, pWCHAR_Title, pWCHAR_Desktop;
pWCHAR_AppName = pWCHAR_CommandLine = pWCHAR_CurDir = pWCHAR_Title = pWCHAR_Desktop = NULL;
// in case there is a premature return from this function.
rc2 = FALSE;
if ( !( rc = OemToCharW_WithAllocation( lpApplicationName , &pWCHAR_AppName ) )) { goto Cleanup; }
if ( !( rc = OemToCharW_WithAllocation( lpCommandLine , &pWCHAR_CommandLine ) )) { goto Cleanup; }
if ( !( rc = OemToCharW_WithAllocation( lpCurrentDirectory , &pWCHAR_CurDir ) )) { goto Cleanup; }
if ( !( rc = OemToCharW_WithAllocation( lpStartupInfo->lpTitle , &pWCHAR_Title ) )) { goto Cleanup; }
if ( !( rc = OemToCharW_WithAllocation( lpStartupInfo->lpDesktop , &pWCHAR_Desktop ) )) { goto Cleanup; }
WCHAR_StartupInfo.cb = lpStartupInfo->cb ; WCHAR_StartupInfo.cbReserved2 = lpStartupInfo->cbReserved2; WCHAR_StartupInfo.dwFillAttribute = lpStartupInfo->dwFillAttribute; WCHAR_StartupInfo.dwFlags = lpStartupInfo->dwFlags; WCHAR_StartupInfo.dwX = lpStartupInfo->dwX; WCHAR_StartupInfo.dwXCountChars = lpStartupInfo->dwXCountChars; WCHAR_StartupInfo.dwXSize = lpStartupInfo->dwXSize; WCHAR_StartupInfo.dwY = lpStartupInfo->dwY; WCHAR_StartupInfo.dwYCountChars = lpStartupInfo->dwYCountChars; WCHAR_StartupInfo.dwYSize = lpStartupInfo->dwYSize; WCHAR_StartupInfo.hStdError = lpStartupInfo->hStdError; WCHAR_StartupInfo.hStdInput = lpStartupInfo->hStdInput; WCHAR_StartupInfo.hStdOutput = lpStartupInfo->hStdOutput; WCHAR_StartupInfo.lpReserved2 = lpStartupInfo->lpReserved2; WCHAR_StartupInfo.wShowWindow = lpStartupInfo->wShowWindow; WCHAR_StartupInfo.lpDesktop = pWCHAR_Desktop; WCHAR_StartupInfo.lpReserved = NULL; WCHAR_StartupInfo.lpTitle = pWCHAR_Title;
rc2 = CreateRemoteSessionProcessW( SessionId, System, hToken, pWCHAR_AppName , pWCHAR_CommandLine, lpProcessAttributes, lpThreadAttributes , bInheritHandles, dwCreationFlags, lpEnvironment, pWCHAR_CurDir, &WCHAR_StartupInfo, lpProcessInformation );
Cleanup:
if ( !rc ) // rc is set to FALSE if an attempted memory allocation has failed.
{ BaseSetLastNTError(STATUS_NO_MEMORY); }
if (pWCHAR_AppName) { LocalFree( pWCHAR_AppName ); }
if (pWCHAR_CommandLine) { LocalFree( pWCHAR_CommandLine ); }
if (pWCHAR_CurDir) { LocalFree( pWCHAR_CurDir ); }
if (pWCHAR_Title) { LocalFree( pWCHAR_Title ); }
if (pWCHAR_Desktop) { LocalFree( pWCHAR_Desktop ); }
return rc2; }
//+---------------------------------------------------------------------------
//
// Function: CreateProcessAsUserA
//
// Synopsis: ANSI wrapper for CreateProcessAsUserW
//
// Arguments: [hToken] --
// [lpApplicationName] --
// [lpCommandLine] --
// [lpProcessAttributes] --
// [lpThreadAttributes] --
// [bInheritHandles] --
// [dwCreationFlags] --
// [lpEnvironment] --
// [lpCurrentDirectory] --
// [lpStartupInfo] --
// [lpProcessInformation] --
//
// Return Values
// If the function succeeds, the return value is nonzero.
// If the function fails, the return value is zero. To get extended error information, call GetLastError.
//
// History: 4-25-95 RichardW Created
// 1-14-98 AraBern add changes for Hydra
//
// 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; DWORD clientSessionID=0; DWORD currentSessionID=0; DWORD resultLength; HANDLE hTmpToken; DWORD curProcId ; NTSTATUS Status ;
CreateFlags = (dwCreationFlags & CREATE_SUSPENDED ? COMMON_CREATE_SUSPENDED : 0);
//
// get the session if (zero means console).
//
currentSessionID = NtCurrentPeb()->SessionId;
if ( !GetTokenInformation ( hToken, TokenSessionId , &clientSessionID,sizeof( DWORD), &resultLength ) ) { //
// get the access token for the client of this call
// use get token instead of process since the client might have only
// impersonated the thread, not the process
//
DBG_DumpOutLastError; ASSERT( FALSE ); currentSessionID = 0;
//
// We should probably return FALSE here, but at this time we don't want to alter the
// non-Hydra code-execution-flow at all.
//
}
KdPrint(("logon32.c: CreateProcessAsUserA(): clientSessionID = %d, currentSessionID = %d \n", clientSessionID, currentSessionID ));
if ( ( clientSessionID != currentSessionID )) { //
// If the client session ID is not the same as the current session ID, then, we are attempting
// to create a process on a remote session from the current session.
// This block of code is used to accomplish such process creation, it is Terminal-Server specific
//
BOOL bHaveImpersonated; HANDLE hCurrentThread; HANDLE hPrevToken = NULL; DWORD rc; TOKEN_TYPE tokenType;
//
// We must send the request to the remote WinStation
// of the requestor
//
// NOTE: The current WinStationCreateProcessW() does not use
// the supplied security descriptor, but creates the
// process under the account of the logged on user.
//
//
// Stop impersonating before doing the WinStationCreateProcess.
// The remote winstation exec thread will launch the app under
// the users context. We must not be impersonating because this
// call only lets SYSTEM request the remote execute.
//
hCurrentThread = GetCurrentThread();
//
// Init bHaveImpersonated to the FALSE state
//
bHaveImpersonated = FALSE;
//
// Since the caller of this function (runas-> SecLogon service ) has already
// impersonated the new (target) user, we do the OpenThreadToken with
// OpenAsSelf = TRUE
//
if ( OpenThreadToken( hCurrentThread, TOKEN_QUERY, TRUE, &hPrevToken ) ) {
bHaveImpersonated = TRUE;
if ( !RevertToSelf() ) { return FALSE; } }
//
// else, we are not impersonating, as reflected by the init value of bHaveImpersonated
//
rc = CreateRemoteSessionProcessA( clientSessionID, FALSE, // not creating a process for System
hToken, lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags | CREATE_SEPARATE_WOW_VDM, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation) ;
//
// Undo the effect of RevertToSelf() if we had impersoanted
//
if ( bHaveImpersonated ) { Status = NtSetInformationThread( NtCurrentThread(), ThreadImpersonationToken, &hPrevToken, sizeof( hPrevToken ) );
NtClose( hPrevToken ); }
if ( rc ) { return TRUE; } else { return FALSE; }
} else //
// this is the standard non-Hydra related call block
//
{ HANDLE hRestrictedToken = NULL; BOOL b = FALSE;
if (!CreateProcessInternalA(hToken, lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, (dwCreationFlags | CREATE_SUSPENDED | CREATE_SEPARATE_WOW_VDM), lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation, &hRestrictedToken)) { //
// The internal routine might return a token even in the failure case
// since it uses try-finally. Free the token if needed.
//
if (hRestrictedToken != NULL) { NtClose(hRestrictedToken); } return(FALSE); }
CreateFlags |= (lpProcessAttributes ? 0 : COMMON_CREATE_PROCESSSD); CreateFlags |= (lpThreadAttributes ? 0 : COMMON_CREATE_THREADSD);
//
// If a restricted token was returned, set it on the process.
// Else use the token provided by the caller.
//
if (hRestrictedToken == NULL) { b = (L32CommonCreate(CreateFlags, hToken, lpProcessInformation)); } else { b = (L32CommonCreate(CreateFlags, hRestrictedToken, lpProcessInformation)); NtClose(hRestrictedToken); }
return b; }
}
|