Windows killswitch strict mode backend part

This commit is contained in:
Mykola Baibuz 2024-12-30 22:50:33 +02:00
parent 212e9b3a91
commit 4d91245376
11 changed files with 378 additions and 201 deletions

View file

@ -29,6 +29,8 @@
#include "platforms/windows/windowsutils.h" #include "platforms/windows/windowsutils.h"
#include "winsock.h" #include "winsock.h"
#include "killswitch.h"
#define IPV6_ADDRESS_SIZE 16 #define IPV6_ADDRESS_SIZE 16
// ID for the Firewall Sublayer // ID for the Firewall Sublayer
@ -185,10 +187,23 @@ bool WindowsFirewall::enableKillSwitch(int vpnAdapterIndex) {
} }
logger.info() << "Enabling Killswitch Using Adapter:" << vpnAdapterIndex; logger.info() << "Enabling Killswitch Using Adapter:" << vpnAdapterIndex;
if (vpnAdapterIndex < 0)
{
IPAddress allv4("0.0.0.0/0");
if (!blockTrafficTo(allv4, MED_WEIGHT,
"Block Internet", "killswitch")) {
return false;
}
IPAddress allv6("::/0");
if (!blockTrafficTo(allv6, MED_WEIGHT,
"Block Internet", "killswitch")) {
return false;
}
} else
FW_OK(allowTrafficOfAdapter(vpnAdapterIndex, MED_WEIGHT, FW_OK(allowTrafficOfAdapter(vpnAdapterIndex, MED_WEIGHT,
"Allow usage of VPN Adapter")); "Allow usage of VPN Adapter"));
FW_OK(allowDHCPTraffic(MED_WEIGHT, "Allow DHCP Traffic")); FW_OK(allowDHCPTraffic(MED_WEIGHT, "Allow DHCP Traffic"));
FW_OK(allowHyperVTraffic(MED_WEIGHT, "Allow Hyper-V Traffic")); FW_OK(allowHyperVTraffic(MAX_WEIGHT, "Allow Hyper-V Traffic"));
FW_OK(allowTrafficForAppOnAll(getCurrentPath(), MAX_WEIGHT, FW_OK(allowTrafficForAppOnAll(getCurrentPath(), MAX_WEIGHT,
"Allow all for AmneziaVPN.exe")); "Allow all for AmneziaVPN.exe"));
FW_OK(blockTrafficOnPort(53, MED_WEIGHT, "Block all DNS")); FW_OK(blockTrafficOnPort(53, MED_WEIGHT, "Block all DNS"));
@ -287,37 +302,41 @@ bool WindowsFirewall::disablePeerTraffic(const QString& pubkey) {
} }
bool WindowsFirewall::disableKillSwitch() { bool WindowsFirewall::disableKillSwitch() {
auto result = FwpmTransactionBegin(m_sessionHandle, NULL); return KillSwitch::instance()->disableKillSwitch();
auto cleanup = qScopeGuard([&] { }
bool WindowsFirewall::allowAllTraffic() {
auto result = FwpmTransactionBegin(m_sessionHandle, NULL);
auto cleanup = qScopeGuard([&] {
if (result != ERROR_SUCCESS) {
FwpmTransactionAbort0(m_sessionHandle);
}
});
if (result != ERROR_SUCCESS) { if (result != ERROR_SUCCESS) {
FwpmTransactionAbort0(m_sessionHandle); logger.error() << "FwpmTransactionBegin0 failed. Return value:.\n"
<< result;
return false;
} }
});
if (result != ERROR_SUCCESS) {
logger.error() << "FwpmTransactionBegin0 failed. Return value:.\n"
<< result;
return false;
}
for (const auto& filterID : m_peerRules.values()) { for (const auto& filterID : m_peerRules.values()) {
FwpmFilterDeleteById0(m_sessionHandle, filterID); FwpmFilterDeleteById0(m_sessionHandle, filterID);
} }
for (const auto& filterID : qAsConst(m_activeRules)) { for (const auto& filterID : qAsConst(m_activeRules)) {
FwpmFilterDeleteById0(m_sessionHandle, filterID); FwpmFilterDeleteById0(m_sessionHandle, filterID);
} }
// Commit! // Commit!
result = FwpmTransactionCommit0(m_sessionHandle); result = FwpmTransactionCommit0(m_sessionHandle);
if (result != ERROR_SUCCESS) { if (result != ERROR_SUCCESS) {
logger.error() << "FwpmTransactionCommit0 failed. Return value:.\n" logger.error() << "FwpmTransactionCommit0 failed. Return value:.\n"
<< result; << result;
return false; return false;
} }
m_peerRules.clear(); m_peerRules.clear();
m_activeRules.clear(); m_activeRules.clear();
logger.debug() << "Firewall Disabled!"; logger.debug() << "Firewall Disabled!";
return true; return true;
} }
bool WindowsFirewall::allowTrafficForAppOnAll(const QString& exePath, bool WindowsFirewall::allowTrafficForAppOnAll(const QString& exePath,

View file

@ -35,6 +35,7 @@ class WindowsFirewall final : public QObject {
bool enablePeerTraffic(const InterfaceConfig& config); bool enablePeerTraffic(const InterfaceConfig& config);
bool disablePeerTraffic(const QString& pubkey); bool disablePeerTraffic(const QString& pubkey);
bool disableKillSwitch(); bool disableKillSwitch();
bool allowAllTraffic();
private: private:
WindowsFirewall(QObject* parent); WindowsFirewall(QObject* parent);

View file

@ -1,7 +1,7 @@
#include "secure_qsettings.h" #include "secure_qsettings.h"
#include "QAead.h" #include "../client/3rd/QSimpleCrypto/src/include/QAead.h"
#include "QBlockCipher.h" #include "../client/3rd/QSimpleCrypto/src/include/QBlockCipher.h"
#include "utilities.h" #include "utilities.h"
#include <QDataStream> #include <QDataStream>
#include <QDebug> #include <QDebug>

View file

@ -6,7 +6,7 @@
#include <QObject> #include <QObject>
#include <QSettings> #include <QSettings>
#include "keychain.h" #include "../client/3rd/qtkeychain/qtkeychain/keychain.h"
constexpr const char *settingsKeyTag = "settingsKeyTag"; constexpr const char *settingsKeyTag = "settingsKeyTag";
constexpr const char *settingsIvTag = "settingsIvTag"; constexpr const char *settingsIvTag = "settingsIvTag";

View file

@ -29,6 +29,7 @@ class IpcInterface
SLOT( void StopRoutingIpv6() ); SLOT( void StopRoutingIpv6() );
SLOT( bool disableKillSwitch() ); SLOT( bool disableKillSwitch() );
SLOT( bool disableAllTraffic() );
SLOT( bool enablePeerTraffic( const QJsonObject &configStr) ); SLOT( bool enablePeerTraffic( const QJsonObject &configStr) );
SLOT( bool enableKillSwitch( const QJsonObject &excludeAddr, int vpnAdapterIndex) ); SLOT( bool enableKillSwitch( const QJsonObject &excludeAddr, int vpnAdapterIndex) );
SLOT( bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) ); SLOT( bool updateResolvers(const QString& ifname, const QList<QHostAddress>& resolvers) );

View file

@ -8,21 +8,12 @@
#include "logger.h" #include "logger.h"
#include "router.h" #include "router.h"
#include "../core/networkUtilities.h" #include "killswitch.h"
#include "../client/protocols/protocols_defs.h"
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include "../client/platforms/windows/daemon/windowsdaemon.h"
#include "../client/platforms/windows/daemon/windowsfirewall.h"
#include "tapcontroller_win.h" #include "tapcontroller_win.h"
#endif #endif
#ifdef Q_OS_LINUX
#include "../client/platforms/linux/daemon/linuxfirewall.h"
#endif
#ifdef Q_OS_MACOS
#include "../client/platforms/macos/daemon/macosfirewall.h"
#endif
IpcServer::IpcServer(QObject *parent) : IpcInterfaceSource(parent) IpcServer::IpcServer(QObject *parent) : IpcInterfaceSource(parent)
@ -35,10 +26,6 @@ int IpcServer::createPrivilegedProcess()
qDebug() << "IpcServer::createPrivilegedProcess"; qDebug() << "IpcServer::createPrivilegedProcess";
#endif #endif
#ifdef Q_OS_WIN
WindowsFirewall::instance()->init();
#endif
m_localpid++; m_localpid++;
ProcessDescriptor pd(this); ProcessDescriptor pd(this);
@ -192,166 +179,22 @@ void IpcServer::setLogsEnabled(bool enabled)
} }
} }
bool IpcServer::disableAllTraffic()
{
return KillSwitch::instance()->disableAllTraffic();
}
bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex) bool IpcServer::enableKillSwitch(const QJsonObject &configStr, int vpnAdapterIndex)
{ {
#ifdef Q_OS_WIN return KillSwitch::instance()->enableKillSwitch(configStr, vpnAdapterIndex);
return WindowsFirewall::instance()->enableKillSwitch(vpnAdapterIndex);
#endif
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
int splitTunnelType = configStr.value("splitTunnelType").toInt();
QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray();
bool blockAll = 0;
bool allowNets = 0;
bool blockNets = 0;
QStringList allownets;
QStringList blocknets;
if (splitTunnelType == 0) {
blockAll = true;
allowNets = true;
allownets.append(configStr.value("vpnServer").toString());
} else if (splitTunnelType == 1) {
blockNets = true;
for (auto v : splitTunnelSites) {
blocknets.append(v.toString());
}
} else if (splitTunnelType == 2) {
blockAll = true;
allowNets = true;
allownets.append(configStr.value("vpnServer").toString());
for (auto v : splitTunnelSites) {
allownets.append(v.toString());
}
}
#endif
#ifdef Q_OS_LINUX
// double-check + ensure our firewall is installed and enabled
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), blockAll);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), allowNets);
LinuxFirewall::updateAllowNets(allownets);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), blockAll);
LinuxFirewall::updateBlockNets(blocknets);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), true);
QStringList dnsServers;
dnsServers.append(configStr.value(amnezia::config_key::dns1).toString());
dnsServers.append(configStr.value(amnezia::config_key::dns2).toString());
dnsServers.append("127.0.0.1");
dnsServers.append("127.0.0.53");
LinuxFirewall::updateDNSServers(dnsServers);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), true);
#endif
#ifdef Q_OS_MACOS
// double-check + ensure our firewall is installed and enabled. This is necessary as
// other software may disable pfctl before re-enabling with their own rules (e.g other VPNs)
if (!MacOSFirewall::isInstalled())
MacOSFirewall::install();
MacOSFirewall::ensureRootAnchorPriority();
MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), blockAll);
MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), allowNets);
MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), allowNets, QStringLiteral("allownets"), allownets);
MacOSFirewall::setAnchorEnabled(QStringLiteral("120.blockNets"), blockNets);
MacOSFirewall::setAnchorTable(QStringLiteral("120.blockNets"), blockNets, QStringLiteral("blocknets"), blocknets);
MacOSFirewall::setAnchorEnabled(QStringLiteral("200.allowVPN"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("290.allowDHCP"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("300.allowLAN"), true);
QStringList dnsServers;
dnsServers.append(configStr.value(amnezia::config_key::dns1).toString());
dnsServers.append(configStr.value(amnezia::config_key::dns2).toString());
MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), true);
MacOSFirewall::setAnchorTable(QStringLiteral("310.blockDNS"), true, QStringLiteral("dnsaddr"), dnsServers);
#endif
return true;
} }
bool IpcServer::disableKillSwitch() bool IpcServer::disableKillSwitch()
{ {
#ifdef Q_OS_WIN return KillSwitch::instance()->disableKillSwitch();
return WindowsFirewall::instance()->disableKillSwitch();
#endif
#ifdef Q_OS_LINUX
LinuxFirewall::uninstall();
#endif
#ifdef Q_OS_MACOS
MacOSFirewall::uninstall();
#endif
return true;
} }
bool IpcServer::enablePeerTraffic(const QJsonObject &configStr) bool IpcServer::enablePeerTraffic(const QJsonObject &configStr)
{ {
#ifdef Q_OS_WIN return KillSwitch::instance()->enablePeerTraffic(configStr);
InterfaceConfig config;
config.m_dnsServer = configStr.value(amnezia::config_key::dns1).toString();
config.m_serverPublicKey = "openvpn";
config.m_serverIpv4Gateway = configStr.value("vpnGateway").toString();
config.m_serverIpv4AddrIn = configStr.value("vpnServer").toString();
int vpnAdapterIndex = configStr.value("vpnAdapterIndex").toInt();
int inetAdapterIndex = configStr.value("inetAdapterIndex").toInt();
int splitTunnelType = configStr.value("splitTunnelType").toInt();
QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray();
QStringList AllowedIPAddesses;
// Use APP split tunnel
if (splitTunnelType == 0 || splitTunnelType == 2) {
config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("0.0.0.0"), 0));
config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("::"), 0));
}
if (splitTunnelType == 1) {
for (auto v : splitTunnelSites) {
QString ipRange = v.toString();
if (ipRange.split('/').size() > 1) {
config.m_allowedIPAddressRanges.append(
IPAddress(QHostAddress(ipRange.split('/')[0]), atoi(ipRange.split('/')[1].toLocal8Bit())));
} else {
config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress(ipRange), 32));
}
}
}
config.m_excludedAddresses.append(configStr.value("vpnServer").toString());
if (splitTunnelType == 2) {
for (auto v : splitTunnelSites) {
QString ipRange = v.toString();
config.m_excludedAddresses.append(ipRange);
}
}
for (const QJsonValue &i : configStr.value(amnezia::config_key::splitTunnelApps).toArray()) {
if (!i.isString()) {
break;
}
config.m_vpnDisabledApps.append(i.toString());
}
// killSwitch toggle
if (QVariant(configStr.value(amnezia::config_key::killSwitchOption).toString()).toBool()) {
WindowsFirewall::instance()->enablePeerTraffic(config);
}
WindowsDaemon::instance()->prepareActivation(config, inetAdapterIndex);
WindowsDaemon::instance()->activateSplitTunnel(config, vpnAdapterIndex);
#endif
return true;
} }

