golang.zx2c4.com/wireguard/windows@v0.5.4-0.20230123132234-dcc0eb72a04b/installer/customactions.c (about) 1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved. 4 */ 5 6 #include <windows.h> 7 #include <ntstatus.h> 8 #include <tlhelp32.h> 9 #include <msi.h> 10 #include <msidefs.h> 11 #include <msiquery.h> 12 #include <shlwapi.h> 13 #include <shlobj.h> 14 #include <stdbool.h> 15 #include <tchar.h> 16 17 #define MANAGER_SERVICE_NAME TEXT("WireGuardManager") 18 #define TUNNEL_SERVICE_PREFIX TEXT("WireGuardTunnel$") 19 20 enum log_level { LOG_LEVEL_INFO, LOG_LEVEL_WARN, LOG_LEVEL_ERR, LOG_LEVEL_MSIERR }; 21 22 static void log_messagef(MSIHANDLE installer, enum log_level level, const TCHAR *format, ...) 23 { 24 MSIHANDLE record = MsiCreateRecord(2); 25 TCHAR *template, *line = NULL; 26 INSTALLMESSAGE type; 27 va_list args; 28 29 if (!record) 30 return; 31 32 va_start(args, format); 33 FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK, 34 format, 0, 0, (void *)&line, 0, &args); 35 va_end(args); 36 if (!line) 37 goto out; 38 39 switch (level) { 40 case LOG_LEVEL_INFO: 41 template = TEXT("WireGuard: [1]"); 42 type = INSTALLMESSAGE_INFO; 43 break; 44 case LOG_LEVEL_WARN: 45 template = TEXT("WireGuard warning: [1]"); 46 type = INSTALLMESSAGE_INFO; 47 break; 48 case LOG_LEVEL_ERR: 49 template = TEXT("WireGuard error: [1]"); 50 type = INSTALLMESSAGE_ERROR; 51 break; 52 case LOG_LEVEL_MSIERR: 53 template = TEXT("[1]"); 54 type = INSTALLMESSAGE_ERROR; 55 break; 56 default: 57 goto out; 58 } 59 MsiRecordSetString(record, 0, template); 60 MsiRecordSetString(record, 1, line); 61 MsiProcessMessage(installer, type, record); 62 out: 63 LocalFree(line); 64 MsiCloseHandle(record); 65 } 66 67 static void log_errorf(MSIHANDLE installer, enum log_level level, DWORD error_code, const TCHAR *prefix_format, ...) 68 { 69 TCHAR *system_message = NULL, *prefix = NULL; 70 va_list args; 71 72 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK, 73 NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 74 (void *)&system_message, 0, NULL); 75 va_start(args, prefix_format); 76 FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK, 77 prefix_format, 0, 0, (void *)&prefix, 0, &args); 78 va_end(args); 79 log_messagef(installer, level, system_message ? TEXT("%1: %3(Code 0x%2!08X!)") : TEXT("%1: Code 0x%2!08X!"), 80 prefix ?: TEXT("Error"), error_code, system_message); 81 LocalFree(prefix); 82 LocalFree(system_message); 83 } 84 85 __declspec(dllexport) UINT __stdcall CheckWow64(MSIHANDLE installer) 86 { 87 UINT ret = ERROR_SUCCESS; 88 bool is_com_initialized = SUCCEEDED(CoInitialize(NULL)); 89 HMODULE kernel32 = GetModuleHandle(TEXT("kernel32.dll")); 90 BOOL(WINAPI *IsWow64Process2)(HANDLE hProcess, USHORT *pProcessMachine, USHORT *pNativeMachine); 91 USHORT process_machine, native_machine; 92 BOOL is_wow64_process; 93 94 if (!kernel32) { 95 ret = GetLastError(); 96 log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("Failed to get kernel32.dll handle")); 97 goto out; 98 } 99 *(FARPROC *)&IsWow64Process2 = GetProcAddress(kernel32, "IsWow64Process2"); 100 if (IsWow64Process2) { 101 if (!IsWow64Process2(GetCurrentProcess(), &process_machine, &native_machine)) { 102 ret = GetLastError(); 103 log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("Failed to determine Wow64 status from IsWow64Process2")); 104 goto out; 105 } 106 if (process_machine == IMAGE_FILE_MACHINE_UNKNOWN) 107 goto out; 108 } else { 109 if (!IsWow64Process(GetCurrentProcess(), &is_wow64_process)) { 110 ret = GetLastError(); 111 log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("Failed to determine Wow64 status from IsWow64Process")); 112 goto out; 113 } 114 if (!is_wow64_process) 115 goto out; 116 } 117 log_messagef(installer, LOG_LEVEL_MSIERR, TEXT("You must use the native version of WireGuard on this computer.")); 118 ret = ERROR_INSTALL_FAILURE; 119 out: 120 if (is_com_initialized) 121 CoUninitialize(); 122 return ret; 123 } 124 125 static UINT insert_service_control(MSIHANDLE installer, MSIHANDLE view, const TCHAR *service_name, bool start) 126 { 127 static unsigned int index = 0; 128 UINT ret; 129 MSIHANDLE record; 130 TCHAR row_identifier[_countof(TEXT("wireguard_service_control_4294967296"))]; 131 132 if (_sntprintf(row_identifier, _countof(row_identifier), TEXT("wireguard_service_control_%u"), ++index) >= _countof(row_identifier)) 133 return ERROR_INSTALL_FAILURE; 134 record = MsiCreateRecord(5); 135 if (!record) 136 return ERROR_INSTALL_FAILURE; 137 138 MsiRecordSetString (record, 1/*ServiceControl*/, row_identifier); 139 MsiRecordSetString (record, 2/*Name */, service_name); 140 MsiRecordSetInteger(record, 3/*Event */, msidbServiceControlEventStop | msidbServiceControlEventUninstallStop | msidbServiceControlEventUninstallDelete); 141 MsiRecordSetString (record, 4/*Component_ */, TEXT("WireGuardExecutable")); 142 MsiRecordSetInteger(record, 5/*Wait */, 1); /* Waits 30 seconds. */ 143 log_messagef(installer, LOG_LEVEL_INFO, TEXT("Scheduling stop on upgrade or removal on uninstall of service %1"), service_name); 144 ret = MsiViewExecute(view, record); 145 if (ret != ERROR_SUCCESS) { 146 log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("MsiViewExecute failed for service %1"), service_name); 147 goto out; 148 } 149 150 if (!start) 151 goto out; 152 153 ret = ERROR_INSTALL_FAILURE; 154 if (_sntprintf(row_identifier, _countof(row_identifier), TEXT("wireguard_service_control_%u"), ++index) >= _countof(row_identifier)) 155 goto out; 156 MsiRecordSetString (record, 1/*ServiceControl*/, row_identifier); 157 MsiRecordSetString (record, 2/*Name */, service_name); 158 MsiRecordSetInteger(record, 3/*Event */, msidbServiceControlEventStart); 159 MsiRecordSetString (record, 4/*Component_ */, TEXT("WireGuardExecutable")); 160 MsiRecordSetInteger(record, 5/*Wait */, 0); /* No wait, so that failure to restart again isn't fatal. */ 161 log_messagef(installer, LOG_LEVEL_INFO, TEXT("Scheduling start on upgrade of service %1"), service_name); 162 ret = MsiViewExecute(view, record); 163 if (ret != ERROR_SUCCESS) { 164 log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("MsiViewExecute failed for service %1"), service_name); 165 goto out; 166 } 167 168 out: 169 MsiCloseHandle(record); 170 return ret; 171 } 172 173 __declspec(dllexport) UINT __stdcall EvaluateWireGuardServices(MSIHANDLE installer) 174 { 175 UINT ret = ERROR_INSTALL_FAILURE; 176 bool is_com_initialized = SUCCEEDED(CoInitialize(NULL)); 177 MSIHANDLE db, view = 0; 178 SC_HANDLE scm = NULL; 179 ENUM_SERVICE_STATUS_PROCESS *service_status = NULL; 180 DWORD service_status_resume = 0; 181 enum { SERVICE_STATUS_PROCESS_SIZE = 0x10000 }; 182 183 db = MsiGetActiveDatabase(installer); 184 if (!db) { 185 log_messagef(installer, LOG_LEVEL_ERR, TEXT("MsiGetActiveDatabase failed")); 186 goto out; 187 } 188 ret = MsiDatabaseOpenView(db, 189 TEXT("INSERT INTO `ServiceControl` (`ServiceControl`, `Name`, `Event`, `Component_`, `Wait`) VALUES(?, ?, ?, ?, ?) TEMPORARY"), 190 &view); 191 if (ret != ERROR_SUCCESS) { 192 log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("MsiDatabaseOpenView failed")); 193 goto out; 194 } 195 scm = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE); 196 if (!scm) { 197 ret = GetLastError(); 198 log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("OpenSCManager failed")); 199 goto out; 200 } 201 202 service_status = LocalAlloc(LMEM_FIXED, SERVICE_STATUS_PROCESS_SIZE); 203 if (!service_status) { 204 ret = GetLastError(); 205 log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("LocalAlloc failed")); 206 goto out; 207 } 208 for (bool more_services = true; more_services;) { 209 DWORD service_status_size = 0, service_status_count = 0; 210 if (EnumServicesStatusEx(scm, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, SERVICE_STATE_ALL, (LPBYTE)service_status, 211 SERVICE_STATUS_PROCESS_SIZE, &service_status_size, &service_status_count, 212 &service_status_resume, NULL)) 213 more_services = false; 214 else { 215 ret = GetLastError(); 216 if (ret != ERROR_MORE_DATA) { 217 log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("EnumServicesStatusEx failed")); 218 break; 219 } 220 } 221 222 for (DWORD i = 0; i < service_status_count; ++i) { 223 if (_tcsicmp(service_status[i].lpServiceName, MANAGER_SERVICE_NAME) && 224 _tcsnicmp(service_status[i].lpServiceName, TUNNEL_SERVICE_PREFIX, _countof(TUNNEL_SERVICE_PREFIX) - 1)) 225 continue; 226 insert_service_control(installer, view, service_status[i].lpServiceName, 227 service_status[i].ServiceStatusProcess.dwCurrentState != SERVICE_STOPPED && 228 service_status[i].ServiceStatusProcess.dwCurrentState != SERVICE_STOP_PENDING); 229 } 230 } 231 ret = ERROR_SUCCESS; 232 233 out: 234 LocalFree(service_status); 235 if (scm) 236 CloseServiceHandle(scm); 237 if (view) 238 MsiCloseHandle(view); 239 if (db) 240 MsiCloseHandle(db); 241 if (is_com_initialized) 242 CoUninitialize(); 243 return ret == ERROR_SUCCESS ? ret : ERROR_INSTALL_FAILURE; 244 } 245 246 __declspec(dllexport) UINT __stdcall LaunchApplicationAndAbort(MSIHANDLE installer) 247 { 248 UINT ret = ERROR_INSTALL_FAILURE; 249 TCHAR path[MAX_PATH]; 250 DWORD path_len = _countof(path); 251 PROCESS_INFORMATION pi; 252 STARTUPINFO si = { .cb = sizeof(STARTUPINFO) }; 253 254 ret = MsiGetProperty(installer, TEXT("WireGuardFolder"), path, &path_len); 255 if (ret != ERROR_SUCCESS) { 256 log_errorf(installer, LOG_LEVEL_WARN, ret, TEXT("MsiGetProperty(\"WireGuardFolder\") failed")); 257 goto out; 258 } 259 if (!path[0] || !PathAppend(path, TEXT("wireguard.exe"))) 260 goto out; 261 log_messagef(installer, LOG_LEVEL_INFO, TEXT("Launching %1"), path); 262 if (!CreateProcess(path, TEXT("wireguard"), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) { 263 log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("Failed to create \"%1\" process"), path); 264 goto out; 265 } 266 CloseHandle(pi.hProcess); 267 CloseHandle(pi.hThread); 268 out: 269 return ERROR_INSTALL_USEREXIT; 270 } 271 272 __declspec(dllexport) UINT __stdcall EvaluateWireGuardComponents(MSIHANDLE installer) 273 { 274 UINT ret = ERROR_INSTALL_FAILURE; 275 bool is_com_initialized = SUCCEEDED(CoInitialize(NULL)); 276 INSTALLSTATE component_installed, component_action; 277 TCHAR path[MAX_PATH]; 278 DWORD path_len = _countof(path); 279 280 ret = MsiGetComponentState(installer, TEXT("WireGuardExecutable"), &component_installed, &component_action); 281 if (ret != ERROR_SUCCESS) { 282 log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("MsiGetComponentState(\"WireGuardExecutable\") failed")); 283 goto out; 284 } 285 ret = MsiGetProperty(installer, TEXT("WireGuardFolder"), path, &path_len); 286 if (ret != ERROR_SUCCESS) { 287 log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("MsiGetProperty(\"WireGuardFolder\") failed")); 288 goto out; 289 } 290 291 if (component_action >= INSTALLSTATE_LOCAL) { 292 /* WireGuardExecutable component shall be installed. */ 293 ret = MsiSetProperty(installer, TEXT("KillWireGuardProcesses"), path); 294 if (ret != ERROR_SUCCESS) { 295 log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("MsiSetProperty(\"KillWireGuardProcesses\") failed")); 296 goto out; 297 } 298 } else if (component_action >= INSTALLSTATE_REMOVED) { 299 /* WireGuardExecutable component shall be uninstalled. */ 300 ret = MsiSetProperty(installer, TEXT("KillWireGuardProcesses"), path); 301 if (ret != ERROR_SUCCESS) { 302 log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("MsiSetProperty(\"KillWireGuardProcesses\") failed")); 303 goto out; 304 } 305 ret = MsiSetProperty(installer, TEXT("RemoveConfigFolder"), path); 306 if (ret != ERROR_SUCCESS) { 307 log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("MsiSetProperty(\"RemoveConfigFolder\") failed")); 308 goto out; 309 } 310 ret = MsiSetProperty(installer, TEXT("RemoveAdapters"), path); 311 if (ret != ERROR_SUCCESS) { 312 log_errorf(installer, LOG_LEVEL_ERR, ret, TEXT("MsiSetProperty(\"RemoveAdapters\") failed")); 313 goto out; 314 } 315 } 316 ret = ERROR_SUCCESS; 317 318 out: 319 if (is_com_initialized) 320 CoUninitialize(); 321 return ret == ERROR_SUCCESS ? ret : ERROR_INSTALL_FAILURE; 322 } 323 324 struct file_id { DWORD volume, index_high, index_low; }; 325 326 static bool calculate_file_id(const TCHAR *path, struct file_id *id) 327 { 328 BY_HANDLE_FILE_INFORMATION file_info = { 0 }; 329 HANDLE file; 330 bool ret; 331 332 file = CreateFile(path, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 333 if (file == INVALID_HANDLE_VALUE) 334 return false; 335 ret = GetFileInformationByHandle(file, &file_info); 336 CloseHandle(file); 337 if (!ret) 338 return false; 339 id->volume = file_info.dwVolumeSerialNumber; 340 id->index_high = file_info.nFileIndexHigh; 341 id->index_low = file_info.nFileIndexLow; 342 return true; 343 } 344 345 __declspec(dllexport) UINT __stdcall KillWireGuardProcesses(MSIHANDLE installer) 346 { 347 HANDLE snapshot, process; 348 PROCESSENTRY32 entry = { .dwSize = sizeof(PROCESSENTRY32) }; 349 TCHAR process_path[MAX_PATH], executable[MAX_PATH]; 350 DWORD process_path_len = _countof(process_path); 351 struct file_id file_ids[3], file_id; 352 size_t file_ids_len = 0; 353 bool is_com_initialized = SUCCEEDED(CoInitialize(NULL)); 354 LSTATUS mret; 355 356 mret = MsiGetProperty(installer, TEXT("CustomActionData"), process_path, &process_path_len); 357 if (mret != ERROR_SUCCESS) { 358 log_errorf(installer, LOG_LEVEL_WARN, mret, TEXT("MsiGetProperty(\"CustomActionData\") failed")); 359 goto out; 360 } 361 if (!process_path[0]) 362 goto out; 363 364 log_messagef(installer, LOG_LEVEL_INFO, TEXT("Detecting running processes")); 365 366 if (PathCombine(executable, process_path, TEXT("wg.exe")) && calculate_file_id(executable, &file_ids[file_ids_len])) 367 ++file_ids_len; 368 if (PathCombine(executable, process_path, TEXT("wireguard.exe")) && calculate_file_id(executable, &file_ids[file_ids_len])) 369 ++file_ids_len; 370 if (!file_ids_len) 371 goto out; 372 373 snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 374 if (snapshot == INVALID_HANDLE_VALUE) 375 goto out; 376 377 for (bool ret = Process32First(snapshot, &entry); ret; ret = Process32Next(snapshot, &entry)) { 378 if (_tcsicmp(entry.szExeFile, TEXT("wireguard.exe")) && _tcsicmp(entry.szExeFile, TEXT("wg.exe"))) 379 continue; 380 process = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_LIMITED_INFORMATION, false, entry.th32ProcessID); 381 if (!process) 382 continue; 383 process_path_len = _countof(process_path); 384 if (!QueryFullProcessImageName(process, 0, process_path, &process_path_len)) 385 goto next; 386 if (!calculate_file_id(process_path, &file_id)) 387 goto next; 388 ret = false; 389 for (size_t i = 0; i < file_ids_len; ++i) { 390 if (!memcmp(&file_id, &file_ids[i], sizeof(file_id))) { 391 ret = true; 392 break; 393 } 394 } 395 if (!ret) 396 goto next; 397 if (TerminateProcess(process, STATUS_DLL_INIT_FAILED_LOGOFF)) { 398 WaitForSingleObject(process, INFINITE); 399 log_messagef(installer, LOG_LEVEL_INFO, TEXT("Killed \"%1\" (pid %2!d!)"), process_path, entry.th32ProcessID); 400 } 401 next: 402 CloseHandle(process); 403 } 404 CloseHandle(snapshot); 405 406 out: 407 if (is_com_initialized) 408 CoUninitialize(); 409 return ERROR_SUCCESS; 410 } 411 412 static bool remove_directory_recursive(MSIHANDLE installer, TCHAR path[MAX_PATH], unsigned int max_depth) 413 { 414 HANDLE find_handle; 415 WIN32_FIND_DATA find_data; 416 TCHAR *path_end; 417 418 if (!max_depth) { 419 log_messagef(installer, LOG_LEVEL_WARN, TEXT("Too many levels of nesting at \"%1\""), path); 420 return false; 421 } 422 423 path_end = path + _tcsnlen(path, MAX_PATH); 424 if (!PathAppend(path, TEXT("*.*"))) { 425 log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("PathAppend(\"%1\", \"*.*\") failed"), path); 426 return false; 427 } 428 find_handle = FindFirstFileEx(path, FindExInfoBasic, &find_data, FindExSearchNameMatch, NULL, 0); 429 if (find_handle == INVALID_HANDLE_VALUE) { 430 log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("FindFirstFileEx(\"%1\") failed"), path); 431 return false; 432 } 433 do { 434 if (find_data.cFileName[0] == TEXT('.') && (find_data.cFileName[1] == TEXT('\0') || (find_data.cFileName[1] == TEXT('.') && find_data.cFileName[2] == TEXT('\0')))) 435 continue; 436 437 path_end[0] = TEXT('\0'); 438 if (!PathAppend(path, find_data.cFileName)) { 439 log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("PathAppend(\"%1\", \"%2\") failed"), path, find_data.cFileName); 440 continue; 441 } 442 443 if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { 444 remove_directory_recursive(installer, path, max_depth - 1); 445 continue; 446 } 447 448 if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_READONLY) && !SetFileAttributes(path, find_data.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)) 449 log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("SetFileAttributes(\"%1\") failed"), path); 450 451 if (DeleteFile(path)) 452 log_messagef(installer, LOG_LEVEL_INFO, TEXT("Deleted \"%1\""), path); 453 else 454 log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("DeleteFile(\"%1\") failed"), path); 455 } while (FindNextFile(find_handle, &find_data)); 456 FindClose(find_handle); 457 458 path_end[0] = TEXT('\0'); 459 if (RemoveDirectory(path)) { 460 log_messagef(installer, LOG_LEVEL_INFO, TEXT("Removed \"%1\""), path); 461 return true; 462 } else { 463 log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("RemoveDirectory(\"%1\") failed"), path); 464 return false; 465 } 466 } 467 468 __declspec(dllexport) UINT __stdcall RemoveConfigFolder(MSIHANDLE installer) 469 { 470 LSTATUS ret; 471 TCHAR path[MAX_PATH]; 472 DWORD path_len = _countof(path); 473 bool is_com_initialized = SUCCEEDED(CoInitialize(NULL)); 474 475 ret = MsiGetProperty(installer, TEXT("CustomActionData"), path, &path_len); 476 if (ret != ERROR_SUCCESS) { 477 log_errorf(installer, LOG_LEVEL_WARN, ret, TEXT("MsiGetProperty(\"CustomActionData\") failed")); 478 goto out; 479 } 480 if (!path[0] || !PathAppend(path, TEXT("Data"))) 481 goto out; 482 remove_directory_recursive(installer, path, 10); 483 RegDeleteKey(HKEY_LOCAL_MACHINE, TEXT("Software\\WireGuard")); // Assumes no WOW. 484 out: 485 if (is_com_initialized) 486 CoUninitialize(); 487 return ERROR_SUCCESS; 488 } 489 490 __declspec(dllexport) UINT __stdcall RemoveAdapters(MSIHANDLE installer) 491 { 492 UINT ret; 493 bool is_com_initialized = SUCCEEDED(CoInitialize(NULL)); 494 TCHAR path[MAX_PATH]; 495 DWORD path_len = _countof(path); 496 HANDLE pipe; 497 char buf[0x200]; 498 DWORD offset = 0, size_read; 499 PROCESS_INFORMATION pi; 500 STARTUPINFO si = { 501 .cb = sizeof(STARTUPINFO), 502 .dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES, 503 .wShowWindow = SW_HIDE 504 }; 505 506 ret = MsiGetProperty(installer, TEXT("CustomActionData"), path, &path_len); 507 if (ret != ERROR_SUCCESS) { 508 log_errorf(installer, LOG_LEVEL_WARN, ret, TEXT("MsiGetProperty(\"CustomActionData\") failed")); 509 goto out; 510 } 511 if (!path[0] || !PathAppend(path, TEXT("wireguard.exe"))) 512 goto out; 513 514 if (!CreatePipe(&pipe, &si.hStdOutput, NULL, 0)) { 515 log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("CreatePipe failed")); 516 goto out; 517 } 518 if (!SetHandleInformation(si.hStdOutput, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) { 519 log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("SetHandleInformation failed")); 520 goto cleanup_pipe_w; 521 } 522 if (!CreateProcess(path, TEXT("wireguard /removedriver"), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) { 523 log_errorf(installer, LOG_LEVEL_WARN, GetLastError(), TEXT("Failed to create \"%1\" process"), path); 524 goto cleanup_pipe_w; 525 } 526 CloseHandle(si.hStdOutput); 527 buf[sizeof(buf) - 1] = '\0'; 528 while (ReadFile(pipe, buf + offset, sizeof(buf) - offset - 1, &size_read, NULL)) { 529 char *nl; 530 buf[offset + size_read] = '\0'; 531 nl = strchr(buf, '\n'); 532 if (!nl) { 533 offset = size_read; 534 continue; 535 } 536 nl[0] = '\0'; 537 log_messagef(installer, LOG_LEVEL_INFO, TEXT("%1!hs!"), buf); 538 offset = strlen(&nl[1]); 539 memmove(buf, &nl[1], offset); 540 } 541 WaitForSingleObject(pi.hProcess, INFINITE); 542 CloseHandle(pi.hProcess); 543 CloseHandle(pi.hThread); 544 goto cleanup_pipe_r; 545 546 cleanup_pipe_w: 547 CloseHandle(si.hStdOutput); 548 cleanup_pipe_r: 549 CloseHandle(pipe); 550 out: 551 if (is_com_initialized) 552 CoUninitialize(); 553 return ERROR_SUCCESS; 554 }