Change Kill Switch behavior

This commit is contained in:
Ludvig Strigeus 2018-09-11 21:10:10 +02:00
parent e499a3d4f7
commit 437c90b6bf
8 changed files with 102 additions and 103 deletions

View file

@ -18,6 +18,7 @@
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_SVE) #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_SVE)
LANGUAGE LANG_SWEDISH, SUBLANG_SWEDISH LANGUAGE LANG_SWEDISH, SUBLANG_SWEDISH
#pragma code_page(1252)
#ifdef APSTUDIO_INVOKED #ifdef APSTUDIO_INVOKED
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
@ -160,10 +161,11 @@ BEGIN
POPUP "Internet &Kill Switch" POPUP "Internet &Kill Switch"
BEGIN BEGIN
MENUITEM "&Off", IDSETT_BLOCKINTERNET_OFF MENUITEM "&Off", IDSETT_BLOCKINTERNET_OFF
MENUITEM SEPARATOR
MENUITEM "Yes, with &Routing Rules", IDSETT_BLOCKINTERNET_ROUTE MENUITEM "Yes, with &Routing Rules", IDSETT_BLOCKINTERNET_ROUTE
MENUITEM "Yes, with &Firewall Rules", IDSETT_BLOCKINTERNET_FIREWALL MENUITEM "Yes, with &Firewall Rules", IDSETT_BLOCKINTERNET_FIREWALL
MENUITEM "Yes, &Both Methods", IDSETT_BLOCKINTERNET_BOTH MENUITEM "Yes, &Both Methods", IDSETT_BLOCKINTERNET_BOTH
MENUITEM SEPARATOR
MENUITEM "Block While &Disconnected", IDSETT_BLOCKINTERNET_DISCONN
END END
POPUP "&Service Mode" POPUP "&Service Mode"
BEGIN BEGIN

View file

@ -1,3 +1,10 @@
2018-??-?? - TunSafe v1.4-rc2
Changes:
1.The kill switch is now remembered across computer restarts and
is deactivated when disconnecting. Without this behavior, the
kill switch is unusable when auto connecting on Windows startup.
2018-08-11 - TunSafe v1.4-rc1 2018-08-11 - TunSafe v1.4-rc1
Changes: Changes:

View file

