994 lines
32 KiB
C++
994 lines
32 KiB
C++
/*+
|
|
*
|
|
* Microsoft Windows
|
|
* Copyright (C) Microsoft Corporation, 1997 - 1998.
|
|
*
|
|
* Name : cseclogn.cxx
|
|
* Author:Jeffrey Richter (v-jeffrr)
|
|
*
|
|
* Abstract:
|
|
* This is the client side for Secondary Logon Service
|
|
* implemented as CreateProcessWithLogon API
|
|
* in advapi32.dll
|
|
*
|
|
* Revision History:
|
|
* PraeritG 10/8/97 To integrate this in to services.exe
|
|
*
|
|
-*/
|
|
|
|
#define UNICODE
|
|
|
|
#define SECURITY_WIN32
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <Windows.h>
|
|
#include <wincred.h>
|
|
#include <rpc.h>
|
|
#include <crypt.h>
|
|
#include <strsafe.h>
|
|
#include <assert.h>
|
|
#include "seclogon.h"
|
|
#include "security.h"
|
|
#include "dbgdef.h"
|
|
|
|
//
|
|
// must move to winbase.h soon!
|
|
#define LOGON_WITH_PROFILE 0x00000001
|
|
#define LOGON_NETCREDENTIALS_ONLY 0x00000002
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Function prototypes:
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL
|
|
CreateProcessWithLogonCommonW(
|
|
HANDLE hToken,
|
|
LPCWSTR lpUsername,
|
|
LPCWSTR lpDomain,
|
|
LPCWSTR lpPassword,
|
|
DWORD dwLogonFlags,
|
|
LPCWSTR lpApplicationName,
|
|
LPWSTR lpCommandLine,
|
|
DWORD dwCreationFlags,
|
|
LPVOID lpEnvironment,
|
|
LPCWSTR lpCurrentDirectory,
|
|
LPSTARTUPINFOW lpStartupInfo,
|
|
LPPROCESS_INFORMATION lpProcessInformation);
|
|
|
|
|
|
DWORD c_SeclCreateProcessWithLogonW(IN SECL_SLI *psli,
|
|
OUT SECL_SLRI *pslri);
|
|
|
|
DWORD To_SECL_BLOB_A(IN LPVOID lpEnvironment,
|
|
OUT SECL_BLOB *psb);
|
|
|
|
DWORD To_SECL_BLOB_W(IN LPVOID lpEnvironment,
|
|
OUT SECL_BLOB *psb);
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Useful macros:
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
#define ARRAYSIZE(array) ((sizeof(array)) / (sizeof(array[0])))
|
|
|
|
//
|
|
// Fill out a string structure to send to the seclogon RPC server.
|
|
// We'll use the convention that the client must allocate an extra byte
|
|
// which the server can use to NULL-terminate the input string
|
|
//
|
|
#define ASSIGN_SECL_STRING(ss, wsz) \
|
|
{ \
|
|
ss.pwsz = wsz; \
|
|
if (NULL != wsz) { \
|
|
size_t len; \
|
|
LastError = StringCchLength(wsz, 0xFFFF-1, &len); \
|
|
if (FAILED(LastError)) { \
|
|
__leave; \
|
|
} \
|
|
ss.ccLength = (WORD)len; \
|
|
ss.ccSize = (WORD)len+1; \
|
|
} \
|
|
else { \
|
|
ss.ccLength = ss.ccSize = 0; \
|
|
} \
|
|
}
|
|
|
|
#define GET_RTL_ENCRYPT_PADDING_LENGTH(dwLen, dwUnits) \
|
|
((RTL_ENCRYPT_MEMORY_SIZE - (((dwLen)*(dwUnits)) % RTL_ENCRYPT_MEMORY_SIZE)) / (dwUnits))
|
|
|
|
#define IS_RTL_MEMORY_BLOCK_MULTIPLE(dwLen, dwUnits) \
|
|
((((dwLen)*(dwUnits)) % RTL_ENCRYPT_MEMORY_SIZE) == 0)
|
|
|
|
#define GET_RTL_ENCRYPT_BLOCK_LENGTH(dwLen, dwUnits) \
|
|
((IS_RTL_MEMORY_BLOCK_MULTIPLE((dwLen), (dwUnits))) ? (dwLen) : ((dwLen) + (GET_RTL_ENCRYPT_PADDING_LENGTH((dwLen), (dwUnits)))))
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Module implementation:
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
extern "C" void *__cdecl _alloca(size_t);
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
CreateProcessWithLogonW(
|
|
LPCWSTR lpUsername,
|
|
LPCWSTR lpDomain,
|
|
LPCWSTR lpPassword,
|
|
DWORD dwLogonFlags,
|
|
LPCWSTR lpApplicationName,
|
|
LPWSTR lpCommandLine,
|
|
DWORD dwCreationFlags,
|
|
LPVOID lpEnvironment,
|
|
LPCWSTR lpCurrentDirectory,
|
|
LPSTARTUPINFOW lpStartupInfo,
|
|
LPPROCESS_INFORMATION lpProcessInformation
|
|
)
|
|
{
|
|
return CreateProcessWithLogonCommonW(
|
|
NULL,
|
|
lpUsername,
|
|
lpDomain,
|
|
lpPassword,
|
|
dwLogonFlags,
|
|
lpApplicationName,
|
|
lpCommandLine,
|
|
dwCreationFlags,
|
|
lpEnvironment,
|
|
lpCurrentDirectory,
|
|
lpStartupInfo,
|
|
lpProcessInformation
|
|
);
|
|
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
CreateProcessWithTokenW(
|
|
HANDLE hToken,
|
|
DWORD dwLogonFlags,
|
|
LPCWSTR lpApplicationName,
|
|
LPWSTR lpCommandLine,
|
|
DWORD dwCreationFlags,
|
|
LPVOID lpEnvironment,
|
|
LPCWSTR lpCurrentDirectory,
|
|
LPSTARTUPINFOW lpStartupInfo,
|
|
LPPROCESS_INFORMATION lpProcessInformation
|
|
)
|
|
{
|
|
LPWSTR szEmpty = L"";
|
|
|
|
return CreateProcessWithLogonCommonW(
|
|
hToken,
|
|
szEmpty,
|
|
szEmpty,
|
|
szEmpty,
|
|
dwLogonFlags,
|
|
lpApplicationName,
|
|
lpCommandLine,
|
|
dwCreationFlags,
|
|
lpEnvironment,
|
|
lpCurrentDirectory,
|
|
lpStartupInfo,
|
|
lpProcessInformation
|
|
);
|
|
|
|
}
|
|
|
|
|
|
BOOL
|
|
CreateProcessWithLogonCommonW(
|
|
HANDLE hToken,
|
|
LPCWSTR lpUsername,
|
|
LPCWSTR lpDomain,
|
|
LPCWSTR lpPassword,
|
|
DWORD dwLogonFlags,
|
|
LPCWSTR lpApplicationName,
|
|
LPWSTR lpCommandLine,
|
|
DWORD dwCreationFlags,
|
|
LPVOID lpEnvironment,
|
|
LPCWSTR lpCurrentDirectory,
|
|
LPSTARTUPINFOW lpStartupInfo,
|
|
LPPROCESS_INFORMATION lpProcessInformation)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
|
|
BOOL fOk = FALSE;
|
|
BOOL fRevertWinsta = FALSE;
|
|
DWORD ccCmdLine;
|
|
DWORD ccPszApplName;
|
|
DWORD ccEncryptedPassword;
|
|
DWORD LastError = ERROR_SUCCESS;
|
|
HANDLE hheap = GetProcessHeap();
|
|
HDESK hDesk = NULL;
|
|
HRESULT hr;
|
|
HWINSTA hWinsta = NULL;
|
|
HWINSTA hWinstaSave = NULL;
|
|
LPWSTR pszApplName = NULL;
|
|
LPWSTR pszCmdLine = NULL;
|
|
LPWSTR pwszEmptyString = L"";
|
|
NTSTATUS ntStatus;
|
|
SECL_SLI sli;
|
|
SECL_SLRI slri;
|
|
UNICODE_STRING uszEncryptedPassword;
|
|
WCHAR wszDesktopName[2*MAX_PATH + 2];
|
|
WCHAR wszEncryptedPassword[GET_RTL_ENCRYPT_BLOCK_LENGTH(CREDUI_MAX_PASSWORD_LENGTH, sizeof(WCHAR)) + 1];
|
|
|
|
ZeroMemory(&sli, sizeof(sli));
|
|
ZeroMemory(&slri, sizeof(slri));
|
|
ZeroMemory(&uszEncryptedPassword, sizeof(uszEncryptedPassword));
|
|
ZeroMemory(&wszDesktopName, sizeof(wszDesktopName));
|
|
ZeroMemory(&wszEncryptedPassword[0], sizeof(wszEncryptedPassword));
|
|
|
|
__try {
|
|
sli.hToken = (unsigned __int64)hToken;
|
|
|
|
if (NULL != lpPassword && L'\0' != lpPassword[0])
|
|
{
|
|
// BUG 547683: we need to encrypt the password across the LPC boundary to prevent
|
|
// password theft. NOTE: we won't actually touch the user's passwd buffer --
|
|
// this behavior would be confusing, and would have minimal gain.
|
|
//
|
|
// The RtlEncryptMemory() routine requires that the
|
|
// memory buffer's size be a multiple of the encryption block length.
|
|
// Copy the password over and pad it with some zeros:
|
|
//
|
|
hr = StringCchCopy(wszEncryptedPassword, ARRAYSIZE(wszEncryptedPassword), lpPassword);
|
|
if (FAILED(hr))
|
|
{
|
|
LastError = (DWORD)hr;
|
|
__leave;
|
|
}
|
|
|
|
// Calculate the size of the encrypted password
|
|
ccEncryptedPassword = wcslen(wszEncryptedPassword);
|
|
ccEncryptedPassword = GET_RTL_ENCRYPT_BLOCK_LENGTH(ccEncryptedPassword, sizeof(WCHAR));
|
|
assert(ccEncryptedPassword < ARRAYSIZE(wszEncryptedPassword));
|
|
|
|
// perform the encryption
|
|
ntStatus = RtlEncryptMemory((PVOID)wszEncryptedPassword, sizeof(WCHAR)*ccEncryptedPassword, RTL_ENCRYPT_OPTION_SAME_LOGON);
|
|
if (!NT_SUCCESS(ntStatus))
|
|
{
|
|
LastError = RtlNtStatusToDosError(ntStatus);
|
|
__leave;
|
|
}
|
|
|
|
// assign the encrypted password to a SECL_STRING.
|
|
// NOTE: we can't use ASSIGN_SECL_STRING, as the encrypted password may have
|
|
// '\0' characters in the middle
|
|
sli.ssPassword.ccLength = (WORD)(ccEncryptedPassword);
|
|
sli.ssPassword.ccSize = (WORD)(ccEncryptedPassword+1);
|
|
sli.ssPassword.pwsz = wszEncryptedPassword;
|
|
}
|
|
else
|
|
{
|
|
lpPassword = L"";
|
|
ASSIGN_SECL_STRING(sli.ssPassword, (LPWSTR)lpPassword);
|
|
}
|
|
|
|
//
|
|
// JMR: Do these flags work: CREATE_SEPARATE_WOW_VDM,
|
|
// CREATE_SHARED_WOW_VDM
|
|
// Valid flags: CREATE_SUSPENDED, CREATE_UNICODE_ENVIRONMENT,
|
|
// *_PRIORITY_CLASS
|
|
//
|
|
// The following flags are illegal. Fail the call if any are specified.
|
|
//
|
|
if ((dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS
|
|
| DETACHED_PROCESS)) != 0) {
|
|
LastError = ERROR_INVALID_PARAMETER;
|
|
__leave;
|
|
}
|
|
|
|
if(dwLogonFlags & ~(LOGON_WITH_PROFILE | LOGON_NETCREDENTIALS_ONLY))
|
|
{
|
|
LastError = ERROR_INVALID_PARAMETER;
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Turn on the flags that MUST be turned on
|
|
//
|
|
// We are overloading CREATE_NEW_CONSOLE to
|
|
// CREATE_WITH_NETWORK_LOGON
|
|
//
|
|
dwCreationFlags |= CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE
|
|
| CREATE_NEW_PROCESS_GROUP;
|
|
|
|
//
|
|
// If no priority class explicitly specified and this process is IDLE, force IDLE (See CreateProcess documentation)
|
|
//
|
|
if ((dwCreationFlags & (NORMAL_PRIORITY_CLASS | IDLE_PRIORITY_CLASS
|
|
| HIGH_PRIORITY_CLASS
|
|
| REALTIME_PRIORITY_CLASS)) == 0) {
|
|
|
|
if (GetPriorityClass(GetCurrentProcess()) == IDLE_PRIORITY_CLASS)
|
|
dwCreationFlags |= IDLE_PRIORITY_CLASS;
|
|
}
|
|
|
|
ccPszApplName = MAX_PATH;
|
|
pszApplName = (LPWSTR) HeapAlloc(hheap, 0, sizeof(WCHAR) * ccPszApplName);
|
|
//
|
|
// Lookup the fullpathname of the specified executable
|
|
//
|
|
ccCmdLine = MAX_PATH + lstrlenW(lpCommandLine);
|
|
pszCmdLine = (LPWSTR) HeapAlloc(hheap, 0, sizeof(WCHAR) * ccCmdLine);
|
|
|
|
if(pszApplName == NULL || pszCmdLine == NULL)
|
|
{
|
|
LastError = ERROR_INVALID_PARAMETER;
|
|
__leave;
|
|
}
|
|
|
|
|
|
if(lpApplicationName == NULL)
|
|
{
|
|
if(lpCommandLine != NULL)
|
|
{
|
|
//
|
|
// Commandline contains the name, we should parse it out and get
|
|
// the full path so that correct executable is invoked.
|
|
//
|
|
|
|
DWORD Length;
|
|
DWORD fileattr;
|
|
WCHAR TempChar = L'\0';
|
|
LPWSTR TempApplName = NULL;
|
|
LPWSTR TempRemainderString = NULL;
|
|
LPWSTR WhiteScan = NULL;
|
|
BOOL SearchRetry = TRUE;
|
|
DWORD ccApplName = lstrlenW(lpCommandLine)+1;
|
|
LPWSTR ApplName = (LPWSTR) HeapAlloc(
|
|
hheap, 0,
|
|
sizeof(WCHAR) * ccApplName);
|
|
|
|
DWORD ccNameBuffer = MAX_PATH+1;
|
|
LPWSTR NameBuffer = (LPWSTR) HeapAlloc(
|
|
hheap, 0,
|
|
sizeof(WCHAR) * ccNameBuffer);
|
|
|
|
if (ApplName == NULL || NameBuffer == NULL)
|
|
{
|
|
LastError = ERROR_NOT_ENOUGH_MEMORY;
|
|
__leave;
|
|
}
|
|
|
|
hr = StringCchCopy(ApplName, ccApplName, lpCommandLine);
|
|
if (FAILED(hr)) {
|
|
LastError = (DWORD)hr;
|
|
__leave;
|
|
}
|
|
|
|
WhiteScan = ApplName;
|
|
|
|
//
|
|
// if there is a leading quote
|
|
//
|
|
if(*WhiteScan == L'\"')
|
|
{
|
|
// we will NOT retry search, as app name is quoted.
|
|
|
|
SearchRetry = FALSE;
|
|
WhiteScan++;
|
|
TempApplName = WhiteScan;
|
|
while(*WhiteScan) {
|
|
if( *WhiteScan == L'\"')
|
|
{
|
|
TempChar = *WhiteScan;
|
|
*WhiteScan = L'\0';
|
|
TempRemainderString = WhiteScan;
|
|
break;
|
|
}
|
|
WhiteScan++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// skip to the first non-white char
|
|
while(*WhiteScan) {
|
|
if( *WhiteScan == L' ' || *WhiteScan == L'\t')
|
|
{
|
|
WhiteScan++;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
TempApplName = WhiteScan;
|
|
|
|
while(*WhiteScan) {
|
|
if( *WhiteScan == L' ' || *WhiteScan == L'\t')
|
|
{
|
|
TempChar = *WhiteScan;
|
|
*WhiteScan = L'\0';
|
|
TempRemainderString = WhiteScan;
|
|
break;
|
|
}
|
|
WhiteScan++;
|
|
}
|
|
|
|
}
|
|
|
|
RetrySearch:
|
|
Length = SearchPathW(
|
|
NULL,
|
|
TempApplName,
|
|
(PWSTR)L".exe",
|
|
MAX_PATH,
|
|
NameBuffer,
|
|
NULL
|
|
);
|
|
|
|
if(!Length || Length > MAX_PATH)
|
|
{
|
|
if(LastError)
|
|
SetLastError(LastError);
|
|
else
|
|
LastError = GetLastError();
|
|
|
|
CoverForDirectoryCase:
|
|
//
|
|
// If we still have command line left, then keep going
|
|
// the point is to march through the command line looking
|
|
// for whitespace so we can try to find an image name
|
|
// launches of things like:
|
|
// c:\word 95\winword.exe /embedding -automation
|
|
// require this. Our first iteration will
|
|
// stop at c:\word, our next
|
|
// will stop at c:\word 95\winword.exe
|
|
//
|
|
if(TempRemainderString)
|
|
{
|
|
*TempRemainderString = TempChar;
|
|
WhiteScan++;
|
|
}
|
|
if(*WhiteScan & SearchRetry)
|
|
{
|
|
// again skip to the first non-white char
|
|
while(*WhiteScan) {
|
|
if( *WhiteScan == L' ' || *WhiteScan == L'\t')
|
|
{
|
|
WhiteScan++;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
while(*WhiteScan) {
|
|
if( *WhiteScan == L' ' || *WhiteScan == L'\t')
|
|
{
|
|
TempChar = *WhiteScan;
|
|
*WhiteScan = L'\0';
|
|
TempRemainderString = WhiteScan;
|
|
break;
|
|
}
|
|
WhiteScan++;
|
|
}
|
|
// we'll do one last try of the whole string.
|
|
if(!WhiteScan) SearchRetry = FALSE;
|
|
goto RetrySearch;
|
|
}
|
|
|
|
//
|
|
// otherwise we have failed.
|
|
//
|
|
if(NameBuffer) HeapFree(hheap, 0, NameBuffer);
|
|
if(ApplName) HeapFree(hheap, 0, ApplName);
|
|
|
|
// we should let CreateProcess do its job.
|
|
if (pszApplName)
|
|
{
|
|
HeapFree(hheap, 0, pszApplName);
|
|
pszApplName = NULL;
|
|
}
|
|
hr = StringCchCopy(pszCmdLine, ccCmdLine, lpCommandLine);
|
|
if (FAILED(hr)) {
|
|
LastError = (DWORD)hr;
|
|
__leave;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// searchpath succeeded.
|
|
// but it can find a directory!
|
|
fileattr = GetFileAttributesW(NameBuffer);
|
|
if ( fileattr != 0xffffffff &&
|
|
(fileattr & FILE_ATTRIBUTE_DIRECTORY) ) {
|
|
Length = 0;
|
|
goto CoverForDirectoryCase;
|
|
}
|
|
|
|
//
|
|
// so it is not a directory.. it must be the real thing!
|
|
//
|
|
hr = StringCchCopy(pszApplName, ccPszApplName, NameBuffer);
|
|
if (FAILED(hr)) {
|
|
LastError = (DWORD)hr;
|
|
__leave;
|
|
}
|
|
|
|
hr = StringCchCopy(pszCmdLine, ccCmdLine, lpCommandLine);
|
|
if (FAILED(hr)) {
|
|
LastError = (DWORD)hr;
|
|
__leave;
|
|
}
|
|
|
|
HeapFree(hheap, 0, ApplName);
|
|
HeapFree(hheap, 0, NameBuffer);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
LastError = ERROR_INVALID_PARAMETER;
|
|
__leave;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// If ApplicationName is not null, we need to handle
|
|
// one case here -- the application name is present in
|
|
// current directory. All other cases will be handled by
|
|
// CreateProcess in the server side anyway.
|
|
//
|
|
|
|
//
|
|
// let us get a FullPath relative to current directory
|
|
// and try to open it. If it succeeds, then the full path
|
|
// is what we'll give as app name.. otherwise will just
|
|
// pass what we got from caller and let CreateProcess deal with it.
|
|
|
|
LPWSTR lpFilePart;
|
|
|
|
DWORD cchFullPath = GetFullPathName(
|
|
lpApplicationName,
|
|
MAX_PATH,
|
|
pszApplName,
|
|
&lpFilePart
|
|
);
|
|
|
|
if(cchFullPath)
|
|
{
|
|
HANDLE hFile;
|
|
//
|
|
// let us try to open it.
|
|
// if it works, pszApplName is already setup correctly.
|
|
// just close the handle.
|
|
|
|
hFile = CreateFile(lpApplicationName, GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if(hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
// otherwise, keep what the caller gave us.
|
|
hr = StringCchCopy(pszApplName, ccPszApplName, lpApplicationName);
|
|
if (FAILED(hr)) {
|
|
LastError = (DWORD)hr;
|
|
__leave;
|
|
}
|
|
}
|
|
else
|
|
CloseHandle(hFile);
|
|
|
|
}
|
|
else {
|
|
// lets keep what the caller gave us.
|
|
hr = StringCchCopy(pszApplName, ccPszApplName, lpApplicationName);
|
|
if (FAILED(hr)) {
|
|
LastError = (DWORD)hr;
|
|
__leave;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Commandline should be kept as is.
|
|
//
|
|
if(lpCommandLine != NULL) {
|
|
hr = StringCchCopy(pszCmdLine, ccCmdLine, lpCommandLine);
|
|
if (FAILED(hr)) {
|
|
LastError = (DWORD)hr;
|
|
__leave;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
HeapFree(hheap, 0, pszCmdLine);
|
|
pszCmdLine = NULL;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
if(lpApplicationName != NULL) lstrcpy(pszApplName,lpApplicationName);
|
|
else {
|
|
HeapFree(hheap, 0, pszApplName);
|
|
pszApplName = NULL;
|
|
}
|
|
if(lpCommandLine != NULL) lstrcpy(pszCmdLine, lpCommandLine);
|
|
else {
|
|
HeapFree(hheap, 0, pszCmdLine);
|
|
pszCmdLine = NULL;
|
|
}
|
|
#endif
|
|
|
|
// Construct a memory block will all the info that needs to go to the server
|
|
|
|
sli.lLogonIdHighPart = 0;
|
|
sli.ulLogonIdLowPart = 0;
|
|
sli.ulLogonFlags = dwLogonFlags;
|
|
sli.ulProcessId = GetCurrentProcessId();
|
|
sli.ulCreationFlags = dwCreationFlags;
|
|
sli.hWinsta = 0; // no longer used
|
|
sli.hDesk = 0; // no longer used
|
|
|
|
ASSIGN_SECL_STRING(sli.ssUsername, (LPWSTR) lpUsername);
|
|
ASSIGN_SECL_STRING(sli.ssDomain, (LPWSTR) lpDomain);
|
|
ASSIGN_SECL_STRING(sli.ssApplicationName, pszApplName);
|
|
ASSIGN_SECL_STRING(sli.ssCommandLine, pszCmdLine);
|
|
ASSIGN_SECL_STRING(sli.ssCurrentDirectory, (LPWSTR)lpCurrentDirectory);
|
|
ASSIGN_SECL_STRING(sli.ssDesktop, lpStartupInfo->lpDesktop);
|
|
ASSIGN_SECL_STRING(sli.ssTitle, lpStartupInfo->lpTitle);
|
|
|
|
if (0 != (sli.ulCreationFlags & CREATE_UNICODE_ENVIRONMENT)) {
|
|
LastError = To_SECL_BLOB_W(lpEnvironment, &(sli.sbEnvironment));
|
|
}
|
|
else {
|
|
LastError = To_SECL_BLOB_A(lpEnvironment, &(sli.sbEnvironment));
|
|
}
|
|
if (ERROR_SUCCESS != LastError) { __leave; }
|
|
|
|
// If the caller hasn't specified their own desktop, we'll do it for
|
|
// them (the seclogon service will take care of granting access
|
|
// to the desktop).
|
|
if (sli.ssDesktop.pwsz == NULL || sli.ssDesktop.pwsz[0] == L'\0')
|
|
{
|
|
DWORD Length;
|
|
HWINSTA Winsta = GetProcessWindowStation();
|
|
HDESK Desk = GetThreadDesktop(GetCurrentThreadId());
|
|
|
|
// Send seclogon the name of the current windowstation and desktop.
|
|
// Default to empty string if we can't get the name:
|
|
ASSIGN_SECL_STRING(sli.ssDesktop, pwszEmptyString);
|
|
|
|
if (GetUserObjectInformation(Winsta, UOI_NAME, wszDesktopName, (MAX_PATH*sizeof(WCHAR)), &Length))
|
|
{
|
|
Length = (DWORD)wcslen(wszDesktopName);
|
|
wszDesktopName[Length++] = L'\\';
|
|
|
|
if(GetUserObjectInformation(Desk, UOI_NAME, &wszDesktopName[Length], (MAX_PATH*sizeof(WCHAR)), &Length))
|
|
{
|
|
// sli.ssDesktop now contains "windowstation\desktop"
|
|
ASSIGN_SECL_STRING(sli.ssDesktop, wszDesktopName);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The caller specified their own desktop
|
|
sli.ulSeclogonFlags |= SECLOGON_CALLER_SPECIFIED_DESKTOP;
|
|
}
|
|
|
|
// Perform the RPC call to the seclogon service:
|
|
LastError = c_SeclCreateProcessWithLogonW(&sli, &slri);
|
|
if (ERROR_SUCCESS != LastError) __leave;
|
|
|
|
fOk = (slri.ulErrorCode == NO_ERROR); // This function succeeds if the server's function succeeds
|
|
|
|
if (!fOk) {
|
|
//
|
|
// If the server function failed, set the server's
|
|
// returned eror code as this thread's error code
|
|
//
|
|
LastError = slri.ulErrorCode;
|
|
SetLastError(slri.ulErrorCode);
|
|
} else {
|
|
//
|
|
// The server function succeeded, return the
|
|
// PROCESS_INFORMATION info
|
|
//
|
|
lpProcessInformation->hProcess = (HANDLE)slri.hProcess;
|
|
lpProcessInformation->hThread = (HANDLE)slri.hThread;
|
|
lpProcessInformation->dwProcessId = slri.ulProcessId;
|
|
lpProcessInformation->dwThreadId = slri.ulThreadId;
|
|
LastError = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
__finally {
|
|
if (NULL != pszCmdLine) HeapFree(hheap, 0, pszCmdLine);
|
|
if (NULL != pszApplName) HeapFree(hheap, 0, pszApplName);
|
|
if (fRevertWinsta) SetProcessWindowStation(hWinstaSave);
|
|
if (NULL != hWinsta) CloseWindowStation(hWinsta);
|
|
if (NULL != hDesk) CloseDesktop(hDesk);
|
|
SetLastError(LastError);
|
|
}
|
|
|
|
return(fOk);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// RPC Utility methods:
|
|
//
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
DWORD To_SECL_BLOB_W(IN LPVOID lpEnvironment,
|
|
OUT SECL_BLOB *psb) {
|
|
DWORD cb = 0;
|
|
DWORD dwResult = NULL;
|
|
HANDLE hHeap = NULL;
|
|
HRESULT hr;
|
|
LPBYTE pb = NULL;
|
|
LPWSTR pwsz = NULL;
|
|
size_t totalLen = 0;
|
|
size_t nextLen;
|
|
|
|
hHeap = GetProcessHeap();
|
|
_JumpCondition(NULL == hHeap, GetProcessHeapError);
|
|
|
|
if (NULL != lpEnvironment) {
|
|
for (pwsz = (LPWSTR)lpEnvironment; pwsz[0] != L'\0'; pwsz += nextLen + 1) {
|
|
// 10K chars is the most we allow for the environment block:
|
|
hr = StringCchLengthW(pwsz, (10*1024)-totalLen, &nextLen);
|
|
if (FAILED(hr))
|
|
goto StringCchLengthWError;
|
|
totalLen += nextLen + 1;
|
|
}
|
|
cb = sizeof(WCHAR) * ((DWORD)(1 + totalLen));
|
|
pb = (LPBYTE)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, cb);
|
|
_JumpCondition(NULL == pb, MemoryError);
|
|
|
|
CopyMemory(pb, (LPBYTE)lpEnvironment, cb);
|
|
}
|
|
|
|
psb->cb = cb;
|
|
psb->pb = pb;
|
|
dwResult = ERROR_SUCCESS;
|
|
CommonReturn:
|
|
return dwResult;
|
|
|
|
ErrorReturn:
|
|
if (NULL != pb) { HeapFree(hHeap, 0, pb); }
|
|
goto CommonReturn;
|
|
|
|
SET_DWRESULT(GetProcessHeapError, GetLastError());
|
|
SET_DWRESULT(MemoryError, ERROR_NOT_ENOUGH_MEMORY);
|
|
SET_DWRESULT(StringCchLengthWError, (DWORD)hr);
|
|
}
|
|
|
|
DWORD To_SECL_BLOB_A(IN LPVOID lpEnvironment,
|
|
OUT SECL_BLOB *psb) {
|
|
DWORD cb = 0;
|
|
DWORD dwResult;
|
|
HANDLE hHeap = NULL;
|
|
HRESULT hr;
|
|
LPBYTE pb = NULL;
|
|
LPSTR psz = NULL;
|
|
size_t totalLen = 0;
|
|
size_t nextLen;
|
|
|
|
hHeap = GetProcessHeap();
|
|
_JumpCondition(NULL == hHeap, GetProcessHeapError);
|
|
|
|
if (NULL != lpEnvironment) {
|
|
for (psz = (LPSTR)lpEnvironment; psz[0] != '\0'; psz += nextLen + 1) {
|
|
// 10K chars is the most we allow for the environment block:
|
|
hr = StringCchLengthA(psz, (10*1024)-totalLen, &nextLen);
|
|
if (FAILED(hr))
|
|
goto StringCchLengthAError;
|
|
totalLen += nextLen + 1;
|
|
}
|
|
|
|
cb = (DWORD)(1 + totalLen);
|
|
pb = (LPBYTE)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, cb);
|
|
_JumpCondition(NULL == pb, MemoryError);
|
|
|
|
CopyMemory(pb, (LPBYTE)lpEnvironment, cb);
|
|
}
|
|
|
|
psb->cb = cb;
|
|
psb->pb = pb;
|
|
dwResult = ERROR_SUCCESS;
|
|
CommonReturn:
|
|
return dwResult;
|
|
|
|
ErrorReturn:
|
|
if (NULL != pb) { HeapFree(hHeap, 0, pb); }
|
|
goto CommonReturn;
|
|
|
|
SET_DWRESULT(GetProcessHeapError, GetLastError());
|
|
SET_DWRESULT(MemoryError, ERROR_NOT_ENOUGH_MEMORY);
|
|
SET_DWRESULT(StringCchLengthAError, (DWORD)hr);
|
|
}
|
|
|
|
|
|
DWORD StartSeclogonService() {
|
|
BOOL fResult;
|
|
DWORD dwInitialCount;
|
|
DWORD dwResult;
|
|
SC_HANDLE hSCM = NULL;
|
|
SC_HANDLE hService = NULL;
|
|
SERVICE_STATUS sSvcStatus;
|
|
|
|
hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
|
|
_JumpCondition(hSCM == NULL, OpenSCManagerError);
|
|
|
|
hService = OpenService(hSCM, wszSvcName, SERVICE_START | SERVICE_QUERY_STATUS);
|
|
_JumpCondition(NULL == hService, OpenServiceError);
|
|
|
|
fResult = StartService(hService, NULL, NULL);
|
|
_JumpCondition(FALSE == fResult, StartServiceError);
|
|
|
|
// Wait until the service has actually started.
|
|
// Set timeout to 20 seconds.
|
|
dwInitialCount = GetTickCount();
|
|
|
|
// Keep polling to see if the service has started ...
|
|
for (;;)
|
|
{
|
|
fResult = QueryServiceStatus(hService, &sSvcStatus);
|
|
_JumpCondition(FALSE == fResult, QueryServiceStatusError);
|
|
|
|
// The service is running. We can stop waiting for it.
|
|
if (sSvcStatus.dwCurrentState == SERVICE_RUNNING)
|
|
break;
|
|
|
|
// Check to see if we've timed out. If GetTickCount() rolls over,
|
|
// then at worst we time out early.
|
|
_JumpCondition((GetTickCount() - dwInitialCount) > 20000, ServiceTimeoutError);
|
|
|
|
// Don't hose the service.
|
|
SleepEx(100, FALSE);
|
|
}
|
|
|
|
// Ok, the service has successfully started.
|
|
dwResult = ERROR_SUCCESS;
|
|
CommonReturn:
|
|
if (NULL != hSCM) { CloseServiceHandle(hSCM); }
|
|
if (NULL != hService) { CloseServiceHandle(hService); }
|
|
return dwResult;
|
|
|
|
ErrorReturn:
|
|
goto CommonReturn;
|
|
|
|
SET_DWRESULT(OpenSCManagerError, GetLastError());
|
|
SET_DWRESULT(OpenServiceError, GetLastError());
|
|
SET_DWRESULT(QueryServiceStatusError, GetLastError());
|
|
SET_DWRESULT(StartServiceError, GetLastError());
|
|
SET_DWRESULT(ServiceTimeoutError, ERROR_SERVICE_REQUEST_TIMEOUT);
|
|
}
|
|
|
|
DWORD SetupLocalRPCSecurity(handle_t hRPCBinding)
|
|
{
|
|
CHAR szDomainName[128];
|
|
CHAR szName[128];
|
|
DWORD cbDomainName;
|
|
DWORD cbName;
|
|
DWORD dwResult;
|
|
PSID pSid = NULL;
|
|
RPC_SECURITY_QOS SecurityQOS;
|
|
SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_NT_AUTHORITY;
|
|
SID_NAME_USE SidNameUse;
|
|
|
|
// We're doing LRPC -- we need to get the account name of the service to do mutual auth
|
|
if (!AllocateAndInitializeSid(&SidAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &pSid))
|
|
goto AllocateAndInitializeSidError;
|
|
|
|
cbName = sizeof(szName);
|
|
cbDomainName = sizeof(szDomainName);
|
|
if (!LookupAccountSidA(NULL, pSid, szName, &cbName, szDomainName, &cbDomainName, &SidNameUse))
|
|
goto LookupAccountSidAError;
|
|
|
|
// Specify quality of service parameters.
|
|
SecurityQOS.ImpersonationType = RPC_C_IMP_LEVEL_IMPERSONATE; // the server will need to impersonate us
|
|
SecurityQOS.Version = RPC_C_SECURITY_QOS_VERSION;
|
|
SecurityQOS.Capabilities = RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH; // we need mutual auth
|
|
SecurityQOS.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC; // calls go to the server under the identity that created the binding handle
|
|
|
|
dwResult = RpcBindingSetAuthInfoExA(hRPCBinding, (unsigned char *)szName, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_WINNT, NULL, 0, &SecurityQOS);
|
|
if (RPC_S_OK != dwResult)
|
|
goto RpcBindingSetAuthInfoExAError;
|
|
|
|
dwResult = ERROR_SUCCESS;
|
|
ErrorReturn:
|
|
if (NULL != pSid) {
|
|
FreeSid(pSid);
|
|
}
|
|
return dwResult;
|
|
|
|
SET_DWRESULT(AllocateAndInitializeSidError, GetLastError());
|
|
SET_DWRESULT(LookupAccountSidAError, GetLastError());
|
|
SET_DWRESULT(RpcBindingSetAuthInfoExAError, dwResult);
|
|
}
|
|
|
|
DWORD c_SeclCreateProcessWithLogonW
|
|
(IN SECL_SLI *psli,
|
|
OUT SECL_SLRI *pslri)
|
|
{
|
|
DWORD dwResult;
|
|
LPWSTR pwszBinding = NULL;
|
|
RPC_BINDING_HANDLE hRPCBinding = NULL;
|
|
|
|
dwResult = RpcStringBindingCompose
|
|
(NULL,
|
|
(USHORT *)L"ncalrpc",
|
|
NULL,
|
|
(USHORT *)wszSeclogonSharedProcEndpointName,
|
|
(USHORT *)L"Security=impersonation static false",
|
|
(USHORT **)&pwszBinding);
|
|
_JumpCondition(RPC_S_OK != dwResult, RpcStringBindingComposeError);
|
|
|
|
dwResult = RpcBindingFromStringBinding((USHORT *)pwszBinding, &hRPCBinding);
|
|
_JumpCondition(0 != dwResult, RpcBindingFromStringBindingError);
|
|
|
|
dwResult = SetupLocalRPCSecurity(hRPCBinding);
|
|
_JumpCondition(ERROR_SUCCESS != dwResult, SetupLocalRpcSecurityError);
|
|
|
|
// Perform the RPC call to the seclogon service. If the call fails because the
|
|
// service was not started, try again. If the call still fails, give up.
|
|
for (BOOL fFirstTry = TRUE; /*TRUE*/; fFirstTry = FALSE) {
|
|
__try {
|
|
SeclCreateProcessWithLogonW(hRPCBinding, psli, pslri);
|
|
break;
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER) {
|
|
dwResult = RpcExceptionCode();
|
|
if ((RPC_S_SERVER_UNAVAILABLE == dwResult || RPC_S_UNKNOWN_IF == dwResult) &&
|
|
(TRUE == fFirstTry)) {
|
|
// Ok, the seclogon service is probably just not started.
|
|
// Attempt to start it up and try again.
|
|
dwResult = StartSeclogonService();
|
|
_JumpCondition(ERROR_SUCCESS != dwResult, SeclCreateProcessWithLogonWError);
|
|
}
|
|
else {
|
|
goto SeclCreateProcessWithLogonWError;
|
|
}
|
|
}
|
|
}
|
|
|
|
dwResult = ERROR_SUCCESS;
|
|
CommonReturn:
|
|
if (NULL != pwszBinding) { RpcStringFree((USHORT **)&pwszBinding); }
|
|
if (NULL != hRPCBinding) { RpcBindingFree(&hRPCBinding); }
|
|
return dwResult;
|
|
|
|
ErrorReturn:
|
|
goto CommonReturn;
|
|
|
|
SET_DWRESULT(RpcBindingFromStringBindingError, dwResult);
|
|
SET_DWRESULT(RpcStringBindingComposeError, dwResult);
|
|
SET_DWRESULT(SeclCreateProcessWithLogonWError, dwResult);
|
|
SET_DWRESULT(SetupLocalRpcSecurityError, dwResult);
|
|
}
|
|
|
|
void DbgPrintf( DWORD /*dwSubSysId*/, LPCSTR /*pszFormat*/ , ...)
|
|
{
|
|
}
|
|
|
|
//////////////////////////////// End Of File /////////////////////////////////
|