/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "windowsfirewall.h" //#include "logger.h" #include "leakdetector.h" //#include "windowscommons.h" //#include "../../daemon/interfaceconfig.h" //#include "../../ipaddress.h" //#include #include #include #include #include #include #include #include #include #include #include #include //#include "winsock.h" #include #include #include #define IPV6_ADDRESS_SIZE 16 // ID for the Firewall Sublayer DEFINE_GUID(ST_FW_WINFW_BASELINE_SUBLAYER_KEY, 0xc78056ff, 0x2bc1, 0x4211, 0xaa, 0xdd, 0x7f, 0x35, 0x8d, 0xef, 0x20, 0x2d); // ID for the Mullvad Split-Tunnel Sublayer Provider DEFINE_GUID(ST_FW_PROVIDER_KEY, 0xe2c114ee, 0xf32a, 0x4264, 0xa6, 0xcb, 0x3f, 0xa7, 0x99, 0x63, 0x56, 0xd9); namespace { //Logger logger(LOG_WINDOWS, "WindowsFirewall"); WindowsFirewall* s_instance = nullptr; // Note Filter Weight may be between 0-15! constexpr uint8_t LOW_WEIGHT = 0; constexpr uint8_t MED_WEIGHT = 7; constexpr uint8_t HIGH_WEIGHT = 13; constexpr uint8_t MAX_WEIGHT = 15; } // namespace WindowsFirewall* WindowsFirewall::instance() { if (s_instance == nullptr) { s_instance = new WindowsFirewall(qApp); } return s_instance; } WindowsFirewall::WindowsFirewall(QObject* parent) : QObject(parent) { MVPN_COUNT_CTOR(WindowsFirewall); Q_ASSERT(s_instance == nullptr); HANDLE engineHandle = NULL; DWORD result = ERROR_SUCCESS; // Use dynamic sessions for efficiency and safety: // -> Filtering policy objects are deleted even when the application crashes/ // deamon goes down FWPM_SESSION0 session; memset(&session, 0, sizeof(session)); session.flags = FWPM_SESSION_FLAG_DYNAMIC; qDebug() << "Opening the filter engine."; result = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &engineHandle); if (result != ERROR_SUCCESS) { qDebug()<<"FwpmEngineOpen0 failed "< Hyper-V outbound. filter.layerKey = FWPM_LAYER_OUTBOUND_MAC_FRAME_NATIVE; if (!enableFilter(&filter, title, "Permit Hyper-V => Hyper-V outbound")) { return false; } // #2 Permit Hyper-V => Hyper-V inbound. filter.layerKey = FWPM_LAYER_INBOUND_MAC_FRAME_NATIVE; if (!enableFilter(&filter, title, "Permit Hyper-V => Hyper-V inbound")) { return false; } return true; } bool WindowsFirewall::blockTrafficTo(const IPAddress& addr, uint8_t weight, const QString& title, const QString& peer) { QString description("Block traffic %1 %2 "); auto lower = addr.address(); auto upper = addr.broadcastAddress(); const bool isV4 = addr.type() == QAbstractSocket::IPv4Protocol; const GUID layerKeyOut = isV4 ? FWPM_LAYER_ALE_AUTH_CONNECT_V4 : FWPM_LAYER_ALE_AUTH_CONNECT_V6; const GUID layerKeyIn = isV4 ? FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 : FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6; // Assemble the Filter base FWPM_FILTER0 filter{}; memset(&filter, 0, sizeof(filter)); filter.action.type = FWP_ACTION_BLOCK; filter.weight.type = FWP_UINT8; filter.weight.uint8 = weight; filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY; FWPM_FILTER_CONDITION0 cond[1] = {0}; FWP_RANGE0 ipRange; QByteArray lowIpV6Buffer; QByteArray highIpV6Buffer; importAddress(lower, ipRange.valueLow, &lowIpV6Buffer); importAddress(upper, ipRange.valueHigh, &highIpV6Buffer); cond[0].fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS; cond[0].matchType = FWP_MATCH_RANGE; cond[0].conditionValue.type = FWP_RANGE_TYPE; cond[0].conditionValue.rangeValue = &ipRange; filter.numFilterConditions = 1; filter.filterCondition = cond; filter.layerKey = layerKeyOut; if (!enableFilter(&filter, title, description.arg("to").arg(addr.toString()), peer)) { return false; } filter.layerKey = layerKeyIn; if (!enableFilter(&filter, title, description.arg("from").arg(addr.toString()), peer)) { return false; } return true; } bool WindowsFirewall::blockTrafficTo(const QList& rangeList, uint8_t weight, const QString& title, const QString& peer) { for (auto range : rangeList) { if (!blockTrafficTo(range, weight, title, peer)) { qDebug() << "Setting Range of" << range.toString() << "failed"; return false; } } return true; } // Returns the Path of the Current Executable this runs in QString WindowsFirewall::getCurrentPath() { const unsigned char initValue = 0xff; QByteArray buffer(2048, initValue); auto ok = GetModuleFileNameA(NULL, buffer.data(), buffer.size()); if (ok == ERROR_INSUFFICIENT_BUFFER) { buffer.resize(buffer.size() * 2); ok = GetModuleFileNameA(NULL, buffer.data(), buffer.size()); } if (ok == 0) { //WindowsCommons::windowsLog("Err fetching dos path"); return ""; } return QString::fromLocal8Bit(buffer); } void WindowsFirewall::importAddress(const QHostAddress& addr, OUT FWP_VALUE0_& value, OUT QByteArray* v6DataBuffer) { const bool isV4 = addr.protocol() == QAbstractSocket::IPv4Protocol; if (isV4) { value.type = FWP_UINT32; value.uint32 = addr.toIPv4Address(); return; } auto v6bytes = addr.toIPv6Address(); v6DataBuffer->append((const char*)v6bytes.c, IPV6_ADDRESS_SIZE); value.type = FWP_BYTE_ARRAY16_TYPE; value.byteArray16 = (FWP_BYTE_ARRAY16*)v6DataBuffer->data(); } void WindowsFirewall::importAddress(const QHostAddress& addr, OUT FWP_CONDITION_VALUE0_& value, OUT QByteArray* v6DataBuffer) { const bool isV4 = addr.protocol() == QAbstractSocket::IPv4Protocol; if (isV4) { value.type = FWP_UINT32; value.uint32 = addr.toIPv4Address(); return; } auto v6bytes = addr.toIPv6Address(); v6DataBuffer->append((const char*)v6bytes.c, IPV6_ADDRESS_SIZE); value.type = FWP_BYTE_ARRAY16_TYPE; value.byteArray16 = (FWP_BYTE_ARRAY16*)v6DataBuffer->data(); } bool WindowsFirewall::blockTrafficOnPort(uint port, uint8_t weight, const QString& title) { // Allow Traffic to IP with PORT using any protocol FWPM_FILTER_CONDITION0 conds[3]; conds[0].fieldKey = FWPM_CONDITION_IP_PROTOCOL; conds[0].matchType = FWP_MATCH_EQUAL; conds[0].conditionValue.type = FWP_UINT8; conds[0].conditionValue.uint8 = (IPPROTO_UDP); conds[1].fieldKey = FWPM_CONDITION_IP_PROTOCOL; conds[1].matchType = FWP_MATCH_EQUAL; conds[1].conditionValue.type = FWP_UINT8; conds[1].conditionValue.uint8 = (IPPROTO_TCP); conds[2].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT; conds[2].matchType = FWP_MATCH_EQUAL; conds[2].conditionValue.type = FWP_UINT16; conds[2].conditionValue.uint16 = port; // Assemble the Filter base FWPM_FILTER0 filter; memset(&filter, 0, sizeof(filter)); filter.filterCondition = conds; filter.numFilterConditions = 3; filter.action.type = FWP_ACTION_BLOCK; filter.weight.type = FWP_UINT8; filter.weight.uint8 = weight; filter.subLayerKey = ST_FW_WINFW_BASELINE_SUBLAYER_KEY; QString description("Block %1 on Port %2"); filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V6; if (!enableFilter(&filter, title, description.arg("outgoing v6").arg(port))) { return false; } filter.layerKey = FWPM_LAYER_ALE_AUTH_CONNECT_V4; if (!enableFilter(&filter, title, description.arg("outgoing v4").arg(port))) { return false; } filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4; if (!enableFilter(&filter, title, description.arg("incoming v4").arg(port))) { return false; } filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6; if (!enableFilter(&filter, title, description.arg("incoming v6").arg(port))) { return false; } return true; } bool WindowsFirewall::enableFilter(FWPM_FILTER0* filter, const QString& title, const QString& description, const QString& peer) { uint64_t filterID = 0; auto name = title.toStdWString(); auto desc = description.toStdWString(); filter->displayData.name = (PWSTR)name.c_str(); filter->displayData.description = (PWSTR)desc.c_str(); auto result = FwpmFilterAdd0(m_sessionHandle, filter, NULL, &filterID); if (result != ERROR_SUCCESS) { qDebug() << "Failed to enable filter: " << title << " " << description<< "with result "< networkInterfaces = QNetworkInterface::allInterfaces(); for (const auto& iface : networkInterfaces) { if (iface.type() != QNetworkInterface::Loopback) { continue; } if (!allowTrafficOfAdapter(iface.index(), weight, title.arg(iface.name()))) { return false; } } return true; }