static _Callback_ NTSTATUS PhpSetServiceSecurity( _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, _In_ SECURITY_INFORMATION SecurityInformation, _In_opt_ PVOID Context ) { NTSTATUS status; PPH_STD_OBJECT_SECURITY stdObjectSecurity; stdObjectSecurity = (PPH_STD_OBJECT_SECURITY)Context; status = PhStdSetObjectSecurity(SecurityDescriptor, SecurityInformation, Context); if ((status == STATUS_ACCESS_DENIED || status == NTSTATUS_FROM_WIN32(ERROR_ACCESS_DENIED)) && !PhGetOwnTokenAttributes().Elevated) { // Elevate using phsvc. if (PhUiConnectToPhSvc(NULL, FALSE)) { status = PhSvcCallSetServiceSecurity( ((PPH_SERVICE_ITEM)stdObjectSecurity->Context)->Name->Buffer, SecurityInformation, SecurityDescriptor ); PhUiDisconnectFromPhSvc(); } } return status; }
NTSTATUS KhsStartFiltering( _In_ DWORD NumberOfScanThreads, _In_ PHS_SCAN_FILE_ROUTINE FileScanRoutine) { if (!HsKhsPortHandle) return STATUS_INVALID_HANDLE; HsFileScanRoutine = FileScanRoutine; HsKhsCompletionPort = CreateIoCompletionPort( HsKhsPortHandle, NULL, 0, NumberOfScanThreads); if (HsKhsCompletionPort) { for (DWORD i = 0; i < NumberOfScanThreads; i++) { HANDLE threadHandle = CreateThread( NULL, 0, KhspUserScanWorker, NULL, 0, NULL); CloseHandle(threadHandle); } return STATUS_SUCCESS; } else return NTSTATUS_FROM_WIN32(GetLastError()); }
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; }
NTSTATUS EspLoadTriggerInfo( _In_ HWND hwndDlg, _In_ PSERVICE_TRIGGERS_CONTEXT Context ) { NTSTATUS status = STATUS_SUCCESS; SC_HANDLE serviceHandle; if (!(serviceHandle = PhOpenService(Context->ServiceItem->Name->Buffer, SERVICE_QUERY_CONFIG))) return NTSTATUS_FROM_WIN32(GetLastError()); EsLoadServiceTriggerInfo(Context->TriggerContext, serviceHandle); CloseServiceHandle(serviceHandle); return status; }
/** * Converts a Win32 error code to a NTSTATUS value. * * \remarks Only a small number of cases are currently supported. Other status values are wrapped * using FACILITY_NTWIN32. */ NTSTATUS PhDosErrorToNtStatus( _In_ ULONG DosError ) { switch (DosError) { case ERROR_INVALID_FUNCTION: return STATUS_ILLEGAL_FUNCTION; case ERROR_FILE_NOT_FOUND: return STATUS_NO_SUCH_FILE; case ERROR_ACCESS_DENIED: return STATUS_ACCESS_DENIED; case ERROR_INVALID_HANDLE: return STATUS_INVALID_HANDLE; case ERROR_HANDLE_EOF: return STATUS_END_OF_FILE; case ERROR_NOT_SUPPORTED: return STATUS_NOT_SUPPORTED; case ERROR_INVALID_PARAMETER: return STATUS_INVALID_PARAMETER; case ERROR_NOT_LOCKED: return STATUS_NOT_LOCKED; case ERROR_MORE_DATA: return STATUS_MORE_ENTRIES; case ERROR_NOACCESS: return STATUS_ACCESS_VIOLATION; case ERROR_STACK_OVERFLOW: return STATUS_STACK_OVERFLOW; case ERROR_INTERNAL_ERROR: return STATUS_INTERNAL_ERROR; default: return NTSTATUS_FROM_WIN32(DosError); } }
int nla_server_authenticate(rdpNla* nla) { if (nla_server_init(nla) < 1) return -1; while (TRUE) { /* receive authentication token */ nla->inputBufferDesc.ulVersion = SECBUFFER_VERSION; nla->inputBufferDesc.cBuffers = 1; nla->inputBufferDesc.pBuffers = &nla->inputBuffer; nla->inputBuffer.BufferType = SECBUFFER_TOKEN; if (nla_recv(nla) < 0) return -1; WLog_DBG(TAG, "Receiving Authentication Token"); nla_buffer_print(nla); nla->inputBuffer.pvBuffer = nla->negoToken.pvBuffer; nla->inputBuffer.cbBuffer = nla->negoToken.cbBuffer; if (nla->negoToken.cbBuffer < 1) { WLog_ERR(TAG, "CredSSP: invalid negoToken!"); return -1; } nla->outputBufferDesc.ulVersion = SECBUFFER_VERSION; nla->outputBufferDesc.cBuffers = 1; nla->outputBufferDesc.pBuffers = &nla->outputBuffer; nla->outputBuffer.BufferType = SECBUFFER_TOKEN; nla->outputBuffer.cbBuffer = nla->cbMaxToken; nla->outputBuffer.pvBuffer = malloc(nla->outputBuffer.cbBuffer); if (!nla->outputBuffer.pvBuffer) return -1; nla->status = nla->table->AcceptSecurityContext(&nla->credentials, nla-> haveContext? &nla->context: NULL, &nla->inputBufferDesc, nla->fContextReq, SECURITY_NATIVE_DREP, &nla->context, &nla->outputBufferDesc, &nla->pfContextAttr, &nla->expiration); WLog_VRB(TAG, "AcceptSecurityContext status %s [%08X]", GetSecurityStatusString(nla->status), nla->status); nla->negoToken.pvBuffer = nla->outputBuffer.pvBuffer; nla->negoToken.cbBuffer = nla->outputBuffer.cbBuffer; if ((nla->status == SEC_I_COMPLETE_AND_CONTINUE) || (nla->status == SEC_I_COMPLETE_NEEDED)) { if (nla->table->CompleteAuthToken) { SECURITY_STATUS status; status = nla->table->CompleteAuthToken(&nla->context, &nla->outputBufferDesc); if (status != SEC_E_OK) { WLog_WARN(TAG, "CompleteAuthToken status %s [%08X]", GetSecurityStatusString(status), status); return -1; } } if (nla->status == SEC_I_COMPLETE_NEEDED) nla->status = SEC_E_OK; else if (nla->status == SEC_I_COMPLETE_AND_CONTINUE) nla->status = SEC_I_CONTINUE_NEEDED; } if (nla->status == SEC_E_OK) { if (nla->outputBuffer.cbBuffer != 0) { if (!nla_send(nla)) { nla_buffer_free(nla); return -1; } if (nla_recv(nla) < 0) return -1; WLog_DBG(TAG, "Receiving pubkey Token"); nla_buffer_print(nla); } nla->havePubKeyAuth = TRUE; nla->status = nla->table->QueryContextAttributes(&nla->context, SECPKG_ATTR_SIZES, &nla->ContextSizes); if (nla->status != SEC_E_OK) { WLog_ERR(TAG, "QueryContextAttributes SECPKG_ATTR_SIZES failure %s [%08X]", GetSecurityStatusString(nla->status), nla->status); return -1; } nla->status = nla_decrypt_public_key_echo(nla); if (nla->status != SEC_E_OK) { WLog_ERR(TAG, "Error: could not verify client's public key echo %s [%08X]", GetSecurityStatusString(nla->status), nla->status); return -1; } sspi_SecBufferFree(&nla->negoToken); nla->negoToken.pvBuffer = NULL; nla->negoToken.cbBuffer = 0; nla->status = nla_encrypt_public_key_echo(nla); if (nla->status != SEC_E_OK) return -1; } if ((nla->status != SEC_E_OK) && (nla->status != SEC_I_CONTINUE_NEEDED)) { /* Special handling of these specific error codes as NTSTATUS_FROM_WIN32 unfortunately does not map directly to the corresponding NTSTATUS values */ switch (GetLastError()) { case ERROR_PASSWORD_MUST_CHANGE: nla->errorCode = STATUS_PASSWORD_MUST_CHANGE; break; case ERROR_PASSWORD_EXPIRED: nla->errorCode = STATUS_PASSWORD_EXPIRED; break; case ERROR_ACCOUNT_DISABLED: nla->errorCode = STATUS_ACCOUNT_DISABLED; break; default: nla->errorCode = NTSTATUS_FROM_WIN32(GetLastError()); break; } WLog_ERR(TAG, "AcceptSecurityContext status %s [%08X]", GetSecurityStatusString(nla->status), nla->status); nla_send(nla); return -1; /* Access Denied */ } /* send authentication token */ WLog_DBG(TAG, "Sending Authentication Token"); nla_buffer_print(nla); if (!nla_send(nla)) { nla_buffer_free(nla); return -1; } nla_buffer_free(nla); if (nla->status != SEC_I_CONTINUE_NEEDED) break; nla->haveContext = TRUE; } /* Receive encrypted credentials */ if (nla_recv(nla) < 0) return -1; nla->status = nla_decrypt_ts_credentials(nla); if (nla->status != SEC_E_OK) { WLog_ERR(TAG, "Could not decrypt TSCredentials status %s [%08X]", GetSecurityStatusString(nla->status), nla->status); return -1; } nla->status = nla->table->ImpersonateSecurityContext(&nla->context); if (nla->status != SEC_E_OK) { WLog_ERR(TAG, "ImpersonateSecurityContext status %s [%08X]", GetSecurityStatusString(nla->status), nla->status); return -1; } else { nla->status = nla->table->RevertSecurityContext(&nla->context); if (nla->status != SEC_E_OK) { WLog_ERR(TAG, "RevertSecurityContext status %s [%08X]", GetSecurityStatusString(nla->status), nla->status); return -1; } } nla->status = nla->table->FreeContextBuffer(nla->pPackageInfo); if (nla->status != SEC_E_OK) { WLog_ERR(TAG, "DeleteSecurityContext status %s [%08X]", GetSecurityStatusString(nla->status), nla->status); return -1; } return 1; }
/** * Executes the run-as service. * * \param Parameters The run-as parameters. * * \remarks This function requires administrator-level access. */ NTSTATUS PhExecuteRunAsCommand( _In_ PPH_RUNAS_SERVICE_PARAMETERS Parameters ) { NTSTATUS status; ULONG win32Result; PPH_STRING commandLine; SC_HANDLE scManagerHandle; SC_HANDLE serviceHandle; PPH_STRING portName; UNICODE_STRING portNameUs; ULONG attempts; LARGE_INTEGER interval; if (!(scManagerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE))) return PhGetLastWin32ErrorAsNtStatus(); commandLine = PhFormatString(L"\"%s\" -ras \"%s\"", PhApplicationFileName->Buffer, Parameters->ServiceName); serviceHandle = CreateService( scManagerHandle, Parameters->ServiceName, Parameters->ServiceName, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE, commandLine->Buffer, NULL, NULL, NULL, L"LocalSystem", L"" ); win32Result = GetLastError(); PhDereferenceObject(commandLine); CloseServiceHandle(scManagerHandle); if (!serviceHandle) { return NTSTATUS_FROM_WIN32(win32Result); } PhSetDesktopWinStaAccess(); StartService(serviceHandle, 0, NULL); DeleteService(serviceHandle); portName = PhConcatStrings2(L"\\BaseNamedObjects\\", Parameters->ServiceName); PhStringRefToUnicodeString(&portName->sr, &portNameUs); attempts = 10; // Try to connect several times because the server may take // a while to initialize. do { status = PhSvcConnectToServer(&portNameUs, 0); if (NT_SUCCESS(status)) break; interval.QuadPart = -50 * PH_TIMEOUT_MS; NtDelayExecution(FALSE, &interval); } while (--attempts != 0); PhDereferenceObject(portName); if (NT_SUCCESS(status)) { status = PhSvcCallInvokeRunAsService(Parameters); PhSvcDisconnectFromServer(); } if (serviceHandle) CloseServiceHandle(serviceHandle); return status; }
// This routine is called by the framework when the PnP manager is revoking // ownership of our resources. This may be in response to either // IRP_MN_STOP_DEVICE or IRP_MN_REMOVE_DEVICE. This routine is responsible for // performing cleanup of resources allocated in PrepareHardware callback. // This callback is invoked before passing the request down to the lower driver. // This routine will also be invoked by the framework if the prepare hardware // callback returns a failure. NTSTATUS ActivityDevice::OnReleaseHardware( _In_ WDFDEVICE device, // Supplies a handle to the framework device object _In_ WDFCMRESLIST /*ResourcesTranslated*/) // Supplies a handle to a collection of framework // resource objects. This collection identifies the translated // (system-physical) hardware resources that have been assigned to the // device. The resources appear from the CPU's point of view. { NTSTATUS status = STATUS_SUCCESS; SENSOR_FunctionEnter(); // Get sensor instance SENSOROBJECT sensorInstance = NULL; ULONG sensorInstanceCount = 1; // only expect 1 sensor instance status = SensorsCxDeviceGetSensorList(device, &sensorInstance, &sensorInstanceCount); if (!NT_SUCCESS(status) || sensorInstanceCount == 0 || sensorInstance == NULL) { status = STATUS_INVALID_PARAMETER; TraceError("ACT %!FUNC! SensorsCxDeviceGetSensorList failed %!STATUS!", status); } else { PActivityDevice pDevice = GetActivityContextFromSensorInstance(sensorInstance); if (nullptr == pDevice) { status = STATUS_INVALID_PARAMETER; TraceError("ACT %!FUNC! GetActivityContextFromSensorInstance failed %!STATUS!", status); } else { // Cleanup activity simulator if (NULL != pDevice->m_SimulatorInstance) { PHardwareSimulator pSimulator = GetHardwareSimulatorContextFromInstance(pDevice->m_SimulatorInstance); if (nullptr != pSimulator) { pSimulator->Deinitialize(); } // Continue tearing down WdfObjectDelete(pDevice->m_SimulatorInstance); pDevice->m_SimulatorInstance = NULL; } // Close handle to history retrieval thread if (NULL != pDevice->m_hThread) { SetEvent(pDevice->m_ExitEvent); DWORD result = WaitForSingleObjectEx(pDevice->m_hThread, Act_TimeoutForHistoryThread_Ms, FALSE); if (WAIT_OBJECT_0 != result) { // continue tearing down status = NTSTATUS_FROM_WIN32(result); TraceError("ACT %!FUNC! WaitForSingleObjectEx failed %!STATUS!", status); } CloseHandle(pDevice->m_hThread); pDevice->m_hThread = NULL; } // Close handle to exit event if (NULL != pDevice->m_ExitEvent) { CloseHandle(pDevice->m_ExitEvent); pDevice->m_ExitEvent = NULL; } // Delete history lock if (NULL != pDevice->m_HistoryLock) { WdfObjectDelete(pDevice->m_HistoryLock); pDevice->m_HistoryLock = NULL; } // Delete lock if (NULL != pDevice->m_Lock) { WdfObjectDelete(pDevice->m_Lock); pDevice->m_Lock = NULL; } // Delete sensor instance. if (NULL != pDevice->m_SensorInstance) { // Memory created by WdfMemoryCreate with m_SensorInstance as parent object will be // destroyed automatically when m_SensorInstance is deleted. pDevice will be no longer // accessible at beyond this point. WdfObjectDelete(pDevice->m_SensorInstance); pDevice = nullptr; } } } SENSOR_FunctionExit(status); return status; }
NTSTATUS EspLoadOtherInfo( _In_ HWND hwndDlg, _In_ PSERVICE_OTHER_CONTEXT Context ) { NTSTATUS status = STATUS_SUCCESS; SC_HANDLE serviceHandle; ULONG returnLength; SERVICE_PRESHUTDOWN_INFO preshutdownInfo; LPSERVICE_REQUIRED_PRIVILEGES_INFO requiredPrivilegesInfo; SERVICE_SID_INFO sidInfo; SERVICE_LAUNCH_PROTECTED_INFO launchProtectedInfo; 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)PhCountStringZ(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 = 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; } // SID type if (QueryServiceConfig2(serviceHandle, SERVICE_CONFIG_SERVICE_SID_INFO, (PBYTE)&sidInfo, sizeof(SERVICE_SID_INFO), &returnLength )) { PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_SIDTYPE), EspGetServiceSidTypeString(sidInfo.dwServiceSidType), FALSE); Context->SidTypeValid = TRUE; } // Launch protected if (QueryServiceConfig2(serviceHandle, SERVICE_CONFIG_LAUNCH_PROTECTED, (PBYTE)&launchProtectedInfo, sizeof(SERVICE_LAUNCH_PROTECTED_INFO), &returnLength )) { PhSelectComboBoxString(GetDlgItem(hwndDlg, IDC_PROTECTION), EspGetServiceLaunchProtectedString(launchProtectedInfo.dwLaunchProtected), FALSE); Context->LaunchProtectedValid = TRUE; Context->OriginalLaunchProtected = launchProtectedInfo.dwLaunchProtected; } CloseServiceHandle(serviceHandle); return status; }
NTSTATUS EspLoadRecoveryInfo( _In_ HWND hwndDlg, _In_ PSERVICE_RECOVERY_CONTEXT Context ) { NTSTATUS status = STATUS_SUCCESS; SC_HANDLE serviceHandle; LPSERVICE_FAILURE_ACTIONS failureActions; SERVICE_FAILURE_ACTIONS_FLAG failureActionsFlag; SC_ACTION_TYPE lastType; ULONG returnLength; ULONG i; if (!(serviceHandle = PhOpenService(Context->ServiceItem->Name->Buffer, SERVICE_QUERY_CONFIG))) return NTSTATUS_FROM_WIN32(GetLastError()); if (!(failureActions = PhQueryServiceVariableSize(serviceHandle, SERVICE_CONFIG_FAILURE_ACTIONS))) { CloseServiceHandle(serviceHandle); return NTSTATUS_FROM_WIN32(GetLastError()); } // Failure action types Context->NumberOfActions = failureActions->cActions; if (failureActions->cActions != 0 && failureActions->cActions != 3) status = STATUS_SOME_NOT_MAPPED; // If failure actions are not defined for a particular fail count, the // last failure action is used. Here we duplicate this behaviour when there // are fewer than 3 failure actions. lastType = SC_ACTION_NONE; ServiceActionToComboBox(GetDlgItem(hwndDlg, IDC_FIRSTFAILURE), failureActions->cActions >= 1 ? (lastType = failureActions->lpsaActions[0].Type) : lastType); ServiceActionToComboBox(GetDlgItem(hwndDlg, IDC_SECONDFAILURE), failureActions->cActions >= 2 ? (lastType = failureActions->lpsaActions[1].Type) : lastType); ServiceActionToComboBox(GetDlgItem(hwndDlg, IDC_SUBSEQUENTFAILURES), failureActions->cActions >= 3 ? (lastType = failureActions->lpsaActions[2].Type) : lastType); // Reset fail count after SetDlgItemInt(hwndDlg, IDC_RESETFAILCOUNT, failureActions->dwResetPeriod / (60 * 60 * 24), FALSE); // s to days // Restart service after SetDlgItemText(hwndDlg, IDC_RESTARTSERVICEAFTER, L"1"); for (i = 0; i < failureActions->cActions; i++) { if (failureActions->lpsaActions[i].Type == SC_ACTION_RESTART) { if (failureActions->lpsaActions[i].Delay != 0) { SetDlgItemInt(hwndDlg, IDC_RESTARTSERVICEAFTER, failureActions->lpsaActions[i].Delay / (1000 * 60), FALSE); // ms to min } break; } } // Enable actions for stops with errors // This is Vista and above only. if (WindowsVersion >= WINDOWS_VISTA && QueryServiceConfig2( serviceHandle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, (BYTE *)&failureActionsFlag, sizeof(SERVICE_FAILURE_ACTIONS_FLAG), &returnLength )) { Button_SetCheck(GetDlgItem(hwndDlg, IDC_ENABLEFORERRORSTOPS), failureActionsFlag.fFailureActionsOnNonCrashFailures ? BST_CHECKED : BST_UNCHECKED); Context->EnableFlagCheckBox = TRUE; } else { Context->EnableFlagCheckBox = FALSE; } // Restart computer options Context->RebootAfter = 1 * 1000 * 60; for (i = 0; i < failureActions->cActions; i++) { if (failureActions->lpsaActions[i].Type == SC_ACTION_REBOOT) { if (failureActions->lpsaActions[i].Delay != 0) Context->RebootAfter = failureActions->lpsaActions[i].Delay; break; } } if (failureActions->lpRebootMsg && failureActions->lpRebootMsg[0] != 0) PhMoveReference(&Context->RebootMessage, PhCreateString(failureActions->lpRebootMsg)); else PhClearReference(&Context->RebootMessage); // Run program SetDlgItemText(hwndDlg, IDC_RUNPROGRAM, failureActions->lpCommand); PhFree(failureActions); CloseServiceHandle(serviceHandle); return status; }