2025-05-13 19:45:22 +03:00

594 lines
16 KiB
C

/*
* Process Hacker -
* object security editor
*
* Copyright (C) 2010 wj32
*
* This file is part of Process Hacker.
*
* Process Hacker is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Process Hacker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ph.h>
#include <guisup.h>
#include <secedit.h>
#include <seceditp.h>
#include <hndlinfo.h>
static ISecurityInformationVtbl PhSecurityInformation_VTable =
{
PhSecurityInformation_QueryInterface,
PhSecurityInformation_AddRef,
PhSecurityInformation_Release,
PhSecurityInformation_GetObjectInformation,
PhSecurityInformation_GetSecurity,
PhSecurityInformation_SetSecurity,
PhSecurityInformation_GetAccessRights,
PhSecurityInformation_MapGeneric,
PhSecurityInformation_GetInheritTypes,
PhSecurityInformation_PropertySheetPageCallback
};
static PH_INITONCE SecurityEditorInitOnce = PH_INITONCE_INIT;
static _CreateSecurityPage CreateSecurityPage_I;
static _EditSecurity EditSecurity_I;
FORCEINLINE VOID PhpSecurityEditorInitialization(
VOID
)
{
if (PhBeginInitOnce(&SecurityEditorInitOnce))
{
HMODULE aclui;
aclui = LoadLibrary(L"aclui.dll");
CreateSecurityPage_I = (PVOID)GetProcAddress(aclui, "CreateSecurityPage");
EditSecurity_I = (PVOID)GetProcAddress(aclui, "EditSecurity");
PhEndInitOnce(&SecurityEditorInitOnce);
}
}
/**
* Creates a security editor page.
*
* \param ObjectName The name of the object.
* \param GetObjectSecurity A callback function executed to retrieve the security descriptor of the
* object.
* \param SetObjectSecurity A callback function executed to modify the security descriptor of the
* object.
* \param Context A user-defined value to pass to the callback functions.
* \param AccessEntries An array of access mask descriptors.
* \param NumberOfAccessEntries The number of elements in \a AccessEntries.
*/
HPROPSHEETPAGE PhCreateSecurityPage(
_In_ PWSTR ObjectName,
_In_ PPH_GET_OBJECT_SECURITY GetObjectSecurity,
_In_ PPH_SET_OBJECT_SECURITY SetObjectSecurity,
_In_opt_ PVOID Context,
_In_ PPH_ACCESS_ENTRY AccessEntries,
_In_ ULONG NumberOfAccessEntries
)
{
ISecurityInformation *info;
HPROPSHEETPAGE page;
PhpSecurityEditorInitialization();
if (!CreateSecurityPage_I)
return NULL;
info = PhSecurityInformation_Create(
ObjectName,
GetObjectSecurity,
SetObjectSecurity,
Context,
AccessEntries,
NumberOfAccessEntries
);
page = CreateSecurityPage_I(info);
PhSecurityInformation_Release(info);
return page;
}
/**
* Displays a security editor dialog.
*
* \param hWnd The parent window of the dialog.
* \param ObjectName The name of the object.
* \param GetObjectSecurity A callback function executed to retrieve the security descriptor of the
* object.
* \param SetObjectSecurity A callback function executed to modify the security descriptor of the
* object.
* \param Context A user-defined value to pass to the callback functions.
* \param AccessEntries An array of access mask descriptors.
* \param NumberOfAccessEntries The number of elements in \a AccessEntries.
*/
VOID PhEditSecurity(
_In_ HWND hWnd,
_In_ PWSTR ObjectName,
_In_ PPH_GET_OBJECT_SECURITY GetObjectSecurity,
_In_ PPH_SET_OBJECT_SECURITY SetObjectSecurity,
_In_opt_ PVOID Context,
_In_ PPH_ACCESS_ENTRY AccessEntries,
_In_ ULONG NumberOfAccessEntries
)
{
ISecurityInformation *info;
PhpSecurityEditorInitialization();
if (!EditSecurity_I)
return;
info = PhSecurityInformation_Create(
ObjectName,
GetObjectSecurity,
SetObjectSecurity,
Context,
AccessEntries,
NumberOfAccessEntries
);
EditSecurity_I(hWnd, info);
PhSecurityInformation_Release(info);
}
ISecurityInformation *PhSecurityInformation_Create(
_In_ PWSTR ObjectName,
_In_ PPH_GET_OBJECT_SECURITY GetObjectSecurity,
_In_ PPH_SET_OBJECT_SECURITY SetObjectSecurity,
_In_opt_ PVOID Context,
_In_ PPH_ACCESS_ENTRY AccessEntries,
_In_ ULONG NumberOfAccessEntries
)
{
PhSecurityInformation *info;
ULONG i;
info = PhAllocate(sizeof(PhSecurityInformation));
info->VTable = &PhSecurityInformation_VTable;
info->RefCount = 1;
info->ObjectName = PhCreateString(ObjectName);
info->GetObjectSecurity = GetObjectSecurity;
info->SetObjectSecurity = SetObjectSecurity;
info->Context = Context;
info->AccessEntries = PhAllocate(sizeof(SI_ACCESS) * NumberOfAccessEntries);
info->NumberOfAccessEntries = NumberOfAccessEntries;
for (i = 0; i < NumberOfAccessEntries; i++)
{
memset(&info->AccessEntries[i], 0, sizeof(SI_ACCESS));
info->AccessEntries[i].pszName = AccessEntries[i].Name;
info->AccessEntries[i].mask = AccessEntries[i].Access;
if (AccessEntries[i].General)
info->AccessEntries[i].dwFlags |= SI_ACCESS_GENERAL;
if (AccessEntries[i].Specific)
info->AccessEntries[i].dwFlags |= SI_ACCESS_SPECIFIC;
}
return (ISecurityInformation *)info;
}
HRESULT STDMETHODCALLTYPE PhSecurityInformation_QueryInterface(
_In_ ISecurityInformation *This,
_In_ REFIID Riid,
_Out_ PVOID *Object
)
{
if (
IsEqualIID(Riid, &IID_IUnknown) ||
IsEqualIID(Riid, &IID_ISecurityInformation)
)
{
PhSecurityInformation_AddRef(This);
*Object = This;
return S_OK;
}
*Object = NULL;
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE PhSecurityInformation_AddRef(
_In_ ISecurityInformation *This
)
{
PhSecurityInformation *this = (PhSecurityInformation *)This;
this->RefCount++;
return this->RefCount;
}
ULONG STDMETHODCALLTYPE PhSecurityInformation_Release(
_In_ ISecurityInformation *This
)
{
PhSecurityInformation *this = (PhSecurityInformation *)This;
this->RefCount--;
if (this->RefCount == 0)
{
if (this->ObjectName) PhDereferenceObject(this->ObjectName);
PhFree(this->AccessEntries);
PhFree(this);
return 0;
}
return this->RefCount;
}
HRESULT STDMETHODCALLTYPE PhSecurityInformation_GetObjectInformation(
_In_ ISecurityInformation *This,
_Out_ PSI_OBJECT_INFO ObjectInfo
)
{
PhSecurityInformation *this = (PhSecurityInformation *)This;
memset(ObjectInfo, 0, sizeof(SI_OBJECT_INFO));
ObjectInfo->dwFlags =
SI_EDIT_AUDITS |
SI_EDIT_OWNER |
SI_EDIT_PERMS |
SI_ADVANCED |
SI_NO_ACL_PROTECT |
SI_NO_TREE_APPLY;
ObjectInfo->hInstance = NULL;
ObjectInfo->pszObjectName = this->ObjectName->Buffer;
return S_OK;
}
HRESULT STDMETHODCALLTYPE PhSecurityInformation_GetSecurity(
_In_ ISecurityInformation *This,
_In_ SECURITY_INFORMATION RequestedInformation,
_Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor,
_In_ BOOL Default
)
{
PhSecurityInformation *this = (PhSecurityInformation *)This;
NTSTATUS status;
PSECURITY_DESCRIPTOR securityDescriptor;
ULONG sdLength;
PSECURITY_DESCRIPTOR newSd;
status = this->GetObjectSecurity(
&securityDescriptor,
RequestedInformation,
this->Context
);
if (!NT_SUCCESS(status))
return HRESULT_FROM_WIN32(PhNtStatusToDosError(status));
sdLength = RtlLengthSecurityDescriptor(securityDescriptor);
newSd = LocalAlloc(0, sdLength);
memcpy(newSd, securityDescriptor, sdLength);
PhFree(securityDescriptor);
*SecurityDescriptor = newSd;
return S_OK;
}
HRESULT STDMETHODCALLTYPE PhSecurityInformation_SetSecurity(
_In_ ISecurityInformation *This,
_In_ SECURITY_INFORMATION SecurityInformation,
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor
)
{
PhSecurityInformation *this = (PhSecurityInformation *)This;
NTSTATUS status;
status = this->SetObjectSecurity(
SecurityDescriptor,
SecurityInformation,
this->Context
);
if (!NT_SUCCESS(status))
return HRESULT_FROM_WIN32(PhNtStatusToDosError(status));
return S_OK;
}
HRESULT STDMETHODCALLTYPE PhSecurityInformation_GetAccessRights(
_In_ ISecurityInformation *This,
_In_ const GUID *ObjectType,
_In_ ULONG Flags,
_Out_ PSI_ACCESS *Access,
_Out_ PULONG Accesses,
_Out_ PULONG DefaultAccess
)
{
PhSecurityInformation *this = (PhSecurityInformation *)This;
*Access = this->AccessEntries;
*Accesses = this->NumberOfAccessEntries;
*DefaultAccess = 0;
return S_OK;
}
HRESULT STDMETHODCALLTYPE PhSecurityInformation_MapGeneric(
_In_ ISecurityInformation *This,
_In_ const GUID *ObjectType,
_In_ PUCHAR AceFlags,
_Inout_ PACCESS_MASK Mask
)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE PhSecurityInformation_GetInheritTypes(
_In_ ISecurityInformation *This,
_Out_ PSI_INHERIT_TYPE *InheritTypes,
_Out_ PULONG InheritTypesCount
)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE PhSecurityInformation_PropertySheetPageCallback(
_In_ ISecurityInformation *This,
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ SI_PAGE_TYPE uPage
)
{
return E_NOTIMPL;
}
NTSTATUS PhpGetObjectSecurityWithTimeout(
_In_ HANDLE Handle,
_In_ SECURITY_INFORMATION SecurityInformation,
_Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor
)
{
NTSTATUS status;
ULONG bufferSize;
PVOID buffer;
bufferSize = 0x100;
buffer = PhAllocate(bufferSize);
// This is required (especially for File objects) because some drivers don't seem to handle
// QuerySecurity properly.
memset(buffer, 0, bufferSize);
status = PhCallNtQuerySecurityObjectWithTimeout(
Handle,
SecurityInformation,
buffer,
bufferSize,
&bufferSize
);
if (status == STATUS_BUFFER_TOO_SMALL)
{
PhFree(buffer);
buffer = PhAllocate(bufferSize);
memset(buffer, 0, bufferSize);
status = PhCallNtQuerySecurityObjectWithTimeout(
Handle,
SecurityInformation,
buffer,
bufferSize,
&bufferSize
);
}
if (!NT_SUCCESS(status))
{
PhFree(buffer);
return status;
}
*SecurityDescriptor = (PSECURITY_DESCRIPTOR)buffer;
return status;
}
/**
* Retrieves the security descriptor of an object.
*
* \param SecurityDescriptor A variable which receives a pointer to the security descriptor of the
* object. The security descriptor must be freed using PhFree() when no longer needed.
* \param SecurityInformation The security information to retrieve.
* \param Context A pointer to a PH_STD_OBJECT_SECURITY structure describing the object.
*
* \remarks This function may be used for the \a GetObjectSecurity callback in
* PhCreateSecurityPage() or PhEditSecurity().
*/
_Callback_ NTSTATUS PhStdGetObjectSecurity(
_Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor,
_In_ SECURITY_INFORMATION SecurityInformation,
_In_opt_ PVOID Context
)
{
NTSTATUS status;
PPH_STD_OBJECT_SECURITY stdObjectSecurity;
HANDLE handle;
stdObjectSecurity = (PPH_STD_OBJECT_SECURITY)Context;
status = stdObjectSecurity->OpenObject(
&handle,
PhGetAccessForGetSecurity(SecurityInformation),
stdObjectSecurity->Context
);
if (!NT_SUCCESS(status))
return status;
if (PhEqualStringZ(stdObjectSecurity->ObjectType, L"Service", TRUE))
{
status = PhGetSeObjectSecurity(handle, SE_SERVICE, SecurityInformation, SecurityDescriptor);
CloseServiceHandle(handle);
}
else if (PhEqualStringZ(stdObjectSecurity->ObjectType, L"File", TRUE))
{
status = PhpGetObjectSecurityWithTimeout(handle, SecurityInformation, SecurityDescriptor);
NtClose(handle);
}
else
{
status = PhGetObjectSecurity(handle, SecurityInformation, SecurityDescriptor);
NtClose(handle);
}
return status;
}
/**
* Modifies the security descriptor of an object.
*
* \param SecurityDescriptor A security descriptor containing security information to set.
* \param SecurityInformation The security information to retrieve.
* \param Context A pointer to a PH_STD_OBJECT_SECURITY structure describing the object.
*
* \remarks This function may be used for the \a SetObjectSecurity callback in
* PhCreateSecurityPage() or PhEditSecurity().
*/
_Callback_ NTSTATUS PhStdSetObjectSecurity(
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
_In_ SECURITY_INFORMATION SecurityInformation,
_In_opt_ PVOID Context
)
{
NTSTATUS status;
PPH_STD_OBJECT_SECURITY stdObjectSecurity;
HANDLE handle;
stdObjectSecurity = (PPH_STD_OBJECT_SECURITY)Context;
status = stdObjectSecurity->OpenObject(
&handle,
PhGetAccessForSetSecurity(SecurityInformation),
stdObjectSecurity->Context
);
if (!NT_SUCCESS(status))
return status;
if (PhEqualStringZ(stdObjectSecurity->ObjectType, L"Service", TRUE))
{
status = PhSetSeObjectSecurity(handle, SE_SERVICE, SecurityInformation, SecurityDescriptor);
CloseServiceHandle(handle);
}
else
{
status = PhSetObjectSecurity(handle, SecurityInformation, SecurityDescriptor);
NtClose(handle);
}
return status;
}
NTSTATUS PhGetSeObjectSecurity(
_In_ HANDLE Handle,
_In_ ULONG ObjectType,
_In_ SECURITY_INFORMATION SecurityInformation,
_Out_ PSECURITY_DESCRIPTOR *SecurityDescriptor
)
{
ULONG win32Result;
PSECURITY_DESCRIPTOR securityDescriptor;
win32Result = GetSecurityInfo(
Handle,
ObjectType,
SecurityInformation,
NULL,
NULL,
NULL,
NULL,
&securityDescriptor
);
if (win32Result != ERROR_SUCCESS)
return NTSTATUS_FROM_WIN32(win32Result);
*SecurityDescriptor = PhAllocateCopy(securityDescriptor, RtlLengthSecurityDescriptor(securityDescriptor));
LocalFree(securityDescriptor);
return STATUS_SUCCESS;
}
NTSTATUS PhSetSeObjectSecurity(
_In_ HANDLE Handle,
_In_ ULONG ObjectType,
_In_ SECURITY_INFORMATION SecurityInformation,
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor
)
{
ULONG win32Result;
SECURITY_INFORMATION securityInformation = 0;
BOOLEAN present;
BOOLEAN defaulted;
PSID owner = NULL;
PSID group = NULL;
PACL dacl = NULL;
PACL sacl = NULL;
if (SecurityInformation & OWNER_SECURITY_INFORMATION)
{
if (NT_SUCCESS(RtlGetOwnerSecurityDescriptor(SecurityDescriptor, &owner, &defaulted)))
securityInformation |= OWNER_SECURITY_INFORMATION;
}
if (SecurityInformation & GROUP_SECURITY_INFORMATION)
{
if (NT_SUCCESS(RtlGetGroupSecurityDescriptor(SecurityDescriptor, &group, &defaulted)))
securityInformation |= GROUP_SECURITY_INFORMATION;
}
if (SecurityInformation & DACL_SECURITY_INFORMATION)
{
if (NT_SUCCESS(RtlGetDaclSecurityDescriptor(SecurityDescriptor, &present, &dacl, &defaulted)) && present)
securityInformation |= DACL_SECURITY_INFORMATION;
}
if (SecurityInformation & SACL_SECURITY_INFORMATION)
{
if (NT_SUCCESS(RtlGetSaclSecurityDescriptor(SecurityDescriptor, &present, &sacl, &defaulted)) && present)
securityInformation |= SACL_SECURITY_INFORMATION;
}
win32Result = SetSecurityInfo(
Handle,
ObjectType,
SecurityInformation,
owner,
group,
dacl,
sacl
);
if (win32Result != ERROR_SUCCESS)
return NTSTATUS_FROM_WIN32(win32Result);
return STATUS_SUCCESS;
}