@ -20,6 +20,7 @@
#include "util.h" #include "util.h"
#include <algorithm> #include <algorithm>
#include "network_win32_dnsblock.h" #include "network_win32_dnsblock.h"
#include "util_win32.h"
enum { enum {
HARD_MAXIMUM_QUEUE_SIZE = 102400, HARD_MAXIMUM_QUEUE_SIZE = 102400,
@ -27,13 +28,6 @@ enum {
MAX_BYTES_IN_UDP_OUT_QUEUE_SMALL = (256 + 64) * 1024, MAX_BYTES_IN_UDP_OUT_QUEUE_SMALL = (256 + 64) * 1024,
}; };
enum {
ROUTE_BLOCK_UNKNOWN = 0,
ROUTE_BLOCK_OFF = 1,
ROUTE_BLOCK_ON = 2,
ROUTE_BLOCK_PENDING = 3,
};
enum { enum {
kMetricNone = -1, kMetricNone = -1,
kMetricAutomatic = 0, kMetricAutomatic = 0,
@ -41,10 +35,13 @@ enum {
static uint8 internet_route_blocking_state; static uint8 internet_route_blocking_state;
static SLIST_HEADER freelist_head; static SLIST_HEADER freelist_head;
static HKEY g_hklm_reg_key;
static uint8 g_killswitch_curr = 255, g_killswitch_want;
bool g_allow_pre_post; bool g_allow_pre_post;
static InternetBlockState GetInternetBlockState(bool *is_activated); static InternetBlockState GetInternetBlockState(bool *is_activated);
static void DeactivateKillSwitch(InternetBlockState want);
Packet *AllocPacket() { Packet *AllocPacket() {
Packet *packet = (Packet*)InterlockedPopEntrySList(&freelist_head); Packet *packet = (Packet*)InterlockedPopEntrySList(&freelist_head);
@ -899,7 +896,6 @@ void ThreadedPacketQueue::Stop() {
CloseHandle(handle_); CloseHandle(handle_);
handle_ = NULL; handle_ = NULL;
} }
} }
void ThreadedPacketQueue::AbortingDriver() { void ThreadedPacketQueue::AbortingDriver() {
@ -1288,10 +1284,13 @@ bool TunWin32Adapter::InitAdapter(const TunInterface::TunConfig &&config, TunInt
} else { } else {
dns_blocker_->RestoreDns(); dns_blocker_->RestoreDns();
} }
// Cache the state of internet blocking
GetInternetBlockState(NULL);
uint8 ibs = config.internet_blocking; uint8 ibs = config.internet_blocking;
if (ibs == kBlockInternet_Default || ibs == kBlockInternet_DefaultOn) { if (ibs == kBlockInternet_Default || ibs == kBlockInternet_DefaultOn) {
uint8 new_ibs = GetInternetBlockState(NULL); uint8 new_ibs = (g_killswitch_want & (kBlockInternet_Firewall | kBlockInternet_Route));
ibs = (new_ibs == kBlockInternet_Off && ibs == kBlockInternet_DefaultOn) ? kBlockInternet_Firewall : new_ibs; ibs = (new_ibs == kBlockInternet_Off && ibs == kBlockInternet_DefaultOn) ? kBlockInternet_Firewall : new_ibs;
} }
@ -1331,18 +1330,18 @@ bool TunWin32Adapter::InitAdapter(const TunInterface::TunConfig &&config, TunInt
if (!AddMultipleCatchallRoutes(AF_INET6, 1, (uint8*)&dst, localhost_luid, NULL)) if (!AddMultipleCatchallRoutes(AF_INET6, 1, (uint8*)&dst, localhost_luid, NULL))
RERROR("Unable to add IPv6 routes for route based blocking."); RERROR("Unable to add IPv6 routes for route based blocking.");
} }
g_killswitch_curr |= kBlockInternet_Route;
} }
} }
internet_route_blocking_state = block_all_traffic_route + ROUTE_BLOCK_OFF;
if (ibs & kBlockInternet_Firewall) { if (ibs & kBlockInternet_Firewall) {
RINFO("Blocking all regular Internet traffic%s", ri.found_default_adapter ? " (except DHCP)" : ""); RINFO("Blocking all regular Internet traffic using firewall rules");
AddPersistentInternetBlocking(ri.found_default_adapter ? &ri.default_adapter : NULL, interface_luid_, config.ipv6_cidr != 0); if (AddKillSwitchFirewall(ri.found_default_adapter ? &ri.default_adapter : NULL, interface_luid_, config.ipv6_cidr != 0))
} else { g_killswitch_curr |= kBlockInternet_Firewall;
SetInternetFwBlockingState(false);
} }
DeactivateKillSwitch((InternetBlockState)ibs);
// Configure default route? // Configure default route?
if (config.use_ipv4_default_route) { if (config.use_ipv4_default_route) {
// Add a bypass route to the original gateway? // Add a bypass route to the original gateway?
@ -2040,8 +2039,10 @@ TunsafeBackendWin32::TunsafeBackendWin32(Delegate *delegate) : delegate_(delegat
last_tun_adapter_failed_ = 0; last_tun_adapter_failed_ = 0;
want_periodic_stats_ = false; want_periodic_stats_ = false;
internet_route_blocking_state = ROUTE_BLOCK_UNKNOWN; if (g_hklm_reg_key == NULL) {
ClearInternetFwBlockingStateCache(); RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\TunSafe", NULL, NULL, 0, KEY_ALL_ACCESS, NULL, &g_hklm_reg_key, NULL);
g_killswitch_want = RegReadInt(g_hklm_reg_key, "KillSwitch", 0);
}
delegate_->OnStateChanged(); delegate_->OnStateChanged();
} }
@ -2115,6 +2116,9 @@ void TunsafeBackendWin32::StopInner(bool is_restart) {
config_file_ = NULL; config_file_ = NULL;
is_started_ = false; is_started_ = false;
status_ = kStatusStopped; status_ = kStatusStopped;
if (!is_restart && !(g_killswitch_want & kBlockInternet_BlockOnDisconnect))
DeactivateKillSwitch(kBlockInternet_Off);
} }
} }
@ -2149,46 +2153,60 @@ LinearizedGraph *TunsafeBackendWin32::GetGraph(int type) {
return graph; return graph;
} }
static bool GetKillSwitchRouteActive() {
static uint8 GetInternetRouteBlockingState() { RouteInfo ri;
if (internet_route_blocking_state == ROUTE_BLOCK_UNKNOWN) { return (GetDefaultRouteAndDeleteOldRoutes(AF_INET, NULL, TRUE, NULL, &ri) && ri.found_null_routes == 2);
RouteInfo ri;
internet_route_blocking_state =
(GetDefaultRouteAndDeleteOldRoutes(AF_INET, NULL, TRUE, NULL, &ri) && ri.found_null_routes == 2) + ROUTE_BLOCK_OFF;
}
return internet_route_blocking_state;
} }
static void SetInternetRouteBlockingState(bool want) { static void RemoveKillSwitchRoute() {
if (want) { RouteInfo ri;
internet_route_blocking_state = ROUTE_BLOCK_PENDING; GetDefaultRouteAndDeleteOldRoutes(AF_INET, NULL, FALSE, NULL, &ri);
} else if (internet_route_blocking_state != ROUTE_BLOCK_OFF) { GetDefaultRouteAndDeleteOldRoutes(AF_INET6, NULL, FALSE, NULL, &ri);
RouteInfo ri;
GetDefaultRouteAndDeleteOldRoutes(AF_INET, NULL, FALSE, NULL, &ri);
GetDefaultRouteAndDeleteOldRoutes(AF_INET6, NULL, FALSE, NULL, &ri);
internet_route_blocking_state = ROUTE_BLOCK_OFF;
}
} }
static InternetBlockState GetInternetBlockState(bool *is_activated) { static InternetBlockState GetInternetBlockState(bool *is_activated) {
int a = GetInternetRouteBlockingState(); // Read the value only once.
int b = GetInternetFwBlockingState(); if (g_killswitch_curr == 255) {
g_killswitch_curr = GetKillSwitchRouteActive() * kBlockInternet_Route +
GetKillSwitchFirewallActive() * kBlockInternet_Firewall;
}
if (is_activated) if (is_activated)
*is_activated = (a == ROUTE_BLOCK_ON || b == IBS_ACTIVE); *is_activated = g_killswitch_curr != 0;
return (InternetBlockState)g_killswitch_want;
return (InternetBlockState)(
(a >= ROUTE_BLOCK_ON) * kBlockInternet_Route +
(b >= IBS_ACTIVE) * kBlockInternet_Firewall);
} }
InternetBlockState TunsafeBackendWin32::GetInternetBlockState(bool *is_activated) { InternetBlockState TunsafeBackendWin32::GetInternetBlockState(bool *is_activated) {
return ::GetInternetBlockState(is_activated); return ::GetInternetBlockState(is_activated);
} }
void TunsafeBackendWin32::SetInternetBlockState(InternetBlockState s) { static void DeactivateKillSwitch(InternetBlockState want) {
SetInternetRouteBlockingState((s & kBlockInternet_Route) != 0); // Disable blocking without reconnecting
SetInternetFwBlockingState((s & kBlockInternet_Firewall) != 0); int maybeon = g_killswitch_curr | g_killswitch_want;
if ((maybeon & kBlockInternet_Route) > (want & kBlockInternet_Route)) {
if (g_killswitch_curr & kBlockInternet_Route) {
g_killswitch_curr &= ~kBlockInternet_Route;
RINFO("Removing the routing rule internet block");
}
RemoveKillSwitchRoute();
}
if ((maybeon & kBlockInternet_Firewall) > (want & kBlockInternet_Firewall)) {
if (g_killswitch_curr & kBlockInternet_Firewall) {
g_killswitch_curr &= ~kBlockInternet_Firewall;
RINFO("Removing the firewall internet block");
}
RemoveKillSwitchFirewall();
}
}
void TunsafeBackendWin32::SetInternetBlockState(InternetBlockState want) {
GetInternetBlockState(NULL); // ensure cache is read
if (worker_thread_ == NULL && !(want & kBlockInternet_BlockOnDisconnect))
DeactivateKillSwitch(kBlockInternet_Off);
else
DeactivateKillSwitch(want);
g_killswitch_want = want;
RegWriteInt(g_hklm_reg_key, "KillSwitch", (int)want);
} }
void TunsafeBackendWin32::SetServiceStartupFlags(uint32 flags) { void TunsafeBackendWin32::SetServiceStartupFlags(uint32 flags) {

View file

@ -203,8 +203,6 @@ static bool RemovePersistentInternetBlockingInner(HANDLE handle) {
goto getout; goto getout;
} }
internet_fw_blocking_state = IBS_INACTIVE;
getout: getout:
if (enum_handle != NULL) { if (enum_handle != NULL) {
FwpmFilterDestroyEnumHandle0(handle, enum_handle); FwpmFilterDestroyEnumHandle0(handle, enum_handle);
@ -212,7 +210,7 @@ getout:
return false; return false;
} }
bool AddPersistentInternetBlocking(const NET_LUID *default_interface, const NET_LUID &luid_to_allow, bool also_ipv6) { bool AddKillSwitchFirewall(const NET_LUID *default_interface, const NET_LUID &luid_to_allow, bool also_ipv6) {
FWPM_SUBLAYER0 *sublayer_p = NULL; FWPM_SUBLAYER0 *sublayer_p = NULL;
FWP_BYTE_BLOB *fwp_appid = NULL; FWP_BYTE_BLOB *fwp_appid = NULL;
FWPM_FILTER0 filter; FWPM_FILTER0 filter;
@ -312,7 +310,6 @@ bool AddPersistentInternetBlocking(const NET_LUID *default_interface, const NET_
goto getout; goto getout;
success = true; success = true;
internet_fw_blocking_state = IBS_ACTIVE;
getout: getout:
if (handle != NULL) { if (handle != NULL) {
@ -327,7 +324,7 @@ getout:
return success; return success;
} }
static bool RemovePersistentInternetBlocking() { void RemoveKillSwitchFirewall() {
DWORD err; DWORD err;
HANDLE handle = NULL; HANDLE handle = NULL;
FWPM_SUBLAYER0 *sublayer_p = NULL; FWPM_SUBLAYER0 *sublayer_p = NULL;
@ -345,8 +342,6 @@ static bool RemovePersistentInternetBlocking() {
// The sublayer exists // The sublayer exists
FwpmFreeMemory0((void **)&sublayer_p); FwpmFreeMemory0((void **)&sublayer_p);
} else { } else {
// Sublayer does not exist
internet_fw_blocking_state = IBS_INACTIVE;
goto getout; goto getout;
} }
@ -357,17 +352,9 @@ getout:
FwpmEngineClose0(handle); FwpmEngineClose0(handle);
handle = NULL; handle = NULL;
} }
return false;
} }
void ClearInternetFwBlockingStateCache() { bool GetKillSwitchFirewallActive() {
internet_fw_blocking_state = 0;
}
uint8 GetInternetFwBlockingState() {
if (internet_fw_blocking_state != 0)
return internet_fw_blocking_state;
DWORD err; DWORD err;
HANDLE handle = NULL; HANDLE handle = NULL;
FWPM_SUBLAYER0 *sublayer_p = NULL; FWPM_SUBLAYER0 *sublayer_p = NULL;
@ -395,18 +382,5 @@ getout:
FwpmEngineClose0(handle); FwpmEngineClose0(handle);
handle = NULL; handle = NULL;
} }
return result;
return internet_fw_blocking_state = result + IBS_INACTIVE;
} }
void SetInternetFwBlockingState(bool want) {
uint8 old_state = GetInternetFwBlockingState();
if ((old_state >= IBS_ACTIVE) != want) {
if (!want) {
RemovePersistentInternetBlocking();
} else {
internet_fw_blocking_state = IBS_PENDING;
}
}
}

