878 lines
24 KiB
C++
878 lines
24 KiB
C++
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Microsoft Windows
|
||
|
// Copyright (C) Microsoft Corporation, 1997.
|
||
|
//
|
||
|
// File: security.cxx
|
||
|
//
|
||
|
// Contents:
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
#include "act.hxx"
|
||
|
|
||
|
// the constant generic mapping structure
|
||
|
GENERIC_MAPPING sGenericMapping = {
|
||
|
READ_CONTROL,
|
||
|
READ_CONTROL,
|
||
|
READ_CONTROL,
|
||
|
READ_CONTROL};
|
||
|
|
||
|
//-------------------------------------------------------------------------
|
||
|
//
|
||
|
// CheckForAccess
|
||
|
//
|
||
|
// Checks whether the given token has COM_RIGHTS_EXECUTE access in the
|
||
|
// given security descriptor.
|
||
|
//
|
||
|
//-------------------------------------------------------------------------
|
||
|
BOOL
|
||
|
CheckForAccess(
|
||
|
IN CToken * pToken,
|
||
|
IN SECURITY_DESCRIPTOR * pSD
|
||
|
)
|
||
|
{
|
||
|
// if we have an empty SD, deny everyone
|
||
|
if ( ! pSD )
|
||
|
return FALSE;
|
||
|
|
||
|
//
|
||
|
// pToken is NULL during an unsecure activation, in which case we check
|
||
|
// if EVERYONE is granted access in the security descriptor.
|
||
|
//
|
||
|
if ( pToken )
|
||
|
{
|
||
|
HANDLE hToken = pToken->GetToken();
|
||
|
BOOL fAccess = FALSE;
|
||
|
BOOL fSuccess = FALSE;
|
||
|
DWORD dwGrantedAccess;
|
||
|
PRIVILEGE_SET sPrivilegeSet;
|
||
|
DWORD dwSetLen = sizeof( sPrivilegeSet );
|
||
|
|
||
|
sPrivilegeSet.PrivilegeCount = 1;
|
||
|
sPrivilegeSet.Control = 0;
|
||
|
|
||
|
fSuccess = AccessCheck( (PSECURITY_DESCRIPTOR) pSD,
|
||
|
hToken,
|
||
|
COM_RIGHTS_EXECUTE,
|
||
|
&sGenericMapping,
|
||
|
&sPrivilegeSet,
|
||
|
&dwSetLen,
|
||
|
&dwGrantedAccess,
|
||
|
&fAccess );
|
||
|
|
||
|
if ( fSuccess && fAccess )
|
||
|
return TRUE;
|
||
|
|
||
|
if ( !fSuccess )
|
||
|
{
|
||
|
CairoleDebugOut((DEB_ERROR, "Bad Security Descriptor 0x%08x, Access Check returned 0x%x\n", pSD, GetLastError() ));
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
BOOL bStatus;
|
||
|
BOOL bDaclPresent;
|
||
|
BOOL bDaclDefaulted;
|
||
|
DWORD Index;
|
||
|
HRESULT hr;
|
||
|
PACL pDacl;
|
||
|
PACCESS_ALLOWED_ACE pAllowAce;
|
||
|
SID SidEveryone = { SID_REVISION,
|
||
|
1,
|
||
|
SECURITY_WORLD_SID_AUTHORITY,
|
||
|
0 };
|
||
|
|
||
|
pDacl = 0;
|
||
|
|
||
|
bStatus = GetSecurityDescriptorDacl(
|
||
|
(void *)pSD,
|
||
|
&bDaclPresent,
|
||
|
&pDacl,
|
||
|
&bDaclDefaulted );
|
||
|
|
||
|
if ( ! bStatus )
|
||
|
return FALSE;
|
||
|
|
||
|
// Bug 95306: Can assume dacl exists only if both
|
||
|
// bDaclPresent is TRUE and pDacl is not NULL
|
||
|
if ( (!pDacl) || (!bDaclPresent) )
|
||
|
return TRUE;
|
||
|
|
||
|
bStatus = FALSE;
|
||
|
|
||
|
for ( Index = 0; Index < pDacl->AceCount; Index++ )
|
||
|
{
|
||
|
if ( ! GetAce( pDacl, Index, (void **) &pAllowAce ) )
|
||
|
break;
|
||
|
|
||
|
if ( pAllowAce->Header.AceType != ACCESS_ALLOWED_ACE_TYPE )
|
||
|
continue;
|
||
|
|
||
|
if ( ! (pAllowAce->Mask & COM_RIGHTS_EXECUTE) )
|
||
|
continue;
|
||
|
|
||
|
if ( EqualSid( (PSID)(&pAllowAce->SidStart), &SidEveryone ) )
|
||
|
{
|
||
|
bStatus = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return bStatus;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
HANDLE
|
||
|
GetRunAsToken(
|
||
|
DWORD clsctx,
|
||
|
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 ( !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 = (USHORT)((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);
|
||
|
|
||
|
// Possible for LsaRetrievePrivateData to return success but with a NULL
|
||
|
// psPassword. If this happens we fail.
|
||
|
if (!psPassword)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Log the specifed user on
|
||
|
if (!LogonUserW(pwszRunAsUserName, pwszRunAsDomainName, psPassword->Buffer,
|
||
|
LOGON32_LOGON_BATCH, LOGON32_PROVIDER_DEFAULT, &hToken))
|
||
|
{
|
||
|
memset(psPassword->Buffer, 0, psPassword->Length);
|
||
|
LsaFreeMemory( psPassword );
|
||
|
|
||
|
// a-sergiv (Sergei O. Ivanov), 6-17-99
|
||
|
// Fix for com+ 9383/nt 272085
|
||
|
|
||
|
// Apply event filters
|
||
|
DWORD dwActLogLvl = GetActivationFailureLoggingLevel();
|
||
|
if(dwActLogLvl == 2)
|
||
|
return 0;
|
||
|
if(dwActLogLvl != 1 && clsctx & CLSCTX_NO_FAILURE_LOG)
|
||
|
return 0;
|
||
|
|
||
|
// 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;
|
||
|
LPWSTR 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);
|
||
|
LsaFreeMemory( psPassword );
|
||
|
|
||
|
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));
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
DuplicateTokenForSessionUse(
|
||
|
HANDLE hUserToken,
|
||
|
HANDLE *hDuplicate
|
||
|
)
|
||
|
|
||
|
{
|
||
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
|
PSECURITY_DESCRIPTOR psdNewProcessTokenSD;
|
||
|
NTSTATUS NtStatus;
|
||
|
|
||
|
if (hUserToken == NULL) {
|
||
|
return(TRUE);
|
||
|
}
|
||
|
|
||
|
*hDuplicate = NULL;
|
||
|
|
||
|
|
||
|
InitializeObjectAttributes(
|
||
|
&ObjectAttributes,
|
||
|
NULL,
|
||
|
0,
|
||
|
NULL,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
|
||
|
NtStatus = NtDuplicateToken(
|
||
|
hUserToken, // Duplicate this token
|
||
|
TOKEN_ALL_ACCESS, //Give me this access to the resulting token
|
||
|
&ObjectAttributes,
|
||
|
FALSE, // EffectiveOnly
|
||
|
TokenPrimary, // TokenType
|
||
|
hDuplicate // 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);
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/***************************************************************************\
|
||
|
* 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) PrivMemAlloc(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);
|
||
|
PrivMemFree((HANDLE)pUser);
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// Alloc buffer for copy of SID
|
||
|
|
||
|
dwBytesRequired = RtlLengthSid(pUser->User.Sid);
|
||
|
pSid = (PSID) PrivMemAlloc(dwBytesRequired);
|
||
|
if (pSid == NULL)
|
||
|
{
|
||
|
CairoleDebugOut((DEB_ERROR, "Failed to allocate %d bytes\n", dwBytesRequired));
|
||
|
if (fAllocatedBuffer == TRUE)
|
||
|
{
|
||
|
PrivMemFree((HANDLE)pUser);
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// Copy SID
|
||
|
|
||
|
NtStatus = RtlCopySid(dwBytesRequired, pSid, pUser->User.Sid);
|
||
|
if (fAllocatedBuffer == TRUE)
|
||
|
{
|
||
|
PrivMemFree((HANDLE)pUser);
|
||
|
}
|
||
|
|
||
|
|
||
|
if (!NT_SUCCESS(NtStatus))
|
||
|
{
|
||
|
CairoleDebugOut((DEB_ERROR, "RtlCopySid failed, status = 0x%lx\n", NtStatus));
|
||
|
Win4Assert(NtStatus != STATUS_SUCCESS);
|
||
|
PrivMemFree(pSid);
|
||
|
pSid = NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
return pSid;
|
||
|
}
|
||
|
|
||
|
// Initialzed in InitializeSCM during boot.
|
||
|
CRITICAL_SECTION ShellQueryCS;
|
||
|
|
||
|
HANDLE GetShellProcessToken(
|
||
|
ULONG ulSessionId
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS NtStatus;
|
||
|
BOOL bStatus;
|
||
|
HKEY hReg;
|
||
|
LONG RegStatus;
|
||
|
DWORD RegSize, RegType;
|
||
|
WCHAR * pwszImageName;
|
||
|
WCHAR * pwszSearch;
|
||
|
WCHAR * pwszNext;
|
||
|
WCHAR * pNull;
|
||
|
DWORD Pid;
|
||
|
DWORD n;
|
||
|
BYTE StackInfoBuffer[4096];
|
||
|
PBYTE pProcessInfoBuffer;
|
||
|
ULONG ProcessInfoBufferSize;
|
||
|
ULONG TotalOffset;
|
||
|
HANDLE hProcess;
|
||
|
HANDLE hToken;
|
||
|
PSYSTEM_PROCESS_INFORMATION pProcessInfo;
|
||
|
|
||
|
static HANDLE hShellProcess = 0;
|
||
|
static HANDLE hShellProcessToken = 0;
|
||
|
static WCHAR * pwszShellRegValue = 0;
|
||
|
static WCHAR ** apwszShells = 0;
|
||
|
static DWORD nShells = 0;
|
||
|
|
||
|
EnterCriticalSection( &ShellQueryCS );
|
||
|
|
||
|
if ( ! pwszShellRegValue )
|
||
|
{
|
||
|
nShells = 0;
|
||
|
|
||
|
//
|
||
|
// This code follows logic similar to userinit for finding the name of
|
||
|
// the shell process.
|
||
|
//
|
||
|
|
||
|
RegStatus = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
||
|
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon",
|
||
|
0,
|
||
|
KEY_READ,
|
||
|
&hReg );
|
||
|
|
||
|
if ( ERROR_SUCCESS == RegStatus )
|
||
|
{
|
||
|
RegStatus = ReadStringValue( hReg, L"Shell", &pwszShellRegValue );
|
||
|
RegCloseKey( hReg );
|
||
|
}
|
||
|
|
||
|
if ( RegStatus != ERROR_SUCCESS )
|
||
|
pwszShellRegValue = L"explorer.exe";
|
||
|
|
||
|
pwszSearch = pwszShellRegValue;
|
||
|
|
||
|
for ( ;; )
|
||
|
{
|
||
|
if ( ! FindExeComponent( pwszSearch, L" ,", &pNull, &pwszNext ) )
|
||
|
break;
|
||
|
|
||
|
nShells++;
|
||
|
|
||
|
while ( *pwszNext && *pwszNext != L',' )
|
||
|
pwszNext++;
|
||
|
|
||
|
pwszSearch = pwszNext;
|
||
|
}
|
||
|
|
||
|
apwszShells = (WCHAR **) PrivMemAlloc( nShells * sizeof(WCHAR **) );
|
||
|
|
||
|
if ( ! apwszShells )
|
||
|
{
|
||
|
LeaveCriticalSection( &ShellQueryCS );
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
for ( pwszSearch = pwszShellRegValue, n = 0; n < nShells; n++ )
|
||
|
{
|
||
|
FindExeComponent( pwszSearch, L" ,", &apwszShells[n], &pwszNext );
|
||
|
|
||
|
pNull = pwszNext;
|
||
|
|
||
|
while ( *pwszNext && *pwszNext != L',' )
|
||
|
pwszNext++;
|
||
|
|
||
|
//
|
||
|
// When using the const string L"explorer.exe" we can't
|
||
|
// automatically write a null or we'll AV on the read only memory.
|
||
|
//
|
||
|
if ( *pNull )
|
||
|
*pNull = 0;
|
||
|
|
||
|
pwszSearch = pwszNext;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Make sure the shell process is still alive before using its token.
|
||
|
//
|
||
|
if ( hShellProcess && !ulSessionId )
|
||
|
{
|
||
|
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 &&
|
||
|
( pProcessInfo->SessionId == ulSessionId ) )
|
||
|
{
|
||
|
pwszImageName = &pProcessInfo->ImageName.Buffer[pProcessInfo->ImageName.Length / sizeof(WCHAR)];
|
||
|
|
||
|
while ( (pwszImageName != pProcessInfo->ImageName.Buffer) &&
|
||
|
(pwszImageName[-1] != '\\') )
|
||
|
pwszImageName--;
|
||
|
|
||
|
for ( n = 0; n < nShells; n++ )
|
||
|
{
|
||
|
if ( lstrcmpiW( apwszShells[n], pwszImageName ) == 0 )
|
||
|
{
|
||
|
Pid = PtrToUlong(pProcessInfo->UniqueProcessId);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( pProcessInfo->NextEntryOffset == 0 )
|
||
|
break;
|
||
|
|
||
|
TotalOffset += pProcessInfo->NextEntryOffset;
|
||
|
pProcessInfo = (PSYSTEM_PROCESS_INFORMATION) &pProcessInfoBuffer[TotalOffset];
|
||
|
}
|
||
|
|
||
|
AllDone:
|
||
|
|
||
|
if ( pProcessInfoBuffer != StackInfoBuffer )
|
||
|
PrivMemFree( pProcessInfoBuffer );
|
||
|
|
||
|
hProcess = 0;
|
||
|
hToken = 0;
|
||
|
|
||
|
if ( Pid != 0 )
|
||
|
{
|
||
|
hProcess = OpenProcess( PROCESS_ALL_ACCESS,
|
||
|
FALSE,
|
||
|
Pid );
|
||
|
|
||
|
if ( hProcess )
|
||
|
{
|
||
|
bStatus = OpenProcessToken( hProcess,
|
||
|
TOKEN_ALL_ACCESS,
|
||
|
&hToken );
|
||
|
if ( !bStatus ) {
|
||
|
CloseHandle( hProcess );
|
||
|
hProcess = 0;
|
||
|
hToken = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( ulSessionId )
|
||
|
{
|
||
|
if ( hProcess )
|
||
|
CloseHandle( hProcess );
|
||
|
|
||
|
LeaveCriticalSection( &ShellQueryCS );
|
||
|
|
||
|
return hToken;
|
||
|
}
|
||
|
|
||
|
hShellProcess = hProcess;
|
||
|
hShellProcessToken = hToken;
|
||
|
|
||
|
LeaveCriticalSection( &ShellQueryCS );
|
||
|
|
||
|
// Callers should not close this token unless they want to hose us!
|
||
|
return hShellProcessToken;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Global default launch permissions
|
||
|
CSecDescriptor* gpDefaultLaunchPermissions;
|
||
|
|
||
|
|
||
|
CSecDescriptor*
|
||
|
GetDefaultLaunchPermissions()
|
||
|
{
|
||
|
CSecDescriptor* pSD = NULL;
|
||
|
|
||
|
gpClientLock->LockShared();
|
||
|
|
||
|
pSD = gpDefaultLaunchPermissions;
|
||
|
if (pSD)
|
||
|
pSD->IncRefCount();
|
||
|
|
||
|
gpClientLock->UnlockShared();
|
||
|
|
||
|
return pSD;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
SetDefaultLaunchPermissions(CSecDescriptor* pNewLaunchPerms)
|
||
|
{
|
||
|
CSecDescriptor* pOldSD = NULL;
|
||
|
|
||
|
gpClientLock->LockExclusive();
|
||
|
|
||
|
pOldSD = gpDefaultLaunchPermissions;
|
||
|
gpDefaultLaunchPermissions = pNewLaunchPerms;
|
||
|
if (gpDefaultLaunchPermissions)
|
||
|
gpDefaultLaunchPermissions->IncRefCount();
|
||
|
|
||
|
gpClientLock->UnlockExclusive();
|
||
|
|
||
|
if (pOldSD)
|
||
|
pOldSD->DecRefCount();
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
CSecDescriptor::CSecDescriptor(SECURITY_DESCRIPTOR* pSD) : _lRefs(1)
|
||
|
{
|
||
|
ASSERT(pSD);
|
||
|
_pSD = pSD; // we own it now
|
||
|
}
|
||
|
|
||
|
CSecDescriptor::~CSecDescriptor()
|
||
|
{
|
||
|
ASSERT(_lRefs == 0);
|
||
|
ASSERT(_pSD);
|
||
|
PrivMemFree(_pSD);
|
||
|
}
|
||
|
|
||
|
void CSecDescriptor::IncRefCount()
|
||
|
{
|
||
|
ASSERT(_lRefs > 0);
|
||
|
LONG lRefs = InterlockedIncrement(&_lRefs);
|
||
|
}
|
||
|
|
||
|
void CSecDescriptor::DecRefCount()
|
||
|
{
|
||
|
ASSERT(_lRefs > 0);
|
||
|
LONG lRefs = InterlockedDecrement(&_lRefs);
|
||
|
if (lRefs == 0)
|
||
|
{
|
||
|
delete this;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SECURITY_DESCRIPTOR* CSecDescriptor::GetSD()
|
||
|
{
|
||
|
ASSERT(_pSD);
|
||
|
ASSERT(_lRefs > 0);
|
||
|
return _pSD;
|
||
|
}
|