//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1993.
//
// File: clsdata.cxx
//
// Contents: implements methods of classes defined in clsdata.hxx
//
// Functions: CLocalServer::StartServer
// CLocalServer::SetEndPoint
//
// History: 21-Apr-93 Ricksa Created
// 31-Dec-93 ErikGav Chicago port
// 31-Mar-94 AndyH Start EXEs in client's security Context
// 10-Jun-94 BruceMa Fix to debug SCM as non-Service
//
//--------------------------------------------------------------------------
#include <headers.cxx>
#pragma hdrstop
#include <scm.hxx>
#include <scmrotif.hxx>
#include <sem.hxx>
#include "port.hxx"
#include "cls.hxx"
#include <clsdata.hxx>
#include "access.hxx"
#include <ntlsa.h>
//
// Private prototypes
//
BOOL
CreateAndSetProcessToken (
PPROCESS_INFORMATION ProcessInformation,
HANDLE Token,
PSID psidUserSid );
VOID
DeleteUserSid (
PSID Sid );
PSID
GetUserSid (
HANDLE hUserToken );
PSECURITY_DESCRIPTOR
CreateUserThreadSD (
PSID psidUserSid,
PSID psidSCMSid );
BOOL
IsInteractive(
HANDLE hUserToken );
DWORD
GetShellProcessID();
//
// Memory macros
//
#define Alloc(c) ((PVOID)LocalAlloc(LPTR, c))
#define ReAlloc(p, c) ((PVOID)LocalReAlloc(p, c, LPTR | LMEM_MOVEABLE))
#define Free(p) ((VOID)LocalFree(p))
//
// Globals
//
extern PSID psidMySid;
extern ULONG fUseSeparateWOW;
extern CClassCacheList *gpClassCache;
#ifndef _CHICAGO_
// From ..\objex\objex.cxx
extern USHORT cMyProtseqs;
extern USHORT *aMyProtseqs;
extern "C" {
// From ..\wrapper\epts.c
extern PROTSEQ_INFO gaProtseqInfo[];
}
#endif
//+-------------------------------------------------------------------------
//
// Member: CLocalServer::StartServer
//
// Synopsis: Start a server process
//
// Arguments: none
//
// Returns: S_OK - Server started
// CO_E_SERVER_EXEC_FAILURE - couldn't start server
//
// Algorithm:
//
// History: 21-Apr-93 Ricksa Created
// 04-Jan-94 Ricksa Modified for class starting sync.
// 31-Mar-94 AndyH Start processes as client
// 09-Jun-95 SusiA Added Chicago ANSI optimization
//
//--------------------------------------------------------------------------
BOOL CLocalServer::StartServer(CLSID &clsid,
WCHAR * pwszAppID,
CToken * pClientToken,
WCHAR * pwszWinstaDesktop,
HANDLE * phProcess,
WCHAR *pwszRunAsDomainName,
WCHAR *pwszRunAsUserName,
WCHAR* pwszSurrogatePath,
BOOL fSurrogate)
{
*phProcess = NULL;
CairoleDebugOut((DEB_ITRACE, "CLocalServer: App Name=%ws\n", (fSurrogate ? L"Surrogate Launch" : _pwszPath)));
// Where we put the command line
WCHAR awszTmpCmdLine[MAX_PATH];
WCHAR *pwszTmpCmdLine = awszTmpCmdLine;
DWORD cbData;
if(!fSurrogate)
{
// size of command line buffer =
// size of server path
// size of -Embedding (including preceding blank and terminating null
cbData = (lstrlenW(_pwszPath) + 12) * sizeof(WCHAR);
}
else
{
// size of command line buffer =
// size of surrogate command line
// size of -Embedding (including preceding blank and terminating null
cbData = (lstrlenW(pwszSurrogatePath) + 12) * sizeof(WCHAR);
}
if (cbData > sizeof(awszTmpCmdLine))
{
pwszTmpCmdLine = (WCHAR *) PrivMemAlloc (cbData);
if (pwszTmpCmdLine == NULL)
{
return FALSE;
}
}
//
// Build command line for server. Use the path from the registry.
// This may be absolute or relative. In either case, use the path
// searching rules from CreateProcess.
//
lstrcpyW(pwszTmpCmdLine, fSurrogate ? pwszSurrogatePath : _pwszPath );
lstrcatW(pwszTmpCmdLine, L" -Embedding");
#ifndef _CHICAGO_
// Check if this is a RunAs activation
if (pwszRunAsUserName != NULL)
{
return StartRunAsServer(clsid,
pwszAppID,
phProcess,
pClientToken,
pwszRunAsDomainName,
pwszRunAsUserName,
pwszTmpCmdLine,
fSurrogate);
}
#endif // !_CHICAGO_
PROCESS_INFORMATION procinfo;
procinfo.hProcess = NULL;
procinfo.hThread = NULL;
STARTUPINFO startupinfo;
startupinfo.cb = sizeof(STARTUPINFO);
startupinfo.lpReserved = NULL;
startupinfo.lpDesktop = pwszWinstaDesktop;
startupinfo.lpTitle = fSurrogate ? pwszSurrogatePath : _pwszPath;
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;
BOOL fResultOK;
#ifndef _CHICAGO_
// Creation flags for create process
DWORD fdwCreationFlags = CREATE_NEW_CONSOLE | CREATE_SUSPENDED;
//
// Set WOW flag for CreateProces
//
if (SCM_FORCE_SEPARATE_WOW == fUseSeparateWOW)
{
fdwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
}
RPC_STATUS RpcStatus = RpcImpersonateClient( (RPC_BINDING_HANDLE) 0 );
if (RpcStatus != RPC_S_OK)
{
CairoleDebugOut((DEB_ERROR, "Failed RpcImpersonateClient\n"));
// Bail out now!
if (pwszTmpCmdLine != awszTmpCmdLine)
{
PrivMemFree (pwszTmpCmdLine);
}
return FALSE;
}
//
// 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.
//
// I added the Thread SD since ntpsapi.h says that THREAD_QUERY_INFORMATION
// access is needed and I want to make darn sure we have that access.
//
// JimK say's it ain't so, and the code works fine without specifying
// sec. attributes for the thread, so I removed that code on 4/29/94.
//
SECURITY_ATTRIBUTES saProcess;
PSECURITY_DESCRIPTOR psdNewProcessSD;
CAccessInfo AccessInfo(pClientToken->GetSid());
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 == NULL)
{
CairoleDebugOut((DEB_ERROR, "Failed to create SD for process\n"));
RpcStatus = RpcRevertToSelf();
Win4Assert(RPC_S_OK == RpcStatus);
fResultOK = FALSE;
goto ExitProcessing;
}
saProcess.nLength = sizeof(SECURITY_ATTRIBUTES);
saProcess.lpSecurityDescriptor = psdNewProcessSD;
saProcess.bInheritHandle = FALSE;
//
// Do the exec while impersonating so the file access gets ACL
// checked correctly. Create the app suspended so we can stuff
// a new token and resume the process.
//
fResultOK = CreateProcess(NULL, // application name
pwszTmpCmdLine, // command line
&saProcess, // process sec attributes
NULL, // default thread sec attributes
// (this was &saThread, but isn't needed)
FALSE, // dont inherit handles
fdwCreationFlags, // creation flags
NULL, // use same enviroment block
NULL, // use same directory
&startupinfo, // startup info
&procinfo); // proc info returned
//
// Everything else we do as ourself.
//
RpcStatus = RpcRevertToSelf();
Win4Assert(RPC_S_OK == RpcStatus);
if (!fResultOK)
{
CairoleDebugOut((DEB_ERROR, "%ws failed create process. Error = %d\n",
pwszTmpCmdLine, GetLastError()));
#ifndef _CHICAGO_
// for this message,
// %1 is the command line, and %2 is the error number string
// %3 is the CLSID
HANDLE LogHandle;
LPTSTR Strings[3]; // array of message strings.
WCHAR wszErrnum[20];
WCHAR wszClsid[GUIDSTR_MAX];
// Save the command line
Strings[0] = pwszTmpCmdLine;
// Save the error number
wsprintf(wszErrnum, L"%lu",GetLastError() );
Strings[1] = wszErrnum;
// Get the clsid
wStringFromGUID2(clsid, wszClsid, sizeof(wszClsid));
Strings[2] = wszClsid;
// Get the log handle, then report then event.
LogHandle = RegisterEventSource( NULL,
SCM_EVENT_SOURCE );
if ( LogHandle )
{
ReportEvent( LogHandle,
EVENTLOG_ERROR_TYPE,
0, // event category
EVENT_RPCSS_CREATEPROCESS_FAILURE,
pClientToken->GetSid(), // SID
3, // 3 strings passed
0, // 0 bytes of binary
(LPCTSTR *)Strings, // array of strings
NULL ); // no raw data
// clean up the event log handle
DeregisterEventSource(LogHandle);
}
#endif // _CHICAGO_
goto ExitProcessing;
}
//
// if the process was "started" in the shared WOW, we don't stuff the token
// or attempt to resume the thread. when a created process is in the shared
// wow its hThread is NULL.
//
if (NULL != procinfo.hThread)
{
// Set the primary token for the app
fResultOK = CreateAndSetProcessToken(
&procinfo,
pClientToken->GetToken(),
pClientToken->GetSid() );
if (!fResultOK)
{
CairoleDebugOut((DEB_ERROR, "failed to set token for process\n"));
}
else
{
if ( ResumeThread(procinfo.hThread) == -1 )
TerminateProcess(procinfo.hProcess, 0);
}
}
ExitProcessing:
if (NULL != procinfo.hThread)
CloseHandle(procinfo.hThread);
if (fResultOK && procinfo.hThread != NULL)
{
*phProcess = procinfo.hProcess;
}
else if (procinfo.hProcess != NULL)
{
CloseHandle(procinfo.hProcess);
}
if (pwszTmpCmdLine != awszTmpCmdLine)
{
PrivMemFree (pwszTmpCmdLine);
}
return fResultOK;
#else
//
// This is code we use for _CHICAGO_
//
//
// For Chicago, we just do the CreateProcess.
//
fResultOK = CreateProcess(NULL, // application name
pwszTmpCmdLine, // command line
NULL, // default process sec attributes
NULL, // default thread sec attributes
FALSE, // dont inherit handles
CREATE_NEW_CONSOLE, // creation flags
NULL, // use same enviroment block
NULL, // use same directory
&startupinfo, // startup info
&procinfo); // proc info returned
if (!fResultOK)
{
CairoleDebugOut((DEB_ERROR, "%ws failed create process. Error = %d\n",
pwszTmpCmdLine, GetLastError()));
}
else
{ // CreateProcess OK
CairoleDebugOut((DEB_ITRACE,"ProcID =0x%x\n", procinfo.dwProcessId));
CairoleDebugOut((DEB_ITRACE,"ThreadID=0x%x\n\n", procinfo.dwThreadId));
CloseHandle(procinfo.hThread);
*phProcess = procinfo.hProcess;
}
if (pwszTmpCmdLine != awszTmpCmdLine)
{
PrivMemFree (pwszTmpCmdLine);
}
return fResultOK;
#endif // _CHICAGO_
}
#ifndef _CHICAGO_
// nothing else in this file is needed for chicago.
//+-------------------------------------------------------------------------
//
// Member: CLocalServer::StartRunAsServer
//
// Synopsis: Start a RunAs server process
//
// Arguments: CLSID& - clsid
// HANDLE - process handle
// WCHAR* - domain name
// WCHAR* - user name
// WCHAR* - command line
//
// Returns: BOOL - TRUE if successful
//
// Algorithm:
//
// History: 07-Dec-95 BruceMa Created
//
//--------------------------------------------------------------------------
BOOL CLocalServer::StartRunAsServer(CLSID &clsid,
WCHAR *pwszAppID,
HANDLE *phProcess,
CToken *pClientToken,
WCHAR *pwszRunAsDomainName,
WCHAR *pwszRunAsUserName,
WCHAR *pwszCommandLine,
BOOL fSurrogate)
{
NTSTATUS err;
HANDLE hToken;
SECURITY_ATTRIBUTES saProcess;
PSECURITY_DESCRIPTOR psdNewProcessSD;
STARTUPINFO sStartupInfo;
PROCESS_INFORMATION sProcInfo;
PSID psidUserSid = NULL;
BOOL Result;
BOOL CloseToken = FALSE;
// Initialize
hToken = NULL;
Result = FALSE;
*phProcess = NULL;
sStartupInfo.cb = sizeof(STARTUPINFO);
sStartupInfo.lpReserved = NULL;
sStartupInfo.lpDesktop = NULL;
sStartupInfo.lpTitle = fSurrogate ? NULL : _pwszPath;
sStartupInfo.dwFlags = 0;
sStartupInfo.cbReserved2 = 0;
sStartupInfo.lpReserved2 = NULL;
if ( lstrcmpiW( pwszRunAsUserName, L"Interactive User" ) == 0 )
{
hToken = GetShellProcessToken();
}
else
{
hToken = GetRunAsToken( pwszAppID,
pwszRunAsDomainName,
pwszRunAsUserName );
CloseToken = TRUE;
}
if ( hToken == 0 )
return FALSE;
// Build the security descriptor for the process we're creating
psidUserSid = GetUserSid(hToken);
CAccessInfo AccessInfo(psidUserSid);
// We have to get past the CAccessInfo before we can use a goto.
if ( psidUserSid == NULL )
{
CairoleDebugOut((DEB_ERROR, "Failed to get RunAs security ID\n"));
goto CleanupExit;
}
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 == NULL)
{
CairoleDebugOut((DEB_ERROR, "Failed to create RunAs SD for process\n"));
goto CleanupExit;
}
saProcess.nLength = sizeof(SECURITY_ATTRIBUTES);
saProcess.lpSecurityDescriptor = psdNewProcessSD;
saProcess.bInheritHandle = FALSE;
if (!CreateProcessAsUser(hToken, NULL, pwszCommandLine,
&saProcess, NULL,
FALSE, CREATE_NEW_CONSOLE, NULL, NULL,
&sStartupInfo, &sProcInfo))
{
CairoleDebugOut((DEB_ERROR, "Failed to create RunAs process\n"));
// for this message,
// %1 is the command line, and %2 is the error number string
// %3 is the CLSID, %4 is the RunAs domain name, %5 is the RunAs Userid
HANDLE LogHandle;
LPTSTR Strings[5]; // array of message strings.
WCHAR wszErrnum[20];
WCHAR wszClsid[GUIDSTR_MAX];
// Save the command line
Strings[0] = pwszCommandLine;
// Save the error number
wsprintf(wszErrnum, L"%lu",GetLastError() );
Strings[1] = wszErrnum;
// Get the clsid
wStringFromGUID2(clsid, wszClsid, sizeof(wszClsid));
Strings[2] = wszClsid;
// Put in the RunAs identity
Strings[3] = pwszRunAsDomainName;
Strings[4] = pwszRunAsUserName;
// Get the log handle, then report then event.
LogHandle = RegisterEventSource( NULL,
SCM_EVENT_SOURCE );
if ( LogHandle )
{
ReportEvent( LogHandle,
EVENTLOG_ERROR_TYPE,
0, // event category
EVENT_RPCSS_RUNAS_CREATEPROCESS_FAILURE,
pClientToken ? pClientToken->GetSid() : NULL, // SID
5, // 5 strings passed
0, // 0 bytes of binary
(LPCTSTR *)Strings, // array of strings
NULL ); // no raw data
// clean up the event log handle
DeregisterEventSource(LogHandle);
}
goto CleanupExit;
}
Result = TRUE;
// Return the handle of the new process
*phProcess = sProcInfo.hProcess;
NtClose( sProcInfo.hThread );
CleanupExit:
if ( CloseToken )
NtClose( hToken );
if(!fSurrogate)
{
//
// Don't delete the SID. We cache it to use in checks when a server
// registers a RunAs CLSID.
//
_pRunAsSid = psidUserSid;
}
else
{
// we need to delete the SID because surrogates share the same local server
DeleteUserSid(psidUserSid);
Win4Assert(_pRunAsSid == NULL);
}
return Result;
}
HANDLE
GetRunAsToken(
WCHAR *pwszAppID,
WCHAR *pwszRunAsDomainName,
WCHAR *pwszRunAsUserName )
{
LSA_OBJECT_ATTRIBUTES sObjAttributes;
HANDLE hPolicy = NULL;
LSA_UNICODE_STRING sKey;
WCHAR wszKey[CLSIDSTR_MAX+5];
PLSA_UNICODE_STRING psPassword;
HANDLE hToken;
if ( lstrcmpiW( pwszRunAsUserName, L"Interactive User" ) == 0 )
{
//
// Should call GetShellProcessToken() instead and not free the
// resulting token.
//
return 0;
}
if ( !pwszAppID )
{
// if we have a RunAs, we'd better have an appid....
return 0;
}
// formulate the access key
lstrcpyW(wszKey, L"SCM:");
lstrcatW(wszKey, pwszAppID );
// UNICODE_STRING length fields are in bytes and include the NULL
// terminator
sKey.Length = (lstrlenW(wszKey) + 1) * sizeof(WCHAR);
sKey.MaximumLength = (CLSIDSTR_MAX + 5) * sizeof(WCHAR);
sKey.Buffer = wszKey;
// Open the local security policy
InitializeObjectAttributes(&sObjAttributes, NULL, 0L, NULL, NULL);
if (!NT_SUCCESS(LsaOpenPolicy(NULL, &sObjAttributes,
POLICY_GET_PRIVATE_INFORMATION, &hPolicy)))
{
return 0;
}
// Read the user's password
if (!NT_SUCCESS(LsaRetrievePrivateData(hPolicy, &sKey, &psPassword)))
{
LsaClose(hPolicy);
return 0;
}
// Close the policy handle, we're done with it now.
LsaClose(hPolicy);
// Log the specifed user on
if (!LogonUser(pwszRunAsUserName, pwszRunAsDomainName, psPassword->Buffer,
LOGON32_LOGON_BATCH, LOGON32_PROVIDER_DEFAULT, &hToken))
{
memset(psPassword->Buffer, 0, psPassword->Length);
// for this message,
// %1 is the error number string
// %2 is the domain name
// %3 is the user name
// %4 is the CLSID
HANDLE LogHandle;
LPTSTR Strings[4]; // array of message strings.
WCHAR wszErrnum[20];
WCHAR wszClsid[GUIDSTR_MAX];
// Save the error number
wsprintf(wszErrnum, L"%lu",GetLastError() );
Strings[0] = wszErrnum;
// Put in the RunAs identity
Strings[1] = pwszRunAsDomainName;
Strings[2] = pwszRunAsUserName;
// Get the clsid
Strings[3] = pwszAppID;
// Get the log handle, then report then event.
LogHandle = RegisterEventSource( NULL,
SCM_EVENT_SOURCE );
if ( LogHandle )
{
ReportEvent( LogHandle,
EVENTLOG_ERROR_TYPE,
0, // event category
EVENT_RPCSS_RUNAS_CANT_LOGIN,
NULL, // SID
4, // 4 strings passed
0, // 0 bytes of binary
(LPCTSTR *)Strings, // array of strings
NULL ); // no raw data
// clean up the event log handle
DeregisterEventSource(LogHandle);
}
return 0;
}
// Clear the password
memset(psPassword->Buffer, 0, psPassword->Length);
return hToken;
}
/***************************************************************************\
* CreateAndSetProcessToken
*
* Set the primary token of the specified process
* If the specified token is NULL, this routine does nothing.
*
* It assumed that the handles in ProcessInformation are the handles returned
* on creation of the process and therefore have all access.
*
* Returns TRUE on success, FALSE on failure.
*
* 01-31-91 Davidc Created.
* 31-Mar-94 AndyH Started from Winlogon; added SetToken
\***************************************************************************/
BOOL
CreateAndSetProcessToken(
PPROCESS_INFORMATION ProcessInformation,
HANDLE hUserToken,
PSID psidUserSid
)
{
NTSTATUS NtStatus, NtAdjustStatus;
PROCESS_ACCESS_TOKEN PrimaryTokenInfo;
HANDLE hTokenToAssign;
OBJECT_ATTRIBUTES ObjectAttributes;
BOOLEAN fWasEnabled;
PSECURITY_DESCRIPTOR psdNewProcessTokenSD;
//
// Check for a NULL token. (No need to do anything)
// The process will run in the parent process's context and inherit
// the default ACL from the parent process's token.
//
if (hUserToken == NULL) {
return(TRUE);
}
//
// Create the security descriptor that we want to put in the Token.
// Need to destroy it before we leave this function.
//
CAccessInfo AccessInfo(psidUserSid);
psdNewProcessTokenSD = AccessInfo.IdentifyAccess(
FALSE,
TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS |
TOKEN_ADJUST_DEFAULT | TOKEN_QUERY |
TOKEN_DUPLICATE | TOKEN_IMPERSONATE | READ_CONTROL,
TOKEN_QUERY
);
if (psdNewProcessTokenSD == NULL)
{
CairoleDebugOut((DEB_ERROR, "Failed to create SD for process token\n"));
return(FALSE);
}
//
// A primary token can only be assigned to one process.
// Duplicate the logon token so we can assign one to the new
// process.
//
InitializeObjectAttributes(
&ObjectAttributes,
NULL,
0,
NULL,
psdNewProcessTokenSD
);
NtStatus = NtDuplicateToken(
hUserToken, // Duplicate this token
TOKEN_ASSIGN_PRIMARY, // Give me this access to the resulting token
&ObjectAttributes,
FALSE, // EffectiveOnly
TokenPrimary, // TokenType
&hTokenToAssign // Duplicate token handle stored here
);
if (!NT_SUCCESS(NtStatus)) {
CairoleDebugOut((DEB_ERROR, "CreateAndSetProcessToken failed to duplicate primary token for new user process, status = 0x%lx\n", NtStatus));
return(FALSE);
}
//
// Set the process's primary token
//
//
// Enable the required privilege
//
NtStatus = RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE, TRUE,
FALSE, &fWasEnabled);
if (NT_SUCCESS(NtStatus)) {
PrimaryTokenInfo.Token = hTokenToAssign;
PrimaryTokenInfo.Thread = ProcessInformation->hThread;
NtStatus = NtSetInformationProcess(
ProcessInformation->hProcess,
ProcessAccessToken,
(PVOID)&PrimaryTokenInfo,
(ULONG)sizeof(PROCESS_ACCESS_TOKEN)
);
//
// if we just started the Shared WOW, the handle we get back
// is really just a handle to an event.
//
if (STATUS_OBJECT_TYPE_MISMATCH == NtStatus)
{
HANDLE hRealProcess = OpenProcess(
PROCESS_SET_INFORMATION | PROCESS_TERMINATE | SYNCHRONIZE,
FALSE,
ProcessInformation->dwProcessId);
if (hRealProcess)
{
NtStatus = NtSetInformationProcess(
hRealProcess,
ProcessAccessToken,
(PVOID)&PrimaryTokenInfo,
(ULONG)sizeof(PROCESS_ACCESS_TOKEN)
);
CloseHandle(hRealProcess);
}
}
//
// Restore the privilege to its previous state
//
NtAdjustStatus = RtlAdjustPrivilege(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE,
fWasEnabled, FALSE, &fWasEnabled);
if (!NT_SUCCESS(NtAdjustStatus)) {
CairoleDebugOut((DEB_ERROR, "failed to restore assign-primary-token privilege to previous enabled state\n"));
}
if (NT_SUCCESS(NtStatus)) {
NtStatus = NtAdjustStatus;
}
} else {
CairoleDebugOut((DEB_ERROR, "failed to enable assign-primary-token privilege\n"));
}
//
// We're finished with the token handle and the SD
//
CloseHandle(hTokenToAssign);
if (!NT_SUCCESS(NtStatus)) {
CairoleDebugOut((DEB_ERROR, "CreateAndSetProcessToken failed to set primary token for new user process, Status = 0x%lx\n", NtStatus));
}
return (NT_SUCCESS(NtStatus));
}
/***************************************************************************\
* GetUserSid
*
* Allocs space for the user sid, fills it in and returns a pointer.
* The sid should be freed by calling DeleteUserSid.
*
* Note the sid returned is the user's real sid, not the per-logon sid.
*
* Returns pointer to sid or NULL on failure.
*
* History:
* 26-Aug-92 Davidc Created.
* 31-Mar-94 AndyH Copied from Winlogon, changed arg from pGlobals
\***************************************************************************/
PSID
GetUserSid(
HANDLE hUserToken
)
{
BYTE achBuffer[100];
PTOKEN_USER pUser = (PTOKEN_USER) &achBuffer;
PSID pSid;
DWORD dwBytesRequired;
NTSTATUS NtStatus;
BOOL fAllocatedBuffer = FALSE;
NtStatus = NtQueryInformationToken(
hUserToken, // Handle
TokenUser, // TokenInformationClass
pUser, // TokenInformation
sizeof(achBuffer), // TokenInformationLength
&dwBytesRequired // ReturnLength
);
if (!NT_SUCCESS(NtStatus))
{
if (NtStatus != STATUS_BUFFER_TOO_SMALL)
{
Win4Assert(NtStatus == STATUS_BUFFER_TOO_SMALL);
return NULL;
}
//
// Allocate space for the user info
//
pUser = (PTOKEN_USER) Alloc(dwBytesRequired);
if (pUser == NULL)
{
CairoleDebugOut((DEB_ERROR, "Failed to allocate %d bytes\n", dwBytesRequired));
Win4Assert(pUser != NULL);
return NULL;
}
fAllocatedBuffer = TRUE;
//
// Read in the UserInfo
//
NtStatus = NtQueryInformationToken(
hUserToken, // Handle
TokenUser, // TokenInformationClass
pUser, // TokenInformation
dwBytesRequired, // TokenInformationLength
&dwBytesRequired // ReturnLength
);
if (!NT_SUCCESS(NtStatus))
{
CairoleDebugOut((DEB_ERROR, "Failed to query user info from user token, status = 0x%lx\n", NtStatus));
Win4Assert(NtStatus == STATUS_SUCCESS);
Free((HANDLE)pUser);
return NULL;
}
}
// Alloc buffer for copy of SID
dwBytesRequired = RtlLengthSid(pUser->User.Sid);
pSid = Alloc(dwBytesRequired);
if (pSid == NULL)
{
CairoleDebugOut((DEB_ERROR, "Failed to allocate %d bytes\n", dwBytesRequired));
if (fAllocatedBuffer == TRUE)
{
Free((HANDLE)pUser);
}
return NULL;
}
// Copy SID
NtStatus = RtlCopySid(dwBytesRequired, pSid, pUser->User.Sid);
if (fAllocatedBuffer == TRUE)
{
Free((HANDLE)pUser);
}
if (!NT_SUCCESS(NtStatus))
{
CairoleDebugOut((DEB_ERROR, "RtlCopySid failed, status = 0x%lx\n", NtStatus));
Win4Assert(NtStatus != STATUS_SUCCESS);
Free(pSid);
pSid = NULL;
}
return pSid;
}
/***************************************************************************\
* DeleteUserSid
*
* Deletes a user sid previously returned by GetUserSid()
*
* Returns nothing.
*
* History:
* 26-Aug-92 Davidc Created
*
\***************************************************************************/
VOID
DeleteUserSid(
PSID Sid
)
{
Free(Sid);
}
//+-------------------------------------------------------------------------
//
// Function: GetUserSidHelper
//
// Synopsis: Helper function to return the user SID of the caller
//
// Arguments: &PSID - Where to store the caller's PSID
//
// Returns: HRESULT - S_OK if successful
// E_FAIL otherwise
//
// Algorithm:
//
// History: 09-Jan-95 BruceMa Created
//
// Note: If successful and *ppUserSid is non-NULL then it returns
// in an impersonation state
//
//--------------------------------------------------------------------------
HRESULT GetUserSidHelper(PSID *ppUserSid)
{
// Initialize
*ppUserSid = NULL;
// Impersonate the client
RPC_STATUS RpcStatus = RpcImpersonateClient( (RPC_BINDING_HANDLE) 0 );
if (RpcStatus != RPC_S_OK)
{
CairoleDebugOut((DEB_ERROR, "GetUserSidHelper: Failed RpcImpersonateClient\n"));
return E_FAIL;
}
// Get caller's token while impersonating
HANDLE hUserToken = NULL;
NTSTATUS NtStatus;
if (!NT_SUCCESS(NtOpenThreadToken(NtCurrentThread(),
TOKEN_DUPLICATE | TOKEN_QUERY,
TRUE,
&hUserToken)))
{
RpcRevertToSelf();
CairoleDebugOut((DEB_ERROR, "GetUserSidHelper: Failed NtOpenThreadToken\n"));
return E_FAIL;
}
// Get the user sid
*ppUserSid = GetUserSid(hUserToken);
NtClose( hUserToken );
if (*ppUserSid == NULL)
{
RpcRevertToSelf();
return E_FAIL;
}
else
{
return S_OK;
}
}
// Initialzed in InitializeSCM during boot.
CRITICAL_SECTION ShellQueryCS;
HANDLE GetShellProcessToken()
{
NTSTATUS NtStatus;
BOOL bStatus;
HKEY hReg;
LONG RegStatus;
DWORD RegSize, RegType;
WCHAR * pwszImageName;
DWORD Pid;
BYTE StackInfoBuffer[4096];
PBYTE pProcessInfoBuffer;
ULONG ProcessInfoBufferSize;
ULONG TotalOffset;
PSYSTEM_PROCESS_INFORMATION pProcessInfo;
static HANDLE hShellProcess = 0;
static HANDLE hShellProcessToken = 0;
static WCHAR * pwszShellName = 0;
EnterCriticalSection( &ShellQueryCS );
if ( ! pwszShellName )
{
RegStatus = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
0,
KEY_READ,
&hReg );
if ( RegStatus != ERROR_SUCCESS )
{
LeaveCriticalSection( &ShellQueryCS );
return 0;
}
// Shell will usually be explorer.exe.
RegSize = 13 * sizeof(WCHAR);
pwszShellName = (WCHAR *) PrivMemAlloc( RegSize );
if ( ! pwszShellName )
return 0;
RegStatus = RegQueryValueEx( hReg,
L"Shell",
0,
&RegType,
(LPBYTE)pwszShellName,
&RegSize );
if ( RegStatus == ERROR_MORE_DATA )
{
PrivMemFree( pwszShellName );
pwszShellName = (WCHAR *) PrivMemAlloc( RegSize );
if ( ! pwszShellName )
return 0;
RegStatus = RegQueryValueEx( hReg,
L"Shell",
0,
&RegType,
(LPBYTE)pwszShellName,
&RegSize );
}
RegCloseKey( hReg );
if ( RegStatus != ERROR_SUCCESS )
{
PrivMemFree( pwszShellName );
pwszShellName = 0;
LeaveCriticalSection( &ShellQueryCS );
return 0;
}
}
if ( hShellProcess )
{
if ( WaitForSingleObject( hShellProcess, 0 ) == WAIT_TIMEOUT )
{
LeaveCriticalSection( &ShellQueryCS );
return hShellProcessToken;
}
CloseHandle( hShellProcessToken );
CloseHandle( hShellProcess );
hShellProcessToken = 0;
hShellProcess = 0;
}
Pid = 0;
pProcessInfoBuffer = StackInfoBuffer;
ProcessInfoBufferSize = sizeof(StackInfoBuffer);
for (;;)
{
NtStatus = NtQuerySystemInformation( SystemProcessInformation,
pProcessInfoBuffer,
ProcessInfoBufferSize,
NULL );
if ( NtStatus == STATUS_INFO_LENGTH_MISMATCH )
{
ProcessInfoBufferSize += 4096;
if ( pProcessInfoBuffer != StackInfoBuffer )
PrivMemFree( pProcessInfoBuffer );
pProcessInfoBuffer = (PBYTE) PrivMemAlloc( ProcessInfoBufferSize );
if ( ! pProcessInfoBuffer )
goto AllDone;
continue;
}
if ( ! NT_SUCCESS(NtStatus) )
goto AllDone;
break;
}
pProcessInfo = (PSYSTEM_PROCESS_INFORMATION) pProcessInfoBuffer;
TotalOffset = 0;
for (;;)
{
if ( pProcessInfo->ImageName.Buffer )
{
pwszImageName = &pProcessInfo->ImageName.Buffer[pProcessInfo->ImageName.Length / sizeof(WCHAR)];
while ( (pwszImageName != pProcessInfo->ImageName.Buffer) &&
(pwszImageName[-1] != '\\') )
pwszImageName--;
if ( lstrcmpiW( pwszShellName, pwszImageName ) == 0 )
{
Pid = (DWORD)pProcessInfo->UniqueProcessId;
break;
}
}
if ( pProcessInfo->NextEntryOffset == 0 )
break;
TotalOffset += pProcessInfo->NextEntryOffset;
pProcessInfo = (PSYSTEM_PROCESS_INFORMATION) &pProcessInfoBuffer[TotalOffset];
}
AllDone:
if ( pProcessInfoBuffer != StackInfoBuffer )
PrivMemFree( pProcessInfoBuffer );
if ( Pid != 0 )
{
hShellProcess = OpenProcess( PROCESS_ALL_ACCESS,
FALSE,
Pid );
if ( hShellProcess )
{
bStatus = OpenProcessToken( hShellProcess,
TOKEN_ALL_ACCESS,
&hShellProcessToken );
}
}
LeaveCriticalSection( &ShellQueryCS );
// Callers should not close this token unless they want to hose us!
return hShellProcessToken;
}
#endif // large block of code ignored for _CHICAGO_
//+-------------------------------------------------------------------------
//
// Member: CStringID::CStringID
//
// Synopsis: Create a string ID
//
// Arguments: [pwszPath] - path to use for id
//
// History: 21-Apr-93 Ricksa Created
// 28-Jul-94 DavePl Changed to special-case EXE names
// which contain trailing options
//
//--------------------------------------------------------------------------
CStringID::CStringID(const WCHAR *pwszPath, HRESULT &hr)
: _culRefs(0), _pwszPath(NULL)
#if DBG==1
,_ulSig(STRINGSIG)
#endif
{
hr = S_OK;
// Calculate size of path in characters
_cPath = lstrlenW(pwszPath) + 1;
// Calculate size of path in bytes
_cPathBytes = _cPath * sizeof(WCHAR);
// Allocate path
_pwszPath = (WCHAR *) ScmMemAlloc(_cPathBytes);
if (_pwszPath == NULL)
{
hr = E_OUTOFMEMORY;
return;
}
// Copy in path
memcpy(_pwszPath, pwszPath, _cPathBytes);
//
// Don't convert the marker string (0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00)
// to upper case or else the CharUpperW() routine will muck it up.
//
if (_cPathBytes != 6 || *(LONG UNALIGNED *)pwszPath != ~0)
{
// If the server name contains a trailing switch, such as "/Automation", we
// cannot blindly upper-case the entire string, as some apps (such as Word)
// may do a string-sensitive compare against the switches.
if (NULL == wcschr(_pwszPath, L' ')) // Look for a space in the name
{
// If there is no space in the EXE name, there can be no trailing
// arguments, so we are safe to upper-case the string
CharUpperW(_pwszPath);
}
}
}
//+-------------------------------------------------------------------------
//
// Member: CStringID::CStringID
//
// Synopsis: Copy constructor
//
// Arguments: [pwszPath] - path to use for id
//
// History: 21-Apr-93 Ricksa Created
//
//--------------------------------------------------------------------------
CStringID::CStringID(const CStringID& strid, HRESULT &hr)
: _culRefs(0), _pwszPath(NULL)
#if DBG==1
,_ulSig(STRINGSIG)
#endif
{
// Calculate size of path in characters
_cPath = strid._cPath;
// Calculate size of path in bytes
_cPathBytes = strid._cPathBytes;
// Allocate path
_pwszPath = (WCHAR *) ScmMemAlloc(_cPathBytes);
// BUGBUG: What to do when this fails
// Answer: check the return value and propagate to caller !
if (_pwszPath == NULL)
{
hr = E_OUTOFMEMORY;
return;
}
// Copy in path - no up case because it already is.
memcpy(_pwszPath, strid._pwszPath, _cPathBytes);
}
//+-------------------------------------------------------------------------
//
// Member: CStringID::Compare
//
// Synopsis: Compare the string keys
//
// History: 21-Apr-93 Ricksa Created
//
//--------------------------------------------------------------------------
int CStringID::Compare(const CStringID& cstrid) const
{
int cCmp = (_cPathBytes < cstrid._cPathBytes)
? _cPathBytes : cstrid._cPathBytes;
// Note that the _cPath includes the trailing NULL so if the
// memcmp returns 0 the strings are equal.
return memcmp(_pwszPath, cstrid._pwszPath, cCmp);
}
//+-------------------------------------------------------------------------
//
// Member: CStringID::GetPath
//
// Synopsis: Make a copy of the path
//
// History: 21-Apr-93 Ricksa Created
//
//--------------------------------------------------------------------------
void CStringID::GetPath(WCHAR **ppwszPath)
{
// Allocate memory for the path
if (*ppwszPath = (WCHAR *) PrivMemAlloc(_cPath * sizeof(WCHAR)))
{
// Copy data into the memory
memcpy(*ppwszPath, _pwszPath, _cPathBytes);
}
return;
}
//+-------------------------------------------------------------------------
//
// Member: CStringID::Release
//
// Synopsis: Decrement refcnt and remove from list if zero
//
// History: 21-Apr-93 Ricksa Created
// 20-Oct-94 BillMo Demacroisation
//
//--------------------------------------------------------------------------
ULONG CStringID::Release(CStringList &sl)
{
ULONG ulTmp = --_culRefs;
if (_culRefs == 0)
{
sl.Remove(this);
delete this;
}
return ulTmp;
}
//+-------------------------------------------------------------------
//
// Member: CStringList::Add
//
// Synopsis: Add the given string to the string list.
//
// Effects: If it already exists, then use an existing copy.
//
// Arguments: [pwszPath] -- string to copy.
// [hr] -- HRESULT& to set on failure only.
//
// Returns: NULL on failure.
//
// Notes:
//
//--------------------------------------------------------------------
CStringID * CStringList::Add(const WCHAR *pwszPath, HRESULT &hr)
{
CStringID csid(pwszPath, hr);
if (FAILED(hr))
return(NULL);
CStringID *pString = (CStringID *) Search(&csid);
if (pString == NULL)
{
pString = new CStringID(pwszPath, hr);
if (pString == NULL ||
FAILED(hr) ||
Insert(pString) == NULL)
{
if (pString == NULL || SUCCEEDED(hr))
hr = E_OUTOFMEMORY;
delete pString;
return(NULL);
}
}
pString->AddRef();
return pString;
}
//+-------------------------------------------------------------------
//
// Member: CLocalServer::Add
//
// Synopsis: Add the given local server to the server list.
//
// Effects: If it already exists, then use an existing copy.
//
// Arguments: [pwszPath] -- string to copy.
// [hr] -- HRESULT& to set on failure only.
//
// Returns: NULL on failure.
//
// Notes:
//
//--------------------------------------------------------------------
CLocalServer * CLocSrvList::Add(const WCHAR *pwszPath, HRESULT &hr)
{
CStringID csid(pwszPath, hr);
if (FAILED(hr))
return(NULL);
CLocalServer *pLocalServer = (CLocalServer *) Search(&csid);
if (pLocalServer == NULL)
{
pLocalServer = new CLocalServer(pwszPath, hr);
if (pLocalServer == NULL ||
FAILED(hr) ||
Insert(pLocalServer) == NULL)
{
if (pLocalServer == NULL || SUCCEEDED(hr))
hr = E_OUTOFMEMORY;
delete pLocalServer;
return(NULL);
}
}
pLocalServer->AddRef();
return pLocalServer;
}
//+-------------------------------------------------------------------------
//
// Member: CLocalServer::Release
//
// Synopsis: Decrement refcnt and remove from list if zero
//
// History: 21-Apr-93 Ricksa Created
// 20-Oct-94 BillMo Demacroisation
//
//--------------------------------------------------------------------------
ULONG CLocalServer::Release(CLocSrvList &lsl)
{
ULONG ulTmp = --_culRefs;
if (_culRefs == 0)
{
lsl.Remove(this);
delete this;
}
return ulTmp;
}
//+-------------------------------------------------------------------
//
// Function: SkipListCompareStringIDs,
// SkipListDeleteStringID
// SkipListDeleteLocalServer
//
// Synopsis: Routines called by CStringList's CSkipList.
//
// Notes:
//
//--------------------------------------------------------------------
int SkipListCompareStringIDs(void *pkey1, void * pkey2)
{
CStringID *p1 = (CStringID*)pkey1;
const CStringID *p2 = (CStringID*)pkey2;
p1->CheckSig();
p2->CheckSig();
return(p1->Compare(*p2));
}
void SkipListDeleteStringID(void *pkey1)
{
CStringID *p = (CStringID*)pkey1;
p->CheckSig();
delete p;
}
void SkipListDeleteLocalServer(void *pvLocalServer)
{
CLocalServer *p = (CLocalServer*)pvLocalServer;
p->CheckSig();
delete p;
}