mirror of https://github.com/lianthony/NT4.0
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.
1582 lines
44 KiB
1582 lines
44 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
|
|
|
|
|