WindowsXP-SP1/mergedcomponents/advapi32/logon32.c

3423 lines
96 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>
#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;
}
}