View file

@ -34,6 +34,7 @@ public:
virtual bool deleteTun(const QString &dev) override; virtual bool deleteTun(const QString &dev) override;
virtual void StartRoutingIpv6() override; virtual void StartRoutingIpv6() override;
virtual void StopRoutingIpv6() override; virtual void StopRoutingIpv6() override;
virtual bool disableAllTraffic() override;
virtual bool enablePeerTraffic(const QJsonObject &configStr) override; virtual bool enablePeerTraffic(const QJsonObject &configStr) override;
virtual bool enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapterIndex) override; virtual bool enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapterIndex) override;
virtual bool disableKillSwitch() override; virtual bool disableKillSwitch() override;

View file

@ -12,8 +12,47 @@ qt_standard_project_setup()
configure_file(${CMAKE_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h) configure_file(${CMAKE_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h)
set(QSIMPLECRYPTO_DIR ${CMAKE_CURRENT_LIST_DIR}/../../client/3rd/QSimpleCrypto/src)
set(OPENSSL_ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/../../client/3rd-prebuilt/3rd-prebuilt/openssl/")
set(OPENSSL_LIBRARIES_DIR "${OPENSSL_ROOT_DIR}/lib")
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/windows/include")
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/windows/win64/libssl.lib")
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win64/libcrypto.lib")
else()
set(OPENSSL_LIB_SSL_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libssl.lib")
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libcrypto.lib")
endif()
if(WIN32)
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/windows/include")
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win64/libcrypto.lib")
else()
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/windows/win32/libcrypto.lib")
endif()
elseif(APPLE AND NOT IOS)
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/macos/include")
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/macos/lib/libcrypto.a")
elseif(LINUX)
set(OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/linux/include")
set(OPENSSL_LIB_CRYPTO_PATH "${OPENSSL_ROOT_DIR}/linux/x86_64/libcrypto.a")
endif()
set(OPENSSL_USE_STATIC_LIBS TRUE)
include_directories(
${OPENSSL_INCLUDE_DIR}
${QSIMPLECRYPTO_DIR}
)
set(HEADERS set(HEADERS
${CMAKE_CURRENT_LIST_DIR}/../../client/utilities.h ${CMAKE_CURRENT_LIST_DIR}/../../client/utilities.h
${CMAKE_CURRENT_LIST_DIR}/../../client/secure_qsettings.h
${CMAKE_CURRENT_LIST_DIR}/../../client/core/networkUtilities.h ${CMAKE_CURRENT_LIST_DIR}/../../client/core/networkUtilities.h
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipc.h ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipc.h
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.h ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.h
@ -22,12 +61,20 @@ set(HEADERS
${CMAKE_CURRENT_LIST_DIR}/localserver.h ${CMAKE_CURRENT_LIST_DIR}/localserver.h
${CMAKE_CURRENT_LIST_DIR}/../../common/logger/logger.h ${CMAKE_CURRENT_LIST_DIR}/../../common/logger/logger.h
${CMAKE_CURRENT_LIST_DIR}/router.h ${CMAKE_CURRENT_LIST_DIR}/router.h
${CMAKE_CURRENT_LIST_DIR}/killswitch.h
${CMAKE_CURRENT_LIST_DIR}/systemservice.h ${CMAKE_CURRENT_LIST_DIR}/systemservice.h
${CMAKE_CURRENT_BINARY_DIR}/version.h ${CMAKE_CURRENT_BINARY_DIR}/version.h
${QSIMPLECRYPTO_DIR}/include/QAead.h
${QSIMPLECRYPTO_DIR}/include/QBlockCipher.h
${QSIMPLECRYPTO_DIR}/include/QRsa.h
${QSIMPLECRYPTO_DIR}/include/QSimpleCrypto_global.h
${QSIMPLECRYPTO_DIR}/include/QX509.h
${QSIMPLECRYPTO_DIR}/include/QX509Store.h
) )
set(SOURCES set(SOURCES
${CMAKE_CURRENT_LIST_DIR}/../../client/utilities.cpp ${CMAKE_CURRENT_LIST_DIR}/../../client/utilities.cpp
${CMAKE_CURRENT_LIST_DIR}/../../client/secure_qsettings.cpp
${CMAKE_CURRENT_LIST_DIR}/../../client/core/networkUtilities.cpp ${CMAKE_CURRENT_LIST_DIR}/../../client/core/networkUtilities.cpp
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.cpp ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserver.cpp
${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserverprocess.cpp ${CMAKE_CURRENT_LIST_DIR}/../../ipc/ipcserverprocess.cpp
@ -36,7 +83,13 @@ set(SOURCES
${CMAKE_CURRENT_LIST_DIR}/../../common/logger/logger.cpp ${CMAKE_CURRENT_LIST_DIR}/../../common/logger/logger.cpp
${CMAKE_CURRENT_LIST_DIR}/main.cpp ${CMAKE_CURRENT_LIST_DIR}/main.cpp
${CMAKE_CURRENT_LIST_DIR}/router.cpp ${CMAKE_CURRENT_LIST_DIR}/router.cpp
${CMAKE_CURRENT_LIST_DIR}/killswitch.cpp
${CMAKE_CURRENT_LIST_DIR}/systemservice.cpp ${CMAKE_CURRENT_LIST_DIR}/systemservice.cpp
${QSIMPLECRYPTO_DIR}/sources/QAead.cpp
${QSIMPLECRYPTO_DIR}/sources/QBlockCipher.cpp
${QSIMPLECRYPTO_DIR}/sources/QRsa.cpp
${QSIMPLECRYPTO_DIR}/sources/QX509.cpp
${QSIMPLECRYPTO_DIR}/sources/QX509Store.cpp
) )
# Mozilla headres # Mozilla headres
@ -132,6 +185,7 @@ if(WIN32)
set(SOURCES ${SOURCES} set(SOURCES ${SOURCES}
${CMAKE_CURRENT_LIST_DIR}/tapcontroller_win.cpp ${CMAKE_CURRENT_LIST_DIR}/tapcontroller_win.cpp
${CMAKE_CURRENT_LIST_DIR}/router_win.cpp ${CMAKE_CURRENT_LIST_DIR}/router_win.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/daemon/windowsdaemon.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/daemon/windowsdaemon.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/daemon/windowsdaemontunnel.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/daemon/windowsdaemontunnel.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/daemon/windowsfirewall.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/windows/daemon/windowsfirewall.cpp
@ -158,6 +212,8 @@ if(WIN32)
gdi32 gdi32
Advapi32 Advapi32
Kernel32 Kernel32
${OPENSSL_LIB_CRYPTO_PATH}
qt6keychain
) )
add_compile_definitions(_WINSOCKAPI_) add_compile_definitions(_WINSOCKAPI_)

View file

@ -0,0 +1,228 @@
#include "killswitch.h"
#include "../client/protocols/protocols_defs.h"
#include "qjsonarray.h"
#include "version.h"
#ifdef Q_OS_WIN
#include "../client/platforms/windows/daemon/windowsfirewall.h"
#include "../client/platforms/windows/daemon/windowsdaemon.h"
#endif
#ifdef Q_OS_LINUX
#include "../client/platforms/linux/daemon/linuxfirewall.h"
#endif
#ifdef Q_OS_MACOS
#include "../client/platforms/macos/daemon/macosfirewall.h"
#endif
KillSwitch* s_instance = nullptr;
KillSwitch* KillSwitch::instance()
{
if (s_instance == nullptr) {
s_instance = new KillSwitch(qApp);
}
return s_instance;
}
bool KillSwitch::init()
{
WindowsFirewall::instance()->init();
m_appSettigns = QSharedPointer<SecureQSettings>(new SecureQSettings(ORGANIZATION_NAME, APPLICATION_NAME, nullptr));
if (isStrictKillSwitchEnabled()) {
return disableAllTraffic();
}
}
bool KillSwitch::isStrictKillSwitchEnabled()
{
return m_appSettigns->value("Conf/strictKillSwitchEnabled", false).toBool();
}
bool KillSwitch::disableKillSwitch() {
if (isStrictKillSwitchEnabled()) {
return disableAllTraffic();
}
#ifdef Q_OS_WIN
return WindowsFirewall::instance()->allowAllTraffic();
#endif
#ifdef Q_OS_LINUX
LinuxFirewall::uninstall();
#endif
#ifdef Q_OS_MACOS
MacOSFirewall::uninstall();
#endif
}
bool KillSwitch::disableAllTraffic() {
#ifdef Q_OS_WIN
WindowsFirewall::instance()->enableKillSwitch(-1);
#endif
#ifdef Q_OS_LINUX
if (!LinuxFirewall::isInstalled()) {
LinuxFirewall::install();
}
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true);
#endif
#ifdef Q_OS_MACOS
// double-check + ensure our firewall is installed and enabled. This is necessary as
// other software may disable pfctl before re-enabling with their own rules (e.g other VPNs)
if (!MacOSFirewall::isInstalled())
MacOSFirewall::install();
MacOSFirewall::ensureRootAnchorPriority();
MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true);
#endif
return true;
}
bool KillSwitch::enablePeerTraffic(const QJsonObject &configStr) {
#ifdef Q_OS_WIN
InterfaceConfig config;
config.m_dnsServer = configStr.value(amnezia::config_key::dns1).toString();
config.m_serverPublicKey = "openvpn";
config.m_serverIpv4Gateway = configStr.value("vpnGateway").toString();
config.m_serverIpv4AddrIn = configStr.value("vpnServer").toString();
int vpnAdapterIndex = configStr.value("vpnAdapterIndex").toInt();
int inetAdapterIndex = configStr.value("inetAdapterIndex").toInt();
int splitTunnelType = configStr.value("splitTunnelType").toInt();
QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray();
// Use APP split tunnel
if (splitTunnelType == 0 || splitTunnelType == 2) {
config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("0.0.0.0"), 0));
config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress("::"), 0));
}
if (splitTunnelType == 1) {
for (auto v : splitTunnelSites) {
QString ipRange = v.toString();
if (ipRange.split('/').size() > 1) {
config.m_allowedIPAddressRanges.append(
IPAddress(QHostAddress(ipRange.split('/')[0]), atoi(ipRange.split('/')[1].toLocal8Bit())));
} else {
config.m_allowedIPAddressRanges.append(IPAddress(QHostAddress(ipRange), 32));
}
}
}
config.m_excludedAddresses.append(configStr.value("vpnServer").toString());
if (splitTunnelType == 2) {
for (auto v : splitTunnelSites) {
QString ipRange = v.toString();
config.m_excludedAddresses.append(ipRange);
}
}
for (const QJsonValue &i : configStr.value(amnezia::config_key::splitTunnelApps).toArray()) {
if (!i.isString()) {
break;
}
config.m_vpnDisabledApps.append(i.toString());
}
// killSwitch toggle
if (QVariant(configStr.value(amnezia::config_key::killSwitchOption).toString()).toBool()) {
WindowsFirewall::instance()->enablePeerTraffic(config);
}
WindowsDaemon::instance()->prepareActivation(config, inetAdapterIndex);
WindowsDaemon::instance()->activateSplitTunnel(config, vpnAdapterIndex);
#endif
return true;
}
bool KillSwitch::enableKillSwitch(const QJsonObject &excludeAddr, int vpnAdapterIndex) {
#ifdef Q_OS_WIN
return WindowsFirewall::instance()->enableKillSwitch(vpnAdapterIndex);
#endif
#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
int splitTunnelType = configStr.value("splitTunnelType").toInt();
QJsonArray splitTunnelSites = configStr.value("splitTunnelSites").toArray();
bool blockAll = 0;
bool allowNets = 0;
bool blockNets = 0;
QStringList allownets;
QStringList blocknets;
if (splitTunnelType == 0) {
blockAll = true;
allowNets = true;
allownets.append(configStr.value("vpnServer").toString());
} else if (splitTunnelType == 1) {
blockNets = true;
for (auto v : splitTunnelSites) {
blocknets.append(v.toString());
}
} else if (splitTunnelType == 2) {
blockAll = true;
allowNets = true;
allownets.append(configStr.value("vpnServer").toString());
for (auto v : splitTunnelSites) {
allownets.append(v.toString());
}
}
#endif
#ifdef Q_OS_LINUX
if (!LinuxFirewall::isInstalled()) {
LinuxFirewall::install();
// double-check + ensure our firewall is installed and enabled
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("000.allowLoopback"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("100.blockAll"), blockAll);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("110.allowNets"), allowNets);
LinuxFirewall::updateAllowNets(allownets);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("120.blockNets"), blockAll);
LinuxFirewall::updateBlockNets(blocknets);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("200.allowVPN"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv6, QStringLiteral("250.blockIPv6"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("290.allowDHCP"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("300.allowLAN"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("310.blockDNS"), true);
QStringList dnsServers;
dnsServers.append(configStr.value(amnezia::config_key::dns1).toString());
dnsServers.append(configStr.value(amnezia::config_key::dns2).toString());
dnsServers.append("127.0.0.1");
dnsServers.append("127.0.0.53");
LinuxFirewall::updateDNSServers(dnsServers);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::IPv4, QStringLiteral("320.allowDNS"), true);
LinuxFirewall::setAnchorEnabled(LinuxFirewall::Both, QStringLiteral("400.allowPIA"), true);
#endif
#ifdef Q_OS_MACOS
// double-check + ensure our firewall is installed and enabled. This is necessary as
// other software may disable pfctl before re-enabling with their own rules (e.g other VPNs)
if (!MacOSFirewall::isInstalled())
MacOSFirewall::install();
MacOSFirewall::ensureRootAnchorPriority();
MacOSFirewall::setAnchorEnabled(QStringLiteral("000.allowLoopback"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("100.blockAll"), blockAll);
MacOSFirewall::setAnchorEnabled(QStringLiteral("110.allowNets"), allowNets);
MacOSFirewall::setAnchorTable(QStringLiteral("110.allowNets"), allowNets, QStringLiteral("allownets"), allownets);
MacOSFirewall::setAnchorEnabled(QStringLiteral("120.blockNets"), blockNets);
MacOSFirewall::setAnchorTable(QStringLiteral("120.blockNets"), blockNets, QStringLiteral("blocknets"), blocknets);
MacOSFirewall::setAnchorEnabled(QStringLiteral("200.allowVPN"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("250.blockIPv6"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("290.allowDHCP"), true);
MacOSFirewall::setAnchorEnabled(QStringLiteral("300.allowLAN"), true);
QStringList dnsServers;
dnsServers.append(configStr.value(amnezia::config_key::dns1).toString());
dnsServers.append(configStr.value(amnezia::config_key::dns2).toString());
MacOSFirewall::setAnchorEnabled(QStringLiteral("310.blockDNS"), true);
MacOSFirewall::setAnchorTable(QStringLiteral("310.blockDNS"), true, QStringLiteral("dnsaddr"), dnsServers);
#endif
return true;
}

View file

@ -0,0 +1,27 @@
#ifndef KILLSWITCH_H
#define KILLSWITCH_H
#include <QJsonObject>
#include <QSharedPointer>
#include "secure_qsettings.h"
class KillSwitch : public QObject
{
Q_OBJECT
public:
static KillSwitch *instance();
bool init();
bool disableKillSwitch();
bool disableAllTraffic();
bool enablePeerTraffic( const QJsonObject &configStr);
bool enableKillSwitch( const QJsonObject &excludeAddr, int vpnAdapterIndex);
bool isStrictKillSwitchEnabled();
private:
KillSwitch(QObject* parent) {};
QSharedPointer<SecureQSettings> m_appSettigns;
};
#endif // KILLSWITCH_H

View file

@ -5,13 +5,12 @@
#include "ipc.h" #include "ipc.h"
#include "localserver.h" #include "localserver.h"
#include "utilities.h"
#include "router.h" #include "killswitch.h"
#include "logger.h" #include "logger.h"
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include "tapcontroller_win.h" #include "tapcontroller_win.h"
#endif #endif
namespace { namespace {
@ -47,6 +46,8 @@ LocalServer::LocalServer(QObject *parent) : QObject(parent),
return; return;
} }
KillSwitch::instance()->init();
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
// Signal handling for a proper shutdown. // Signal handling for a proper shutdown.
QObject::connect(qApp, &QCoreApplication::aboutToQuit, QObject::connect(qApp, &QCoreApplication::aboutToQuit,