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.
1127 lines
34 KiB
1127 lines
34 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1997.
|
|
//
|
|
// File: launch.cxx
|
|
//
|
|
// Contents:
|
|
//
|
|
// History: ?-??-?? ??? Created
|
|
// 6-17-99 a-sergiv Added event log filtering
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include "act.hxx"
|
|
|
|
#include <winbasep.h> // For CreateProcessInternalW
|
|
|
|
extern "C" {
|
|
#include <execsrv.h>
|
|
}
|
|
#include "execclt.hxx"
|
|
|
|
const ULONG MAX_SERVICE_ARGS = 16;
|
|
|
|
const WCHAR REGEVENT_PREFIX[] = L"RPCSS_REGEVENT:";
|
|
const DWORD REGEVENT_PREFIX_STRLEN = sizeof(REGEVENT_PREFIX) / sizeof(WCHAR) - 1;
|
|
|
|
const WCHAR INITEVENT_PREFIX[] = L"RPCSS_INITEVENT:";
|
|
const DWORD INITEVENT_PREFIX_STRLEN = sizeof(INITEVENT_PREFIX) / sizeof(WCHAR) - 1;
|
|
|
|
|
|
HRESULT
|
|
CClsidData::GetAAASaferToken(
|
|
IN CToken *pClientToken,
|
|
OUT HANDLE *pTokenOut
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the token that will be used in an Activate As Activator
|
|
launch. This token is the more restricted of the incoming
|
|
token and the configured safer level.
|
|
|
|
Arguments:
|
|
|
|
pClientToken - token of the user doing the activation
|
|
|
|
pTokenOut - out parameter that will recieve the handle to use
|
|
in the activation.
|
|
|
|
Return Value:
|
|
|
|
S_OK: Everything went fine. The caller owns a reference on
|
|
pTokenOut and must close it.
|
|
S_FALSE: Everything went fine. The caller does not own a
|
|
reference on pToken out and does not need to close it.
|
|
|
|
Anything else: An error occured.
|
|
|
|
--*/
|
|
{
|
|
HANDLE hSaferToken = NULL;
|
|
HRESULT hr = S_OK;
|
|
BOOL bStatus = TRUE;
|
|
|
|
*pTokenOut = NULL;
|
|
|
|
ASSERT(SaferLevel() && "Called GetAAASaferToken with SAFER disabled!");
|
|
if (!SaferLevel()) return E_UNEXPECTED;
|
|
|
|
// Get the safer token for this configuration.
|
|
bStatus = SaferComputeTokenFromLevel(SaferLevel(),
|
|
pClientToken->GetToken(),
|
|
&hSaferToken,
|
|
0,
|
|
NULL);
|
|
if (bStatus)
|
|
{
|
|
hr = pClientToken->CompareSaferLevels(hSaferToken);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
CloseHandle(hSaferToken);
|
|
hSaferToken = pClientToken->GetToken();
|
|
|
|
// Shared reference, return S_FALSE.
|
|
hr = S_FALSE;
|
|
}
|
|
else
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
*pTokenOut = hSaferToken;
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
CClsidData::LaunchActivatorServer(
|
|
IN CToken * pClientToken,
|
|
IN WCHAR * pEnvBlock,
|
|
IN DWORD EnvBlockLength,
|
|
IN BOOL fIsRemoteActivation,
|
|
IN BOOL fClientImpersonating,
|
|
IN WCHAR* pwszWinstaDesktop,
|
|
IN DWORD clsctx,
|
|
OUT HANDLE * phProcess,
|
|
OUT DWORD * pdwProcessId
|
|
)
|
|
{
|
|
WCHAR * pwszCommandLine;
|
|
WCHAR * pFinalEnvBlock;
|
|
STARTUPINFO StartupInfo;
|
|
PROCESS_INFORMATION ProcessInfo;
|
|
SECURITY_ATTRIBUTES saProcess;
|
|
PSECURITY_DESCRIPTOR psdNewProcessSD;
|
|
HRESULT hr;
|
|
DWORD CreateFlags;
|
|
BOOL bStatus = FALSE;
|
|
ULONG SessionId = 0;
|
|
HANDLE hSaferToken = NULL;
|
|
BOOL bCloseSaferToken = TRUE;
|
|
|
|
*phProcess = NULL;
|
|
*pdwProcessId = 0;
|
|
|
|
if ( ! pClientToken )
|
|
return (E_ACCESSDENIED);
|
|
|
|
pFinalEnvBlock = NULL;
|
|
StartupInfo.cb = sizeof(StartupInfo);
|
|
StartupInfo.lpReserved = NULL;
|
|
|
|
// Choose desktop for new server:
|
|
// client remote -> system chooses desktop
|
|
// client local, not impersonating -> we pick client's desktop
|
|
// client local, impersonating -> system chooses desktop
|
|
StartupInfo.lpDesktop = (fIsRemoteActivation || fClientImpersonating) ? L"" : pwszWinstaDesktop;
|
|
StartupInfo.lpTitle = (SERVERTYPE_SURROGATE == _ServerType) ? NULL : _pwszServer;
|
|
StartupInfo.dwX = 40;
|
|
StartupInfo.dwY = 40;
|
|
StartupInfo.dwXSize = 80;
|
|
StartupInfo.dwYSize = 40;
|
|
StartupInfo.dwFlags = 0;
|
|
StartupInfo.wShowWindow = SW_SHOWNORMAL;
|
|
StartupInfo.cbReserved2 = 0;
|
|
StartupInfo.lpReserved2 = NULL;
|
|
|
|
ProcessInfo.hThread = 0;
|
|
ProcessInfo.hProcess = 0;
|
|
ProcessInfo.dwProcessId = 0;
|
|
|
|
hr = GetLaunchCommandLine( &pwszCommandLine );
|
|
if ( hr != S_OK )
|
|
return (hr);
|
|
|
|
if ( pEnvBlock )
|
|
{
|
|
hr = AddAppPathsToEnv( pEnvBlock, EnvBlockLength, &pFinalEnvBlock );
|
|
if ( hr != S_OK )
|
|
{
|
|
PrivMemFree( pwszCommandLine );
|
|
return (hr);
|
|
}
|
|
}
|
|
|
|
CreateFlags = CREATE_NEW_CONSOLE;
|
|
|
|
if ( pFinalEnvBlock )
|
|
CreateFlags |= CREATE_UNICODE_ENVIRONMENT;
|
|
|
|
CAccessInfo AccessInfo( pClientToken->GetSid() );
|
|
|
|
//
|
|
// Apply configured safer restrictions to the token we're using to launch.
|
|
// SAFER might not be enabled at all, in which case we don't need to do this.
|
|
//
|
|
if ( gbSAFERAAAChecksEnabled && SaferLevel() )
|
|
{
|
|
hr = GetAAASaferToken( pClientToken, &hSaferToken );
|
|
if ( FAILED(hr) ) goto LaunchServerEnd;
|
|
if ( hr == S_FALSE )
|
|
{
|
|
// GetAAASaferToken has returned a shared reference
|
|
// to pClientToken.
|
|
bCloseSaferToken = FALSE;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
bCloseSaferToken = TRUE;
|
|
}
|
|
else
|
|
{
|
|
hSaferToken = pClientToken->GetToken();
|
|
bCloseSaferToken = FALSE;
|
|
}
|
|
|
|
//
|
|
// This should never fail here, but if it did that would be a really,
|
|
// really bad security breach, so check it anyway.
|
|
//
|
|
if ( ! ImpersonateLoggedOnUser( hSaferToken ) )
|
|
{
|
|
hr = E_ACCESSDENIED;
|
|
goto LaunchServerEnd;
|
|
}
|
|
|
|
//
|
|
// Initialize process security info (SDs). We need both SIDs to
|
|
// do this, so here is the 1st time we can. We Delete the SD right
|
|
// after the CreateProcess call, no matter what happens.
|
|
//
|
|
psdNewProcessSD = AccessInfo.IdentifyAccess(
|
|
FALSE,
|
|
PROCESS_ALL_ACCESS,
|
|
PROCESS_SET_INFORMATION | // Allow primary token to be set
|
|
PROCESS_TERMINATE | SYNCHRONIZE // Allow screen-saver control
|
|
);
|
|
|
|
if ( ! psdNewProcessSD )
|
|
{
|
|
RevertToSelf();
|
|
hr = E_OUTOFMEMORY;
|
|
goto LaunchServerEnd;
|
|
}
|
|
|
|
saProcess.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
saProcess.lpSecurityDescriptor = psdNewProcessSD;
|
|
saProcess.bInheritHandle = FALSE;
|
|
|
|
SessionId = fIsRemoteActivation ? 0 : pClientToken->GetSessionId();
|
|
|
|
if( SessionId != 0 ) {
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
// We do not stuff the security descriptor, so clear the suspend flag
|
|
#if DBGX
|
|
CairoleDebugOut((DEB_TRACE, "SCM: Sending CreateProcess to SessionId %d\n",SessionId));
|
|
#endif
|
|
|
|
// Non-zero sessions have only one winstation/desktop which is
|
|
// the default one. We will ignore the winstation/desktop passed
|
|
// in and use the default one.
|
|
// review: Figure this out TarunA 05/07/99
|
|
//StartupInfo.lpDesktop = L"WinSta0\\Default";
|
|
|
|
// jsimmons 4/6/00 -- note that if the client was impersonating, then we won't
|
|
// launch the server under the correct identity. More work needed to determine
|
|
// if we can fully support this.
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
RevertToSelf();
|
|
|
|
HANDLE hDuplicate = NULL;
|
|
|
|
// We need to pass in the SAFER blessed token to TS so that the
|
|
// server can use that token
|
|
|
|
// TS code will call CreateProcessAsUser, so we need to get a primary token
|
|
|
|
if (DuplicateTokenForSessionUse(hSaferToken, &hDuplicate))
|
|
{
|
|
if (bCloseSaferToken)
|
|
{
|
|
CloseHandle(hSaferToken);
|
|
}
|
|
hSaferToken = hDuplicate;
|
|
bCloseSaferToken = TRUE;
|
|
bStatus = CreateRemoteSessionProcess(
|
|
SessionId,
|
|
hSaferToken,
|
|
FALSE, // Run as Logged on USER
|
|
ServerExecutable(), // application name
|
|
pwszCommandLine, // command line
|
|
&saProcess, // process sec attributes
|
|
NULL, // default thread sec attributes
|
|
// (this was &saThread, but isn't needed)
|
|
FALSE, // dont inherit handles
|
|
CreateFlags, // creation flags
|
|
pFinalEnvBlock, // use same enviroment block as the client
|
|
NULL, // use same directory
|
|
&StartupInfo, // startup info
|
|
&ProcessInfo // proc info returned
|
|
);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
HANDLE hPrimary = NULL;
|
|
if (DuplicateTokenAsPrimary(hSaferToken, pClientToken->GetSid(), &hPrimary))
|
|
{
|
|
if (bCloseSaferToken)
|
|
{
|
|
CloseHandle(hSaferToken);
|
|
}
|
|
hSaferToken = hPrimary;
|
|
bCloseSaferToken = TRUE;
|
|
//
|
|
// Do the exec while impersonating so the file access gets ACL
|
|
// checked correctly.
|
|
//
|
|
//
|
|
bStatus = CreateProcessAsUser(
|
|
hSaferToken,
|
|
ServerExecutable(),
|
|
pwszCommandLine,
|
|
&saProcess,
|
|
NULL,
|
|
FALSE,
|
|
CreateFlags,
|
|
pFinalEnvBlock,
|
|
NULL,
|
|
&StartupInfo,
|
|
&ProcessInfo);
|
|
|
|
}
|
|
|
|
RevertToSelf();
|
|
}
|
|
|
|
if ( ! bStatus )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
LogServerStartError( &_Clsid, clsctx, pClientToken, pwszCommandLine );
|
|
goto LaunchServerEnd;
|
|
}
|
|
|
|
LaunchServerEnd:
|
|
|
|
if ( pFinalEnvBlock && pFinalEnvBlock != pEnvBlock )
|
|
PrivMemFree( pFinalEnvBlock );
|
|
|
|
if ( ProcessInfo.hThread )
|
|
CloseHandle( ProcessInfo.hThread );
|
|
|
|
if ( ProcessInfo.hProcess && ! bStatus )
|
|
{
|
|
CloseHandle( ProcessInfo.hProcess );
|
|
ProcessInfo.hProcess = 0;
|
|
}
|
|
|
|
if ( hSaferToken && bCloseSaferToken )
|
|
{
|
|
CloseHandle( hSaferToken );
|
|
}
|
|
|
|
*phProcess = ProcessInfo.hProcess;
|
|
*pdwProcessId = ProcessInfo.dwProcessId;
|
|
|
|
PrivMemFree( pwszCommandLine );
|
|
|
|
return (hr);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// LaunchRunAsServer
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
HRESULT
|
|
CClsidData::LaunchRunAsServer(
|
|
IN CToken * pClientToken,
|
|
IN BOOL fIsRemoteActivation,
|
|
IN ActivationPropertiesIn *pActIn,
|
|
IN DWORD clsctx,
|
|
OUT HANDLE * phProcess,
|
|
OUT DWORD * pdwProcessId,
|
|
OUT void** ppvRunAsHandle
|
|
)
|
|
{
|
|
WCHAR * pwszCommandLine;
|
|
STARTUPINFO StartupInfo;
|
|
PROCESS_INFORMATION ProcessInfo = {0};
|
|
HANDLE hToken;
|
|
SECURITY_ATTRIBUTES saProcess;
|
|
PSECURITY_DESCRIPTOR psdNewProcessSD;
|
|
PSID psidUserSid;
|
|
HRESULT hr;
|
|
BOOL bStatus = FALSE;
|
|
ULONG ulSessionId;
|
|
BOOL bFromRunAsCache = FALSE;
|
|
|
|
hr = GetLaunchCommandLine( &pwszCommandLine );
|
|
if ( hr != S_OK )
|
|
return (hr);
|
|
|
|
*phProcess = NULL;
|
|
|
|
*pdwProcessId = 0;
|
|
|
|
hToken = NULL;
|
|
bStatus = FALSE;
|
|
|
|
StartupInfo.cb = sizeof(STARTUPINFO);
|
|
StartupInfo.lpReserved = NULL;
|
|
StartupInfo.lpDesktop = NULL;
|
|
StartupInfo.lpTitle = (SERVERTYPE_SURROGATE == _ServerType) ? NULL : _pwszServer;
|
|
StartupInfo.dwFlags = 0;
|
|
StartupInfo.cbReserved2 = 0;
|
|
StartupInfo.lpReserved2 = NULL;
|
|
|
|
ulSessionId = 0;
|
|
|
|
if( IsInteractiveUser() )
|
|
{
|
|
if (!fIsRemoteActivation)
|
|
{
|
|
// This code seems to be saying, "if the client is local then I
|
|
// should always be able to impersonate him". Which is true under
|
|
// normal circumstances, but we have seen a case (stress machine
|
|
// shutdowns) where the client is local but came in un-authenticated.
|
|
if (!pClientToken)
|
|
return E_ACCESSDENIED;
|
|
|
|
if ( !ImpersonateLoggedOnUser( pClientToken->GetToken() ) )
|
|
{
|
|
PrivMemFree(pwszCommandLine);
|
|
return E_ACCESSDENIED;
|
|
}
|
|
|
|
RevertToSelf();
|
|
}
|
|
|
|
ASSERT(pActIn);
|
|
LONG lSessIdTemp;
|
|
|
|
// Query for incoming session
|
|
GetSessionIDFromActParams(pActIn, &lSessIdTemp);
|
|
if (lSessIdTemp != INVALID_SESSION_ID)
|
|
{
|
|
ulSessionId = lSessIdTemp;
|
|
}
|
|
|
|
// Right now force all complus to
|
|
// session 0. Session based activation
|
|
// is still ill defined for complus
|
|
// servers.
|
|
if (_ServerType == SERVERTYPE_COMPLUS)
|
|
{
|
|
ulSessionId = 0;
|
|
}
|
|
|
|
hToken = GetUserTokenForSession(ulSessionId);
|
|
}
|
|
else
|
|
{
|
|
hToken = GetRunAsToken( clsctx,
|
|
AppidString(),
|
|
RunAsDomain(),
|
|
RunAsUser(),
|
|
TRUE);
|
|
if (hToken)
|
|
{
|
|
hr = RunAsGetTokenElem(&hToken,
|
|
ppvRunAsHandle);
|
|
if (SUCCEEDED(hr))
|
|
bFromRunAsCache = TRUE;
|
|
else
|
|
{
|
|
ASSERT((*ppvRunAsHandle == NULL) && "RunAsGetTokenElem failed but *ppvRunAsHandle is non-NULL");
|
|
PrivMemFree( pwszCommandLine );
|
|
CloseHandle(hToken);
|
|
return hr;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( ! hToken )
|
|
{
|
|
PrivMemFree( pwszCommandLine );
|
|
return (CO_E_RUNAS_LOGON_FAILURE);
|
|
}
|
|
|
|
psdNewProcessSD = 0;
|
|
|
|
psidUserSid = GetUserSid(hToken);
|
|
|
|
CAccessInfo AccessInfo(psidUserSid);
|
|
|
|
// We have to get past the CAccessInfo before we can use a goto.
|
|
|
|
if ( psidUserSid )
|
|
{
|
|
psdNewProcessSD = AccessInfo.IdentifyAccess(
|
|
FALSE,
|
|
PROCESS_ALL_ACCESS,
|
|
PROCESS_SET_INFORMATION | // Allow primary token to be set
|
|
PROCESS_TERMINATE | SYNCHRONIZE // Allow screen-saver control
|
|
);
|
|
}
|
|
|
|
if ( ! psdNewProcessSD )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto LaunchRunAsServerEnd;
|
|
}
|
|
|
|
saProcess.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
saProcess.lpSecurityDescriptor = psdNewProcessSD;
|
|
saProcess.bInheritHandle = FALSE;
|
|
|
|
{
|
|
//
|
|
// Get the environment block of the user
|
|
//
|
|
LPVOID lpEnvBlock = NULL;
|
|
|
|
bStatus = CreateEnvironmentBlock(&lpEnvBlock, hToken, FALSE);
|
|
|
|
HANDLE hSaferToken = NULL;
|
|
if(bStatus && SaferLevel())
|
|
{
|
|
bStatus = SaferComputeTokenFromLevel(SaferLevel(),
|
|
hToken,
|
|
&hSaferToken,
|
|
0,
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
hSaferToken = hToken;
|
|
}
|
|
|
|
if (bStatus && (ulSessionId != 0))
|
|
{
|
|
bStatus = SetTokenInformation(hSaferToken,
|
|
TokenSessionId,
|
|
&ulSessionId,
|
|
sizeof(ulSessionId));
|
|
}
|
|
|
|
if(bStatus)
|
|
{
|
|
//
|
|
// This allows the process create to work with paths to remote machines.
|
|
//
|
|
(void) ImpersonateLoggedOnUser( hSaferToken );
|
|
|
|
bStatus = CreateProcessAsUser(hSaferToken,
|
|
ServerExecutable(),
|
|
pwszCommandLine,
|
|
&saProcess,
|
|
NULL,
|
|
FALSE,
|
|
CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT,
|
|
lpEnvBlock,
|
|
NULL,
|
|
&StartupInfo,
|
|
&ProcessInfo);
|
|
|
|
(void) RevertToSelf();
|
|
}
|
|
|
|
//
|
|
// Free the environment block buffer
|
|
//
|
|
if (lpEnvBlock)
|
|
DestroyEnvironmentBlock(lpEnvBlock);
|
|
|
|
if (hSaferToken && SaferLevel())
|
|
CloseHandle(hSaferToken);
|
|
}
|
|
|
|
if ( ! bStatus )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
|
|
LogRunAsServerStartError(
|
|
&_Clsid,
|
|
clsctx,
|
|
pClientToken,
|
|
pwszCommandLine,
|
|
RunAsUser(),
|
|
RunAsDomain() );
|
|
goto LaunchRunAsServerEnd;
|
|
}
|
|
|
|
*phProcess = ProcessInfo.hProcess;
|
|
*pdwProcessId = ProcessInfo.dwProcessId;
|
|
|
|
NtClose( ProcessInfo.hThread );
|
|
|
|
LaunchRunAsServerEnd:
|
|
|
|
if ( hToken )
|
|
{
|
|
NtClose( hToken );
|
|
}
|
|
|
|
if ( psidUserSid )
|
|
{
|
|
PrivMemFree(psidUserSid);
|
|
}
|
|
|
|
PrivMemFree( pwszCommandLine );
|
|
|
|
if (!bFromRunAsCache)
|
|
*ppvRunAsHandle = NULL;
|
|
else
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
RunAsRelease(*ppvRunAsHandle);
|
|
*ppvRunAsHandle = NULL;
|
|
}
|
|
return (hr);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Member: LaunchService
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
HRESULT
|
|
CClsidData::LaunchService(
|
|
IN CToken * pClientToken,
|
|
IN DWORD clsctx,
|
|
OUT SC_HANDLE * phService
|
|
)
|
|
{
|
|
WCHAR *pwszArgs = NULL;
|
|
ULONG cArgs = 0;
|
|
WCHAR *apwszArgs[MAX_SERVICE_ARGS];
|
|
BOOL bStatus;
|
|
HRESULT hr;
|
|
|
|
ASSERT(g_hServiceController);
|
|
*phService = OpenService( g_hServiceController,
|
|
_pAppid->Service(),
|
|
GENERIC_EXECUTE | GENERIC_READ );
|
|
|
|
if ( ! *phService )
|
|
return (HRESULT_FROM_WIN32( GetLastError() ));
|
|
|
|
// Formulate the arguments (if any)
|
|
if ( ServiceArgs() )
|
|
{
|
|
UINT k = 0;
|
|
|
|
// Make a copy of the service arguments
|
|
pwszArgs = (WCHAR *) PrivMemAlloc(
|
|
(lstrlenW(ServiceArgs()) + 1) * sizeof(WCHAR));
|
|
if ( pwszArgs == NULL )
|
|
{
|
|
CloseServiceHandle(*phService);
|
|
*phService = 0;
|
|
return (E_OUTOFMEMORY);
|
|
}
|
|
lstrcpyW(pwszArgs, ServiceArgs());
|
|
|
|
// Scan the arguments
|
|
do
|
|
{
|
|
// Scan to the next non-whitespace character
|
|
while ( pwszArgs[k] &&
|
|
(pwszArgs[k] == L' ' ||
|
|
pwszArgs[k] == L'\t') )
|
|
{
|
|
k++;
|
|
}
|
|
|
|
// Store the next argument
|
|
if ( pwszArgs[k] )
|
|
{
|
|
apwszArgs[cArgs++] = &pwszArgs[k];
|
|
}
|
|
|
|
// Scan to the next whitespace char
|
|
while ( pwszArgs[k] &&
|
|
pwszArgs[k] != L' ' &&
|
|
pwszArgs[k] != L'\t' )
|
|
{
|
|
k++;
|
|
}
|
|
|
|
// Null terminate the previous argument
|
|
if ( pwszArgs[k] )
|
|
{
|
|
pwszArgs[k++] = L'\0';
|
|
}
|
|
} while ( pwszArgs[k] );
|
|
}
|
|
|
|
bStatus = StartService( *phService,
|
|
cArgs,
|
|
cArgs > 0 ? (LPCTSTR *) apwszArgs : NULL);
|
|
|
|
PrivMemFree(pwszArgs);
|
|
|
|
if ( bStatus )
|
|
return (S_OK);
|
|
|
|
DWORD dwErr = GetLastError();
|
|
|
|
hr = HRESULT_FROM_WIN32( dwErr );
|
|
|
|
if ( dwErr == ERROR_SERVICE_ALREADY_RUNNING )
|
|
return (hr);
|
|
|
|
CairoleDebugOut((DEB_ERROR,
|
|
"StartService %ws failed, error = %#x\n",_pAppid->Service(),GetLastError()));
|
|
CloseServiceHandle(*phService);
|
|
*phService = 0;
|
|
|
|
LogServiceStartError(
|
|
&_Clsid,
|
|
clsctx,
|
|
pClientToken,
|
|
_pAppid->Service(),
|
|
ServiceArgs(),
|
|
dwErr );
|
|
|
|
return (hr);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// LaunchAllowed
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
CClsidData::LaunchAllowed(
|
|
IN CToken * pClientToken,
|
|
IN DWORD clsctx
|
|
)
|
|
{
|
|
BOOL bStatus;
|
|
|
|
ASSERT(pClientToken);
|
|
|
|
#if DBG
|
|
WCHAR wszUser[MAX_PATH];
|
|
ULONG cchSize = MAX_PATH;
|
|
pClientToken->Impersonate();
|
|
GetUserName( wszUser, &cchSize );
|
|
pClientToken->Revert();
|
|
CairoleDebugOut((DEB_TRACE, "RPCSS : CClsidData::LaunchAllowed on %ws\n", wszUser));
|
|
#endif
|
|
|
|
if ( LaunchPermission() )
|
|
bStatus = CheckForAccess( pClientToken, LaunchPermission() );
|
|
else
|
|
{
|
|
CSecDescriptor* pSD = GetDefaultLaunchPermissions();
|
|
if (pSD)
|
|
{
|
|
bStatus = CheckForAccess( pClientToken, pSD->GetSD() );
|
|
|
|
pSD->DecRefCount();
|
|
}
|
|
else
|
|
bStatus = FALSE;
|
|
}
|
|
|
|
if ( ! bStatus )
|
|
{
|
|
LogLaunchAccessFailed(
|
|
&_Clsid,
|
|
clsctx,
|
|
pClientToken,
|
|
0 == LaunchPermission() );
|
|
}
|
|
|
|
return (bStatus);
|
|
}
|
|
|
|
HRESULT
|
|
CClsidData::GetLaunchCommandLine(
|
|
OUT WCHAR ** ppwszCommandLine
|
|
)
|
|
{
|
|
DWORD AllocBytes;
|
|
|
|
*ppwszCommandLine = 0;
|
|
|
|
if ( (SERVERTYPE_EXE16 == _ServerType) || (SERVERTYPE_EXE32 == _ServerType) )
|
|
{
|
|
AllocBytes = ( 1 + lstrlenW( L"-Embedding" ) +
|
|
1 + lstrlenW( _pwszServer ) ) * sizeof(WCHAR);
|
|
*ppwszCommandLine = (WCHAR *) PrivMemAlloc( AllocBytes );
|
|
if ( *ppwszCommandLine != NULL )
|
|
{
|
|
lstrcpyW( *ppwszCommandLine, _pwszServer );
|
|
lstrcatW( *ppwszCommandLine, L" -Embedding" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT( SERVERTYPE_SURROGATE == _ServerType
|
|
|| SERVERTYPE_COMPLUS == _ServerType
|
|
|| SERVERTYPE_DLLHOST == _ServerType );
|
|
|
|
AllocBytes = ( 1 + lstrlenW( DllSurrogate() ) ) * sizeof(WCHAR);
|
|
*ppwszCommandLine = (WCHAR *) PrivMemAlloc( AllocBytes );
|
|
if ( *ppwszCommandLine != NULL )
|
|
{
|
|
lstrcpyW( *ppwszCommandLine, DllSurrogate() );
|
|
}
|
|
}
|
|
|
|
return (*ppwszCommandLine ? S_OK : E_OUTOFMEMORY);
|
|
}
|
|
|
|
CNamedObject*
|
|
CClsidData::ServerLaunchMutex()
|
|
{
|
|
WCHAR* pwszPath = NULL;
|
|
WCHAR* pszPathBuf = NULL;
|
|
|
|
if ( SERVERTYPE_SURROGATE == _ServerType )
|
|
{
|
|
pwszPath = DllSurrogate();
|
|
|
|
// Will never be called any more, ever
|
|
if ( ! pwszPath || ! *pwszPath )
|
|
{
|
|
ASSERT(_pAppid);
|
|
pwszPath = _pAppid->AppidString();
|
|
//pwszPath = L"dllhost.exe";
|
|
}
|
|
}
|
|
else if ( DllHostOrComPlusProcess() )
|
|
{
|
|
ASSERT(_pAppid);
|
|
pwszPath = _pAppid->AppidString();
|
|
//pwszPath = L"dllhost.exe";
|
|
}
|
|
else
|
|
{
|
|
pwszPath = Server();
|
|
|
|
// Need to use the base .exe part of the file path here,
|
|
// there are tests that move the registration of their
|
|
// server from one dir to another and we need to handle
|
|
// this while concurrent activations are happening.
|
|
LPWSTR pszBaseExeName = NULL;
|
|
|
|
// First see how much of a buffer we need
|
|
DWORD dwRet = GetFullPathName(pwszPath,
|
|
0,
|
|
NULL,
|
|
NULL);
|
|
ASSERT(dwRet != 0);
|
|
pszPathBuf = (WCHAR*)PrivMemAlloc(dwRet * sizeof(WCHAR));
|
|
if (!pszPathBuf)
|
|
return NULL;
|
|
|
|
dwRet = GetFullPathName(pwszPath,
|
|
dwRet,
|
|
pszPathBuf,
|
|
&pszBaseExeName);
|
|
if ((dwRet == 0) || !pszBaseExeName)
|
|
{
|
|
ASSERT(!"Unexpected failure from GetFullPathName");
|
|
PrivMemFree(pszPathBuf);
|
|
return NULL;
|
|
}
|
|
|
|
// Use the base exe name for the event name
|
|
pwszPath = pszBaseExeName;
|
|
}
|
|
|
|
if ( !pwszPath )
|
|
{
|
|
ASSERT(0);
|
|
PrivMemFree(pszPathBuf);
|
|
return (NULL);
|
|
}
|
|
|
|
CNamedObject* pObject =
|
|
gpNamedObjectTable->GetNamedObject(pwszPath, CNamedObject::MUTEX);
|
|
|
|
PrivMemFree(pszPathBuf);
|
|
|
|
if (pObject)
|
|
{
|
|
WaitForSingleObject(pObject->Handle(), INFINITE);
|
|
}
|
|
|
|
return pObject;
|
|
}
|
|
|
|
//
|
|
// CClsidData::ServerRegisterEvent
|
|
//
|
|
// Returns a handle to the appropriate register
|
|
// event for the server in question.
|
|
//
|
|
CNamedObject*
|
|
CClsidData::ServerRegisterEvent()
|
|
{
|
|
if ( DllHostOrComPlusProcess() )
|
|
{
|
|
// For dllhost\com+ surrogates, we delegate to the appid
|
|
ASSERT(_pAppid);
|
|
return _pAppid->ServerRegisterEvent();
|
|
}
|
|
|
|
// Prefix the string with a special string; objects with guid
|
|
// names are just a touch too common for me to feel comfortable
|
|
// otherwise.
|
|
WCHAR wszEventName[GUIDSTR_MAX + sizeof(REGEVENT_PREFIX)];
|
|
memcpy(wszEventName, REGEVENT_PREFIX, sizeof(REGEVENT_PREFIX));
|
|
memcpy(wszEventName + REGEVENT_PREFIX_STRLEN, _wszClsid, sizeof(_wszClsid));
|
|
|
|
return gpNamedObjectTable->GetNamedObject(wszEventName, CNamedObject::EVENT);
|
|
}
|
|
|
|
//
|
|
// CClsidData::ServerInitializedEvent
|
|
//
|
|
// Returns a handle to the appropriate register
|
|
// event for the server in question. This event is
|
|
// signaled when initialization is finished.
|
|
//
|
|
CNamedObject*
|
|
CAppidData::ServerInitializedEvent()
|
|
{
|
|
// Prefix the string with a special string; objects with guid
|
|
// names are just a touch too common for me to feel comfortable
|
|
// otherwise.
|
|
WCHAR wszEventName[GUIDSTR_MAX + sizeof(INITEVENT_PREFIX)];
|
|
memcpy(wszEventName, INITEVENT_PREFIX, sizeof(INITEVENT_PREFIX));
|
|
memcpy(wszEventName + INITEVENT_PREFIX_STRLEN, _wszAppid, sizeof(_wszAppid));
|
|
|
|
return gpNamedObjectTable->GetNamedObject(wszEventName, CNamedObject::EVENT);
|
|
}
|
|
|
|
//
|
|
// CClsidData::ServerInitializedEvent
|
|
//
|
|
// Returns a handle to the appropriate register
|
|
// event for the server in question. This event is
|
|
// signaled when initialization is finished.
|
|
//
|
|
// NOTE: The non-DllHost path is currently not used here.
|
|
//
|
|
CNamedObject*
|
|
CClsidData::ServerInitializedEvent()
|
|
{
|
|
if ( DllHostOrComPlusProcess() )
|
|
{
|
|
// For dllhost\com+ surrogates, we delegate to the appid
|
|
ASSERT(_pAppid);
|
|
return _pAppid->ServerInitializedEvent();
|
|
}
|
|
|
|
// Prefix the string with a special string; objects with guid
|
|
// names are just a touch too common for me to feel comfortable
|
|
// otherwise.
|
|
WCHAR wszEventName[GUIDSTR_MAX + sizeof(INITEVENT_PREFIX)];
|
|
memcpy(wszEventName, INITEVENT_PREFIX, sizeof(INITEVENT_PREFIX));
|
|
memcpy(wszEventName + INITEVENT_PREFIX_STRLEN, _wszClsid, sizeof(_wszClsid));
|
|
|
|
return gpNamedObjectTable->GetNamedObject(wszEventName, CNamedObject::EVENT);
|
|
}
|
|
|
|
//
|
|
// CAppidData::ServerRegisterEvent
|
|
//
|
|
// Returns a handle to the appropriate register
|
|
// event for the server in question.
|
|
//
|
|
CNamedObject*
|
|
CAppidData::ServerRegisterEvent()
|
|
{
|
|
// Prefix the string with a special string; objects with guid
|
|
// names are just a touch too common for me to feel comfortable
|
|
// otherwise.
|
|
WCHAR wszEventName[GUIDSTR_MAX + sizeof(REGEVENT_PREFIX)];
|
|
memcpy(wszEventName, REGEVENT_PREFIX, sizeof(REGEVENT_PREFIX));
|
|
memcpy(wszEventName + REGEVENT_PREFIX_STRLEN, _wszAppid, sizeof(_wszAppid));
|
|
|
|
return gpNamedObjectTable->GetNamedObject(wszEventName, CNamedObject::EVENT);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// CClsidData::AddAppPathsToEnv
|
|
//
|
|
// Constructs a new environment block with an exe's AppPath value from the
|
|
// registry appended to the Path environment variable. Simply returns the
|
|
// given environment block if the clsid's server is not a 32 bit exe or if
|
|
// no AppPath is found for the exe.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
HRESULT
|
|
CClsidData::AddAppPathsToEnv(
|
|
IN WCHAR * pEnvBlock,
|
|
IN DWORD EnvBlockLength,
|
|
OUT WCHAR ** ppFinalEnvBlock
|
|
)
|
|
{
|
|
HKEY hAppKey;
|
|
WCHAR * pwszExe;
|
|
WCHAR * pwszExeEnd;
|
|
WCHAR * pwszAppPath;
|
|
WCHAR * pPath;
|
|
WCHAR * pNewEnvBlock;
|
|
WCHAR wszStr[8];
|
|
WCHAR wszKeyName[APP_PATH_LEN+MAX_PATH];
|
|
DWORD AppPathLength;
|
|
DWORD EnvFragLength;
|
|
DWORD Status;
|
|
BOOL bFoundPath;
|
|
|
|
pwszAppPath = 0;
|
|
pNewEnvBlock = 0;
|
|
|
|
*ppFinalEnvBlock = pEnvBlock;
|
|
|
|
if ( _ServerType != SERVERTYPE_EXE32 )
|
|
return (S_OK);
|
|
|
|
//
|
|
// Find the exe name by looking for the first .exe sub string which
|
|
// is followed by a space or null. Only servers registered with a
|
|
// .exe binary are supported. Otherwise the parsing is ambiguous since
|
|
// the LocalServer32 can contain paths with spaces as well as optional
|
|
// arguments.
|
|
//
|
|
if ( ! FindExeComponent( _pwszServer, L" ", &pwszExe, &pwszExeEnd ) )
|
|
return (S_OK);
|
|
|
|
//
|
|
// pwszExe points to the beginning of the binary name
|
|
// pwszExeEnd points to one char past the end of the binary name
|
|
//
|
|
memcpy( wszKeyName, APP_PATH, APP_PATH_LEN * sizeof(WCHAR) );
|
|
memcpy( &wszKeyName[APP_PATH_LEN], pwszExe, (ULONG) (pwszExeEnd - pwszExe) * sizeof(WCHAR) );
|
|
wszKeyName[APP_PATH_LEN + (pwszExeEnd - pwszExe)] = 0;
|
|
|
|
Status = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
wszKeyName,
|
|
0,
|
|
KEY_READ,
|
|
&hAppKey );
|
|
|
|
if ( ERROR_SUCCESS == Status )
|
|
{
|
|
Status = ReadStringValue( hAppKey, L"Path", &pwszAppPath );
|
|
RegCloseKey( hAppKey );
|
|
}
|
|
|
|
if ( Status != ERROR_SUCCESS )
|
|
return (S_OK);
|
|
|
|
AppPathLength = lstrlenW(pwszAppPath);
|
|
|
|
// New env block size includes space for a new ';' separator in the path.
|
|
pNewEnvBlock = (WCHAR *) PrivMemAlloc( (EnvBlockLength + 1 + AppPathLength) * sizeof(WCHAR) );
|
|
|
|
if ( ! pNewEnvBlock )
|
|
{
|
|
PrivMemFree( pwszAppPath );
|
|
return (E_OUTOFMEMORY);
|
|
}
|
|
|
|
pPath = pEnvBlock;
|
|
bFoundPath = FALSE;
|
|
|
|
for ( ; *pPath; )
|
|
{
|
|
memcpy( wszStr, pPath, 5 * sizeof(WCHAR) );
|
|
wszStr[5] = 0;
|
|
|
|
pPath += lstrlenW( pPath ) + 1;
|
|
|
|
if ( lstrcmpiW( wszStr, L"Path=" ) == 0 )
|
|
{
|
|
bFoundPath = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( bFoundPath )
|
|
{
|
|
pPath--;
|
|
|
|
EnvFragLength = (ULONG) (pPath - pEnvBlock);
|
|
|
|
memcpy( pNewEnvBlock,
|
|
pEnvBlock,
|
|
EnvFragLength * sizeof(WCHAR) );
|
|
|
|
pNewEnvBlock[EnvFragLength] = L';';
|
|
|
|
memcpy( &pNewEnvBlock[EnvFragLength + 1],
|
|
pwszAppPath,
|
|
AppPathLength * sizeof(WCHAR) );
|
|
|
|
memcpy( &pNewEnvBlock[EnvFragLength + 1 + AppPathLength],
|
|
pPath,
|
|
(EnvBlockLength - EnvFragLength) * sizeof(WCHAR) );
|
|
|
|
*ppFinalEnvBlock = pNewEnvBlock;
|
|
}
|
|
else
|
|
{
|
|
PrivMemFree( pNewEnvBlock );
|
|
}
|
|
|
|
PrivMemFree( pwszAppPath );
|
|
return (S_OK);
|
|
}
|