Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2321 lines
77 KiB

/*+
*
* Microsoft Windows
* Copyright (C) Microsoft Corporation, 1997 - 1998.
*
* Name : seclogon.cxx
* Author:Jeffrey Richter (v-jeffrr)
*
* Abstract:
* This is the service DLL for Secondary Logon Service
* This service supports the CreateProcessWithLogon API implemented
* in advapi32.dll
*
* Revision History:
* PraeritG 10/8/97 To integrate this in to services.exe
*
-*/
#define STRICT
// Disable some warnings we don't care about:
#pragma warning(disable:4115) // '': named type definition in parentheses
#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
#pragma warning(disable:4204) // nonstandard extension used : non-constant aggregate initializer
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntlsa.h>
#include <Windows.h>
#define SECURITY_WIN32
#define SECURITY_KERBEROS
#include <security.h>
#include <secint.h>
#include <winsafer.h>
#include <shellapi.h>
#include <svcs.h>
#include <userenv.h>
#include <sddl.h>
#include <rpcdcep.h>
#include <crypt.h>
#include <lm.h>
#include <strsafe.h>
#include "seclogon.h"
#include <stdio.h>
#include "stringid.h"
#include "dbgdef.h"
//
// must move to winbase.h soon!
#define LOGON_WITH_PROFILE 0x00000001
#define LOGON_NETCREDENTIALS_ONLY 0x00000002
#define MAXIMUM_SECLOGON_PROCESSES MAXIMUM_WAIT_OBJECTS*4
struct SECL_STATE {
SERVICE_STATUS serviceStatus;
SERVICE_STATUS_HANDLE hServiceStatus;
HANDLE hLSA;
ULONG hMSVPackage;
ULONG hKerbPackage;
BOOL fRPCServerActive;
LIST_ENTRY JobListHead;
} g_state;
typedef struct _SECONDARYLOGONINFOW {
// First fields should all be quad-word types to avoid alignment errors:
LPSTARTUPINFO lpStartupInfo;
LPWSTR lpUsername;
LPWSTR lpDomain;
LPWSTR lpApplicationName;
LPWSTR lpCommandLine;
LPVOID lpEnvironment;
LPCWSTR lpCurrentDirectory;
UNICODE_STRING uszPassword; // use UNICODE_STRING for the passwd (easier to work with Rtl(De/En)cryptMemory())
// Next group of fields are double-word types:
DWORD dwProcessId;
ULONG LogonIdLowPart;
LONG LogonIdHighPart;
DWORD dwLogonFlags;
DWORD dwCreationFlags;
DWORD dwSeclogonFlags;
// Insert smaller types below:
HANDLE hToken; // client access token handle, for CreateProcessWithToken
} SECONDARYLOGONINFOW, *PSECONDARYLOGONINFOW;
typedef struct _SECONDARYLOGONRETINFO {
PROCESS_INFORMATION pi;
DWORD dwErrorCode;
} SECONDARYLOGONRETINFO, *PSECONDARYLOGONRETINFO;
typedef struct _SECONDARYLOGINWATCHINFO {
HANDLE hProcess;
HANDLE hToken;
HANDLE hProfile;
LUID LogonId;
DWORD dwClientSessionId;
PSECONDARYLOGONINFOW psli;
} SECONDARYLOGONWATCHINFO, *PSECONDARYLOGONWATCHINFO;
//
// The SecondaryLogonJob structure contains all of the information needed to cleanup when
//
// a) a secondary logon job group terminates (non-TS case)
// OR b) a secondary logon process terminates (TS case)
//
typedef struct _SecondaryLogonJob {
HANDLE hJob; // The Job to watch for (case 'a')
HANDLE hProcess; // The Process to watch for (case 'b')
HANDLE hRegisteredProcessTerminated; // Used to deregister wait on hProcess (case 'b')
HANDLE hToken; // Used to unload profile on cleanup
HANDLE hProfile; // Used to unload profile on cleanup
LUID RootLogonId; // The root logon session which this process/job is associated with
// BUGBUG: psli no longer has to be preserved until cleanup. However, there's no reason to
// destabilze the codebase by fixing it now.
PSECONDARYLOGONINFOW psli; // More data to free when the process/job goes away
LIST_ENTRY list; // Links this struct to other active jobs
BOOL fHeapAllocated; // TRUE if this job was HeapAlloc'd (must be freed on cleanup).
} SecondaryLogonJob;
#define _JumpCondition(condition, label) \
if (condition) \
{ \
goto label; \
} \
else { }
#define _JumpConditionWithExpr(condition, label, expr) \
if (condition) \
{ \
expr; \
goto label; \
} \
else { }
#define ARRAYSIZE(array) ((sizeof(array)) / (sizeof(array[0])))
#define FIELDOFFSET(s,m) ((size_t)(ULONG_PTR)&(((s *)0)->m))
CRITICAL_SECTION csForProcessCount;
BOOL g_fIsCsInitialized = FALSE;
PSVCHOST_GLOBAL_DATA GlobalData;
HANDLE g_hIOCP = NULL;
BOOL g_fCleanupThreadActive = FALSE;
//
// function prototypes
//
void Free_SECONDARYLOGONINFOW(PSECONDARYLOGONINFOW psli);
void FreeGlobalState();
DWORD InitGlobalState();
DWORD MySetServiceStatus(DWORD dwCurrentState, DWORD dwCheckPoint, DWORD dwWaitHint, DWORD dwExitCode);
DWORD MySetServiceStopped(DWORD dwExitCode);
DWORD SeclStartRpcServer();
DWORD SeclStopRpcServer();
void WINAPI TS_SecondaryLogonCleanupProcess(PVOID pvIgnored, BOOLEAN bIgnored);
VOID SecondaryLogonCleanupJob(LPVOID pvJobIndex, BOOL *pfLastJob);
BOOL SlpLoadUserProfile(HANDLE hToken, PHANDLE hProfile);
DWORD To_SECONDARYLOGONINFOW(PSECL_SLI pSeclSli, PSECONDARYLOGONINFOW *ppsli);
DWORD To_SECL_SLRI(SECONDARYLOGONRETINFO *pslri, PSECL_SLRI pSeclSlri);
void DbgPrintf( DWORD dwSubSysId, LPCSTR pszFormat , ...)
{
#if DBG
va_list args;
CHAR pszBuffer[1024];
HRESULT hr;
va_start(args, pszFormat);
hr = StringCchVPrintfA(pszBuffer, 1024, pszFormat, args);
if (FAILED(hr))
return;
va_end(args);
OutputDebugStringA(pszBuffer);
#else
UNREFERENCED_PARAMETER(pszFormat);
#endif // #if DBG
UNREFERENCED_PARAMETER(dwSubSysId);
}
BOOL
IsSystemProcess(
VOID
)
{
PTOKEN_USER User;
HANDLE Token;
DWORD RetLen;
PSID SystemSid = NULL;
SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_NT_AUTHORITY;
BYTE Buffer[100];
if(AllocateAndInitializeSid(&SidAuthority,1,SECURITY_LOCAL_SYSTEM_RID,
0,0,0,0,0,0,0,&SystemSid))
{
if(OpenThreadToken(GetCurrentThread(), MAXIMUM_ALLOWED, FALSE, &Token))
{
if(GetTokenInformation(Token, TokenUser, Buffer, 100, &RetLen))
{
User = (PTOKEN_USER)Buffer;
CloseHandle(Token);
if(EqualSid(User->User.Sid, SystemSid))
{
FreeSid(SystemSid);
return TRUE;
}
}
else
CloseHandle(Token);
}
FreeSid(SystemSid);
}
return FALSE;
}
DWORD SlpGetClientSessionId(HANDLE hProcess, DWORD *pdwSessionId)
{
DWORD dwResult;
DWORD dwReturnLen;
HANDLE hToken = NULL;
if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) {
goto OpenProcessTokenError;
}
if (!GetTokenInformation (hToken, TokenSessionId, pdwSessionId, sizeof(DWORD), &dwReturnLen)) {
goto GetTokenInformationError;
}
dwResult = ERROR_SUCCESS;
ErrorReturn:
if (NULL != hToken) {
CloseHandle(hToken);
}
return dwResult;
SET_DWRESULT(OpenProcessTokenError, GetLastError());
SET_DWRESULT(GetTokenInformationError, GetLastError());
}
DWORD
SlpGetClientLogonId(
HANDLE Process,
PLUID LogonId
)
{
HANDLE Token;
TOKEN_STATISTICS TokenStats;
DWORD ReturnLength;
//
// Get handle to the process token.
//
if(OpenProcessToken(Process, MAXIMUM_ALLOWED, &Token))
{
if(GetTokenInformation (
Token,
TokenStatistics,
(PVOID)&TokenStats,
sizeof( TOKEN_STATISTICS ),
&ReturnLength
))
{
*LogonId = TokenStats.AuthenticationId;
CloseHandle(Token);
return ERROR_SUCCESS;
}
CloseHandle(Token);
}
return GetLastError();
}
DWORD GetLogonSid(PSID *ppSid)
{
DWORD dwIndex;
DWORD dwResult;
DWORD dwReturnLen = 0;
DWORD dwSidLen;
HANDLE hToken = NULL;
PSID *pSid = NULL;
TOKEN_GROUPS *pTokenGroups = NULL;
// Get the current thread's token
if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken))
goto OpenThreadTokenError;
// Compute the size of this token's groups:
GetTokenInformation(hToken, TokenGroups, NULL, 0, &dwReturnLen);
if (0 == dwReturnLen)
goto GetTokenInformationSizeError;
pTokenGroups = (TOKEN_GROUPS *)HeapAlloc(GetProcessHeap(), 0, dwReturnLen);
if (NULL == pTokenGroups)
goto MemoryError;
// Get this token's groups:
if (!GetTokenInformation(hToken, TokenGroups, pTokenGroups, dwReturnLen, &dwReturnLen))
goto GetTokenInformationError;
// Find the logon sid in this token's groups:
for (dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++) {
if (SE_GROUP_LOGON_ID & pTokenGroups->Groups[dwIndex].Attributes ) {
// we've found it. Copy it to a new sid:
dwSidLen = RtlLengthSid(pTokenGroups->Groups[dwIndex].Sid);
pSid = (PSID)HeapAlloc(GetProcessHeap(), 0, dwSidLen);
if (NULL == pSid)
goto MemoryError;
RtlCopySid(dwSidLen, pSid, pTokenGroups->Groups[dwIndex].Sid);
break;
}
}
// Did we find the logon sid?
if (NULL == pSid)
goto NoLogonSidError; // No: this token doesn't have one.
*ppSid = pSid;
pSid = NULL;
dwResult = ERROR_SUCCESS;
ErrorReturn:
if (NULL != hToken)
CloseHandle(hToken);
if (NULL != pTokenGroups)
HeapFree(GetProcessHeap(), 0, pTokenGroups);
if (NULL != pSid)
HeapFree(GetProcessHeap(), 0, pSid);
return dwResult;
SET_DWRESULT(GetTokenInformationError, GetLastError());
SET_DWRESULT(GetTokenInformationSizeError, GetLastError());
SET_DWRESULT(MemoryError, ERROR_NOT_ENOUGH_MEMORY);
SET_DWRESULT(NoLogonSidError, ERROR_ACCESS_DENIED);
SET_DWRESULT(OpenThreadTokenError, GetLastError());
}
DWORD DecryptString(UNICODE_STRING usz)
{
DWORD dwResult;
NTSTATUS ntStatus;
// We want the MaximumLength to be greater than Length because we'll want to add a NULL
// termination character to prevent buffer overruns.
if (usz.Length >= usz.MaximumLength)
goto InvalidParameterError;
// We've got the empty string, nothing to decrypt
if (0 == usz.Length)
goto done;
// We should've been passed an encrypted string that's a multiple of the block size
if (0 != ((sizeof(WCHAR)*usz.Length) % RTL_ENCRYPT_MEMORY_SIZE))
goto InvalidParameterError;
// Attempt to decrypt the password
ntStatus = RtlDecryptMemory(usz.Buffer, sizeof(WCHAR)*usz.Length, RTL_ENCRYPT_OPTION_SAME_LOGON);
if (!NT_SUCCESS(ntStatus))
goto RtlDecryptMemoryError;
done:
// Terminate the buffer to prevent overrun attacks (probably already done)
// This is OK because we've already checked that MaximumLength is > Length
usz.Buffer[usz.Length] = L'\0';
dwResult = ERROR_SUCCESS;
ErrorReturn:
return dwResult;
SET_DWRESULT(InvalidParameterError, ERROR_INVALID_PARAMETER);
SET_DWRESULT(RtlDecryptMemoryError, RtlNtStatusToDosError(ntStatus));
}
void DestroyAuthInfo(BOOL bUseNTLM, PVOID pvAuthInfo)
{
UNICODE_STRING *pustrPasswd;
if (bUseNTLM)
{
pustrPasswd = &(((MSV1_0_INTERACTIVE_LOGON *)pvAuthInfo)->Password);
} else
{
pustrPasswd = &(((KERB_INTERACTIVE_LOGON *)pvAuthInfo)->Password);
}
// this was allocated with HEAP_ZERO_MEMORY, so this check is valid
if (NULL != pustrPasswd->Buffer)
{
// will the compiler optimize this out?
SecureZeroMemory(pustrPasswd->Buffer, pustrPasswd->Length);
}
HeapFree(GetProcessHeap(), 0, pvAuthInfo);
}
DWORD MakeAuthInfo(BOOL bUseNTLM, LPWSTR pwszUserName, LPWSTR pwszDomainName, LPWSTR pwszPasswd, ULONG *pulAuthPackage, PVOID *ppvAuthInfo, ULONG *pulAuthInfoLength)
{
DWORD dwResult;
HRESULT hr;
LPBYTE pbCurrent;
LPBYTE pbEnd;
PVOID pvAuthInfo = NULL;
ULONG cbAuthInfoStrings;
ULONG ulAuthInfoLength;
ULONG ulAuthPackage;
UNICODE_STRING *pustrUserName;
UNICODE_STRING *pustrDomainName;
UNICODE_STRING *pustrPasswd;
if (bUseNTLM)
{
ulAuthPackage = g_state.hMSVPackage;
ulAuthInfoLength = sizeof(MSV1_0_INTERACTIVE_LOGON);
} else
{
ulAuthPackage = g_state.hKerbPackage;
ulAuthInfoLength = sizeof(KERB_INTERACTIVE_LOGON);
}
ulAuthInfoLength += (ULONG)(sizeof(WCHAR) * (wcslen(pwszUserName)));
ulAuthInfoLength += (ULONG)(sizeof(WCHAR) * (wcslen(pwszDomainName)));
ulAuthInfoLength += (ULONG)(sizeof(WCHAR) * (wcslen(pwszPasswd)+1));
// zero out the allocated memory (checked by DestroyAuthInfo)
pvAuthInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ulAuthInfoLength);
if (NULL == pvAuthInfo)
goto MemoryError;
// fill out the auth info structure:
if (bUseNTLM)
{
((MSV1_0_INTERACTIVE_LOGON *)pvAuthInfo)->MessageType = MsV1_0InteractiveLogon;
pustrUserName = &(((MSV1_0_INTERACTIVE_LOGON *)pvAuthInfo)->UserName);
pustrDomainName = &(((MSV1_0_INTERACTIVE_LOGON *)pvAuthInfo)->LogonDomainName);
pustrPasswd = &(((MSV1_0_INTERACTIVE_LOGON *)pvAuthInfo)->Password);
pbCurrent = ((LPBYTE)pvAuthInfo) + sizeof(MSV1_0_INTERACTIVE_LOGON);
cbAuthInfoStrings = ulAuthInfoLength - sizeof(MSV1_0_INTERACTIVE_LOGON);
} else
{
((KERB_INTERACTIVE_LOGON *)pvAuthInfo)->MessageType = KerbInteractiveLogon;
pustrUserName = &(((KERB_INTERACTIVE_LOGON *)pvAuthInfo)->UserName);
pustrDomainName = &(((KERB_INTERACTIVE_LOGON *)pvAuthInfo)->LogonDomainName);
pustrPasswd = &(((KERB_INTERACTIVE_LOGON *)pvAuthInfo)->Password);
pbCurrent = ((LPBYTE)pvAuthInfo) + sizeof(KERB_INTERACTIVE_LOGON);
cbAuthInfoStrings = ulAuthInfoLength - sizeof(KERB_INTERACTIVE_LOGON);
}
// Initialize a pointer to the end of our buffer.
pbEnd = pbCurrent + cbAuthInfoStrings;
// Copy the auth info strings into our allocated buffer
hr = StringCbCopy((WCHAR *)pbCurrent, pbEnd-pbCurrent, pwszUserName);
if (FAILED(hr))
goto StringCchCopyError;
RtlInitUnicodeString(pustrUserName, (LPWSTR)pbCurrent);
pbCurrent += sizeof(WCHAR) * (wcslen(pwszUserName));
hr = StringCbCopy((WCHAR *)pbCurrent, pbEnd-pbCurrent, pwszDomainName);
if (FAILED(hr))
goto StringCchCopyError;
RtlInitUnicodeString(pustrDomainName, (LPWSTR)pbCurrent);
pbCurrent += sizeof(WCHAR) * (wcslen(pwszDomainName));
hr = StringCbCopy((WCHAR *)pbCurrent, pbEnd-pbCurrent, pwszPasswd);
if (FAILED(hr))
goto StringCchCopyError;
RtlInitUnicodeString(pustrPasswd, (LPWSTR)pbCurrent);
pbCurrent += sizeof(WCHAR) * (wcslen(pwszPasswd));
*pulAuthPackage = ulAuthPackage;
*ppvAuthInfo = pvAuthInfo;
pvAuthInfo = NULL;
*pulAuthInfoLength = ulAuthInfoLength;
dwResult = ERROR_SUCCESS;
ErrorReturn:
if (NULL != pvAuthInfo)
DestroyAuthInfo(bUseNTLM, pvAuthInfo);
return dwResult;
SET_DWRESULT(MemoryError, ERROR_NOT_ENOUGH_MEMORY);
SET_DWRESULT(StringCchCopyError, (DWORD)hr);
}
DWORD LogonUserWrap(LPWSTR pwszUserName,
LPWSTR pwszDomainName,
UNICODE_STRING uszPassword,
DWORD dwLogonType,
DWORD dwLogonProvider,
PSID pLogonSid,
HANDLE *phToken,
LPWSTR *ppwszProfilePath)
{
BOOL bUseNTLM = FALSE;
DWORD cTokenGroups;
DWORD dwLengthSid;
DWORD dwResult;
HRESULT hr;
KERB_INTERACTIVE_LOGON kerbLogon;
LPWSTR pwszProfilePath = NULL;
MSV1_0_INTERACTIVE_LOGON msv10Logon;
NTSTATUS ntStatus;
PSID pLocalSid = NULL;
PUNICODE_STRING puszProfilePath = NULL;
SID_IDENTIFIER_AUTHORITY LocalSidAuthority = SECURITY_LOCAL_SID_AUTHORITY;
// Declare the parameters to LsaLogonUser:
LSA_STRING lsastr_OriginName;
SECURITY_LOGON_TYPE sltLogonType;
ULONG ulAuthPackage;
PVOID pvAuthInfo = NULL;
ULONG ulAuthInfoLength;
TOKEN_GROUPS *pTokenGroups = NULL;
TOKEN_SOURCE sourceContext;
PVOID pvProfileBuffer = NULL;
ULONG ulProfileBufferLength;
LUID LogonId;
QUOTA_LIMITS quotas;
NTSTATUS ntSubStatus;
ZeroMemory(&kerbLogon, sizeof(kerbLogon));
ZeroMemory(&msv10Logon, sizeof(msv10Logon));
// Map NULLs to the empty strings so we don't have to deal with them:
if (NULL == pwszUserName)
pwszUserName = L"";
if (NULL == pwszDomainName)
pwszDomainName = L"";
if (NULL == uszPassword.Buffer)
{
uszPassword.Buffer = L"";
uszPassword.Length = 0;
uszPassword.MaximumLength = 1;
}
// Check if we use NTLM or not:
if (NULL == pwszDomainName || L'\0' == pwszDomainName[0])
{
if (NULL == wcschr(pwszUserName, L'@'))
{
bUseNTLM = TRUE;
}
}
RtlInitString(&lsastr_OriginName, "seclogon");
sltLogonType = dwLogonType;
// The password is current encrypted. Need to decrypt it before we can use it:
dwResult = DecryptString(uszPassword);
if (ERROR_SUCCESS != dwResult)
goto DecryptStringError;
// Get the values of the auth-package dependendent LsaLogonUser parameters.
dwResult = MakeAuthInfo(bUseNTLM, pwszUserName, pwszDomainName, uszPassword.Buffer, &ulAuthPackage, &pvAuthInfo, &ulAuthInfoLength);
if (ERROR_SUCCESS != dwResult)
goto GetAuthInfoError;
// We don't need the password anymore. Zero it out:
SecureZeroMemory(uszPassword.Buffer, sizeof(WCHAR)*uszPassword.Length);
// If the caller specified a logon sid, cram it into a TOKEN_GROUPS structure
if (NULL != pLogonSid)
{
// BUG 522969: If we specify a token groups to LsaLogonUser, it's not going to add the local sid to the token groups.
// So, we have to do that ourselves:
dwLengthSid = RtlLengthRequiredSid(1);
pLocalSid = (PSID)HeapAlloc(GetProcessHeap(), 0, dwLengthSid);
if (NULL == pLocalSid)
goto MemoryError;
RtlInitializeSid(pLocalSid, &LocalSidAuthority, 1);
*(RtlSubAuthoritySid(pLocalSid, 0)) = SECURITY_LOCAL_RID;
// Initialize a TOKEN_GROUPS structure
cTokenGroups = 2; // the local sid, and the logon sid
pTokenGroups = (TOKEN_GROUPS *)HeapAlloc(GetProcessHeap(), 0, sizeof(TOKEN_GROUPS) + ((cTokenGroups - ANYSIZE_ARRAY) * sizeof(SID_AND_ATTRIBUTES)));
if (NULL == pTokenGroups)
goto MemoryError;
pTokenGroups->GroupCount = cTokenGroups;
pTokenGroups->Groups[0].Sid = pLogonSid;
pTokenGroups->Groups[0].Attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_LOGON_ID;
pTokenGroups->Groups[1].Sid = pLocalSid;
pTokenGroups->Groups[1].Attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT;
}
strncpy(sourceContext.SourceName, "seclogon ", sizeof(sourceContext.SourceName));
ntStatus = LsaLogonUser
(g_state.hLSA,
&lsastr_OriginName,
sltLogonType,
ulAuthPackage,
pvAuthInfo,
ulAuthInfoLength,
pTokenGroups,
&sourceContext,
&pvProfileBuffer,
&ulProfileBufferLength,
&LogonId,
phToken,
&quotas,
&ntSubStatus);
if (!NT_SUCCESS(ntStatus))
goto LsaLogonUserError;
if (!NT_SUCCESS(ntSubStatus)) {
ntStatus = ntSubStatus;
goto LsaLogonUserError;
}
if (NULL != pvProfileBuffer) {
puszProfilePath = &(((MSV1_0_INTERACTIVE_PROFILE *)pvProfileBuffer)->ProfilePath);
if (NULL != puszProfilePath->Buffer) {
pwszProfilePath = (LPWSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WCHAR)*(puszProfilePath->Length+1));
if (NULL == pwszProfilePath)
goto MemoryError;
hr = StringCchCopy(pwszProfilePath, puszProfilePath->Length+1, puszProfilePath->Buffer);
if (FAILED(hr))
goto StringCchCopyError;
}
}
*ppwszProfilePath = pwszProfilePath;
pwszProfilePath = NULL;
dwResult = ERROR_SUCCESS;
ErrorReturn:
if (NULL != pvAuthInfo)
DestroyAuthInfo(bUseNTLM, pvAuthInfo);
if (NULL != pLocalSid)
HeapFree(GetProcessHeap(), 0, pLocalSid);
if (NULL != pTokenGroups)
HeapFree(GetProcessHeap(), 0, pTokenGroups);
if (NULL != pvProfileBuffer)
LsaFreeReturnBuffer(pvProfileBuffer);
if (NULL != pwszProfilePath)
HeapFree(GetProcessHeap(), 0, pwszProfilePath);
return dwResult;
SET_DWRESULT(DecryptStringError, dwResult);
SET_DWRESULT(GetAuthInfoError, dwResult);
SET_DWRESULT(LsaLogonUserError, RtlNtStatusToDosError(ntStatus));
SET_DWRESULT(MemoryError, ERROR_NOT_ENOUGH_MEMORY);
SET_DWRESULT(StringCchCopyError, (DWORD)hr);
UNREFERENCED_PARAMETER(dwLogonProvider);
}
DWORD
WINAPI
WaitForNextJobTermination(PVOID pvIgnored)
{
BOOL fResult;
DWORD dwNumberOfBytes;
DWORD dwResult;
OVERLAPPED *po;
ULONG_PTR ulptrCompletionKey;
for (;;)
{
fResult = GetQueuedCompletionStatus(g_hIOCP, &dwNumberOfBytes, &ulptrCompletionKey, &po, INFINITE);
if (!fResult) {
// We've encountered an error. Shutdown our cleanup thread -- the next runas will queue another one.
EnterCriticalSection(&csForProcessCount);
g_fCleanupThreadActive = FALSE;
LeaveCriticalSection(&csForProcessCount);
goto GetQueuedCompletionStatusError;
}
// When waiting on job objects, the dwNumberOfBytes contains a message ID, indicating
// the event which just occured.
switch (dwNumberOfBytes)
{
case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:
{
BOOL fLastJob;
// All of our processes have terminated. Call our cleanup function.
SecondaryLogonCleanupJob((LPVOID)ulptrCompletionKey /*job index*/, &fLastJob);
if (fLastJob)
{
// There are no more jobs -- we're done processing notification.
goto CommonReturn;
}
else
{
// More jobs left to clean up. Keep processing...
}
}
default:;
// some message we don't care about. Try again.
}
}
CommonReturn:
dwResult = ERROR_SUCCESS;
ErrorReturn:
return dwResult;
SET_DWRESULT(GetQueuedCompletionStatusError, GetLastError());
UNREFERENCED_PARAMETER(pvIgnored);
}
VOID
SecondaryLogonCleanupJob(
LPVOID pvslj,
BOOL *pfLastJob
)
/*++
Routine Description:
This routine is a process cleanup handler when one of the secondary
logon process goes away.
Arguments:
dwProcessIndex -- the actual index to the process, the pointer is cast
back to dword. THIS IS SAFE IN SUNDOWN.
fWaitStatus -- status of the wait done by one of services.exe threads.
Return Value:
always 0.
--*/
{
SecondaryLogonJob *pslj = (SecondaryLogonJob *)pvslj;
EnterCriticalSection(&csForProcessCount);
if (NULL != pslj->hJob) {
CloseHandle(pslj->hJob);
}
if (NULL != pslj->hRegisteredProcessTerminated) {
UnregisterWaitEx(pslj->hRegisteredProcessTerminated, 0 /*don't wait*/);
}
if (NULL != pslj->hProcess) {
CloseHandle(pslj->hProcess);
}
if (NULL != pslj->hProfile) {
UnloadUserProfile(pslj->hToken, pslj->hProfile);
}
if (NULL != pslj->hToken) {
CloseHandle(pslj->hToken);
}
if (NULL != pslj->psli) {
Free_SECONDARYLOGONINFOW(pslj->psli);
}
// Unlink us from the list of jobs
RemoveEntryList(&pslj->list);
// Free the list element, if it was allocated.
if (pslj->fHeapAllocated) {
HeapFree(GetProcessHeap(), 0, pslj);
}
// If the list is empty, we don't need the cleanup thread anymore.
*pfLastJob = IsListEmpty(&g_state.JobListHead);
// If it's the last job, the cleanup thread terminates:
g_fCleanupThreadActive = !(*pfLastJob) && g_fCleanupThreadActive;
// Update the service status to reflect whether there is a runas'd process alive.
MySetServiceStatus(SERVICE_RUNNING, 0, 0, 0);
LeaveCriticalSection(&csForProcessCount);
return;
}
void
WINAPI
TS_SecondaryLogonCleanupProcess(PVOID pvIndex, BOOLEAN bIgnored)
{
BOOL bIgnored2;
SecondaryLogonCleanupJob(pvIndex, &bIgnored2);
UNREFERENCED_PARAMETER(bIgnored);
}
DWORD
APIENTRY
SecondaryLogonProcessWatchdogNewProcess(
PSECONDARYLOGONWATCHINFO dwParam
)
/*++
Routine Description:
This routine puts the secondary logon process created on the wait queue
such that cleanup can be done after the process dies.
Arguments:
dwParam -- the pointer to the process information.
Return Value:
none.
--*/
{
BOOL fFailedAllocation = FALSE;
BOOL fEnteredCriticalSection = FALSE;
DWORD dwResult;
JOBOBJECT_ASSOCIATE_COMPLETION_PORT joacp;
LUID ProcessLogonId;
PLIST_ENTRY ple;
SecondaryLogonJob *psljNew = NULL;
SecondaryLogonJob sljDummy;
ZeroMemory(&sljDummy, sizeof(sljDummy));
__try {
if (dwParam != NULL) {
PSECONDARYLOGONWATCHINFO pslwi = (PSECONDARYLOGONWATCHINFO) dwParam;
EnterCriticalSection(&csForProcessCount);
fEnteredCriticalSection = TRUE;
psljNew = (SecondaryLogonJob *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SecondaryLogonJob));
if (NULL == psljNew) {
// Couldn't allocate a new SecondaryLogonJob object in which to hold cleanup info. Use a dummy
// stack object and perform the cleanup immediately.
fFailedAllocation = TRUE;
psljNew = &sljDummy;
}
// Insert the new SecondaryLogonJob into the list of jobs to clean up. If we fail to fully register
// for cleanup, we'll remove it from the list ourselves
InsertTailList(&g_state.JobListHead, &psljNew->list);
psljNew->hProcess = pslwi->hProcess;
psljNew->hToken = pslwi->hToken;
psljNew->hProfile = pslwi->hProfile;
psljNew->psli = pslwi->psli;
psljNew->fHeapAllocated = !fFailedAllocation;
if (fFailedAllocation) {
goto MemoryError;
}
// Initialize this job with the logon ID of the client process.
// If this is a recursive runas, we'll override this value in the following loop
psljNew->RootLogonId.LowPart = pslwi->LogonId.LowPart;
psljNew->RootLogonId.HighPart = pslwi->LogonId.HighPart;
// Search this list of active jobs and determine which logon session the new process should be associated with.
for (ple = g_state.JobListHead.Flink; ple != &g_state.JobListHead; ple = ple->Flink)
{
SecondaryLogonJob *pslj = CONTAINING_RECORD(ple, SecondaryLogonJob, list);
SlpGetClientLogonId(pslj->hProcess, &ProcessLogonId);
if(ProcessLogonId.LowPart == pslwi->LogonId.LowPart && ProcessLogonId.HighPart == pslwi->LogonId.HighPart)
{
psljNew->RootLogonId.LowPart = pslj->RootLogonId.LowPart;
psljNew->RootLogonId.HighPart = pslj->RootLogonId.HighPart;
break;
}
}
// Now determine what kind of cleanup we're going to do. In the non-TS (session 0) case, we'll
// add the process to a job and cleanup when the process count goes to 0. In the TS case,
// we'll wait on the process handle and cleanup when it terminates. This is the best we can do,
// as there is no support for cross-session jobs.
if (0 == pslwi->dwClientSessionId) {
// The console login case
psljNew->hJob = CreateJobObject(NULL, NULL);
if (NULL == psljNew->hJob)
goto CreateJobObjectError;
if (!AssignProcessToJobObject(psljNew->hJob, psljNew->hProcess))
goto AssignProcessToJobObjectError;
// Register our IO completion port to wait for events from this job:
joacp.CompletionKey = (LPVOID)psljNew;
joacp.CompletionPort = g_hIOCP;
if (!SetInformationJobObject(psljNew->hJob, JobObjectAssociateCompletionPortInformation, &joacp, sizeof(joacp)))
goto SetInformationJobObjectError;
// If we don't already have a cleanup thread running, start one now:
if (!g_fCleanupThreadActive)
{
// NOTE: it's acceptable for this to fail -- we'll just cleanup on the next runas
g_fCleanupThreadActive = QueueUserWorkItem(WaitForNextJobTermination, NULL, WT_EXECUTELONGFUNCTION);
}
} else {
// The TS case
// We can't perform cleanup if we can't add the process to a job. The best we can do
// for now is to hope that this is a termsrv client (highly likely), and that csrss will
// do the cleanup for us.
//
// csrss won't unload user profiles, so we'll still have to do that. Wait until the
// process terminates and unload then. We might unload profiles when an application doesn't
// want it unloaded, but they can prevent this by holding open a key in the hive.
//
// This must be doc'd in CPWL documentation.
//
if (!RegisterWaitForSingleObject
(&psljNew->hRegisteredProcessTerminated,
psljNew->hProcess,
TS_SecondaryLogonCleanupProcess,
(PVOID)psljNew,
INFINITE,
WT_EXECUTEONLYONCE)) {
goto RegisterWaitForSingleObjectError;
}
}
// we've registered cleanup for this, we don't need to free it here anymore
psljNew = NULL;
// Update the service status to reflect that there is a runas'd process
// This prevents the service from receiving SERVICE_STOP controls
// while runas'd processes are alive.
// NOTE: this will only be correct if called *AFTER* InsertTailList. It bases its
// decision as to whether to allow a SERVICE_STOP control on whether there are any active
// processes in this list.
MySetServiceStatus(SERVICE_RUNNING, 0, 0, 0);
} else {
//
// We were just awakened in order to terminate the service (nothing to do)
//
}
} __except (EXCEPTION_EXECUTE_HANDLER) {
dwResult = ERROR_INVALID_DATA; // don't give an attacker exception codes
goto ExceptionError;
}
dwResult = ERROR_SUCCESS;
ErrorReturn:
if (NULL != psljNew) {
BOOL bIgnored;
// An error has occured. Terminate the process and cleanup after ourselves.
TerminateProcess(psljNew->hProcess, dwResult);
// NOTE: we want to make this call while holding the critsec, in case winlogon tries to shut us down while we're
// performing this cleanup.
SecondaryLogonCleanupJob(psljNew, &bIgnored);
}
if (fEnteredCriticalSection) {
LeaveCriticalSection(&csForProcessCount);
}
return dwResult;
SET_DWRESULT(AssignProcessToJobObjectError, GetLastError());
SET_DWRESULT(CreateJobObjectError, GetLastError());
SET_DWRESULT(ExceptionError, dwResult);
SET_DWRESULT(MemoryError, ERROR_NOT_ENOUGH_MEMORY);
SET_DWRESULT(RegisterWaitForSingleObjectError, GetLastError());
SET_DWRESULT(SetInformationJobObjectError, GetLastError());
}
DWORD ServiceStop(BOOL fShutdown, DWORD dwExitCode)
{
DWORD dwCheckPoint = 0;
DWORD dwResult;
// Don't want the process count to change while we're shutting down the service!
EnterCriticalSection(&csForProcessCount);
// Only stop if we have no runas'd processes, or if we're shutting down
if (fShutdown || IsListEmpty(&g_state.JobListHead)) {
dwResult = MySetServiceStatus(SERVICE_STOP_PENDING, dwCheckPoint++, 0, 0);
_JumpCondition(ERROR_SUCCESS != dwResult && !fShutdown, MySetServiceStatusError);
// We shouldn't hold the critical section while we're shutting down the RPC server,
// because RPC threads may be trying to acquire it.
LeaveCriticalSection(&csForProcessCount);
dwResult = SeclStopRpcServer();
_JumpCondition(ERROR_SUCCESS != dwResult && !fShutdown, SeclStopRpcServerError);
dwResult = MySetServiceStatus(SERVICE_STOP_PENDING, dwCheckPoint++, 0, 0);
_JumpCondition(ERROR_SUCCESS != dwResult && !fShutdown, MySetServiceStatusError);
if (g_fIsCsInitialized)
{
DeleteCriticalSection(&csForProcessCount);
g_fIsCsInitialized = FALSE;
}
if (NULL != g_hIOCP)
{
CloseHandle(g_hIOCP);
g_hIOCP = NULL;
}
// Unlike MySetServiceStatus, this routine doesn't access any
// global state which could have been freed:
dwResult = MySetServiceStopped(dwExitCode);
_JumpCondition(ERROR_SUCCESS != dwResult && !fShutdown, MySetServiceStopped);
}
dwResult = ERROR_SUCCESS;
ErrorReturn:
return dwResult;
SET_DWRESULT(MySetServiceStatusError, dwResult);
SET_DWRESULT(MySetServiceStopped, dwResult);
SET_DWRESULT(SeclStopRpcServerError, dwResult);
}
void
WINAPI
ServiceHandler(
DWORD fdwControl
)
/*++
Routine Description:
Service handler which wakes up the main service thread when ever
service controller needs to send a message.
Arguments:
fdwControl -- the control from the service controller.
Return Value:
none.
--*/
{
DWORD dwNextState = g_state.serviceStatus.dwCurrentState;
DWORD dwResult;
switch (fdwControl)
{
case SERVICE_CONTROL_CONTINUE:
dwResult = MySetServiceStatus(SERVICE_CONTINUE_PENDING, 0, 0, 0);
_JumpCondition(ERROR_SUCCESS != dwResult, MySetServiceStatusError);
dwResult = SeclStartRpcServer();
_JumpCondition(ERROR_SUCCESS != dwResult, StartRpcServerError);
dwNextState = SERVICE_RUNNING;
break;
case SERVICE_CONTROL_INTERROGATE:
break;
case SERVICE_CONTROL_PAUSE:
dwResult = MySetServiceStatus(SERVICE_PAUSE_PENDING, 0, 0, 0);
_JumpCondition(ERROR_SUCCESS != dwResult, MySetServiceStatusError);
dwResult = SeclStopRpcServer();
_JumpCondition(ERROR_SUCCESS != dwResult, StopRpcServerError);
dwNextState = SERVICE_PAUSED;
break;
case SERVICE_CONTROL_STOP:
dwResult = ServiceStop(FALSE /*fShutdown*/, ERROR_SUCCESS);
_JumpCondition(ERROR_SUCCESS != dwResult, ServiceStopError);
return ; // All global state has been freed, just exit.
case SERVICE_CONTROL_SHUTDOWN:
dwResult = ServiceStop(TRUE /*fShutdown*/, ERROR_SUCCESS);
_JumpCondition(ERROR_SUCCESS != dwResult, ServiceStopError);
return ; // All global state has been freed, just exit.
default:
// Unhandled service control!
goto ErrorReturn;
}
CommonReturn:
// Restore the original state on error, set the new state on success.
dwResult = MySetServiceStatus(dwNextState, 0, 0, 0);
return;
ErrorReturn:
goto CommonReturn;
SET_ERROR(MySetServiceStatusError, dwResult);
SET_ERROR(ServiceStopError, dwResult);
SET_ERROR(StartRpcServerError, dwResult);
SET_ERROR(StopRpcServerError, dwResult);
}
VOID
SlrCreateProcessWithLogon
(IN RPC_BINDING_HANDLE hRPCBinding,
IN PSECONDARYLOGONINFOW *ppsli,
OUT PSECONDARYLOGONRETINFO pslri)
/*++
Routine Description:
The core routine -- it handles a client request to start a secondary
logon process.
Arguments:
psli -- the input structure with client request information
pslri -- the output structure with response back to the client.
Return Value:
none.
--*/
{
HANDLE hCurrentThreadToken = NULL;
HANDLE hToken = NULL;
HANDLE hProfile = NULL;
HANDLE hProcessClient = NULL;
BOOL fCreatedEnvironmentBlock = FALSE;
BOOL fIsImpersonatingRpcClient = FALSE;
BOOL fIsImpersonatingClient = FALSE;
BOOL fInheritHandles = FALSE;
BOOL fOpenedSTDIN = FALSE;
BOOL fOpenedSTDOUT = FALSE;
BOOL fOpenedSTDERR = FALSE;
SECURITY_ATTRIBUTES sa;
PSECONDARYLOGONINFOW psli = *ppsli;
SECONDARYLOGONWATCHINFO slwi;
DWORD dwResult = ERROR_INVALID_PARAMETER; // Correct error code if we fail immediately
DWORD SessionId;
DWORD dwLogonProvider;
SECURITY_LOGON_TYPE LogonType;
PSID pLogonSid = NULL;
LPWSTR pwszProfilePath = NULL;
PROFILEINFO pi;
WCHAR szTemp [ UNLEN + 1 ];
LPWSTR pszUserName = NULL;
HANDLE hClientProcessToken ;
PRIVILEGE_SET ImpersonatePrivilege ;
BOOL PrivilegeTest ;
ZeroMemory(&pi, sizeof(pi));
__try {
//
// Do some security checks:
//
// 1) We should impersonate the client and then try to open
// the process so that we are assured that they didn't
// give us some fake id.
//
dwResult = RpcImpersonateClient(hRPCBinding);
_JumpCondition(RPC_S_OK != dwResult, leave_with_last_error);
fIsImpersonatingRpcClient = TRUE;
hProcessClient = OpenProcess(PROCESS_ALL_ACCESS, FALSE, psli->dwProcessId);
_JumpCondition(hProcessClient == NULL, leave_with_last_error);
#if 0
//
// 2) Check that the client is not running from a restricted account.
//
hCurrentThread = GetCurrentThread(); // Doesn't need to be freed with CloseHandle().
_JumpCondition(NULL == hCurrentThread, leave_with_last_error);
_JumpCondition(FALSE == OpenThreadToken(hCurrentThread,
TOKEN_QUERY | TOKEN_DUPLICATE,
TRUE,
&hCurrentThreadToken),
leave_with_last_error);
#endif
dwResult = RpcRevertToSelfEx(hRPCBinding);
if (RPC_S_OK != dwResult)
{
__leave;
}
fIsImpersonatingRpcClient = FALSE;
#if 0
if (TRUE == IsTokenUntrusted(hCurrentThreadToken))
{
dwResult = ERROR_ACCESS_DENIED;
__leave;
}
#endif
//
// We should get the session id from process id
// we will set this up in the token so that create process
// happens on the correct session.
//
_JumpCondition(!ProcessIdToSessionId(psli->dwProcessId, &SessionId), leave_with_last_error);
//
// Get the unique logonId.
// we will use this to cleanup any running processes
// when the logoff happens.
//
dwResult = SlpGetClientLogonId(hProcessClient, &slwi.LogonId);
if(dwResult != ERROR_SUCCESS)
{
__leave;
}
dwResult = SlpGetClientSessionId(hProcessClient, &slwi.dwClientSessionId);
if (dwResult != ERROR_SUCCESS)
{
__leave;
}
if ((psli->lpStartupInfo->dwFlags & STARTF_USESTDHANDLES) != 0)
{
_JumpCondition(!DuplicateHandle
(hProcessClient,
psli->lpStartupInfo->hStdInput,
GetCurrentProcess(),
&psli->lpStartupInfo->hStdInput,
0,
TRUE, DUPLICATE_SAME_ACCESS),
leave_with_last_error);
fOpenedSTDIN = TRUE;
_JumpCondition(!DuplicateHandle
(hProcessClient,
psli->lpStartupInfo->hStdOutput,
GetCurrentProcess(),
&psli->lpStartupInfo->hStdOutput,
0,
TRUE,
DUPLICATE_SAME_ACCESS),
leave_with_last_error);
fOpenedSTDOUT = TRUE;
_JumpCondition(!DuplicateHandle
(hProcessClient,
psli->lpStartupInfo->hStdError,
GetCurrentProcess(),
&psli->lpStartupInfo->hStdError,
0,
TRUE,
DUPLICATE_SAME_ACCESS),
leave_with_last_error);
fOpenedSTDERR = TRUE;
fInheritHandles = TRUE;
}
else
{
psli->lpStartupInfo->hStdInput = INVALID_HANDLE_VALUE;
psli->lpStartupInfo->hStdOutput = INVALID_HANDLE_VALUE;
psli->lpStartupInfo->hStdError = INVALID_HANDLE_VALUE;
}
if(psli->dwLogonFlags & LOGON_NETCREDENTIALS_ONLY)
{
LogonType = (SECURITY_LOGON_TYPE)LOGON32_LOGON_NEW_CREDENTIALS;
dwLogonProvider = LOGON32_PROVIDER_WINNT50;
}
else
{
LogonType = (SECURITY_LOGON_TYPE) LOGON32_LOGON_INTERACTIVE;
dwLogonProvider = LOGON32_PROVIDER_DEFAULT;
}
// LogonUser does not return profile information, we need to grab
// that out of band after the logon has completed.
//
dwResult = RpcImpersonateClient(hRPCBinding);
_JumpCondition(RPC_S_OK != dwResult, leave_with_last_error);
fIsImpersonatingRpcClient = TRUE;
if (0 == (SECLOGON_CALLER_SPECIFIED_DESKTOP & psli->dwSeclogonFlags))
{
// BUG 477613:
// If the caller did not specify their own desktop, it is our responsibility
// to grant the user access to the default desktop. We'll do this by
// adding the logon sid
dwResult = GetLogonSid(&pLogonSid);
if (ERROR_SUCCESS != dwResult)
__leave;
}
if( psli->hToken != NULL )
{
//
// Caller has supplied a token. Verify that the caller can impersonate
// before proceeding
//
if ( OpenProcessToken(hProcessClient, TOKEN_QUERY | TOKEN_IMPERSONATE, &hClientProcessToken ) )
{
ImpersonatePrivilege.PrivilegeCount = 1;
ImpersonatePrivilege.Privilege[ 0 ].Luid.HighPart = 0;
ImpersonatePrivilege.Privilege[ 0 ].Luid.LowPart = SE_IMPERSONATE_PRIVILEGE ;
if ( !PrivilegeCheck( hClientProcessToken,&ImpersonatePrivilege,&PrivilegeTest ) )
{
PrivilegeTest = FALSE ;
}
CloseHandle( hClientProcessToken );
if ( !PrivilegeTest )
{
dwResult = ERROR_PRIVILEGE_NOT_HELD ;
__leave ;
}
}
else
{
dwResult = GetLastError() ;
__leave ;
}
//
// duplicate the token from the caller, and use that.
//
if(!DuplicateHandle(
hProcessClient,
psli->hToken,
GetCurrentProcess(),
&hToken,
0,
FALSE,
DUPLICATE_SAME_ACCESS
))
{
dwResult = GetLastError();
__leave;
}
dwResult = RpcRevertToSelfEx(hRPCBinding);
if (RPC_S_OK != dwResult)
{
__leave;
}
fIsImpersonatingRpcClient = FALSE;
if(psli->dwLogonFlags & LOGON_WITH_PROFILE)
{
DWORD cchUserName = sizeof(szTemp) / sizeof(WCHAR);
pszUserName = szTemp;
//
// impersonate the token to get the username, for the profile path.
//
if(!ImpersonateLoggedOnUser( hToken ))
{
dwResult = GetLastError();
__leave;
}
if(!GetUserNameW(szTemp, &cchUserName))
{
dwResult = GetLastError();
}
RevertToSelf();
if( ERROR_SUCCESS != dwResult )
{
__leave;
}
//
// TODO: pwszProfilePath ?
//
}
dwResult = ERROR_SUCCESS;
} else {
pszUserName = psli->lpUsername;
dwResult = LogonUserWrap(
psli->lpUsername,
psli->lpDomain,
psli->uszPassword,
LogonType,
dwLogonProvider,
pLogonSid,
&hToken,
&pwszProfilePath);
if (ERROR_SUCCESS != dwResult)
__leave;
dwResult = RpcRevertToSelfEx(hRPCBinding);
if (RPC_S_OK != dwResult)
{
__leave;
}
fIsImpersonatingRpcClient = FALSE;
}
if(psli->dwLogonFlags & LOGON_WITH_PROFILE)
{
// Load the user's profile:
pi.dwSize = sizeof(pi);
pi.lpUserName = pszUserName;
pi.lpProfilePath = pwszProfilePath;
if (!LoadUserProfile(hToken, &pi))
goto leave_with_last_error;
// Save the profile handle so we can unload it later
hProfile = pi.hProfile;
}
// Let us set the SessionId in the Token.
_JumpCondition(!SetTokenInformation(hToken, TokenSessionId, &SessionId, sizeof(DWORD)),
leave_with_last_error);
// we should now impersonate the user.
//
_JumpCondition(!ImpersonateLoggedOnUser(hToken), leave_with_last_error);
fIsImpersonatingClient = TRUE;
// Query Default Owner/ACL from token. Make SD with this stuff, pass for
sa.nLength = sizeof(sa);
sa.bInheritHandle = FALSE;
sa.lpSecurityDescriptor = NULL;
//
// We should set the console control handler so CtrlC is correctly
// handled by the new process.
//
// SetConsoleCtrlHandler(NULL, FALSE);
//
// if lpEnvironment is NULL, we create new one for this user
// using CreateEnvironmentBlock
//
if(NULL == (psli->lpEnvironment))
{
if(FALSE == CreateEnvironmentBlock( &(psli->lpEnvironment), hToken, FALSE ))
{
psli->lpEnvironment = NULL;
}
else
{
// Successfully created environment block.
fCreatedEnvironmentBlock = TRUE;
}
}
// Create process.
// NOTE: we want the primary thread to be suspended until we
// register the thread for cleanup. In case the caller didn't specify it,
// mask CREATE_SUSPENDED in.
_JumpCondition(!CreateProcessAsUser(hToken,
psli->lpApplicationName,
psli->lpCommandLine,
&sa,
&sa,
fInheritHandles,
psli->dwCreationFlags | (fCreatedEnvironmentBlock ? CREATE_UNICODE_ENVIRONMENT : 0) | CREATE_SUSPENDED,
psli->lpEnvironment,
psli->lpCurrentDirectory,
psli->lpStartupInfo,
&pslri->pi),
leave_with_last_error);
SetLastError(NO_ERROR);
leave_with_last_error:
dwResult = GetLastError();
__leave;
}
__finally {
pslri->dwErrorCode = dwResult;
if (fCreatedEnvironmentBlock) { DestroyEnvironmentBlock(psli->lpEnvironment); }
if (fIsImpersonatingClient) { RevertToSelf(); /* Ignore retval: nothing we can do on failure! */ }
if (fIsImpersonatingRpcClient) { RpcRevertToSelfEx(hRPCBinding); /* Ignore retval: nothing we can do on failure! */ }
if (fOpenedSTDIN) { CloseHandle(psli->lpStartupInfo->hStdInput); }
if (fOpenedSTDOUT) { CloseHandle(psli->lpStartupInfo->hStdOutput); }
if (fOpenedSTDERR) { CloseHandle(psli->lpStartupInfo->hStdError); }
if (NULL != pLogonSid) { HeapFree(GetProcessHeap(), 0, pLogonSid); }
if (NULL != pwszProfilePath) { HeapFree(GetProcessHeap(), 0, pwszProfilePath); }
if(pslri->dwErrorCode != NO_ERROR)
{
if (NULL != hProfile) { UnloadUserProfile(hToken, hProfile); }
if (NULL != hToken) { CloseHandle(hToken); }
}
else
{
// Start the watchdog process last so it won't delete psli before we're done with it.
slwi.hProcess = pslri->pi.hProcess;
slwi.hToken = hToken;
slwi.hProfile = hProfile;
// LogonId was already filled up.. right at the begining.
slwi.psli = psli;
// Register for cleanup: SecondaryLogonProcessWatchdogNewProcess MUST BE IN an EH!
// The cleanup method will cleanup all process info, and terminate the process, on failure.
dwResult = SecondaryLogonProcessWatchdogNewProcess(&slwi);
if (ERROR_SUCCESS == dwResult)
{
if (0 == (psli->dwCreationFlags & CREATE_SUSPENDED))
{
// The caller doesn't want to create the process suspended. Resume the primary thread:
ResumeThread(pslri->pi.hThread);
}
// SetConsoleCtrlHandler(NULL, TRUE);
//
// Have the watchdog watch this newly added process so that
// cleanup will occur correctly when the process terminates.
//
// Set up the windowstation and desktop for the process
DuplicateHandle(GetCurrentProcess(), pslri->pi.hProcess,
hProcessClient, &pslri->pi.hProcess, 0, FALSE,
DUPLICATE_SAME_ACCESS);
DuplicateHandle(GetCurrentProcess(), pslri->pi.hThread, hProcessClient,
&pslri->pi.hThread, 0, FALSE,
DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
}
else
{
pslri->dwErrorCode = dwResult; // return the error to the caller
}
*ppsli = NULL; // cleanup method will clean this up now.
}
if (NULL != hProcessClient) { CloseHandle(hProcessClient); }
if (NULL != hCurrentThreadToken) { CloseHandle(hCurrentThreadToken); }
}
}
void
WINAPI
ServiceMain
(IN DWORD dwArgc,
IN WCHAR ** lpszArgv)
/*++
Routine Description:
The main service handler thread routine.
Arguments:
Return Value:
none.
--*/
{
DWORD dwResult;
__try {
InitializeCriticalSection(&csForProcessCount);
g_fIsCsInitialized = TRUE;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
return; // We can't do anything if we can't initialize this critsec
}
g_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0,0);
_JumpCondition(NULL == g_hIOCP, CreateIoCompletionPortError);
dwResult = InitGlobalState();
_JumpCondition(ERROR_SUCCESS != dwResult, InitGlobalStateError);
// NOTE: hSS does not have to be closed.
g_state.hServiceStatus = RegisterServiceCtrlHandler(wszSvcName, ServiceHandler);
_JumpCondition(NULL == g_state.hServiceStatus, RegisterServiceCtrlHandlerError);
dwResult = SeclStartRpcServer();
_JumpCondition(ERROR_SUCCESS != dwResult, StartRpcServerError);
// Tell the SCM we're up and running:
dwResult = MySetServiceStatus(SERVICE_RUNNING, 0, 0, ERROR_SUCCESS);
_JumpCondition(ERROR_SUCCESS != dwResult, MySetServiceStatusError);
SetLastError(ERROR_SUCCESS);
ErrorReturn:
// Shut down the service if we couldn't fully start:
if (ERROR_SUCCESS != GetLastError()) {
ServiceStop(TRUE /*fShutdown*/, GetLastError());
}
return;
SET_ERROR(InitGlobalStateError, dwResult)
SET_ERROR(MySetServiceStatusError, dwResult);
SET_ERROR(RegisterServiceCtrlHandlerError, dwResult);
SET_ERROR(StartRpcServerError, dwResult);
TRACE_ERROR(CreateIoCompletionPortError);
UNREFERENCED_PARAMETER(dwArgc);
UNREFERENCED_PARAMETER(lpszArgv);
}
DWORD
InstallService()
/*++
Routine Description:
It installs the service with service controller, basically creating
the service object.
Arguments:
none.
Return Value:
several - as returned by the service controller.
--*/
{
// TCHAR *szModulePathname;
TCHAR AppName[MAX_PATH];
LPTSTR ptszAppName = NULL;
SC_HANDLE hService;
DWORD dw;
HANDLE hMod;
//
// Open the SCM on this machine.
//
SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
if(hSCM == NULL) {
dw = GetLastError();
return dw;
}
//
// Let us give the service a useful description
// This is not earth shattering... if it works fine, if it
// doesn't it is just too bad :-)
//
hMod = GetModuleHandle(L"seclogon.dll");
//
// we'll try to get the localized name for the service,
// if it fails, we'll just put an english string...
//
if(hMod != NULL)
{
LoadString(hMod,
SECLOGON_STRING_NAME,
AppName,
MAX_PATH
);
ptszAppName = AppName;
}
else
ptszAppName = L"RunAs Service";
//
// Add this service to the SCM's database.
//
hService = CreateService
(hSCM,
wszSvcName,
ptszAppName,
SERVICE_ALL_ACCESS,
SERVICE_WIN32_SHARE_PROCESS,
SERVICE_AUTO_START,
SERVICE_ERROR_IGNORE,
L"%SystemRoot%\\system32\\svchost.exe -k netsvcs",
NULL,
NULL,
NULL,
NULL,
NULL);
if(hService == NULL) {
dw = GetLastError();
CloseServiceHandle(hSCM);
return dw;
}
if(hMod != NULL)
{
WCHAR DescString[500];
SERVICE_DESCRIPTION SvcDesc;
LoadString( hMod,
SECLOGON_STRING_DESCRIPTION,
DescString,
500
);
SvcDesc.lpDescription = DescString;
ChangeServiceConfig2( hService,
SERVICE_CONFIG_DESCRIPTION,
&SvcDesc
);
}
//
// Close the service and the SCM
//
CloseServiceHandle(hService);
CloseServiceHandle(hSCM);
return S_OK;
}
DWORD
RemoveService()
/*++
Routine Description:
deinstalls the service.
Arguments:
none.
Return Value:
as returned by service controller apis.
--*/
{
DWORD dw;
SC_HANDLE hService;
//
// Open the SCM on this machine.
//
SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
if(hSCM == NULL) {
dw = GetLastError();
return dw;
}
//
// Open this service for DELETE access
//
hService = OpenService(hSCM, wszSvcName, DELETE);
if(hService == NULL) {
dw = GetLastError();
CloseServiceHandle(hSCM);
return dw;
}
//
// Remove this service from the SCM's database.
//
DeleteService(hService);
//
// Close the service and the SCM
//
CloseServiceHandle(hService);
CloseServiceHandle(hSCM);
return S_OK;
}
void SvchostPushServiceGlobals(PSVCHOST_GLOBAL_DATA pGlobalData) {
// this entry point is called by svchost.exe
GlobalData=pGlobalData;
}
void SvcEntry_Seclogon
(IN DWORD argc,
IN WCHAR **argv)
/*++
Routine Description:
Entry point for the service dll when running in svchost.exe
Arguments:
Return Value:
--*/
{
ServiceMain(0,NULL);
UNREFERENCED_PARAMETER(argc);
UNREFERENCED_PARAMETER(argv);
}
STDAPI
DllRegisterServer(void)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
return InstallService();
}
STDAPI
DllUnregisterServer(void)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
return RemoveService();
}
DWORD InitGlobalState() {
BOOLEAN bWasEnabled;
DWORD dwResult;
LSA_STRING lsastr_LogonProcessName;
LSA_STRING lsastr_PackageName;
NTSTATUS ntStatus;
ULONG ulSecurityMode;
ZeroMemory(&g_state, sizeof(g_state));
// LsaRegisterLogonProcess() requires TCB privilege
RtlAdjustPrivilege(SE_TCB_PRIVILEGE, TRUE, FALSE, &bWasEnabled);
RtlInitString(&lsastr_LogonProcessName, "Secondary Logon Service");
ntStatus = LsaRegisterLogonProcess(&lsastr_LogonProcessName, &g_state.hLSA, &ulSecurityMode);
if (!NT_SUCCESS(ntStatus))
goto LsaRegisterLogonProcessError;
RtlAdjustPrivilege(SE_TCB_PRIVILEGE, bWasEnabled, FALSE, &bWasEnabled);
RtlInitString(&lsastr_PackageName, "MICROSOFT_AUTHENTICATION_PACKAGE_V1_0");
ntStatus = LsaLookupAuthenticationPackage(g_state.hLSA, &lsastr_PackageName, &g_state.hMSVPackage);
if (!NT_SUCCESS(ntStatus))
goto LsaLookupAuthenticationPackageError;
RtlInitString(&lsastr_PackageName, NEGOSSP_NAME_A);
ntStatus = LsaLookupAuthenticationPackage(g_state.hLSA, &lsastr_PackageName, &g_state.hKerbPackage);
if (!NT_SUCCESS(ntStatus))
goto LsaLookupAuthenticationPackageError;
// Initialize the list of seclogon processes:
InitializeListHead(&g_state.JobListHead);
dwResult = ERROR_SUCCESS;
ErrorReturn:
return dwResult;
SET_DWRESULT(LsaLookupAuthenticationPackageError, RtlNtStatusToDosError(ntStatus));
SET_DWRESULT(LsaRegisterLogonProcessError, RtlNtStatusToDosError(ntStatus));
}
DWORD MySetServiceStatus(DWORD dwCurrentState, DWORD dwCheckPoint, DWORD dwWaitHint, DWORD dwExitCode) {
BOOL fResult;
DWORD dwResult;
DWORD dwAcceptStop;
EnterCriticalSection(&csForProcessCount);
dwAcceptStop = IsListEmpty(&g_state.JobListHead) ? SERVICE_ACCEPT_STOP : 0;
g_state.serviceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
g_state.serviceStatus.dwCurrentState = dwCurrentState;
switch (dwCurrentState)
{
case SERVICE_STOPPED:
case SERVICE_STOP_PENDING:
g_state.serviceStatus.dwControlsAccepted = 0;
break;
case SERVICE_RUNNING:
case SERVICE_PAUSED:
g_state.serviceStatus.dwControlsAccepted =
// SERVICE_ACCEPT_SHUTDOWN
SERVICE_ACCEPT_PAUSE_CONTINUE
| dwAcceptStop;
break;
case SERVICE_START_PENDING:
case SERVICE_CONTINUE_PENDING:
case SERVICE_PAUSE_PENDING:
g_state.serviceStatus.dwControlsAccepted =
// SERVICE_ACCEPT_SHUTDOWN
dwAcceptStop;
break;
}
g_state.serviceStatus.dwWin32ExitCode = dwExitCode;
g_state.serviceStatus.dwCheckPoint = dwCheckPoint;
g_state.serviceStatus.dwWaitHint = dwWaitHint;
fResult = SetServiceStatus(g_state.hServiceStatus, &g_state.serviceStatus);
_JumpCondition(FALSE == fResult, SetServiceStatusError);
dwResult = ERROR_SUCCESS;
CommonReturn:
LeaveCriticalSection(&csForProcessCount);
return dwResult;
ErrorReturn:
goto CommonReturn;
SET_DWRESULT(SetServiceStatusError, GetLastError());
}
DWORD MySetServiceStopped(DWORD dwExitCode) {
BOOL fResult;
DWORD dwResult;
g_state.serviceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
g_state.serviceStatus.dwCurrentState = SERVICE_STOPPED;
g_state.serviceStatus.dwControlsAccepted = 0;
g_state.serviceStatus.dwWin32ExitCode = dwExitCode;
g_state.serviceStatus.dwCheckPoint = 0;
g_state.serviceStatus.dwWaitHint = 0;
fResult = SetServiceStatus(g_state.hServiceStatus, &g_state.serviceStatus);
_JumpCondition(FALSE == fResult, SetServiceStatusError);
dwResult = ERROR_SUCCESS;
CommonReturn:
return dwResult;
ErrorReturn:
goto CommonReturn;
SET_DWRESULT(SetServiceStatusError, GetLastError());
}
////////////////////////////////////////////////////////////////////////
//
// Implementation of RPC interface:
//
////////////////////////////////////////////////////////////////////////
// Make sure that the caller is local (we don't want to allow remote machines to call us!)
// This should be done as soon as possible (so we don't waste resources on hackers).
// Therefore, we put this call in a security callback -- if the callback fails, the RPC
// runtime won't allocate memory for the hacker's parameters.
//
long __stdcall SeclSecurityCallback(void *Interface, void *Context)
{
RPC_STATUS rpcStatus;
unsigned int fClientIsLocal;
rpcStatus = I_RpcBindingIsClientLocal(NULL, &fClientIsLocal);
if (RPC_S_OK != rpcStatus)
goto error;
if (!fClientIsLocal) {
rpcStatus = RPC_S_ACCESS_DENIED;
goto error;
}
rpcStatus = RPC_S_OK;
error:
return rpcStatus;
UNREFERENCED_PARAMETER(Interface);
UNREFERENCED_PARAMETER(Context);
}
void WINAPI SeclCreateProcessWithLogonW
(IN handle_t hRPCBinding,
IN SECL_SLI *pSeclSli,
OUT SECL_SLRI *pSeclSlri)
{
BOOL fEnteredCriticalSection = FALSE;
BOOL fIsImpersonatingClient = FALSE;
DWORD dwResult;
HANDLE hHeap = NULL;
PLIST_ENTRY ple;
PSECONDARYLOGONINFOW psli = NULL;
SECL_SLRI SeclSlri;
SECONDARYLOGONRETINFO slri;
ZeroMemory(&SeclSlri, sizeof(SeclSlri));
ZeroMemory(&slri, sizeof(slri));
// We don't want the service to be stopped while we're creating a process.
EnterCriticalSection(&csForProcessCount);
fEnteredCriticalSection = TRUE;
// Service isn't running anymore ... don't create the process.
_JumpCondition(SERVICE_RUNNING != g_state.serviceStatus.dwCurrentState, ServiceStoppedError);
hHeap = GetProcessHeap();
_JumpCondition(NULL == hHeap, MemoryError);
__try {
dwResult = To_SECONDARYLOGONINFOW(pSeclSli, &psli);
_JumpCondition(ERROR_SUCCESS != dwResult, To_SECONDARYLOGONINFOW_Error);
if (psli->LogonIdHighPart != 0 || psli->LogonIdLowPart != 0)
{
// This is probably a notification from winlogon.exe that
// a client is logging off. If so, we must clean up all processes
// they've left running.
LUID LogonId;
//
// We should impersonate the client,
// check it is LocalSystem and only then proceed.
//
fIsImpersonatingClient = RPC_S_OK == RpcImpersonateClient((RPC_BINDING_HANDLE)hRPCBinding);
if(FALSE == fIsImpersonatingClient || FALSE == IsSystemProcess())
{
slri.dwErrorCode = ERROR_INVALID_PARAMETER;
ZeroMemory(&slri.pi, sizeof(slri.pi));
}
else
{
LogonId.HighPart = psli->LogonIdHighPart;
LogonId.LowPart = psli->LogonIdLowPart;
// Loop over the list of active jobs and find the ones associated with the terminating logon session.
// NOTE: there could be more than one.
for (ple = g_state.JobListHead.Flink; ple != &g_state.JobListHead; ple = ple->Flink)
{
SecondaryLogonJob *pslj = CONTAINING_RECORD(ple, SecondaryLogonJob, list);
if(pslj->RootLogonId.HighPart == LogonId.HighPart && pslj->RootLogonId.LowPart == LogonId.LowPart)
{
// This will be NULL in the terminal services case (we don't use job objects in this case).
// That's OK though, as csrss will clean things up for us.
if (NULL != pslj->hJob)
{
TerminateJobObject(pslj->hJob, 0);
}
}
}
slri.dwErrorCode = ERROR_SUCCESS;
ZeroMemory(&slri.pi, sizeof(slri.pi));
}
if (fIsImpersonatingClient)
{
// Ignore error: nothing we can do on failure!
if (RPC_S_OK == RpcRevertToSelfEx((RPC_BINDING_HANDLE)hRPCBinding))
{
fIsImpersonatingClient = FALSE;
}
}
if (NULL != psli)
{
Free_SECONDARYLOGONINFOW(psli);
psli = NULL;
}
}
else
{
// Ok, this isn't notification from winlogon, it's really a user
// trying to use the service. Create a process for them.
//
SlrCreateProcessWithLogon((RPC_BINDING_HANDLE)hRPCBinding, &psli, &slri);
}
// If we've errored out, jump to the error handler.
_JumpCondition(NO_ERROR != slri.dwErrorCode, UnspecifiedSeclogonError);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
// If anything goes wrong, return the exception code to the client
dwResult = GetExceptionCode();
goto ExceptionError;
}
CommonReturn:
// Do not free slri: this will be freed by the watchdog!
SeclSlri.hProcess = (unsigned __int64)slri.pi.hProcess;
SeclSlri.hThread = (unsigned __int64)slri.pi.hThread;
SeclSlri.ulProcessId = slri.pi.dwProcessId;
SeclSlri.ulThreadId = slri.pi.dwThreadId;
SeclSlri.ulErrorCode = slri.dwErrorCode;
if (fEnteredCriticalSection)
LeaveCriticalSection(&csForProcessCount);
// Assign the OUT parameter:
*pSeclSlri = SeclSlri;
return;
ErrorReturn:
ZeroMemory(&slri.pi, sizeof(slri.pi));
if (NULL != psli) { Free_SECONDARYLOGONINFOW(psli); }
slri.dwErrorCode = dwResult;
goto CommonReturn;
SET_DWRESULT(ExceptionError, dwResult);
SET_DWRESULT(MemoryError, ERROR_NOT_ENOUGH_MEMORY);
SET_DWRESULT(ServiceStoppedError, ERROR_SERVICE_NOT_ACTIVE);
SET_DWRESULT(To_SECONDARYLOGONINFOW_Error, dwResult);
SET_DWRESULT(UnspecifiedSeclogonError, slri.dwErrorCode);
}
////////////////////////////////////////////////////////////////////////
//
// RPC Utility methods:
//
////////////////////////////////////////////////////////////////////////
DWORD SeclStartRpcServer() {
DWORD dwResult;
RPC_STATUS RpcStatus;
EnterCriticalSection(&csForProcessCount);
if (!g_state.fRPCServerActive) {
RpcStatus = RpcServerUseProtseqEpW(L"ncalrpc", RPC_C_PROTSEQ_MAX_REQS_DEFAULT, wszSeclogonSharedProcEndpointName, NULL);
if (RPC_S_DUPLICATE_ENDPOINT == RpcStatus)
RpcStatus = RPC_S_OK;
if (RPC_S_OK != RpcStatus)
goto RpcServerUseProtseqEpWError;
RpcStatus = RpcServerRegisterIfEx(ISeclogon_v1_0_s_ifspec, NULL, NULL, RPC_IF_AUTOLISTEN | RPC_IF_ALLOW_SECURE_ONLY, RPC_C_PROTSEQ_MAX_REQS_DEFAULT, SeclSecurityCallback);
if (RPC_S_OK != RpcStatus)
goto StartRpcServerError;
g_state.fRPCServerActive = TRUE;
}
dwResult = ERROR_SUCCESS;
CommonReturn:
LeaveCriticalSection(&csForProcessCount);
return dwResult;
ErrorReturn:
goto CommonReturn;
SET_DWRESULT(RpcServerUseProtseqEpWError, RpcStatus);
SET_DWRESULT(StartRpcServerError, RpcStatus);
}
DWORD SeclStopRpcServer() {
DWORD dwResult;
RPC_STATUS RpcStatus;
EnterCriticalSection(&csForProcessCount);
if (g_state.fRPCServerActive) {
RpcStatus = RpcServerUnregisterIf(ISeclogon_v1_0_s_ifspec, 0, 0);
if (RPC_S_OK != RpcStatus)
goto RpcServerUnregisterIfError;
g_state.fRPCServerActive = FALSE;
}
dwResult = ERROR_SUCCESS;
CommonReturn:
LeaveCriticalSection(&csForProcessCount);
return dwResult;
ErrorReturn:
goto CommonReturn;
SET_DWRESULT(RpcServerUnregisterIfError, RpcStatus);
}
void Free_SECONDARYLOGONINFOW(IN PSECONDARYLOGONINFOW psli) {
HANDLE hHeap = GetProcessHeap();
if (NULL == hHeap)
return;
if (NULL != psli) {
if (NULL != psli->lpStartupInfo) {
HeapFree(hHeap, 0, psli->lpStartupInfo);
}
HeapFree(hHeap, 0, psli);
}
}
DWORD To_LPWSTR(IN SECL_STRING *pss,
OUT LPWSTR *ppwsz)
{
DWORD dwResult;
if (NULL != pss->pwsz) {
// Ensure that the string is NULL-terminated where the caller says it is:
if (pss->ccSize <= pss->ccLength) {
goto InvalidParameterError;
}
// NULL-terminate the string ourself
pss->pwsz[pss->ccLength] = L'\0';
}
*ppwsz = pss->pwsz;
dwResult = ERROR_SUCCESS;
ErrorReturn:
return dwResult;
SET_DWRESULT(InvalidParameterError, ERROR_INVALID_PARAMETER);
}
DWORD To_SECONDARYLOGONINFOW(IN PSECL_SLI pSeclSli,
OUT PSECONDARYLOGONINFOW *ppsli)
{
DWORD dwAllocFlags = HEAP_ZERO_MEMORY;
DWORD dwIndex;
DWORD dwResult;
HANDLE hHeap = NULL;
PSECONDARYLOGONINFOW psli = NULL;
hHeap = GetProcessHeap();
_JumpCondition(NULL == hHeap, GetProcessHeapError);
psli = (PSECONDARYLOGONINFOW)HeapAlloc(hHeap, dwAllocFlags, sizeof(SECONDARYLOGONINFOW));
_JumpCondition(NULL == psli, MemoryError);
psli->lpStartupInfo = (LPSTARTUPINFO)HeapAlloc(hHeap, dwAllocFlags, sizeof(STARTUPINFO));
_JumpCondition(NULL == psli->lpStartupInfo, MemoryError);
__try {
{
struct {
SECL_STRING *pss;
LPWSTR *ppwsz;
} rg_StringsToMap[] = {
{ &(pSeclSli->ssDesktop), /* Is mapped to ----> */ &(psli->lpStartupInfo->lpDesktop) },
{ &(pSeclSli->ssTitle), /* Is mapped to ----> */ &(psli->lpStartupInfo->lpTitle) },
{ &(pSeclSli->ssUsername), /* Is mapped to ----> */ &(psli->lpUsername) },
{ &(pSeclSli->ssDomain), /* Is mapped to ----> */ &(psli->lpDomain) },
{ &(pSeclSli->ssApplicationName), /* Is mapped to ----> */ &(psli->lpApplicationName) },
{ &(pSeclSli->ssCommandLine), /* Is mapped to ----> */ &(psli->lpCommandLine) },
{ &(pSeclSli->ssCurrentDirectory), /* Is mapped to ----> */ (LPWSTR *)&(psli->lpCurrentDirectory) }
};
for (dwIndex = 0; dwIndex < ARRAYSIZE(rg_StringsToMap); dwIndex++) {
dwResult = To_LPWSTR(rg_StringsToMap[dwIndex].pss, rg_StringsToMap[dwIndex].ppwsz);
_JumpCondition(ERROR_SUCCESS != dwResult, To_LPWSTR_Error);
}
}
// Get the (possibly encrypted) passwd:
psli->uszPassword.Buffer = pSeclSli->ssPassword.pwsz;
psli->uszPassword.Length = pSeclSli->ssPassword.ccLength;
psli->uszPassword.MaximumLength = pSeclSli->ssPassword.ccSize;
if (NULL != psli->uszPassword.Buffer && psli->uszPassword.MaximumLength > 0)
{
psli->uszPassword.Buffer[psli->uszPassword.MaximumLength-1] = L'\0';
}
// Grab the environment from the SECL_SLI block:
psli->lpEnvironment = pSeclSli->sbEnvironment.pb;
// Ensure that our environment parameter is NULL-terminated:
if (NULL != psli->lpEnvironment)
{
// The environment block is terminated by 2 NULL chars.
DWORD cbTerm = (CREATE_UNICODE_ENVIRONMENT & pSeclSli->ulCreationFlags) ? sizeof(WCHAR)*2 : sizeof(CHAR)*2;
// ensure we have a large enough buffer to contain the NULL termination chars:
if (pSeclSli->sbEnvironment.cb < cbTerm)
goto InvalidParameterError;
// add terminated NULLs to the end of the environment block
for (; cbTerm > 0; cbTerm--)
{
pSeclSli->sbEnvironment.pb[pSeclSli->sbEnvironment.cb - cbTerm] = 0;
}
}
} __except (EXCEPTION_EXECUTE_HANDLER) {
// Don't give the caller too much information: the input was bad, that's all they need to know.
dwResult = ERROR_INVALID_PARAMETER;
goto ExceptionError;
}
psli->dwProcessId = pSeclSli->ulProcessId;
psli->LogonIdLowPart = pSeclSli->ulLogonIdLowPart;
psli->LogonIdHighPart = pSeclSli->lLogonIdHighPart;
psli->dwLogonFlags = pSeclSli->ulLogonFlags;
psli->dwCreationFlags = pSeclSli->ulCreationFlags;
psli->dwSeclogonFlags = pSeclSli->ulSeclogonFlags;
psli->hToken = (HANDLE)((ULONG_PTR)pSeclSli->hToken);
*ppsli = psli;
dwResult = ERROR_SUCCESS;
CommonReturn:
return dwResult;
ErrorReturn:
Free_SECONDARYLOGONINFOW(psli);
goto CommonReturn;
SET_DWRESULT(ExceptionError, dwResult);
SET_DWRESULT(GetProcessHeapError, GetLastError());
SET_DWRESULT(InvalidParameterError, ERROR_INVALID_PARAMETER);
SET_DWRESULT(MemoryError, ERROR_NOT_ENOUGH_MEMORY);
SET_DWRESULT(To_LPWSTR_Error, dwResult);
} //////////////////////////////// End Of File /////////////////////////////////