View file

@ -18,16 +18,6 @@ public:
bool also_ipv6_; bool also_ipv6_;
}; };
bool AddKillSwitchFirewall(const NET_LUID *default_interface, const NET_LUID &luid_to_allow, bool also_ipv6);
bool AddPersistentInternetBlocking(const NET_LUID *default_interface, const NET_LUID &luid_to_allow, bool also_ipv6); void RemoveKillSwitchFirewall();
bool GetKillSwitchFirewallActive();
enum {
IBS_UNKOWN,
IBS_INACTIVE,
IBS_ACTIVE,
IBS_PENDING,
};
void SetInternetFwBlockingState(bool want);
uint8 GetInternetFwBlockingState();
void ClearInternetFwBlockingStateCache();

View file

@ -27,6 +27,7 @@
#define IDSETT_SERVICE_BACKGROUND 23 #define IDSETT_SERVICE_BACKGROUND 23
#define IDSETT_SERVICE_CONNECT_AUTO 24 #define IDSETT_SERVICE_CONNECT_AUTO 24
#define IDSETT_SERVICE_MINIMIZE_AUTO 25 #define IDSETT_SERVICE_MINIMIZE_AUTO 25
#define IDSETT_BLOCKINTERNET_DISCONN 26
#define IDC_PAINTBOX 30 #define IDC_PAINTBOX 30
#define IDC_GRAPHBOX 31 #define IDC_GRAPHBOX 31
#define IDC_ADVANCEDBOX 32 #define IDC_ADVANCEDBOX 32
@ -40,12 +41,13 @@
#define IDC_PUBLIC_KEY 109 #define IDC_PUBLIC_KEY 109
#define IDC_TAB 110 #define IDC_TAB 110
// Next default values for new objects // Next default values for new objects
// //
#ifdef APSTUDIO_INVOKED #ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS #ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 113 #define _APS_NEXT_RESOURCE_VALUE 113
#define _APS_NEXT_COMMAND_VALUE 40023 #define _APS_NEXT_COMMAND_VALUE 40026
#define _APS_NEXT_CONTROL_VALUE 1016 #define _APS_NEXT_CONTROL_VALUE 1016
#define _APS_NEXT_SYMED_VALUE 101 #define _APS_NEXT_SYMED_VALUE 101
#endif #endif

