/// <summary> /// Child process thread which will be executed upon calling the FirefoxPrivateNetwork.exe with the "broker" switch. /// </summary> /// <param name="parentPID">Parent process ID.</param> /// <param name="readPipeHandle">hWnd of the read pipe.</param> /// <param name="writePipeHandle">hWnd of the write pipe.</param> /// <returns>Process success result.</returns> public static bool ChildProcess(int parentPID, string readPipeHandle, string writePipeHandle) { // Grab the main app (parent) process based on the provided process ID Process parentProcess; try { parentProcess = Process.GetProcessById(parentPID); // Make sure we check if the process is active and responding if (!parentProcess.Responding) { ErrorHandling.ErrorHandler.Handle("Parent process not responding", ErrorHandling.LogLevel.Error); return(false); } } catch (ArgumentException e) { ErrorHandling.ErrorHandler.Handle(e, ErrorHandling.LogLevel.Error); return(false); } // Duplicate the read and write pipe handles from the main app (parent) so that this process (child) owns it IntPtr parentProcessHandle = parentProcess.Handle; var handleStatusRead = Kernel32.DuplicateHandle(parentProcessHandle, new IntPtr(long.Parse(readPipeHandle)), Process.GetCurrentProcess().Handle, out IntPtr brokerReadPipe, 0, false, (uint)DuplicateOptions.DUPLICATE_SAME_ACCESS | (uint)DuplicateOptions.DUPLICATE_CLOSE_SOURCE); var handleStatusWrite = Kernel32.DuplicateHandle(parentProcessHandle, new IntPtr(long.Parse(writePipeHandle)), Process.GetCurrentProcess().Handle, out IntPtr brokerWritePipe, 0, false, (uint)DuplicateOptions.DUPLICATE_SAME_ACCESS | (uint)DuplicateOptions.DUPLICATE_CLOSE_SOURCE); // Check if the duplication was successful if (!handleStatusRead || !handleStatusWrite) { ErrorHandling.ErrorHandler.Handle("Failure in creating read/write pipes", ErrorHandling.LogLevel.Error); return(false); } // Main child process loop - start the listener thread and listen for commands var ipc = new WireGuard.IPC(brokerReadPipe, brokerWritePipe); ipc.StartListenerThread(); // If the listener is not active, sleep for 1 second and wait until it does become active while (ipc.IsListenerActive()) { Thread.Sleep(TimeSpan.FromSeconds(1)); } return(true); }
/// <summary> /// Launches the broker process in an elevated form (with a UAC popup). /// </summary> /// <returns>True on successful broker launch, false otherwise.</returns> public bool LaunchChildProcess() { ErrorHandling.DebugLogger.LogDebugMsg("Broker process launch initiated"); // If the broker child process read/write pipes are already active, don't launch if ((masterReadPipe != IntPtr.Zero || masterWritePipe != IntPtr.Zero) && IsActive()) { ErrorHandling.DebugLogger.LogDebugMsg("Broker is already active, not launching a new one"); return(false); } IntPtr brokerReadPipe, brokerWritePipe; // No pipes are currently active, proceed to initiate pipes try { // Destroy any existing pipes DestroyPipes(); Kernel32.CreatePipe(out masterReadPipe, out brokerWritePipe, null, 0); Kernel32.CreatePipe(out brokerReadPipe, out masterWritePipe, null, 0); } catch (Exception e) { ErrorHandling.ErrorHandler.Handle(e, ErrorHandling.LogLevel.Error); return(false); } try { ErrorHandling.DebugLogger.LogDebugMsg("Broker process elevating"); // Run this exe with the "broker" option, with the current process ID and the read/write pipe handles // This runs the broker process bool runSuccess = false; try { if (UACShellExecute(Application.ExecutablePath, string.Format("broker {0} {1} {2}", Process.GetCurrentProcess().Id, brokerReadPipe, brokerWritePipe))) { // Successfully launched broker runSuccess = true; } else { ErrorHandling.ErrorHandler.Handle(new ErrorHandling.UserFacingMessage("toast-vpn-start-error"), ErrorHandling.UserFacingErrorType.Toast, ErrorHandling.UserFacingSeverity.ShowError, ErrorHandling.LogLevel.Error); } } catch (System.ComponentModel.Win32Exception) { ErrorHandling.ErrorHandler.Handle(new ErrorHandling.UserFacingMessage("toast-user-acess-control-error"), ErrorHandling.UserFacingErrorType.Toast, ErrorHandling.UserFacingSeverity.ShowNotice, ErrorHandling.LogLevel.Info); } catch (Exception) { ErrorHandling.ErrorHandler.Handle(new ErrorHandling.UserFacingMessage("toast-vpn-start-error"), ErrorHandling.UserFacingErrorType.Toast, ErrorHandling.UserFacingSeverity.ShowError, ErrorHandling.LogLevel.Error); } // Failure to run elevated broker process, destroy pipes and exit if (!runSuccess) { DestroyPipes(); return(false); } // Start the pipe listener thread brokerIPC = new WireGuard.IPC(masterReadPipe, masterWritePipe); brokerIPC.StartListenerThread(); // Process has been successfully started, but we need to wait until it becomes active var waitCounter = 0; while (!IsActive(false) && waitCounter++ < ChildProcessTimeoutSeconds * 10) { if (waitCounter % 10 == 0) { ErrorHandling.DebugLogger.LogDebugMsg("Waiting on broker to send heartbeat"); } Thread.Sleep(100); } if (!IsActive(false)) { ErrorHandling.ErrorHandler.Handle(new ErrorHandling.UserFacingMessage("toast-vpn-launch-error"), ErrorHandling.UserFacingErrorType.Toast, ErrorHandling.UserFacingSeverity.ShowError, ErrorHandling.LogLevel.Error); Manager.MainWindowViewModel.Status = Models.ConnectionState.Unprotected; return(false); } else { // Start the broker <-> main app checker thread var pipeStatusCheckProcess = new Thread(BrokerStatusCheckThread) { IsBackground = true, }; ReportHeartbeat(); pipeStatusCheckProcess.Start(); } ErrorHandling.DebugLogger.LogDebugMsg("Broker process launched"); } catch (Exception e) { DestroyPipes(); ErrorHandling.ErrorHandler.Handle(e, ErrorHandling.LogLevel.Error); return(false); } return(true); }