/* * Process Hacker Extended Services - * other information * * Copyright (C) 2010-2011 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 . */ #include #include #include "extsrv.h" #include "resource.h" typedef struct _SERVICE_OTHER_CONTEXT { PPH_SERVICE_ITEM ServiceItem; BOOLEAN Ready; BOOLEAN Dirty; BOOLEAN PreshutdownTimeoutValid; BOOLEAN RequiredPrivilegesValid; HWND PrivilegesLv; PPH_LIST PrivilegeList; HWND TriggersLv; struct _ES_TRIGGER_CONTEXT *TriggerContext; } SERVICE_OTHER_CONTEXT, *PSERVICE_OTHER_CONTEXT; NTSTATUS EspLoadOtherInfo( __in HWND hwndDlg, __in PSERVICE_OTHER_CONTEXT Context ) { NTSTATUS status = STATUS_SUCCESS; SC_HANDLE serviceHandle; SERVICE_PRESHUTDOWN_INFO preshutdownInfo; LPSERVICE_REQUIRED_PRIVILEGES_INFO requiredPrivilegesInfo; ULONG returnLength; if (!(serviceHandle = PhOpenService(Context->ServiceItem->Name->Buffer, SERVICE_QUERY_CONFIG))) return NTSTATUS_FROM_WIN32(GetLastError()); // Preshutdown timeout if (QueryServiceConfig2(serviceHandle, SERVICE_CONFIG_PRESHUTDOWN_INFO, (PBYTE)&preshutdownInfo, sizeof(SERVICE_PRESHUTDOWN_INFO), &returnLength )) { SetDlgItemInt(hwndDlg, IDC_PRESHUTDOWNTIMEOUT, preshutdownInfo.dwPreshutdownTimeout, FALSE); Context->PreshutdownTimeoutValid = TRUE; } // Required privileges if (requiredPrivilegesInfo = PhQueryServiceVariableSize(serviceHandle, SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO)) { PWSTR privilege; ULONG privilegeLength; INT lvItemIndex; PH_STRINGREF privilegeSr; PPH_STRING privilegeString; PPH_STRING displayName; privilege = requiredPrivilegesInfo->pmszRequiredPrivileges; if (privilege) { while (TRUE) { privilegeLength = (ULONG)wcslen(privilege); if (privilegeLength == 0) break; privilegeString = PhCreateStringEx(privilege, privilegeLength * sizeof(WCHAR)); PhAddItemList(Context->PrivilegeList, privilegeString); lvItemIndex = PhAddListViewItem(Context->PrivilegesLv, MAXINT, privilege, privilegeString); privilegeSr.Buffer = privilege; privilegeSr.Length = (USHORT)(privilegeLength * sizeof(WCHAR)); if (PhLookupPrivilegeDisplayName(&privilegeSr, &displayName)) { PhSetListViewSubItem(Context->PrivilegesLv, lvItemIndex, 1, displayName->Buffer); PhDereferenceObject(displayName); } privilege += privilegeLength + 1; } } ExtendedListView_SortItems(Context->PrivilegesLv); PhFree(requiredPrivilegesInfo); Context->RequiredPrivilegesValid = TRUE; } // Triggers if (Context->TriggerContext) { EsLoadServiceTriggerInfo(Context->TriggerContext, serviceHandle); } CloseServiceHandle(serviceHandle); return status; } static int __cdecl PrivilegeNameCompareFunction( __in const void *elem1, __in const void *elem2 ) { PWSTR string1 = *(PWSTR *)elem1; PWSTR string2 = *(PWSTR *)elem2; return wcscmp(string1, string2); } INT_PTR CALLBACK EspServiceOtherDlgProc( __in HWND hwndDlg, __in UINT uMsg, __in WPARAM wParam, __in LPARAM lParam ) { PSERVICE_OTHER_CONTEXT context; if (uMsg == WM_INITDIALOG) { context = PhAllocate(sizeof(SERVICE_OTHER_CONTEXT)); memset(context, 0, sizeof(SERVICE_OTHER_CONTEXT)); SetProp(hwndDlg, L"Context", (HANDLE)context); } else { context = (PSERVICE_OTHER_CONTEXT)GetProp(hwndDlg, L"Context"); if (uMsg == WM_DESTROY) RemoveProp(hwndDlg, L"Context"); } if (!context) return FALSE; switch (uMsg) { case WM_INITDIALOG: { NTSTATUS status; LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam; PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)propSheetPage->lParam; HWND privilegesLv; HWND triggersLv; context->ServiceItem = serviceItem; context->PrivilegesLv = privilegesLv = GetDlgItem(hwndDlg, IDC_PRIVILEGES); PhSetListViewStyle(privilegesLv, FALSE, TRUE); PhSetControlTheme(privilegesLv, L"explorer"); PhAddListViewColumn(privilegesLv, 0, 0, 0, LVCFMT_LEFT, 140, L"Name"); PhAddListViewColumn(privilegesLv, 1, 1, 1, LVCFMT_LEFT, 220, L"Display Name"); PhSetExtendedListView(privilegesLv); context->PrivilegeList = PhCreateList(32); if (context->ServiceItem->Type == SERVICE_KERNEL_DRIVER || context->ServiceItem->Type == SERVICE_FILE_SYSTEM_DRIVER) { // Drivers don't support required privileges. EnableWindow(GetDlgItem(hwndDlg, IDC_ADD), FALSE); } EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVE), FALSE); context->TriggersLv = triggersLv = GetDlgItem(hwndDlg, IDC_TRIGGERS); if (WindowsVersion >= WINDOWS_7) { context->TriggerContext = EsCreateServiceTriggerContext( context->ServiceItem, hwndDlg, triggersLv ); } else { EnableWindow(GetDlgItem(hwndDlg, IDC_TRIGGERS_LABEL), FALSE); EnableWindow(triggersLv, FALSE); EnableWindow(GetDlgItem(hwndDlg, IDC_NEW), FALSE); EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT), FALSE); EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), FALSE); } status = EspLoadOtherInfo(hwndDlg, context); if (!NT_SUCCESS(status)) { PhShowWarning(hwndDlg, L"Unable to query service information: %s", ((PPH_STRING)PHA_DEREFERENCE(PhGetNtMessage(status)))->Buffer); } context->Ready = TRUE; } break; case WM_DESTROY: { if (context->PrivilegeList) { PhDereferenceObjects(context->PrivilegeList->Items, context->PrivilegeList->Count); PhDereferenceObject(context->PrivilegeList); } if (context->TriggerContext) EsDestroyServiceTriggerContext(context->TriggerContext); PhFree(context); } break; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_ADD: { NTSTATUS status; LSA_HANDLE policyHandle; LSA_ENUMERATION_HANDLE enumContext; PPOLICY_PRIVILEGE_DEFINITION buffer; ULONG count; ULONG i; PPH_LIST choices; PPH_STRING selectedChoice = NULL; choices = PhCreateList(100); if (!NT_SUCCESS(status = PhOpenLsaPolicy(&policyHandle, POLICY_VIEW_LOCAL_INFORMATION, NULL))) { PhShowStatus(hwndDlg, L"Unable to open LSA policy", status, 0); break; } enumContext = 0; while (TRUE) { status = LsaEnumeratePrivileges( policyHandle, &enumContext, &buffer, 0x100, &count ); if (status == STATUS_NO_MORE_ENTRIES) break; if (!NT_SUCCESS(status)) break; for (i = 0; i < count; i++) { PhAddItemList(choices, PhaCreateStringEx(buffer[i].Name.Buffer, buffer[i].Name.Length)->Buffer); } } LsaClose(policyHandle); qsort(choices->Items, choices->Count, sizeof(PWSTR), PrivilegeNameCompareFunction); while (PhaChoiceDialog( hwndDlg, L"Add privilege", L"Select a privilege to add:", (PWSTR *)choices->Items, choices->Count, NULL, PH_CHOICE_DIALOG_CHOICE, &selectedChoice, NULL, NULL )) { BOOLEAN found = FALSE; PPH_STRING privilegeString; INT lvItemIndex; PPH_STRING displayName; // Check for duplicates. for (i = 0; i < context->PrivilegeList->Count; i++) { if (PhEqualString(context->PrivilegeList->Items[i], selectedChoice, FALSE)) { found = TRUE; break; } } if (found) { if (PhShowMessage( hwndDlg, MB_OKCANCEL | MB_ICONERROR, L"The selected privilege has already been added." ) == IDOK) { continue; } else { break; } } privilegeString = selectedChoice; PhReferenceObject(privilegeString); PhAddItemList(context->PrivilegeList, privilegeString); lvItemIndex = PhAddListViewItem(context->PrivilegesLv, MAXINT, privilegeString->Buffer, privilegeString); if (PhLookupPrivilegeDisplayName(&privilegeString->sr, &displayName)) { PhSetListViewSubItem(context->PrivilegesLv, lvItemIndex, 1, displayName->Buffer); PhDereferenceObject(displayName); } ExtendedListView_SortItems(context->PrivilegesLv); context->Dirty = TRUE; context->RequiredPrivilegesValid = TRUE; break; } PhDereferenceObject(choices); } break; case IDC_REMOVE: { INT lvItemIndex; PPH_STRING privilegeString; ULONG index; lvItemIndex = ListView_GetNextItem(context->PrivilegesLv, -1, LVNI_SELECTED); if (lvItemIndex != -1 && PhGetListViewItemParam(context->PrivilegesLv, lvItemIndex, (PPVOID)&privilegeString)) { index = PhFindItemList(context->PrivilegeList, privilegeString); if (index != -1) { PhDereferenceObject(privilegeString); PhRemoveItemList(context->PrivilegeList, index); PhRemoveListViewItem(context->PrivilegesLv, lvItemIndex); context->Dirty = TRUE; context->RequiredPrivilegesValid = TRUE; } } } break; case IDC_NEW: if (context->TriggerContext) EsHandleEventServiceTrigger(context->TriggerContext, ES_TRIGGER_EVENT_NEW); break; case IDC_EDIT: if (context->TriggerContext) EsHandleEventServiceTrigger(context->TriggerContext, ES_TRIGGER_EVENT_EDIT); break; case IDC_DELETE: if (context->TriggerContext) EsHandleEventServiceTrigger(context->TriggerContext, ES_TRIGGER_EVENT_DELETE); break; } switch (HIWORD(wParam)) { case EN_CHANGE: { if (context->Ready) { context->Dirty = TRUE; context->PreshutdownTimeoutValid = TRUE; } } break; } } break; case WM_NOTIFY: { LPNMHDR header = (LPNMHDR)lParam; switch (header->code) { case PSN_KILLACTIVE: { SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, FALSE); } return TRUE; case PSN_APPLY: { SC_HANDLE serviceHandle; ULONG win32Result = 0; SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); if (context->Dirty) { if (serviceHandle = PhOpenService(context->ServiceItem->Name->Buffer, SERVICE_CHANGE_CONFIG)) { SERVICE_PRESHUTDOWN_INFO preshutdownInfo; SERVICE_REQUIRED_PRIVILEGES_INFO requiredPrivilegesInfo; if (context->PreshutdownTimeoutValid) { preshutdownInfo.dwPreshutdownTimeout = GetDlgItemInt(hwndDlg, IDC_PRESHUTDOWNTIMEOUT, NULL, FALSE); if (!ChangeServiceConfig2(serviceHandle, SERVICE_CONFIG_PRESHUTDOWN_INFO, &preshutdownInfo)) { win32Result = GetLastError(); } } if (context->RequiredPrivilegesValid) { PH_STRING_BUILDER sb; ULONG i; PhInitializeStringBuilder(&sb, 100); for (i = 0; i < context->PrivilegeList->Count; i++) { PhAppendStringBuilder(&sb, context->PrivilegeList->Items[i]); PhAppendCharStringBuilder(&sb, 0); } requiredPrivilegesInfo.pmszRequiredPrivileges = sb.String->Buffer; if (win32Result == 0 && !ChangeServiceConfig2(serviceHandle, SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO, &requiredPrivilegesInfo)) { win32Result = GetLastError(); } PhDeleteStringBuilder(&sb); } CloseServiceHandle(serviceHandle); } else { win32Result = GetLastError(); } if (win32Result != 0) { if (PhShowMessage( hwndDlg, MB_ICONERROR | MB_RETRYCANCEL, L"Unable to change service information: %s", ((PPH_STRING)PHA_DEREFERENCE(PhGetWin32Message(win32Result)))->Buffer ) == IDRETRY) { SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID); goto EndOfSave; } } } if (context->TriggerContext) { if (!EsSaveServiceTriggerInfo(context->TriggerContext, &win32Result)) { if (PhShowMessage( hwndDlg, MB_ICONERROR | MB_RETRYCANCEL, L"Unable to change service trigger information: %s", ((PPH_STRING)PHA_DEREFERENCE(PhGetWin32Message(win32Result)))->Buffer ) == IDRETRY) { SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID); goto EndOfSave; } } } EndOfSave: return TRUE; } break; case LVN_ITEMCHANGED: { if (header->hwndFrom == context->TriggersLv && context->TriggerContext) { EsHandleEventServiceTrigger(context->TriggerContext, ES_TRIGGER_EVENT_SELECTIONCHANGED); } else if (header->hwndFrom == context->PrivilegesLv) { EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVE), ListView_GetSelectedCount(context->PrivilegesLv) == 1); } } break; case NM_DBLCLK: { if (header->hwndFrom == context->TriggersLv && context->TriggerContext) { EsHandleEventServiceTrigger(context->TriggerContext, ES_TRIGGER_EVENT_EDIT); } } break; } } break; } return FALSE; }