View file

@ -123,7 +123,6 @@ static bool GetConfigFullName(const char *basename, char *fullname, size_t fulln
return true; return true;
} }
void StopTunsafeBackend(UpdateIconWhy why) { void StopTunsafeBackend(UpdateIconWhy why) {
if (g_backend->is_started()) { if (g_backend->is_started()) {
g_backend->Stop(); g_backend->Stop();
@ -916,20 +915,25 @@ static void HandleClickedItem(HWND hWnd, int wParam) {
case IDSETT_BLOCKINTERNET_FIREWALL: case IDSETT_BLOCKINTERNET_FIREWALL:
case IDSETT_BLOCKINTERNET_BOTH: case IDSETT_BLOCKINTERNET_BOTH:
{ {
InternetBlockState old_state = g_backend->GetInternetBlockState(NULL); uint32 old_state = g_backend->GetInternetBlockState(NULL);
InternetBlockState new_state = (InternetBlockState)(wParam - IDSETT_BLOCKINTERNET_OFF); uint32 new_state = wParam - IDSETT_BLOCKINTERNET_OFF;
if (old_state == kBlockInternet_Off && new_state != kBlockInternet_Off) { if ((old_state & kBlockInternet_Both) == kBlockInternet_Off && new_state != kBlockInternet_Off) {
if (MessageBoxA(g_ui_window, "Warning! All Internet traffic will be blocked until you restart your computer. Only traffic through TunSafe will be allowed.\r\n\r\nThe blocking is activated the next time you connect to a VPN server.\r\n\r\nDo you want to continue?", "TunSafe", MB_ICONWARNING | MB_OKCANCEL) == IDCANCEL) if (MessageBoxA(g_ui_window, "Warning! All Internet traffic will be blocked while TunSafe is active. Only traffic through TunSafe will be allowed.\r\n\r\nThe blocking is activated the next time TunSafe connects.\r\n\r\nDo you want to continue?", "TunSafe", MB_ICONWARNING | MB_OKCANCEL) == IDCANCEL)
return; return;
} }
g_backend->SetInternetBlockState((InternetBlockState)(new_state | (old_state & kBlockInternet_BlockOnDisconnect)));
g_backend->SetInternetBlockState(new_state);
if ((~old_state & new_state) && g_backend->is_started()) if ((~old_state & new_state) && g_backend->is_started())
StartTunsafeBackend(UIW_START); StartTunsafeBackend(UIW_START);
return; return;
} }
case IDSETT_BLOCKINTERNET_DISCONN: {
uint32 old_state = g_backend->GetInternetBlockState(NULL);
g_backend->SetInternetBlockState((InternetBlockState)(old_state ^ kBlockInternet_BlockOnDisconnect));
return;
}
case IDSETT_SERVICE_OFF: case IDSETT_SERVICE_OFF:
case IDSETT_SERVICE_FOREGROUND: case IDSETT_SERVICE_FOREGROUND:
case IDSETT_SERVICE_BACKGROUND: case IDSETT_SERVICE_BACKGROUND:
@ -1034,9 +1038,9 @@ static INT_PTR WINAPI DlgProc(HWND hWnd, UINT message, WPARAM wParam,
bool is_activated = false; bool is_activated = false;
int value = g_backend->GetInternetBlockState(&is_activated); int value = g_backend->GetInternetBlockState(&is_activated);
CheckMenuRadioItem(menu, IDSETT_BLOCKINTERNET_OFF, IDSETT_BLOCKINTERNET_BOTH, IDSETT_BLOCKINTERNET_OFF + value, MF_BYCOMMAND); CheckMenuRadioItem(menu, IDSETT_BLOCKINTERNET_OFF, IDSETT_BLOCKINTERNET_BOTH, IDSETT_BLOCKINTERNET_OFF + (value & kBlockInternet_Both), MF_BYCOMMAND);
CheckMenuRadioItem(menu, IDSETT_SERVICE_OFF, IDSETT_SERVICE_BACKGROUND, IDSETT_SERVICE_OFF + (g_startup_flags & 3), MF_BYCOMMAND); CheckMenuRadioItem(menu, IDSETT_SERVICE_OFF, IDSETT_SERVICE_BACKGROUND, IDSETT_SERVICE_OFF + (g_startup_flags & 3), MF_BYCOMMAND);
CheckMenuItem(menu, IDSETT_BLOCKINTERNET_DISCONN, (value & kBlockInternet_BlockOnDisconnect) ? MF_CHECKED : 0);
break; break;
} }

View file

@ -52,10 +52,12 @@ public:
}; };
enum InternetBlockState { enum InternetBlockState {
kBlockInternet_Off, kBlockInternet_Off = 0,
kBlockInternet_Route, kBlockInternet_Route = 1,
kBlockInternet_Firewall, kBlockInternet_Firewall = 2,
kBlockInternet_Both, kBlockInternet_Both = 3,
kBlockInternet_BlockOnDisconnect = 16,
// An unspecified value that uses either route or firewall // An unspecified value that uses either route or firewall
kBlockInternet_DefaultOn = 254, kBlockInternet_DefaultOn = 254,